From 92a5a5d41998fff45a9e2d67d4ba15d91c3b1e0f Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Mon, 20 Nov 2023 19:37:34 +0100 Subject: catfile: Introduce a state-machine based commit parser Currently we have multiple sources for parsing commit messages: 1. `catfile` package has `parser` which implements `ParseCommits` 2. `commit` package has `extractSignature` While the end usage of the two implementations may vary, they implement logic to parse a given commit message. Parsing commit messages while simple is ugly, because any errors can lead to vulnerabilities. Combined with the fact that commits are more human readable than machine readable, we need to ensure that our implementation is as close to git's own while maintaining readability. In this accord, implement a new parser in the `catfile` package, which borrows logic from the existing parser but uses a simple state machine to navigate and parse the commit. This new implementation also fixes a security bug wherein we were considering and parsing headers after the signature in the commit, while not considering the headers in the payload of the commit. This meant that malicious users could introduce headers into new malformed commits while copying the rest of the data from existing signed commits. The new commit would still be shown as signed by Gitaly since we were simply skipping the malformed data but the headers would be considered. In the following commits, we will also remove the other parsers. --- internal/git/catfile/parse_commit.go | 252 ++++++++++++++ internal/git/catfile/parse_commit_test.go | 531 ++++++++++++++++++++++++++++++ 2 files changed, 783 insertions(+) create mode 100644 internal/git/catfile/parse_commit.go create mode 100644 internal/git/catfile/parse_commit_test.go diff --git a/internal/git/catfile/parse_commit.go b/internal/git/catfile/parse_commit.go new file mode 100644 index 000000000..c79c0071f --- /dev/null +++ b/internal/git/catfile/parse_commit.go @@ -0,0 +1,252 @@ +package catfile + +import ( + "bytes" + "errors" + "fmt" + "io" + "strings" + + "gitlab.com/gitlab-org/gitaly/v16/internal/git" + "gitlab.com/gitlab-org/gitaly/v16/internal/helper" + "gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb" +) + +const ( + gpgSignaturePrefix = "gpgsig" + gpgSignaturePrefixSha256 = "gpgsig-sha256" +) + +// parseCommitState is a type used to define the current state while parsing +// a commit message. +// +// To understand the state macihne of parsing commit messages, we need +// to understand the different sections of a commit message. +// +// # Sections of a commit message +// +// Let's consider a sample commit message +// +// tree 798e5474fafac9754ee6b82ab17af8d70df4fbd3 +// parent 86f06b3f55e6334abb99fc168e2dd925895c4e49 +// author John doe 1699964265 +0100 +// committer John doe 1699964265 +0100 +// gpgsig -----BEGIN PGP SIGNATURE----- +// +// iHUEABYKAB0WIQReCOKeBZren2AFN0T+9BKLUsDX/wUCZVNlaQAKCRD+9BKLUsDX +// /219AP9j8jfQuLieg0Fl8xrOS74eJguYqIsPYI6lPDUvM5XmgQEAkhDUoWFd0ypR +// vXTEU/0CxcaXmlco/ThX2rCYwEUT6wA= +// =Wt+j +// -----END PGP SIGNATURE----- +// +// Commit subject +// +// With this, we can see that commit messages have +// - The first section consiting of headers. This is everything before +// commit message. In our example this consists of the tree, parent, +// author, committer and gpgsig. +// - The headers can also contain the signature. This is either gpgsig +// or gpgsig-256. +// - After the first line, all lines of the signature start with a ' ' +// space character. +// - Any headers post the signature are not parsed but will still be +// considered as part of the signature payload. +// - Post the headers, there is a newline to differentiate the upcoming +// commit body. The commit body consists of the commit subject and the +// message. +// +// Using this information we can now write a parser which represents a state +// machine which can be used to parse commits. +type parseCommitState uint + +const ( + parseCommitStateHeader parseCommitState = iota + parseCommitStateSignature + parseCommitStateUnexpected + parseCommitStateBody + parseCommitStateEnd +) + +// SignatureData holds the raw data used to validate a signed commit. +type SignatureData struct { + // Signatures refers to the signatures present in the commit. Note that + // Git only considers the first signature when parsing commits + Signatures [][]byte + // Payload refers to the commit data which is signed by the signature, + // generally this is everything apart from the signature in the commit. + // Headers present after the signature are not considered in the payload. + Payload []byte +} + +// Commit wraps the gitalypb.GitCommit structure and includes signature information. +type Commit struct { + *gitalypb.GitCommit + SignatureData SignatureData +} + +// parseCommit implements a state machine to parse the various sections +// of a commit. To understand the state machine, see the definition +// for parseState above. +// +// The goal is to maintain feature parity with how git [1] (see +// parse_buffer_signed_by_header()) itself parses commits. This ensures +// that we throw errors only wherever git does. +// +// [1]: https://gitlab.com/gitlab-org/git/-/blob/master/commit.c +func (p *parser) parseCommit(object git.Object) (*Commit, error) { + commit := &gitalypb.GitCommit{Id: object.ObjectID().String()} + var payload []byte + currentSignatureIndex := 0 + signatures := [][]byte{} + + bytesRemaining := object.ObjectSize() + p.bufferedReader.Reset(object) + + for state := parseCommitStateHeader; state != parseCommitStateEnd; { + receivedEOF := false + + line, err := p.bufferedReader.ReadString('\n') + if errors.Is(err, io.EOF) { + receivedEOF = true + } else if err != nil { + return nil, fmt.Errorf("parse raw commit: %w", err) + } + bytesRemaining -= int64(len(line)) + + // If the line only consists of a newline, we can skip + // the state to commit body. + if line == "\n" { + state = parseCommitStateBody + } + + switch state { + case parseCommitStateHeader: + key, value, ok := strings.Cut(line, " ") + if !ok { + // TODO: Current tests allow empty commits, we might want + // to change this behavior. + goto loopEnd + } + + // For headers, we trim the newline to make it easier + // to parse. + value = strings.TrimSuffix(value, "\n") + + switch key { + case "parent": + commit.ParentIds = append(commit.ParentIds, value) + case "author": + commit.Author = parseCommitAuthor(value) + case "committer": + commit.Committer = parseCommitAuthor(value) + case "tree": + commit.TreeId = value + case "encoding": + commit.Encoding = value + case gpgSignaturePrefix, gpgSignaturePrefixSha256: + // Since Git only considers the first signature, we only + // capture the first signature's type. + commit.SignatureType = detectSignatureType(value) + + state = parseCommitStateSignature + signatures = append(signatures, []byte(value+"\n")) + + goto loopEnd + } + + payload = append(payload, []byte(line)...) + + case parseCommitStateSignature: + if after, ok := strings.CutPrefix(line, " "); ok { + // All signature lines, must start with a ' ' (space). + signatures[currentSignatureIndex] = append(signatures[currentSignatureIndex], []byte(after)...) + goto loopEnd + } else { + currentSignatureIndex++ + + // Multiple signatures might be present in the commit. + if key, value, ok := strings.Cut(line, " "); ok { + if key == gpgSignaturePrefix || key == gpgSignaturePrefixSha256 { + signatures = append(signatures, []byte(value)) + goto loopEnd + } + } + + // If there is no ' ' (space), it means there is some unexpected + // data. + // + // Note that we don't go back to parsing headers. This is because + // any headers which are present after the signature are not parsed + // by Git as information. But, they still constitute to the signature + // payload. So any data after the signature and before the commit body + // is considered unexpected. + state = parseCommitStateUnexpected + } + + fallthrough + + case parseCommitStateUnexpected: + // If the line is only a newline, that means we have reached + // the commit body. If not, we keep looping till we do. + if line != "\n" { + payload = append(payload, []byte(line)...) + goto loopEnd + } + + fallthrough + + case parseCommitStateBody: + payload = append(payload, []byte(line)...) + + body := make([]byte, bytesRemaining) + if _, err := io.ReadFull(p.bufferedReader, body); err != nil { + return nil, fmt.Errorf("reading commit message: %w", err) + } + + // After we have copied the body, we must make sure that there really is no + // additional data. For once, this is to detect bugs in our implementation where we + // would accidentally have truncated the commit message. On the other hand, we also + // need to do this such that we observe the EOF, which we must observe in order to + // unblock reading the next object. + // + // This all feels a bit complicated, where it would be much easier to just read into + // a preallocated `bytes.Buffer`. But this complexity is indeed required to optimize + // allocations. So if you want to change this, please make sure to execute the + // `BenchmarkListAllCommits` benchmark. + if n, err := io.Copy(io.Discard, p.bufferedReader); err != nil { + return nil, fmt.Errorf("reading commit message: %w", err) + } else if n != 0 { + return nil, fmt.Errorf( + "commit message exceeds expected length %v by %v bytes", + object.ObjectSize(), n, + ) + } + + if len(body) > 0 { + commit.Subject = subjectFromBody(body) + commit.BodySize = int64(len(body)) + commit.Body = body + if max := helper.MaxCommitOrTagMessageSize; len(body) > max { + commit.Body = commit.Body[:max] + } + payload = append(payload, body...) + } + + state = parseCommitStateEnd + } + + loopEnd: + if receivedEOF { + state = parseCommitStateEnd + } + } + + for i, signature := range signatures { + signatures[i] = bytes.TrimSuffix(signature, []byte("\n")) + } + + return &Commit{ + GitCommit: commit, + SignatureData: SignatureData{Signatures: signatures, Payload: payload}, + }, nil +} diff --git a/internal/git/catfile/parse_commit_test.go b/internal/git/catfile/parse_commit_test.go new file mode 100644 index 000000000..e79c45ef3 --- /dev/null +++ b/internal/git/catfile/parse_commit_test.go @@ -0,0 +1,531 @@ +package catfile + +import ( + "fmt" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/require" + "gitlab.com/gitlab-org/gitaly/v16/internal/git" + "gitlab.com/gitlab-org/gitaly/v16/internal/git/gittest" + "gitlab.com/gitlab-org/gitaly/v16/internal/testhelper" + "gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb" + "google.golang.org/protobuf/types/known/timestamppb" +) + +const ( + pgpSignature = `-----BEGIN PGP SIGNATURE----- +Version: ObjectivePGP +Comment: https://www.objectivepgp.com +Charset: UTF-8 + +wsFcBAABCgAGBQJecon1AAoJEDYMjTn1G2THmSsP/At/jskLdF0i7p0nKf4JLjeeqRJ4k2IUg87U +ZwV6mbLo5XFm8Sq7CJBAGAhlOZE4BAwKALuawmgs5XMEZwK2z6AIgosGTVpmxDTTI11bXt4XIOdz +qF7c/gUrJOZzjFXOqDsd5UuPRupwznC5eKlLbfImR+NYxKryo8JGdF5t52ph4kChcQsKlSkXuYNI ++9UgbaMclEjb0OLm+mcP9QxW+Cs9JS2Jb4Jh6XONWW1nDN3ZTDDskguIqqF47UxIgSImrmpMcEj9 +YSNU0oMoHM4+1DoXp1t99EGPoAMvO+a5g8gd1jouCIrI6KOX+GeG/TFFM0mQwg/d/N9LR049m8ed +vgqg/lMiWUxQGL2IPpYPcgiUEqfn7ete+NMzQV5zstxF/q7Yj2BhM2L7FPHxKaoy/w5Q/DcAO4wN +5gxVmIvbCDk5JOx8I+boIS8ZxSvIlJ5IWaPrcjg5Mc40it+WHvMqxVnCzH0c6KcXaJ2SibVb59HR +pdRhEXXw/hRN65l/xwyM8sklQalAGu755gNJZ4k9ApBVUssZyiu+te2+bDirAcmK8/x1jvMQY6bn +DFxBE7bMHDp24IFPaVID84Ryt3vSSBEkrUGm7OkyDESTpHCr4sfD5o3LCUCIibTqv/CAhe59mhbB +2AXL7X+EzylKy6C1N5KUUiMTW94AuF6f8FqBoxnf +=U6zM +-----END PGP SIGNATURE-----` + + sshSignature = `-----BEGIN SSH SIGNATURE----- +U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgtc+Qk8jhMwVZk/jFEFCM16LNQb +30q5kK30bbetfjyTMAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5 +AAAAQADE1oOMKxqQu86XUQbhCoWx8GnnYHQ/i3mHdA0zPycIlDv8N6BRVDS6b0ja2Avj+s +uNvjRqSEGQJ4q6vhKOnQw= +-----END SSH SIGNATURE-----` +) + +func TestParseCommits(t *testing.T) { + t.Parallel() + + type setupData struct { + content string + oid git.ObjectID + expectedCommit *Commit + expectedErr error + } + + // Valid-but-interesting commits should be test at the FindCommit level. + // Invalid objects (that Git would complain about during fsck) can be + // tested here. + // + // Once a repository contains a pathological object it can be hard to get + // rid of it. Because of this I think it's nicer to ignore such objects + // than to throw hard errors. + for _, tc := range []struct { + desc string + setup func(t *testing.T) setupData + }{ + { + desc: "empty commit object", + setup: func(_ *testing.T) setupData { + return setupData{ + content: "", + oid: gittest.DefaultObjectHash.EmptyTreeOID, + expectedCommit: &Commit{ + GitCommit: &gitalypb.GitCommit{Id: gittest.DefaultObjectHash.EmptyTreeOID.String()}, + SignatureData: SignatureData{Signatures: [][]byte{}}, + }, + } + }, + }, + { + desc: "no email", + setup: func(_ *testing.T) setupData { + return setupData{ + content: "author Jane Doe", + oid: gittest.DefaultObjectHash.EmptyTreeOID, + expectedCommit: &Commit{ + GitCommit: &gitalypb.GitCommit{ + Id: gittest.DefaultObjectHash.EmptyTreeOID.String(), + Author: &gitalypb.CommitAuthor{Name: []byte("Jane Doe")}, + }, + SignatureData: SignatureData{Payload: []byte("author Jane Doe"), Signatures: [][]byte{}}, + }, + } + }, + }, + { + desc: "normal author", + setup: func(_ *testing.T) setupData { + return setupData{ + content: "author Au Thor 1625121079 +0000", + oid: gittest.DefaultObjectHash.EmptyTreeOID, + expectedCommit: &Commit{ + GitCommit: &gitalypb.GitCommit{ + Id: gittest.DefaultObjectHash.EmptyTreeOID.String(), + Author: &gitalypb.CommitAuthor{ + Name: []byte("Au Thor"), + Email: []byte("au.thor@example.com"), + Date: timestamppb.New(time.Unix(1625121079, 0)), + Timezone: []byte("+0000"), + }, + }, + SignatureData: SignatureData{Payload: []byte("author Au Thor 1625121079 +0000"), Signatures: [][]byte{}}, + }, + } + }, + }, + { + desc: "author with missing mail", + setup: func(_ *testing.T) setupData { + return setupData{ + content: "author Au Thor <> 1625121079 +0000", + oid: gittest.DefaultObjectHash.EmptyTreeOID, + expectedCommit: &Commit{ + GitCommit: &gitalypb.GitCommit{ + Id: gittest.DefaultObjectHash.EmptyTreeOID.String(), + Author: &gitalypb.CommitAuthor{ + Name: []byte("Au Thor"), + Date: timestamppb.New(time.Unix(1625121079, 0)), + Timezone: []byte("+0000"), + }, + }, + SignatureData: SignatureData{Payload: []byte("author Au Thor <> 1625121079 +0000"), Signatures: [][]byte{}}, + }, + } + }, + }, + { + desc: "author with missing date", + setup: func(_ *testing.T) setupData { + return setupData{ + content: "author Au Thor ", + oid: gittest.DefaultObjectHash.EmptyTreeOID, + expectedCommit: &Commit{ + GitCommit: &gitalypb.GitCommit{ + Id: gittest.DefaultObjectHash.EmptyTreeOID.String(), + Author: &gitalypb.CommitAuthor{ + Name: []byte("Au Thor"), + Email: []byte("au.thor@example.com"), + }, + }, + SignatureData: SignatureData{Payload: []byte("author Au Thor "), Signatures: [][]byte{}}, + }, + } + }, + }, + { + desc: "unmatched <", + setup: func(_ *testing.T) setupData { + return setupData{ + content: "author Jane Doe ", + setup: func(_ *testing.T) setupData { + return setupData{ + content: "author Jane Doe janedoe@example.com>", + oid: gittest.DefaultObjectHash.EmptyTreeOID, + expectedCommit: &Commit{ + GitCommit: &gitalypb.GitCommit{ + Id: gittest.DefaultObjectHash.EmptyTreeOID.String(), + Author: &gitalypb.CommitAuthor{Name: []byte("Jane Doe janedoe@example.com>")}, + }, + SignatureData: SignatureData{Payload: []byte("author Jane Doe janedoe@example.com>"), Signatures: [][]byte{}}, + }, + } + }, + }, + { + desc: "date too high", + setup: func(_ *testing.T) setupData { + return setupData{ + content: "author Jane Doe 9007199254740993 +0200", + oid: gittest.DefaultObjectHash.EmptyTreeOID, + expectedCommit: &Commit{ + GitCommit: &gitalypb.GitCommit{ + Id: gittest.DefaultObjectHash.EmptyTreeOID.String(), + Author: &gitalypb.CommitAuthor{ + Name: []byte("Jane Doe"), + Email: []byte("janedoe@example.com"), + Date: ×tamppb.Timestamp{Seconds: 9223371974719179007}, + Timezone: []byte("+0200"), + }, + }, + SignatureData: SignatureData{Payload: []byte("author Jane Doe 9007199254740993 +0200"), Signatures: [][]byte{}}, + }, + } + }, + }, + { + desc: "date negative", + setup: func(_ *testing.T) setupData { + return setupData{ + content: "author Jane Doe -1 +0200", + oid: gittest.DefaultObjectHash.EmptyTreeOID, + expectedCommit: &Commit{ + GitCommit: &gitalypb.GitCommit{ + Id: gittest.DefaultObjectHash.EmptyTreeOID.String(), + Author: &gitalypb.CommitAuthor{ + Name: []byte("Jane Doe"), + Email: []byte("janedoe@example.com"), + Date: ×tamppb.Timestamp{Seconds: 9223371974719179007}, + Timezone: []byte("+0200"), + }, + }, + SignatureData: SignatureData{Payload: []byte("author Jane Doe -1 +0200"), Signatures: [][]byte{}}, + }, + } + }, + }, + { + desc: "huge", + setup: func(_ *testing.T) setupData { + repeat := strings.Repeat("A", 100000) + content := "author " + repeat + + return setupData{ + content: content, + oid: gittest.DefaultObjectHash.EmptyTreeOID, + expectedCommit: &Commit{ + GitCommit: &gitalypb.GitCommit{ + Id: gittest.DefaultObjectHash.EmptyTreeOID.String(), + Author: &gitalypb.CommitAuthor{ + Name: []byte(repeat), + }, + }, + SignatureData: SignatureData{Payload: []byte(content), Signatures: [][]byte{}}, + }, + } + }, + }, + { + desc: "has encoding", + setup: func(_ *testing.T) setupData { + return setupData{ + content: "encoding Windows-1251", + oid: gittest.DefaultObjectHash.EmptyTreeOID, + expectedCommit: &Commit{ + GitCommit: &gitalypb.GitCommit{ + Id: gittest.DefaultObjectHash.EmptyTreeOID.String(), + Encoding: "Windows-1251", + }, + SignatureData: SignatureData{Payload: []byte("encoding Windows-1251"), Signatures: [][]byte{}}, + }, + } + }, + }, + { + desc: "PGP signed commit", + setup: func(_ *testing.T) setupData { + commitData, signedCommitData := createSignedCommitData(gpgSignaturePrefix, pgpSignature, "random commit message") + + return setupData{ + content: signedCommitData, + oid: gittest.DefaultObjectHash.EmptyTreeOID, + expectedCommit: &Commit{ + GitCommit: &gitalypb.GitCommit{ + Id: gittest.DefaultObjectHash.EmptyTreeOID.String(), + Author: &gitalypb.CommitAuthor{ + Name: []byte("Bug Fixer"), + Email: []byte("bugfixer@email.com"), + Date: ×tamppb.Timestamp{Seconds: 1584564725}, + Timezone: []byte("+0100"), + }, + Committer: &gitalypb.CommitAuthor{ + Name: []byte("Bug Fixer"), + Email: []byte("bugfixer@email.com"), + Date: ×tamppb.Timestamp{Seconds: 1584564725}, + Timezone: []byte("+0100"), + }, + SignatureType: gitalypb.SignatureType_PGP, + TreeId: gittest.DefaultObjectHash.EmptyTreeOID.String(), + Subject: []byte("random commit message"), + Body: []byte("random commit message\n"), + BodySize: 22, + }, + SignatureData: SignatureData{Payload: []byte(commitData), Signatures: [][]byte{[]byte(pgpSignature)}}, + }, + } + }, + }, + { + desc: "PGP SHA256-signed signed commit", + setup: func(_ *testing.T) setupData { + commitData, signedCommitData := createSignedCommitData(gpgSignaturePrefixSha256, pgpSignature, "random commit message") + + return setupData{ + content: signedCommitData, + oid: gittest.DefaultObjectHash.EmptyTreeOID, + expectedCommit: &Commit{ + GitCommit: &gitalypb.GitCommit{ + Id: gittest.DefaultObjectHash.EmptyTreeOID.String(), + Author: &gitalypb.CommitAuthor{ + Name: []byte("Bug Fixer"), + Email: []byte("bugfixer@email.com"), + Date: ×tamppb.Timestamp{Seconds: 1584564725}, + Timezone: []byte("+0100"), + }, + Committer: &gitalypb.CommitAuthor{ + Name: []byte("Bug Fixer"), + Email: []byte("bugfixer@email.com"), + Date: ×tamppb.Timestamp{Seconds: 1584564725}, + Timezone: []byte("+0100"), + }, + SignatureType: gitalypb.SignatureType_PGP, + TreeId: gittest.DefaultObjectHash.EmptyTreeOID.String(), + Subject: []byte("random commit message"), + Body: []byte("random commit message\n"), + BodySize: 22, + }, + SignatureData: SignatureData{Payload: []byte(commitData), Signatures: [][]byte{[]byte(pgpSignature)}}, + }, + } + }, + }, + { + desc: "SSH signed commit", + setup: func(_ *testing.T) setupData { + commitData, signedCommitData := createSignedCommitData(gpgSignaturePrefix, sshSignature, "random commit message") + + return setupData{ + content: signedCommitData, + oid: gittest.DefaultObjectHash.EmptyTreeOID, + expectedCommit: &Commit{ + GitCommit: &gitalypb.GitCommit{ + Id: gittest.DefaultObjectHash.EmptyTreeOID.String(), + Author: &gitalypb.CommitAuthor{ + Name: []byte("Bug Fixer"), + Email: []byte("bugfixer@email.com"), + Date: ×tamppb.Timestamp{Seconds: 1584564725}, + Timezone: []byte("+0100"), + }, + Committer: &gitalypb.CommitAuthor{ + Name: []byte("Bug Fixer"), + Email: []byte("bugfixer@email.com"), + Date: ×tamppb.Timestamp{Seconds: 1584564725}, + Timezone: []byte("+0100"), + }, + SignatureType: gitalypb.SignatureType_SSH, + TreeId: gittest.DefaultObjectHash.EmptyTreeOID.String(), + Subject: []byte("random commit message"), + Body: []byte("random commit message\n"), + BodySize: 22, + }, + SignatureData: SignatureData{Payload: []byte(commitData), Signatures: [][]byte{[]byte(sshSignature)}}, + }, + } + }, + }, + { + desc: "garbage signed commit", + setup: func(_ *testing.T) setupData { + _, signedCommitData := createSignedCommitData("gpgsig-garbage", sshSignature, "garbage-signed commit message") + + return setupData{ + content: signedCommitData, + oid: gittest.DefaultObjectHash.EmptyTreeOID, + expectedCommit: &Commit{ + GitCommit: &gitalypb.GitCommit{ + Id: gittest.DefaultObjectHash.EmptyTreeOID.String(), + Author: &gitalypb.CommitAuthor{ + Name: []byte("Bug Fixer"), + Email: []byte("bugfixer@email.com"), + Date: ×tamppb.Timestamp{Seconds: 1584564725}, + Timezone: []byte("+0100"), + }, + Committer: &gitalypb.CommitAuthor{ + Name: []byte("Bug Fixer"), + Email: []byte("bugfixer@email.com"), + Date: ×tamppb.Timestamp{Seconds: 1584564725}, + Timezone: []byte("+0100"), + }, + TreeId: gittest.DefaultObjectHash.EmptyTreeOID.String(), + Subject: []byte("garbage-signed commit message"), + Body: []byte("garbage-signed commit message\n"), + BodySize: 30, + }, + SignatureData: SignatureData{Payload: []byte(signedCommitData), Signatures: [][]byte{}}, + }, + } + }, + }, + { + desc: "commits with multiple signatures", + setup: func(_ *testing.T) setupData { + commitData, signedCommitData := createSignedCommitData("gpgsig", pgpSignature, "commit message") + + signatureLines := strings.Split(sshSignature, "\n") + for i, signatureLine := range signatureLines { + signatureLines[i] = " " + signatureLine + } + + signedCommitData = strings.Replace(signedCommitData, "\n\n", fmt.Sprintf("\n%s%s\n\n", "gpgsig-sha256", strings.Join(signatureLines, "\n")), 1) + + return setupData{ + content: signedCommitData, + oid: gittest.DefaultObjectHash.EmptyTreeOID, + expectedCommit: &Commit{ + GitCommit: &gitalypb.GitCommit{ + Id: gittest.DefaultObjectHash.EmptyTreeOID.String(), + Author: &gitalypb.CommitAuthor{ + Name: []byte("Bug Fixer"), + Email: []byte("bugfixer@email.com"), + Date: ×tamppb.Timestamp{Seconds: 1584564725}, + Timezone: []byte("+0100"), + }, + Committer: &gitalypb.CommitAuthor{ + Name: []byte("Bug Fixer"), + Email: []byte("bugfixer@email.com"), + Date: ×tamppb.Timestamp{Seconds: 1584564725}, + Timezone: []byte("+0100"), + }, + SignatureType: gitalypb.SignatureType_PGP, + TreeId: gittest.DefaultObjectHash.EmptyTreeOID.String(), + Subject: []byte("commit message"), + Body: []byte("commit message\n"), + BodySize: 15, + }, + SignatureData: SignatureData{Payload: []byte(commitData), Signatures: [][]byte{[]byte(pgpSignature), []byte(sshSignature)}}, + }, + } + }, + }, + { + desc: "PGP signed commit with headers after the signature", + setup: func(_ *testing.T) setupData { + commitMessage := "random commit message" + commitData, signedCommitData := createSignedCommitData(gpgSignaturePrefix, pgpSignature, "random commit message") + + // Headers after the signature shouldn't be ignored and should be + // part of the commit data. If ignored, malicious users can draft a + // commit similar to a previously signed commit but with additional + // headers and the new commit would still show as verified since we + // won't add the additional headers to commit data. + postSignatureHeader := fmt.Sprintf("parent %s", gittest.DefaultObjectHash.EmptyTreeOID) + modifyCommitData := func(data string) string { + return strings.Replace( + data, + fmt.Sprintf("\n%s", commitMessage), + fmt.Sprintf("%s\n\n%s", postSignatureHeader, commitMessage), + 1, + ) + } + + // We expect that these post signature headers are added to the commit data, + // that way any modified commit will fail verification. + signedCommitData = modifyCommitData(signedCommitData) + commitData = modifyCommitData(commitData) + + return setupData{ + content: signedCommitData, + oid: gittest.DefaultObjectHash.EmptyTreeOID, + expectedCommit: &Commit{ + GitCommit: &gitalypb.GitCommit{ + Id: gittest.DefaultObjectHash.EmptyTreeOID.String(), + Author: &gitalypb.CommitAuthor{ + Name: []byte("Bug Fixer"), + Email: []byte("bugfixer@email.com"), + Date: ×tamppb.Timestamp{Seconds: 1584564725}, + Timezone: []byte("+0100"), + }, + Committer: &gitalypb.CommitAuthor{ + Name: []byte("Bug Fixer"), + Email: []byte("bugfixer@email.com"), + Date: ×tamppb.Timestamp{Seconds: 1584564725}, + Timezone: []byte("+0100"), + }, + SignatureType: gitalypb.SignatureType_PGP, + TreeId: gittest.DefaultObjectHash.EmptyTreeOID.String(), + Subject: []byte("random commit message"), + Body: []byte("random commit message\n"), + BodySize: 22, + }, + SignatureData: SignatureData{Payload: []byte(commitData), Signatures: [][]byte{[]byte(pgpSignature)}}, + }, + } + }, + }, + } { + tc := tc + + t.Run(tc.desc, func(t *testing.T) { + t.Parallel() + + setup := tc.setup(t) + + commit, err := newParser().parseCommit(newStaticObject(setup.content, "commit", setup.oid)) + require.Equal(t, setup.expectedErr, err) + testhelper.ProtoEqual(t, setup.expectedCommit.GitCommit, commit.GitCommit) + require.Equal(t, setup.expectedCommit.SignatureData.Payload, commit.SignatureData.Payload) + require.Equal(t, setup.expectedCommit.SignatureData.Signatures, commit.SignatureData.Signatures) + }) + } +} + +func createSignedCommitData(signatureField, signature, commitMessage string) (string, string) { + commitData := fmt.Sprintf(`tree %s +author Bug Fixer 1584564725 +0100 +committer Bug Fixer 1584564725 +0100 + +%s +`, gittest.DefaultObjectHash.EmptyTreeOID, commitMessage) + + // Each line of the signature needs to start with a space so that Git recognizes it as a continuation of the + // field. + signatureLines := strings.Split(signature, "\n") + for i, signatureLine := range signatureLines { + signatureLines[i] = " " + signatureLine + } + + signedCommitData := strings.Replace(commitData, "\n\n", fmt.Sprintf("\n%s%s\n\n", signatureField, strings.Join(signatureLines, "\n")), 1) + + return commitData, signedCommitData +} -- cgit v1.2.3 From bb179faf54b7defe191ba6efcd7815b593669609 Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Mon, 20 Nov 2023 20:31:48 +0100 Subject: catfile: Replace the previous implementation of ParseCommit In the previous commit, we introduced a state-machine based `parseCommit()`. Let's replace the existing `ParseCommit()` function with the one we introduced. This involves changing the interface to now return `catfile.Commit` instead of the previous `gitalypb.GitCommit`. --- internal/git/catfile/commit.go | 4 +- internal/git/catfile/commit_test.go | 7 +- internal/git/catfile/parse_commit.go | 4 +- internal/git/catfile/parse_commit_test.go | 2 +- internal/git/catfile/parser.go | 92 +--------- internal/git/catfile/parser_test.go | 199 --------------------- internal/git/catfile/tag.go | 10 +- internal/git/localrepo/commit.go | 5 +- internal/git/log/last_commit.go | 2 +- internal/git/log/parser.go | 2 +- internal/gitaly/service/commit/find_commits.go | 2 +- .../gitaly/service/commit/last_commit_for_path.go | 2 +- internal/gitaly/service/commit/list_all_commits.go | 2 +- internal/gitaly/service/commit/list_commits.go | 2 +- .../gitaly/service/commit/list_commits_by_oid.go | 2 +- .../service/commit/list_commits_by_ref_name.go | 2 +- .../service/commit/list_last_commits_for_tree.go | 2 +- internal/gitaly/service/operations/tags.go | 2 +- internal/gitaly/service/ref/find_all_tags.go | 5 +- internal/gitaly/service/ref/find_tag.go | 2 +- internal/gitaly/service/ref/util.go | 4 +- 21 files changed, 37 insertions(+), 317 deletions(-) diff --git a/internal/git/catfile/commit.go b/internal/git/catfile/commit.go index 0af44fee8..94277bb10 100644 --- a/internal/git/catfile/commit.go +++ b/internal/git/catfile/commit.go @@ -14,7 +14,7 @@ import ( ) // GetCommit looks up a commit by revision using an existing Batch instance. -func GetCommit(ctx context.Context, objectReader ObjectContentReader, revision git.Revision) (*gitalypb.GitCommit, error) { +func GetCommit(ctx context.Context, objectReader ObjectContentReader, revision git.Revision) (*Commit, error) { object, err := objectReader.Object(ctx, revision+"^{commit}") if err != nil { return nil, err @@ -63,7 +63,7 @@ func GetCommitWithTrailers( } } - return commit, nil + return commit.GitCommit, nil } // GetCommitMessage looks up a commit message and returns it in its entirety. diff --git a/internal/git/catfile/commit_test.go b/internal/git/catfile/commit_test.go index 55d6012d9..d4a51457e 100644 --- a/internal/git/catfile/commit_test.go +++ b/internal/git/catfile/commit_test.go @@ -55,8 +55,11 @@ func TestGetCommit(t *testing.T) { } { t.Run(tc.desc, func(t *testing.T) { commit, err := GetCommit(ctx, objectReader, git.Revision(tc.revision)) - require.Equal(t, tc.expectedErr, err) - testhelper.ProtoEqual(t, tc.expectedCommit, commit) + if tc.expectedErr != nil || err != nil { + require.Equal(t, tc.expectedErr, err) + } else { + testhelper.ProtoEqual(t, tc.expectedCommit, commit.GitCommit) + } }) } } diff --git a/internal/git/catfile/parse_commit.go b/internal/git/catfile/parse_commit.go index c79c0071f..1b22d9d4a 100644 --- a/internal/git/catfile/parse_commit.go +++ b/internal/git/catfile/parse_commit.go @@ -84,7 +84,7 @@ type Commit struct { SignatureData SignatureData } -// parseCommit implements a state machine to parse the various sections +// ParseCommit implements a state machine to parse the various sections // of a commit. To understand the state machine, see the definition // for parseState above. // @@ -93,7 +93,7 @@ type Commit struct { // that we throw errors only wherever git does. // // [1]: https://gitlab.com/gitlab-org/git/-/blob/master/commit.c -func (p *parser) parseCommit(object git.Object) (*Commit, error) { +func (p *parser) ParseCommit(object git.Object) (*Commit, error) { commit := &gitalypb.GitCommit{Id: object.ObjectID().String()} var payload []byte currentSignatureIndex := 0 diff --git a/internal/git/catfile/parse_commit_test.go b/internal/git/catfile/parse_commit_test.go index e79c45ef3..2e80dc38f 100644 --- a/internal/git/catfile/parse_commit_test.go +++ b/internal/git/catfile/parse_commit_test.go @@ -501,7 +501,7 @@ func TestParseCommits(t *testing.T) { setup := tc.setup(t) - commit, err := newParser().parseCommit(newStaticObject(setup.content, "commit", setup.oid)) + commit, err := NewParser().ParseCommit(newStaticObject(setup.content, "commit", setup.oid)) require.Equal(t, setup.expectedErr, err) testhelper.ProtoEqual(t, setup.expectedCommit.GitCommit, commit.GitCommit) require.Equal(t, setup.expectedCommit.SignatureData.Payload, commit.SignatureData.Payload) diff --git a/internal/git/catfile/parser.go b/internal/git/catfile/parser.go index 5df14954d..b64fe16eb 100644 --- a/internal/git/catfile/parser.go +++ b/internal/git/catfile/parser.go @@ -11,14 +11,13 @@ import ( "time" "gitlab.com/gitlab-org/gitaly/v16/internal/git" - "gitlab.com/gitlab-org/gitaly/v16/internal/helper" "gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb" "google.golang.org/protobuf/types/known/timestamppb" ) // Parser parses Git objects into their gitalypb representations. type Parser interface { - ParseCommit(object git.Object) (*gitalypb.GitCommit, error) + ParseCommit(object git.Object) (*Commit, error) ParseTag(object git.Object) (*gitalypb.Tag, error) } @@ -37,95 +36,6 @@ func newParser() *parser { } } -// ParseCommit parses the commit data from the Reader. -func (p *parser) ParseCommit(object git.Object) (*gitalypb.GitCommit, error) { - commit := &gitalypb.GitCommit{Id: object.ObjectID().String()} - - var lastLine bool - p.bufferedReader.Reset(object) - - bytesRemaining := object.ObjectSize() - for !lastLine { - line, err := p.bufferedReader.ReadString('\n') - if errors.Is(err, io.EOF) { - lastLine = true - } else if err != nil { - return nil, fmt.Errorf("parse raw commit: header: %w", err) - } - bytesRemaining -= int64(len(line)) - - if len(line) == 0 || line[0] == ' ' { - continue - } - // A blank line indicates the start of the commit body - if line == "\n" { - break - } - - // There might not be a final line break if there was an EOF - if line[len(line)-1] == '\n' { - line = line[:len(line)-1] - } - - key, value, ok := strings.Cut(line, " ") - if !ok { - continue - } - - switch key { - case "parent": - commit.ParentIds = append(commit.ParentIds, value) - case "author": - commit.Author = parseCommitAuthor(value) - case "committer": - commit.Committer = parseCommitAuthor(value) - case "gpgsig", "gpgsig-sha256": - commit.SignatureType = detectSignatureType(value) - case "tree": - commit.TreeId = value - case "encoding": - commit.Encoding = value - } - } - - if !lastLine { - body := make([]byte, bytesRemaining) - if _, err := io.ReadFull(p.bufferedReader, body); err != nil { - return nil, fmt.Errorf("reading commit message: %w", err) - } - - // After we have copied the body, we must make sure that there really is no - // additional data. For once, this is to detect bugs in our implementation where we - // would accidentally have truncated the commit message. On the other hand, we also - // need to do this such that we observe the EOF, which we must observe in order to - // unblock reading the next object. - // - // This all feels a bit complicated, where it would be much easier to just read into - // a preallocated `bytes.Buffer`. But this complexity is indeed required to optimize - // allocations. So if you want to change this, please make sure to execute the - // `BenchmarkListAllCommits` benchmark. - if n, err := io.Copy(io.Discard, p.bufferedReader); err != nil { - return nil, fmt.Errorf("reading commit message: %w", err) - } else if n != 0 { - return nil, fmt.Errorf( - "commit message exceeds expected length %v by %v bytes", - object.ObjectSize(), n, - ) - } - - if len(body) > 0 { - commit.Subject = subjectFromBody(body) - commit.BodySize = int64(len(body)) - commit.Body = body - if max := helper.MaxCommitOrTagMessageSize; len(body) > max { - commit.Body = commit.Body[:max] - } - } - } - - return commit, nil -} - const maxUnixCommitDate = 1 << 53 // fallbackTimeValue is the value returned in case there is a parse error. It's the maximum diff --git a/internal/git/catfile/parser_test.go b/internal/git/catfile/parser_test.go index 6d2e6d53c..b54251965 100644 --- a/internal/git/catfile/parser_test.go +++ b/internal/git/catfile/parser_test.go @@ -1,215 +1,16 @@ package catfile import ( - "bytes" "fmt" - "strings" "testing" - "time" "github.com/stretchr/testify/require" "gitlab.com/gitlab-org/gitaly/v16/internal/git" "gitlab.com/gitlab-org/gitaly/v16/internal/git/gittest" - "gitlab.com/gitlab-org/gitaly/v16/internal/testhelper" "gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb" "google.golang.org/protobuf/types/known/timestamppb" ) -func TestParser_ParseCommit(t *testing.T) { - t.Parallel() - - info := &ObjectInfo{ - Oid: gittest.DefaultObjectHash.EmptyTreeOID, - Type: "commit", - } - - // Valid-but-interesting commits should be test at the FindCommit level. - // Invalid objects (that Git would complain about during fsck) can be - // tested here. - // - // Once a repository contains a pathological object it can be hard to get - // rid of it. Because of this I think it's nicer to ignore such objects - // than to throw hard errors. - for _, tc := range []struct { - desc string - in string - out *gitalypb.GitCommit - }{ - { - desc: "empty commit object", - in: "", - out: &gitalypb.GitCommit{Id: info.Oid.String()}, - }, - { - desc: "no email", - in: "author Jane Doe", - out: &gitalypb.GitCommit{ - Id: info.Oid.String(), - Author: &gitalypb.CommitAuthor{Name: []byte("Jane Doe")}, - }, - }, - { - desc: "unmatched <", - in: "author Jane Doe ", - in: "author Jane Doe janedoe@example.com>", - out: &gitalypb.GitCommit{ - Id: info.Oid.String(), - Author: &gitalypb.CommitAuthor{Name: []byte("Jane Doe janedoe@example.com>")}, - }, - }, - { - desc: "missing date", - in: "author Jane Doe ", - out: &gitalypb.GitCommit{ - Id: info.Oid.String(), - Author: &gitalypb.CommitAuthor{Name: []byte("Jane Doe"), Email: []byte("janedoe@example.com")}, - }, - }, - { - desc: "date too high", - in: "author Jane Doe 9007199254740993 +0200", - out: &gitalypb.GitCommit{ - Id: info.Oid.String(), - Author: &gitalypb.CommitAuthor{ - Name: []byte("Jane Doe"), - Email: []byte("janedoe@example.com"), - Date: ×tamppb.Timestamp{Seconds: 9223371974719179007}, - Timezone: []byte("+0200"), - }, - }, - }, - { - desc: "date negative", - in: "author Jane Doe -1 +0200", - out: &gitalypb.GitCommit{ - Id: info.Oid.String(), - Author: &gitalypb.CommitAuthor{ - Name: []byte("Jane Doe"), - Email: []byte("janedoe@example.com"), - Date: ×tamppb.Timestamp{Seconds: 9223371974719179007}, - Timezone: []byte("+0200"), - }, - }, - }, - { - desc: "ssh signature", - in: `gpgsig -----BEGIN SSH SIGNATURE----- -U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgtc+Qk8jhMwVZk/jFEFCM16LNQb -30q5kK30bbetfjyTMAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5 -AAAAQLSyv010gOFwIs9QTtDvlfIEWiAw2iQL/T9usGcxHXn/W5l0cOFCd7O+WaMDg0t0nW -fF3T79iV8paT4/OfX8Ygg= ------END SSH SIGNATURE-----`, - out: &gitalypb.GitCommit{ - Id: info.Oid.String(), - SignatureType: gitalypb.SignatureType_SSH, - }, - }, - { - desc: "SHA256 signature", - in: fmt.Sprintf(`gpgsig-sha256 -----BEGIN PGP SIGNATURE----- -Version: ObjectivePGP -Comment: https://www.objectivepgp.com -Charset: UTF-8 -%c -wsFcBAABCgAGBQJecon1AAoJEDYMjTn1G2THmSsP/At/jskLdF0i7p0nKf4JLjeeqRJ4k2IUg87U -ZwV6mbLo5XFm8Sq7CJBAGAhlOZE4BAwKALuawmgs5XMEZwK2z6AIgosGTVpmxDTTI11bXt4XIOdz -qF7c/gUrJOZzjFXOqDsd5UuPRupwznC5eKlLbfImR+NYxKryo8JGdF5t52ph4kChcQsKlSkXuYNI -+9UgbaMclEjb0OLm+mcP9QxW+Cs9JS2Jb4Jh6XONWW1nDN3ZTDDskguIqqF47UxIgSImrmpMcEj9 -YSNU0oMoHM4+1DoXp1t99EGPoAMvO+a5g8gd1jouCIrI6KOX+GeG/TFFM0mQwg/d/N9LR049m8ed -vgqg/lMiWUxQGL2IPpYPcgiUEqfn7ete+NMzQV5zstxF/q7Yj2BhM2L7FPHxKaoy/w5Q/DcAO4wN -5gxVmIvbCDk5JOx8I+boIS8ZxSvIlJ5IWaPrcjg5Mc40it+WHvMqxVnCzH0c6KcXaJ2SibVb59HR -pdRhEXXw/hRN65l/xwyM8sklQalAGu755gNJZ4k9ApBVUssZyiu+te2+bDirAcmK8/x1jvMQY6bn -DFxBE7bMHDp24IFPaVID84Ryt3vSSBEkrUGm7OkyDESTpHCr4sfD5o3LCUCIibTqv/CAhe59mhbB -2AXL7X+EzylKy6C1N5KUUiMTW94AuF6f8FqBoxnf -=U6zM ------END PGP SIGNATURE-----`, ' '), - out: &gitalypb.GitCommit{ - Id: info.Oid.String(), - SignatureType: gitalypb.SignatureType_PGP, - }, - }, - { - desc: "huge", - in: "author " + strings.Repeat("A", 100000), - out: &gitalypb.GitCommit{ - Id: info.Oid.String(), - Author: &gitalypb.CommitAuthor{ - Name: bytes.Repeat([]byte("A"), 100000), - }, - }, - }, - { - desc: "has encoding", - in: "encoding Windows-1251", - out: &gitalypb.GitCommit{ - Id: info.Oid.String(), - Encoding: "Windows-1251", - }, - }, - } { - t.Run(tc.desc, func(t *testing.T) { - info.Size = int64(len(tc.in)) - out, err := NewParser().ParseCommit(newStaticObject(tc.in, "commit", info.Oid)) - require.NoError(t, err, "parse error") - require.Equal(t, tc.out, out) - }) - } -} - -func TestParseCommitAuthor(t *testing.T) { - t.Parallel() - - for _, tc := range []struct { - desc string - author string - expected *gitalypb.CommitAuthor - }{ - { - desc: "empty author", - author: "", - expected: &gitalypb.CommitAuthor{}, - }, - { - desc: "normal author", - author: "Au Thor 1625121079 +0000", - expected: &gitalypb.CommitAuthor{ - Name: []byte("Au Thor"), - Email: []byte("au.thor@example.com"), - Date: timestamppb.New(time.Unix(1625121079, 0)), - Timezone: []byte("+0000"), - }, - }, - { - desc: "author with missing mail", - author: "Au Thor <> 1625121079 +0000", - expected: &gitalypb.CommitAuthor{ - Name: []byte("Au Thor"), - Date: timestamppb.New(time.Unix(1625121079, 0)), - Timezone: []byte("+0000"), - }, - }, - { - desc: "author with missing date", - author: "Au Thor ", - expected: &gitalypb.CommitAuthor{ - Name: []byte("Au Thor"), - Email: []byte("au.thor@example.com"), - }, - }, - } { - t.Run(tc.desc, func(t *testing.T) { - testhelper.ProtoEqual(t, tc.expected, parseCommitAuthor(tc.author)) - }) - } -} - func TestParser_ParseTag(t *testing.T) { t.Parallel() diff --git a/internal/git/catfile/tag.go b/internal/git/catfile/tag.go index 0dfbe8755..afce2f26e 100644 --- a/internal/git/catfile/tag.go +++ b/internal/git/catfile/tag.go @@ -61,15 +61,17 @@ func buildAnnotatedTag(ctx context.Context, objectReader ObjectContentReader, ob switch tagged.objectType { case "commit": - tag.TargetCommit, err = GetCommit(ctx, objectReader, git.Revision(tagged.objectID)) + commit, err := GetCommit(ctx, objectReader, git.Revision(tagged.objectID)) if err != nil { return nil, fmt.Errorf("buildAnnotatedTag error when getting target commit: %w", err) } + tag.TargetCommit = commit.GitCommit case "tag": - tag.TargetCommit, err = dereferenceTag(ctx, objectReader, git.Revision(tagged.objectID)) - if err != nil { + if commit, err := dereferenceTag(ctx, objectReader, git.Revision(tagged.objectID)); err != nil { return nil, fmt.Errorf("buildAnnotatedTag error when dereferencing tag: %w", err) + } else if commit != nil { + tag.TargetCommit = commit.GitCommit } } @@ -79,7 +81,7 @@ func buildAnnotatedTag(ctx context.Context, objectReader ObjectContentReader, ob // dereferenceTag recursively dereferences annotated tags until it finds a non-tag object. If it is // a commit, then it will parse and return this commit. Otherwise, if the tagged object is not a // commit, it will simply discard the object and not return an error. -func dereferenceTag(ctx context.Context, objectReader ObjectContentReader, oid git.Revision) (*gitalypb.GitCommit, error) { +func dereferenceTag(ctx context.Context, objectReader ObjectContentReader, oid git.Revision) (*Commit, error) { object, err := objectReader.Object(ctx, oid+"^{}") if err != nil { return nil, fmt.Errorf("peeling tag: %w", err) diff --git a/internal/git/localrepo/commit.go b/internal/git/localrepo/commit.go index df5effc2a..2f77069ef 100644 --- a/internal/git/localrepo/commit.go +++ b/internal/git/localrepo/commit.go @@ -68,7 +68,10 @@ func (repo *Repo) ReadCommit(ctx context.Context, revision git.Revision, opts .. if cfg.withTrailers { commit, err = catfile.GetCommitWithTrailers(ctx, repo.gitCmdFactory, repo, objectReader, revision) } else { - commit, err = catfile.GetCommit(ctx, objectReader, revision) + var c *catfile.Commit + if c, err = catfile.GetCommit(ctx, objectReader, revision); err == nil { + commit = c.GitCommit + } } if err != nil { diff --git a/internal/git/log/last_commit.go b/internal/git/log/last_commit.go index d92251b32..eb5333668 100644 --- a/internal/git/log/last_commit.go +++ b/internal/git/log/last_commit.go @@ -21,7 +21,7 @@ func LastCommitForPath( revision git.Revision, path string, options *gitalypb.GlobalOptions, -) (*gitalypb.GitCommit, error) { +) (*catfile.Commit, error) { var stdout strings.Builder cmd, err := gitCmdFactory.New(ctx, repo, git.Command{ Name: "log", diff --git a/internal/git/log/parser.go b/internal/git/log/parser.go index 1c9e9d566..dce154770 100644 --- a/internal/git/log/parser.go +++ b/internal/git/log/parser.go @@ -55,7 +55,7 @@ func (parser *Parser) Parse(ctx context.Context) bool { return false } - parser.currentCommit = commit + parser.currentCommit = commit.GitCommit return true } diff --git a/internal/gitaly/service/commit/find_commits.go b/internal/gitaly/service/commit/find_commits.go index e2d717b25..15425330c 100644 --- a/internal/gitaly/service/commit/find_commits.go +++ b/internal/gitaly/service/commit/find_commits.go @@ -195,7 +195,7 @@ func (g *GetCommits) Commit(ctx context.Context, trailers, shortStat, refs bool) } } - return commit, nil + return commit.GitCommit, nil } func streamCommits(getCommits *GetCommits, stream gitalypb.CommitService_FindCommitsServer, trailers, shortStat bool, refs bool) error { diff --git a/internal/gitaly/service/commit/last_commit_for_path.go b/internal/gitaly/service/commit/last_commit_for_path.go index e0f69a97c..555a34e77 100644 --- a/internal/gitaly/service/commit/last_commit_for_path.go +++ b/internal/gitaly/service/commit/last_commit_for_path.go @@ -57,7 +57,7 @@ func (s *server) lastCommitForPath(ctx context.Context, in *gitalypb.LastCommitF return &gitalypb.LastCommitForPathResponse{}, nil } - return &gitalypb.LastCommitForPathResponse{Commit: commit}, err + return &gitalypb.LastCommitForPathResponse{Commit: commit.GitCommit}, err } func validateLastCommitForPathRequest(locator storage.Locator, in *gitalypb.LastCommitForPathRequest) error { diff --git a/internal/gitaly/service/commit/list_all_commits.go b/internal/gitaly/service/commit/list_all_commits.go index 0ee326c23..62633d6e5 100644 --- a/internal/gitaly/service/commit/list_all_commits.go +++ b/internal/gitaly/service/commit/list_all_commits.go @@ -79,7 +79,7 @@ func (s *server) ListAllCommits( return structerr.NewInternal("parsing commit: %w", err) } - if err := chunker.Send(commit); err != nil { + if err := chunker.Send(commit.GitCommit); err != nil { return structerr.NewInternal("sending commit: %w", err) } } diff --git a/internal/gitaly/service/commit/list_commits.go b/internal/gitaly/service/commit/list_commits.go index 1a16158a5..09f627873 100644 --- a/internal/gitaly/service/commit/list_commits.go +++ b/internal/gitaly/service/commit/list_commits.go @@ -139,7 +139,7 @@ func (s *server) ListCommits( return structerr.NewInternal("parsing commit: %w", err) } - if err := chunker.Send(commit); err != nil { + if err := chunker.Send(commit.GitCommit); err != nil { return structerr.NewInternal("sending commit: %w", err) } } diff --git a/internal/gitaly/service/commit/list_commits_by_oid.go b/internal/gitaly/service/commit/list_commits_by_oid.go index 3175c4307..f97c2aca5 100644 --- a/internal/gitaly/service/commit/list_commits_by_oid.go +++ b/internal/gitaly/service/commit/list_commits_by_oid.go @@ -50,7 +50,7 @@ func (s *server) ListCommitsByOid(in *gitalypb.ListCommitsByOidRequest, stream g return err } - if err := sender.Send(commit); err != nil { + if err := sender.Send(commit.GitCommit); err != nil { return err } } diff --git a/internal/gitaly/service/commit/list_commits_by_ref_name.go b/internal/gitaly/service/commit/list_commits_by_ref_name.go index e77c9f916..6c6d25b07 100644 --- a/internal/gitaly/service/commit/list_commits_by_ref_name.go +++ b/internal/gitaly/service/commit/list_commits_by_ref_name.go @@ -37,7 +37,7 @@ func (s *server) ListCommitsByRefName(in *gitalypb.ListCommitsByRefNameRequest, } commitByRef := &gitalypb.ListCommitsByRefNameResponse_CommitForRef{ - Commit: commit, RefName: refName, + Commit: commit.GitCommit, RefName: refName, } if err := sender.Send(commitByRef); err != nil { diff --git a/internal/gitaly/service/commit/list_last_commits_for_tree.go b/internal/gitaly/service/commit/list_last_commits_for_tree.go index fa7e7d0f7..85a5d2a69 100644 --- a/internal/gitaly/service/commit/list_last_commits_for_tree.go +++ b/internal/gitaly/service/commit/list_last_commits_for_tree.go @@ -74,7 +74,7 @@ func (s *server) listLastCommitsForTree(in *gitalypb.ListLastCommitsForTreeReque commitForTree := &gitalypb.ListLastCommitsForTreeResponse_CommitForTree{ PathBytes: []byte(entry.Path), - Commit: commit, + Commit: commit.GitCommit, } batch = append(batch, commitForTree) diff --git a/internal/gitaly/service/operations/tags.go b/internal/gitaly/service/operations/tags.go index 83b2d7b1f..bc78ca810 100644 --- a/internal/gitaly/service/operations/tags.go +++ b/internal/gitaly/service/operations/tags.go @@ -331,7 +331,7 @@ func (s *Server) createTag( if err != nil { return nil, "", structerr.NewInternal("getting commit: %w", err) } - tagObject.TargetCommit = peeledTargetCommit + tagObject.TargetCommit = peeledTargetCommit.GitCommit } return tagObject, refObjectID, nil diff --git a/internal/gitaly/service/ref/find_all_tags.go b/internal/gitaly/service/ref/find_all_tags.go index 2091b0011..e58917c15 100644 --- a/internal/gitaly/service/ref/find_all_tags.go +++ b/internal/gitaly/service/ref/find_all_tags.go @@ -100,10 +100,11 @@ func (s *server) findAllTags(ctx context.Context, repo *localrepo.Repo, sortFiel // which refers to a commit object. Otherwise, we discard the object's // contents. if peeledTag.ObjectType() == "commit" { - result.TargetCommit, err = parser.ParseCommit(peeledTag) + commit, err := parser.ParseCommit(peeledTag) if err != nil { return fmt.Errorf("parsing tagged commit: %w", err) } + result.TargetCommit = commit.GitCommit } else { if _, err := io.Copy(io.Discard, peeledTag); err != nil { return fmt.Errorf("discarding tagged object contents: %w", err) @@ -117,7 +118,7 @@ func (s *server) findAllTags(ctx context.Context, repo *localrepo.Repo, sortFiel result = &gitalypb.Tag{ Id: tag.ObjectID().String(), - TargetCommit: commit, + TargetCommit: commit.GitCommit, } default: if _, err := io.Copy(io.Discard, tag); err != nil { diff --git a/internal/gitaly/service/ref/find_tag.go b/internal/gitaly/service/ref/find_tag.go index 69f3d93f8..1c700bd65 100644 --- a/internal/gitaly/service/ref/find_tag.go +++ b/internal/gitaly/service/ref/find_tag.go @@ -57,7 +57,7 @@ func parseTagLine(ctx context.Context, objectReader catfile.ObjectContentReader, if err != nil { return nil, fmt.Errorf("getting commit catfile: %w", err) } - tag.TargetCommit = commit + tag.TargetCommit = commit.GitCommit return tag, nil default: return tag, nil diff --git a/internal/gitaly/service/ref/util.go b/internal/gitaly/service/ref/util.go index 4f90c3b0d..144d83308 100644 --- a/internal/gitaly/service/ref/util.go +++ b/internal/gitaly/service/ref/util.go @@ -35,7 +35,7 @@ func buildAllBranchesBranch(ctx context.Context, objectReader catfile.ObjectCont return &gitalypb.FindAllBranchesResponse_Branch{ Name: elements[0], - Target: target, + Target: target.GitCommit, }, nil } @@ -47,7 +47,7 @@ func buildBranch(ctx context.Context, objectReader catfile.ObjectContentReader, return &gitalypb.Branch{ Name: elements[0], - TargetCommit: target, + TargetCommit: target.GitCommit, }, nil } -- cgit v1.2.3 From d7de749b2850dfbadafcc640e168af9cf38e8787 Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Mon, 20 Nov 2023 20:47:25 +0100 Subject: commit: Remove the `extractSignature` function The `GetCommitSignatures` RPC, uses the `extractSignature` function to obtain the signature and the payload for a given commit. Since we introduced a new parser in the `catfile` package to do the same in the previous commits. Let's use that instead. --- .../gitaly/service/commit/commit_signatures.go | 64 ++++------------------ 1 file changed, 11 insertions(+), 53 deletions(-) diff --git a/internal/gitaly/service/commit/commit_signatures.go b/internal/gitaly/service/commit/commit_signatures.go index 9dc5a9819..122f48c45 100644 --- a/internal/gitaly/service/commit/commit_signatures.go +++ b/internal/gitaly/service/commit/commit_signatures.go @@ -1,7 +1,6 @@ package commit import ( - "bufio" "bytes" "errors" "fmt" @@ -47,6 +46,7 @@ func (s *server) GetCommitSignatures(request *gitalypb.GetCommitSignaturesReques } } + parser := catfile.NewParser() for _, commitID := range request.CommitIds { commitObj, err := objectReader.Object(ctx, git.Revision(commitID)+"^{commit}") if err != nil { @@ -56,19 +56,26 @@ func (s *server) GetCommitSignatures(request *gitalypb.GetCommitSignaturesReques return structerr.NewInternal("%w", err) } - signatureKey, commitText, err := extractSignature(commitObj) + commit, err := parser.ParseCommit(commitObj) if err != nil { return structerr.NewInternal("%w", err) } + signature := []byte{} + if len(commit.SignatureData.Signatures) > 0 { + // While there could be potentially multiple signatures in a Git + // commit, like Git, we only consider the first. + signature = commit.SignatureData.Signatures[0] + } + signer := gitalypb.GetCommitSignaturesResponse_SIGNER_USER if signingKeys != nil { - if err := signingKeys.Verify(signatureKey, commitText); err == nil { + if err := signingKeys.Verify(signature, commit.SignatureData.Payload); err == nil { signer = gitalypb.GetCommitSignaturesResponse_SIGNER_SYSTEM } } - if err = sendResponse(commitID, signatureKey, commitText, signer, stream); err != nil { + if err = sendResponse(commitID, signature, commit.SignatureData.Payload, signer, stream); err != nil { return structerr.NewInternal("%w", err) } } @@ -76,55 +83,6 @@ func (s *server) GetCommitSignatures(request *gitalypb.GetCommitSignaturesReques return nil } -func extractSignature(reader io.Reader) ([]byte, []byte, error) { - commitText := []byte{} - signatureKey := []byte{} - sawSignature := false - inSignature := false - lineBreak := []byte("\n") - whiteSpace := []byte(" ") - bufferedReader := bufio.NewReader(reader) - - for { - line, err := bufferedReader.ReadBytes('\n') - - if errors.Is(err, io.EOF) { - commitText = append(commitText, line...) - break - } - if err != nil { - return nil, nil, err - } - - if !sawSignature && !inSignature { - for _, signatureField := range [][]byte{[]byte("gpgsig "), []byte("gpgsig-sha256 ")} { - if !bytes.HasPrefix(line, signatureField) { - continue - } - - sawSignature, inSignature = true, true - line = bytes.TrimPrefix(line, signatureField) - break - } - } - - if inSignature && !bytes.Equal(line, lineBreak) { - line = bytes.TrimPrefix(line, whiteSpace) - signatureKey = append(signatureKey, line...) - } else if inSignature { - inSignature = false - commitText = append(commitText, line...) - } else { - commitText = append(commitText, line...) - } - } - - // Remove last line break from signature - signatureKey = bytes.TrimSuffix(signatureKey, lineBreak) - - return signatureKey, commitText, nil -} - func sendResponse( commitID string, signatureKey []byte, -- cgit v1.2.3 From c448efe04b7f73ac21658f1594cbbbb536b8cefd Mon Sep 17 00:00:00 2001 From: GitLab Release Tools Bot Date: Sat, 23 Dec 2023 00:22:03 +0000 Subject: Update changelog for 16.7.1 [ci skip] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b34e6ad6c..17dc29e65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Gitaly changelog +## 16.7.1 (2023-12-23) + +No changes. + ## 16.7.0 (2023-12-20) ### Fixed (3 changes) -- cgit v1.2.3 From c7e1de7db94d9446ae74bab8f72f0ee3d1f4ee4c Mon Sep 17 00:00:00 2001 From: GitLab Release Tools Bot Date: Sat, 23 Dec 2023 00:38:23 +0000 Subject: Update changelog for 16.6.3 [ci skip] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17dc29e65..423a5296d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,10 @@ No changes. - [gitaly: Move asynchronous tasks to end of startup](gitlab-org/gitaly@dd4ea4388b4b8e7c49ea423126f8be5e067729cd) ([merge request](gitlab-org/gitaly!6532)) - [cgroups: Create repository cgroups on-demand](gitlab-org/gitaly@105f6dd81689b7819b844b8be1be844bca6f2a67) ([merge request](gitlab-org/gitaly!6499)) +## 16.6.3 (2023-12-23) + +No changes. + ## 16.6.2 (2023-12-13) No changes. -- cgit v1.2.3 From 4fa3e3b11b608030dc6c9d9e878222d7b14bfe30 Mon Sep 17 00:00:00 2001 From: GitLab Release Tools Bot Date: Sat, 23 Dec 2023 00:48:49 +0000 Subject: Update changelog for 16.5.5 [ci skip] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 423a5296d..fc8802e30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,10 @@ No changes. - [backup: Use --all when creating an incremental backup bundle](gitlab-org/gitaly@d874d8c3eb0d0c9b9362b583ba8ccc42221140cd) ([merge request](gitlab-org/gitaly!6421)) +## 16.5.5 (2023-12-23) + +No changes. + ## 16.5.4 (2023-12-13) No changes. -- cgit v1.2.3 From b37da0f238be4b35bab586e0c43aed15ce656676 Mon Sep 17 00:00:00 2001 From: Justin Tobler Date: Mon, 8 Jan 2024 14:51:19 -0600 Subject: Revert "Merge branch 'fix-user-revert-return-structured-error' into 'master'" This reverts commit 7f019337fb51305215538a793cf80171111b6e29, reversing changes made to 82dcc6a7a5692244932c4f6d4d92b3fa615aeb85. --- .../ff_return_structed_errors_in_revert.go | 13 - internal/gitaly/service/operations/revert.go | 78 +- internal/gitaly/service/operations/revert_test.go | 118 +- proto/go/gitalypb/operations.pb.go | 1328 +++++++++----------- proto/operations.proto | 14 - 5 files changed, 626 insertions(+), 925 deletions(-) delete mode 100644 internal/featureflag/ff_return_structed_errors_in_revert.go diff --git a/internal/featureflag/ff_return_structed_errors_in_revert.go b/internal/featureflag/ff_return_structed_errors_in_revert.go deleted file mode 100644 index 52efdae02..000000000 --- a/internal/featureflag/ff_return_structed_errors_in_revert.go +++ /dev/null @@ -1,13 +0,0 @@ -package featureflag - -// ReturnStructuredErrorsInUserRevert enables return structured errors in UserRevert. -// Modify the RPC UserRevert to return structured errors instead of -// inline errors. Modify the handling of the following four -// errors: 'Conflict', 'Changes Already Applied', 'Branch diverged', -// and 'CustomHookError'. Returns the corresponding structured error. -var ReturnStructuredErrorsInUserRevert = NewFeatureFlag( - "return_structured_errors_in_revert", - "v16.8.0", - "https://gitlab.com/gitlab-org/gitaly/-/issues/5752", - false, -) diff --git a/internal/gitaly/service/operations/revert.go b/internal/gitaly/service/operations/revert.go index 72e45dc8f..90d03a7dd 100644 --- a/internal/gitaly/service/operations/revert.go +++ b/internal/gitaly/service/operations/revert.go @@ -6,7 +6,6 @@ import ( "fmt" "time" - "gitlab.com/gitlab-org/gitaly/v16/internal/featureflag" "gitlab.com/gitlab-org/gitaly/v16/internal/git" "gitlab.com/gitlab-org/gitaly/v16/internal/git/localrepo" "gitlab.com/gitlab-org/gitaly/v16/internal/git/remoterepo" @@ -88,46 +87,22 @@ func (s *Server) UserRevert(ctx context.Context, req *gitalypb.UserRevertRequest if err != nil { var conflictErr *localrepo.MergeTreeConflictError if errors.As(err, &conflictErr) { - if featureflag.ReturnStructuredErrorsInUserRevert.IsEnabled(ctx) { - conflictingFiles := make([][]byte, 0, len(conflictErr.ConflictingFileInfo)) - for _, conflictingFileInfo := range conflictErr.ConflictingFileInfo { - conflictingFiles = append(conflictingFiles, []byte(conflictingFileInfo.FileName)) - } - return nil, structerr.NewFailedPrecondition("revert: there are conflicting files").WithDetail( - &gitalypb.UserRevertError{ - Error: &gitalypb.UserRevertError_MergeConflict{ - MergeConflict: &gitalypb.MergeConflictError{ - ConflictingFiles: conflictingFiles, - }, - }, - }) - } else { - return &gitalypb.UserRevertResponse{ - // it's better that this error matches the git2go for now - CreateTreeError: "revert: could not apply due to conflicts", - CreateTreeErrorCode: gitalypb.UserRevertResponse_CONFLICT, - }, nil - } + return &gitalypb.UserRevertResponse{ + // it's better that this error matches the git2go for now + CreateTreeError: "revert: could not apply due to conflicts", + CreateTreeErrorCode: gitalypb.UserRevertResponse_CONFLICT, + }, nil } return nil, structerr.NewInternal("merge-tree: %w", err) } if oursCommit.TreeId == treeOID.String() { - if featureflag.ReturnStructuredErrorsInUserRevert.IsEnabled(ctx) { - return nil, structerr.NewFailedPrecondition("revert: could not apply because the result was empty").WithDetail( - &gitalypb.UserRevertError{ - Error: &gitalypb.UserRevertError_ChangesAlreadyApplied{ - ChangesAlreadyApplied: &gitalypb.ChangesAlreadyAppliedError{}, - }, - }) - } else { - return &gitalypb.UserRevertResponse{ - // it's better that this error matches the git2go for now - CreateTreeError: "revert: could not apply because the result was empty", - CreateTreeErrorCode: gitalypb.UserRevertResponse_EMPTY, - }, nil - } + return &gitalypb.UserRevertResponse{ + // it's better that this error matches the git2go for now + CreateTreeError: "revert: could not apply because the result was empty", + CreateTreeErrorCode: gitalypb.UserRevertResponse_EMPTY, + }, nil } newrev, err = quarantineRepo.WriteCommit( @@ -191,39 +166,18 @@ func (s *Server) UserRevert(ctx context.Context, req *gitalypb.UserRevertRequest return nil, structerr.NewInternal("checking for ancestry: %w", err) } if !ancestor { - if featureflag.ReturnStructuredErrorsInUserRevert.IsEnabled(ctx) { - return nil, structerr.NewFailedPrecondition("revert: branch diverged").WithDetail( - &gitalypb.UserRevertError{ - Error: &gitalypb.UserRevertError_NotAncestor{ - NotAncestor: &gitalypb.NotAncestorError{ - ParentRevision: []byte(oldrev.Revision()), - ChildRevision: []byte(newrev.Revision()), - }, - }, - }) - } else { - return &gitalypb.UserRevertResponse{ - CommitError: "Branch diverged", - }, nil - } + return &gitalypb.UserRevertResponse{ + CommitError: "Branch diverged", + }, nil } } if err := s.updateReferenceWithHooks(ctx, req.GetRepository(), req.User, quarantineDir, referenceName, newrev, oldrev); err != nil { var customHookErr updateref.CustomHookError if errors.As(err, &customHookErr) { - if featureflag.ReturnStructuredErrorsInUserRevert.IsEnabled(ctx) { - return nil, structerr.NewPermissionDenied("revert: custom hook error").WithDetail( - &gitalypb.UserRevertError{ - Error: &gitalypb.UserRevertError_CustomHook{ - CustomHook: customHookErr.Proto(), - }, - }) - } else { - return &gitalypb.UserRevertResponse{ - PreReceiveError: customHookErr.Error(), - }, nil - } + return &gitalypb.UserRevertResponse{ + PreReceiveError: customHookErr.Error(), + }, nil } return nil, fmt.Errorf("update reference with hooks: %w", err) diff --git a/internal/gitaly/service/operations/revert_test.go b/internal/gitaly/service/operations/revert_test.go index 754bf0748..225669662 100644 --- a/internal/gitaly/service/operations/revert_test.go +++ b/internal/gitaly/service/operations/revert_test.go @@ -19,8 +19,6 @@ import ( "gitlab.com/gitlab-org/gitaly/v16/internal/testhelper/testcfg" "gitlab.com/gitlab-org/gitaly/v16/internal/testhelper/testserver" "gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/timestamppb" ) @@ -429,7 +427,6 @@ func TestServer_UserRevert_quarantine(t *testing.T) { testhelper.NewFeatureSets( featureflag.GPGSigning, - featureflag.ReturnStructuredErrorsInUserRevert, ).Run(t, testServerUserRevertQuarantine) } @@ -471,22 +468,9 @@ func testServerUserRevertQuarantine(t *testing.T, ctx context.Context) { Message: []byte("Reverting " + revertedCommit.Id), Timestamp: ×tamppb.Timestamp{Seconds: 12345}, }) - if featureflag.ReturnStructuredErrorsInUserRevert.IsEnabled(ctx) { - expectedError := structerr.NewPermissionDenied("revert: custom hook error").WithDetail( - &gitalypb.UserRevertError{ - Error: &gitalypb.UserRevertError_CustomHook{ - CustomHook: &gitalypb.CustomHookError{ - HookType: gitalypb.CustomHookError_HOOK_TYPE_PRERECEIVE, - }, - }, - }) - testhelper.RequireGrpcError(t, expectedError, err) - } else { - require.NoError(t, err) - require.NotNil(t, response) - require.NotEmpty(t, response.PreReceiveError) - - } + require.NoError(t, err) + require.NotNil(t, response) + require.NotEmpty(t, response.PreReceiveError) objectHash, err := repo.ObjectHash(ctx) require.NoError(t, err) @@ -615,7 +599,6 @@ func TestServer_UserRevert_stableID(t *testing.T) { testhelper.NewFeatureSets( featureflag.GPGSigning, - featureflag.ReturnStructuredErrorsInUserRevert, ).Run(t, testServerUserRevertStableID) } @@ -669,10 +652,8 @@ func testServerUserRevertStableID(t *testing.T, ctx context.Context) { "sha256": "28b57208e72bc2317143571997b9cfc444a51b52a43dde1c0282633a2b60de71", }), }, response.BranchUpdate) - if !featureflag.ReturnStructuredErrorsInUserRevert.IsEnabled(ctx) { - require.Empty(t, response.CreateTreeError) - require.Empty(t, response.CreateTreeErrorCode) - } + require.Empty(t, response.CreateTreeError) + require.Empty(t, response.CreateTreeErrorCode) // headCommit is pointed commit after revert headCommit, err := repo.ReadCommit(ctx, git.Revision(git.DefaultBranch)) @@ -714,7 +695,6 @@ func TestServer_UserRevert_successfulIntoEmptyRepo(t *testing.T) { testhelper.NewFeatureSets( featureflag.GPGSigning, - featureflag.ReturnStructuredErrorsInUserRevert, ).Run(t, testServerUserRevertSuccessfulIntoEmptyRepo) } @@ -772,10 +752,8 @@ func testServerUserRevertSuccessfulIntoEmptyRepo(t *testing.T, ctx context.Conte } require.Equal(t, expectedBranchUpdate, response.BranchUpdate) - if !featureflag.ReturnStructuredErrorsInUserRevert.IsEnabled(ctx) { - require.Empty(t, response.CreateTreeError) - require.Empty(t, response.CreateTreeErrorCode) - } + require.Empty(t, response.CreateTreeError) + require.Empty(t, response.CreateTreeErrorCode) require.Equal(t, request.Message, headCommit.Subject) require.Equal(t, revertedCommit.Id, headCommit.ParentIds[0]) gittest.RequireTree(t, cfg, repoPath, response.BranchUpdate.CommitId, @@ -789,7 +767,6 @@ func TestServer_UserRevert_successfulGitHooks(t *testing.T) { testhelper.NewFeatureSets( featureflag.GPGSigning, - featureflag.ReturnStructuredErrorsInUserRevert, ).Run(t, testServerUserRevertSuccessfulGitHooks) } @@ -829,9 +806,7 @@ func testServerUserRevertSuccessfulGitHooks(t *testing.T, ctx context.Context) { response, err := client.UserRevert(ctx, request) require.NoError(t, err) - if !featureflag.ReturnStructuredErrorsInUserRevert.IsEnabled(ctx) { - require.Empty(t, response.PreReceiveError) - } + require.Empty(t, response.PreReceiveError) headCommit, err := repo.ReadCommit(ctx, git.Revision(destinationBranch)) require.NoError(t, err) gittest.RequireTree(t, cfg, repoPath, headCommit.Id, nil) @@ -847,7 +822,6 @@ func TestServer_UserRevert_failedDueToPreReceiveError(t *testing.T) { testhelper.NewFeatureSets( featureflag.GPGSigning, - featureflag.ReturnStructuredErrorsInUserRevert, ).Run(t, testServerUserRevertFailedDueToPreReceiveError) } @@ -885,19 +859,9 @@ func testServerUserRevertFailedDueToPreReceiveError(t *testing.T, ctx context.Co t.Run(hookName, func(t *testing.T) { gittest.WriteCustomHook(t, repoPath, hookName, hookContent) - if featureflag.ReturnStructuredErrorsInUserRevert.IsEnabled(ctx) { - _, err := client.UserRevert(ctx, request) - actualStatus, _ := status.FromError(err) - require.Equal(t, actualStatus.Code(), codes.PermissionDenied) - require.Equal(t, actualStatus.Message(), "revert: custom hook error") - revertError, ok := actualStatus.Details()[0].(*gitalypb.UserRevertError) - require.True(t, ok) - require.Contains(t, revertError.GetCustomHook().String(), "GL_ID="+gittest.TestUser.GlId) - } else { - response, err := client.UserRevert(ctx, request) - require.NoError(t, err) - require.Contains(t, response.PreReceiveError, "GL_ID="+gittest.TestUser.GlId) - } + response, err := client.UserRevert(ctx, request) + require.NoError(t, err) + require.Contains(t, response.PreReceiveError, "GL_ID="+gittest.TestUser.GlId) }) } } @@ -907,7 +871,6 @@ func TestServer_UserRevert_failedDueToCreateTreeErrorConflict(t *testing.T) { testhelper.NewFeatureSets( featureflag.GPGSigning, - featureflag.ReturnStructuredErrorsInUserRevert, ).Run(t, testServerUserRevertFailedDueToCreateTreeErrorConflict) } @@ -954,20 +917,10 @@ func testServerUserRevertFailedDueToCreateTreeErrorConflict(t *testing.T, ctx co Message: []byte("Reverting " + revertedCommit.Id), } - if featureflag.ReturnStructuredErrorsInUserRevert.IsEnabled(ctx) { - _, err = client.UserRevert(ctx, request) - actualStatus, _ := status.FromError(err) - require.Equal(t, actualStatus.Code(), codes.FailedPrecondition) - require.Equal(t, actualStatus.Message(), "revert: there are conflicting files") - revertError, ok := actualStatus.Details()[0].(*gitalypb.UserRevertError) - require.True(t, ok) - require.NotNil(t, revertError.GetMergeConflict()) - } else { - response, err := client.UserRevert(ctx, request) - require.NoError(t, err) - require.NotEmpty(t, response.CreateTreeError) - require.Equal(t, gitalypb.UserRevertResponse_CONFLICT, response.CreateTreeErrorCode) - } + response, err := client.UserRevert(ctx, request) + require.NoError(t, err) + require.NotEmpty(t, response.CreateTreeError) + require.Equal(t, gitalypb.UserRevertResponse_CONFLICT, response.CreateTreeErrorCode) } func TestServer_UserRevert_failedDueToCreateTreeErrorEmpty(t *testing.T) { @@ -975,7 +928,6 @@ func TestServer_UserRevert_failedDueToCreateTreeErrorEmpty(t *testing.T) { testhelper.NewFeatureSets( featureflag.GPGSigning, - featureflag.ReturnStructuredErrorsInUserRevert, ).Run(t, testServerUserRevertFailedDueToCreateTreeErrorEmpty) } @@ -1037,25 +989,13 @@ func testServerUserRevertFailedDueToCreateTreeErrorEmpty(t *testing.T, ctx conte response, err := client.UserRevert(ctx, request) require.NoError(t, err) + require.Empty(t, response.CreateTreeError) + require.Equal(t, gitalypb.UserRevertResponse_NONE, response.CreateTreeErrorCode) - if featureflag.ReturnStructuredErrorsInUserRevert.IsEnabled(ctx) { - _, err = client.UserRevert(ctx, request) - - expectedError := structerr.NewFailedPrecondition("revert: could not apply because the result was empty").WithDetail( - &gitalypb.UserRevertError{ - Error: &gitalypb.UserRevertError_ChangesAlreadyApplied{}, - }) - testhelper.RequireGrpcError(t, expectedError, err) - } else { - require.NoError(t, err) - require.Empty(t, response.CreateTreeError) - require.Equal(t, gitalypb.UserRevertResponse_NONE, response.CreateTreeErrorCode) - - response, err = client.UserRevert(ctx, request) - require.NoError(t, err) - require.NotEmpty(t, response.CreateTreeError) - require.Equal(t, gitalypb.UserRevertResponse_EMPTY, response.CreateTreeErrorCode) - } + response, err = client.UserRevert(ctx, request) + require.NoError(t, err) + require.NotEmpty(t, response.CreateTreeError) + require.Equal(t, gitalypb.UserRevertResponse_EMPTY, response.CreateTreeErrorCode) } func TestServer_UserRevert_failedDueToCommitError(t *testing.T) { @@ -1063,7 +1003,6 @@ func TestServer_UserRevert_failedDueToCommitError(t *testing.T) { testhelper.NewFeatureSets( featureflag.GPGSigning, - featureflag.ReturnStructuredErrorsInUserRevert, ).Run(t, testServerUserRevertFailedDueToCommitError) } @@ -1106,17 +1045,8 @@ func testServerUserRevertFailedDueToCommitError(t *testing.T, ctx context.Contex Message: []byte("Reverting " + revertedCommit.Id), StartBranchName: []byte(sourceBranch), } - response, err := client.UserRevert(ctx, request) - if featureflag.ReturnStructuredErrorsInUserRevert.IsEnabled(ctx) { - actualStatus, _ := status.FromError(err) - require.Equal(t, actualStatus.Code(), codes.FailedPrecondition) - require.Equal(t, actualStatus.Message(), "revert: branch diverged") - revertError, ok := actualStatus.Details()[0].(*gitalypb.UserRevertError) - require.True(t, ok) - require.NotNil(t, revertError.GetNotAncestor()) - } else { - require.NoError(t, err) - require.Equal(t, "Branch diverged", response.CommitError) - } + response, err := client.UserRevert(ctx, request) + require.NoError(t, err) + require.Equal(t, "Branch diverged", response.CommitError) } diff --git a/proto/go/gitalypb/operations.pb.go b/proto/go/gitalypb/operations.pb.go index f0a62bf94..ce8526742 100644 --- a/proto/go/gitalypb/operations.pb.go +++ b/proto/go/gitalypb/operations.pb.go @@ -138,7 +138,7 @@ func (x UserCommitFilesActionHeader_ActionType) Number() protoreflect.EnumNumber // Deprecated: Use UserCommitFilesActionHeader_ActionType.Descriptor instead. func (UserCommitFilesActionHeader_ActionType) EnumDescriptor() ([]byte, []int) { - return file_operations_proto_rawDescGZIP(), []int{29, 0} + return file_operations_proto_rawDescGZIP(), []int{28, 0} } // UserCreateBranchRequest is a request for the UserCreateBranch RPC. @@ -2538,120 +2538,6 @@ func (x *UserRevertResponse) GetCreateTreeErrorCode() UserRevertResponse_CreateT return UserRevertResponse_NONE } -// UserRevertError is an error returned by the UserRevert RPC. -type UserRevertError struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Types that are assignable to Error: - // - // *UserRevertError_MergeConflict - // *UserRevertError_ChangesAlreadyApplied - // *UserRevertError_CustomHook - // *UserRevertError_NotAncestor - Error isUserRevertError_Error `protobuf_oneof:"error"` -} - -func (x *UserRevertError) Reset() { - *x = UserRevertError{} - if protoimpl.UnsafeEnabled { - mi := &file_operations_proto_msgTypes[28] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *UserRevertError) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UserRevertError) ProtoMessage() {} - -func (x *UserRevertError) ProtoReflect() protoreflect.Message { - mi := &file_operations_proto_msgTypes[28] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UserRevertError.ProtoReflect.Descriptor instead. -func (*UserRevertError) Descriptor() ([]byte, []int) { - return file_operations_proto_rawDescGZIP(), []int{28} -} - -func (m *UserRevertError) GetError() isUserRevertError_Error { - if m != nil { - return m.Error - } - return nil -} - -func (x *UserRevertError) GetMergeConflict() *MergeConflictError { - if x, ok := x.GetError().(*UserRevertError_MergeConflict); ok { - return x.MergeConflict - } - return nil -} - -func (x *UserRevertError) GetChangesAlreadyApplied() *ChangesAlreadyAppliedError { - if x, ok := x.GetError().(*UserRevertError_ChangesAlreadyApplied); ok { - return x.ChangesAlreadyApplied - } - return nil -} - -func (x *UserRevertError) GetCustomHook() *CustomHookError { - if x, ok := x.GetError().(*UserRevertError_CustomHook); ok { - return x.CustomHook - } - return nil -} - -func (x *UserRevertError) GetNotAncestor() *NotAncestorError { - if x, ok := x.GetError().(*UserRevertError_NotAncestor); ok { - return x.NotAncestor - } - return nil -} - -type isUserRevertError_Error interface { - isUserRevertError_Error() -} - -type UserRevertError_MergeConflict struct { - // merge_conflict is returned if there is a conflict when applying the revert. - MergeConflict *MergeConflictError `protobuf:"bytes,1,opt,name=merge_conflict,json=mergeConflict,proto3,oneof"` -} - -type UserRevertError_ChangesAlreadyApplied struct { - // changes_already_applied is returned if the result after applying the revert is empty. - ChangesAlreadyApplied *ChangesAlreadyAppliedError `protobuf:"bytes,2,opt,name=changes_already_applied,json=changesAlreadyApplied,proto3,oneof"` -} - -type UserRevertError_CustomHook struct { - // custom_hook contains the error message if the pre-receive hook failed. - CustomHook *CustomHookError `protobuf:"bytes,3,opt,name=custom_hook,json=customHook,proto3,oneof"` -} - -type UserRevertError_NotAncestor struct { - // not_ancestor is returned if the old tip of the target branch is not an ancestor of the new commit. - NotAncestor *NotAncestorError `protobuf:"bytes,4,opt,name=not_ancestor,json=notAncestor,proto3,oneof"` -} - -func (*UserRevertError_MergeConflict) isUserRevertError_Error() {} - -func (*UserRevertError_ChangesAlreadyApplied) isUserRevertError_Error() {} - -func (*UserRevertError_CustomHook) isUserRevertError_Error() {} - -func (*UserRevertError_NotAncestor) isUserRevertError_Error() {} - // UserCommitFilesActionHeader contains the details of the action to be performed. type UserCommitFilesActionHeader struct { state protoimpl.MessageState @@ -2688,7 +2574,7 @@ type UserCommitFilesActionHeader struct { func (x *UserCommitFilesActionHeader) Reset() { *x = UserCommitFilesActionHeader{} if protoimpl.UnsafeEnabled { - mi := &file_operations_proto_msgTypes[29] + mi := &file_operations_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2701,7 +2587,7 @@ func (x *UserCommitFilesActionHeader) String() string { func (*UserCommitFilesActionHeader) ProtoMessage() {} func (x *UserCommitFilesActionHeader) ProtoReflect() protoreflect.Message { - mi := &file_operations_proto_msgTypes[29] + mi := &file_operations_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2714,7 +2600,7 @@ func (x *UserCommitFilesActionHeader) ProtoReflect() protoreflect.Message { // Deprecated: Use UserCommitFilesActionHeader.ProtoReflect.Descriptor instead. func (*UserCommitFilesActionHeader) Descriptor() ([]byte, []int) { - return file_operations_proto_rawDescGZIP(), []int{29} + return file_operations_proto_rawDescGZIP(), []int{28} } func (x *UserCommitFilesActionHeader) GetAction() UserCommitFilesActionHeader_ActionType { @@ -2775,7 +2661,7 @@ type UserCommitFilesAction struct { func (x *UserCommitFilesAction) Reset() { *x = UserCommitFilesAction{} if protoimpl.UnsafeEnabled { - mi := &file_operations_proto_msgTypes[30] + mi := &file_operations_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2788,7 +2674,7 @@ func (x *UserCommitFilesAction) String() string { func (*UserCommitFilesAction) ProtoMessage() {} func (x *UserCommitFilesAction) ProtoReflect() protoreflect.Message { - mi := &file_operations_proto_msgTypes[30] + mi := &file_operations_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2801,7 +2687,7 @@ func (x *UserCommitFilesAction) ProtoReflect() protoreflect.Message { // Deprecated: Use UserCommitFilesAction.ProtoReflect.Descriptor instead. func (*UserCommitFilesAction) Descriptor() ([]byte, []int) { - return file_operations_proto_rawDescGZIP(), []int{30} + return file_operations_proto_rawDescGZIP(), []int{29} } func (m *UserCommitFilesAction) GetUserCommitFilesActionPayload() isUserCommitFilesAction_UserCommitFilesActionPayload { @@ -2898,7 +2784,7 @@ type UserCommitFilesRequestHeader struct { func (x *UserCommitFilesRequestHeader) Reset() { *x = UserCommitFilesRequestHeader{} if protoimpl.UnsafeEnabled { - mi := &file_operations_proto_msgTypes[31] + mi := &file_operations_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2911,7 +2797,7 @@ func (x *UserCommitFilesRequestHeader) String() string { func (*UserCommitFilesRequestHeader) ProtoMessage() {} func (x *UserCommitFilesRequestHeader) ProtoReflect() protoreflect.Message { - mi := &file_operations_proto_msgTypes[31] + mi := &file_operations_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2924,7 +2810,7 @@ func (x *UserCommitFilesRequestHeader) ProtoReflect() protoreflect.Message { // Deprecated: Use UserCommitFilesRequestHeader.ProtoReflect.Descriptor instead. func (*UserCommitFilesRequestHeader) Descriptor() ([]byte, []int) { - return file_operations_proto_rawDescGZIP(), []int{31} + return file_operations_proto_rawDescGZIP(), []int{30} } func (x *UserCommitFilesRequestHeader) GetRepository() *Repository { @@ -3027,7 +2913,7 @@ type UserCommitFilesRequest struct { func (x *UserCommitFilesRequest) Reset() { *x = UserCommitFilesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_operations_proto_msgTypes[32] + mi := &file_operations_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3040,7 +2926,7 @@ func (x *UserCommitFilesRequest) String() string { func (*UserCommitFilesRequest) ProtoMessage() {} func (x *UserCommitFilesRequest) ProtoReflect() protoreflect.Message { - mi := &file_operations_proto_msgTypes[32] + mi := &file_operations_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3053,7 +2939,7 @@ func (x *UserCommitFilesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UserCommitFilesRequest.ProtoReflect.Descriptor instead. func (*UserCommitFilesRequest) Descriptor() ([]byte, []int) { - return file_operations_proto_rawDescGZIP(), []int{32} + return file_operations_proto_rawDescGZIP(), []int{31} } func (m *UserCommitFilesRequest) GetUserCommitFilesRequestPayload() isUserCommitFilesRequest_UserCommitFilesRequestPayload { @@ -3114,7 +3000,7 @@ type UserCommitFilesResponse struct { func (x *UserCommitFilesResponse) Reset() { *x = UserCommitFilesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_operations_proto_msgTypes[33] + mi := &file_operations_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3127,7 +3013,7 @@ func (x *UserCommitFilesResponse) String() string { func (*UserCommitFilesResponse) ProtoMessage() {} func (x *UserCommitFilesResponse) ProtoReflect() protoreflect.Message { - mi := &file_operations_proto_msgTypes[33] + mi := &file_operations_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3140,7 +3026,7 @@ func (x *UserCommitFilesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UserCommitFilesResponse.ProtoReflect.Descriptor instead. func (*UserCommitFilesResponse) Descriptor() ([]byte, []int) { - return file_operations_proto_rawDescGZIP(), []int{33} + return file_operations_proto_rawDescGZIP(), []int{32} } func (x *UserCommitFilesResponse) GetBranchUpdate() *OperationBranchUpdate { @@ -3182,7 +3068,7 @@ type UserCommitFilesError struct { func (x *UserCommitFilesError) Reset() { *x = UserCommitFilesError{} if protoimpl.UnsafeEnabled { - mi := &file_operations_proto_msgTypes[34] + mi := &file_operations_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3195,7 +3081,7 @@ func (x *UserCommitFilesError) String() string { func (*UserCommitFilesError) ProtoMessage() {} func (x *UserCommitFilesError) ProtoReflect() protoreflect.Message { - mi := &file_operations_proto_msgTypes[34] + mi := &file_operations_proto_msgTypes[33] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3208,7 +3094,7 @@ func (x *UserCommitFilesError) ProtoReflect() protoreflect.Message { // Deprecated: Use UserCommitFilesError.ProtoReflect.Descriptor instead. func (*UserCommitFilesError) Descriptor() ([]byte, []int) { - return file_operations_proto_rawDescGZIP(), []int{34} + return file_operations_proto_rawDescGZIP(), []int{33} } func (m *UserCommitFilesError) GetError() isUserCommitFilesError_Error { @@ -3282,7 +3168,7 @@ type UserRebaseConfirmableRequest struct { func (x *UserRebaseConfirmableRequest) Reset() { *x = UserRebaseConfirmableRequest{} if protoimpl.UnsafeEnabled { - mi := &file_operations_proto_msgTypes[35] + mi := &file_operations_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3295,7 +3181,7 @@ func (x *UserRebaseConfirmableRequest) String() string { func (*UserRebaseConfirmableRequest) ProtoMessage() {} func (x *UserRebaseConfirmableRequest) ProtoReflect() protoreflect.Message { - mi := &file_operations_proto_msgTypes[35] + mi := &file_operations_proto_msgTypes[34] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3308,7 +3194,7 @@ func (x *UserRebaseConfirmableRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UserRebaseConfirmableRequest.ProtoReflect.Descriptor instead. func (*UserRebaseConfirmableRequest) Descriptor() ([]byte, []int) { - return file_operations_proto_rawDescGZIP(), []int{35} + return file_operations_proto_rawDescGZIP(), []int{34} } func (m *UserRebaseConfirmableRequest) GetUserRebaseConfirmableRequestPayload() isUserRebaseConfirmableRequest_UserRebaseConfirmableRequestPayload { @@ -3370,7 +3256,7 @@ type UserRebaseConfirmableResponse struct { func (x *UserRebaseConfirmableResponse) Reset() { *x = UserRebaseConfirmableResponse{} if protoimpl.UnsafeEnabled { - mi := &file_operations_proto_msgTypes[36] + mi := &file_operations_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3383,7 +3269,7 @@ func (x *UserRebaseConfirmableResponse) String() string { func (*UserRebaseConfirmableResponse) ProtoMessage() {} func (x *UserRebaseConfirmableResponse) ProtoReflect() protoreflect.Message { - mi := &file_operations_proto_msgTypes[36] + mi := &file_operations_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3396,7 +3282,7 @@ func (x *UserRebaseConfirmableResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UserRebaseConfirmableResponse.ProtoReflect.Descriptor instead. func (*UserRebaseConfirmableResponse) Descriptor() ([]byte, []int) { - return file_operations_proto_rawDescGZIP(), []int{36} + return file_operations_proto_rawDescGZIP(), []int{35} } func (m *UserRebaseConfirmableResponse) GetUserRebaseConfirmableResponsePayload() isUserRebaseConfirmableResponse_UserRebaseConfirmableResponsePayload { @@ -3472,7 +3358,7 @@ type UserSquashRequest struct { func (x *UserSquashRequest) Reset() { *x = UserSquashRequest{} if protoimpl.UnsafeEnabled { - mi := &file_operations_proto_msgTypes[37] + mi := &file_operations_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3485,7 +3371,7 @@ func (x *UserSquashRequest) String() string { func (*UserSquashRequest) ProtoMessage() {} func (x *UserSquashRequest) ProtoReflect() protoreflect.Message { - mi := &file_operations_proto_msgTypes[37] + mi := &file_operations_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3498,7 +3384,7 @@ func (x *UserSquashRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UserSquashRequest.ProtoReflect.Descriptor instead. func (*UserSquashRequest) Descriptor() ([]byte, []int) { - return file_operations_proto_rawDescGZIP(), []int{37} + return file_operations_proto_rawDescGZIP(), []int{36} } func (x *UserSquashRequest) GetRepository() *Repository { @@ -3563,7 +3449,7 @@ type UserSquashResponse struct { func (x *UserSquashResponse) Reset() { *x = UserSquashResponse{} if protoimpl.UnsafeEnabled { - mi := &file_operations_proto_msgTypes[38] + mi := &file_operations_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3576,7 +3462,7 @@ func (x *UserSquashResponse) String() string { func (*UserSquashResponse) ProtoMessage() {} func (x *UserSquashResponse) ProtoReflect() protoreflect.Message { - mi := &file_operations_proto_msgTypes[38] + mi := &file_operations_proto_msgTypes[37] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3589,7 +3475,7 @@ func (x *UserSquashResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UserSquashResponse.ProtoReflect.Descriptor instead. func (*UserSquashResponse) Descriptor() ([]byte, []int) { - return file_operations_proto_rawDescGZIP(), []int{38} + return file_operations_proto_rawDescGZIP(), []int{37} } func (x *UserSquashResponse) GetSquashSha() string { @@ -3615,7 +3501,7 @@ type UserRebaseConfirmableError struct { func (x *UserRebaseConfirmableError) Reset() { *x = UserRebaseConfirmableError{} if protoimpl.UnsafeEnabled { - mi := &file_operations_proto_msgTypes[39] + mi := &file_operations_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3628,7 +3514,7 @@ func (x *UserRebaseConfirmableError) String() string { func (*UserRebaseConfirmableError) ProtoMessage() {} func (x *UserRebaseConfirmableError) ProtoReflect() protoreflect.Message { - mi := &file_operations_proto_msgTypes[39] + mi := &file_operations_proto_msgTypes[38] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3641,7 +3527,7 @@ func (x *UserRebaseConfirmableError) ProtoReflect() protoreflect.Message { // Deprecated: Use UserRebaseConfirmableError.ProtoReflect.Descriptor instead. func (*UserRebaseConfirmableError) Descriptor() ([]byte, []int) { - return file_operations_proto_rawDescGZIP(), []int{39} + return file_operations_proto_rawDescGZIP(), []int{38} } func (m *UserRebaseConfirmableError) GetError() isUserRebaseConfirmableError_Error { @@ -3702,7 +3588,7 @@ type UserSquashError struct { func (x *UserSquashError) Reset() { *x = UserSquashError{} if protoimpl.UnsafeEnabled { - mi := &file_operations_proto_msgTypes[40] + mi := &file_operations_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3715,7 +3601,7 @@ func (x *UserSquashError) String() string { func (*UserSquashError) ProtoMessage() {} func (x *UserSquashError) ProtoReflect() protoreflect.Message { - mi := &file_operations_proto_msgTypes[40] + mi := &file_operations_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3728,7 +3614,7 @@ func (x *UserSquashError) ProtoReflect() protoreflect.Message { // Deprecated: Use UserSquashError.ProtoReflect.Descriptor instead. func (*UserSquashError) Descriptor() ([]byte, []int) { - return file_operations_proto_rawDescGZIP(), []int{40} + return file_operations_proto_rawDescGZIP(), []int{39} } func (m *UserSquashError) GetError() isUserSquashError_Error { @@ -3788,7 +3674,7 @@ type UserApplyPatchRequest struct { func (x *UserApplyPatchRequest) Reset() { *x = UserApplyPatchRequest{} if protoimpl.UnsafeEnabled { - mi := &file_operations_proto_msgTypes[41] + mi := &file_operations_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3801,7 +3687,7 @@ func (x *UserApplyPatchRequest) String() string { func (*UserApplyPatchRequest) ProtoMessage() {} func (x *UserApplyPatchRequest) ProtoReflect() protoreflect.Message { - mi := &file_operations_proto_msgTypes[41] + mi := &file_operations_proto_msgTypes[40] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3814,7 +3700,7 @@ func (x *UserApplyPatchRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UserApplyPatchRequest.ProtoReflect.Descriptor instead. func (*UserApplyPatchRequest) Descriptor() ([]byte, []int) { - return file_operations_proto_rawDescGZIP(), []int{41} + return file_operations_proto_rawDescGZIP(), []int{40} } func (m *UserApplyPatchRequest) GetUserApplyPatchRequestPayload() isUserApplyPatchRequest_UserApplyPatchRequestPayload { @@ -3870,7 +3756,7 @@ type UserApplyPatchResponse struct { func (x *UserApplyPatchResponse) Reset() { *x = UserApplyPatchResponse{} if protoimpl.UnsafeEnabled { - mi := &file_operations_proto_msgTypes[42] + mi := &file_operations_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3883,7 +3769,7 @@ func (x *UserApplyPatchResponse) String() string { func (*UserApplyPatchResponse) ProtoMessage() {} func (x *UserApplyPatchResponse) ProtoReflect() protoreflect.Message { - mi := &file_operations_proto_msgTypes[42] + mi := &file_operations_proto_msgTypes[41] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3896,7 +3782,7 @@ func (x *UserApplyPatchResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UserApplyPatchResponse.ProtoReflect.Descriptor instead. func (*UserApplyPatchResponse) Descriptor() ([]byte, []int) { - return file_operations_proto_rawDescGZIP(), []int{42} + return file_operations_proto_rawDescGZIP(), []int{41} } func (x *UserApplyPatchResponse) GetBranchUpdate() *OperationBranchUpdate { @@ -3944,7 +3830,7 @@ type UserUpdateSubmoduleRequest struct { func (x *UserUpdateSubmoduleRequest) Reset() { *x = UserUpdateSubmoduleRequest{} if protoimpl.UnsafeEnabled { - mi := &file_operations_proto_msgTypes[43] + mi := &file_operations_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3957,7 +3843,7 @@ func (x *UserUpdateSubmoduleRequest) String() string { func (*UserUpdateSubmoduleRequest) ProtoMessage() {} func (x *UserUpdateSubmoduleRequest) ProtoReflect() protoreflect.Message { - mi := &file_operations_proto_msgTypes[43] + mi := &file_operations_proto_msgTypes[42] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3970,7 +3856,7 @@ func (x *UserUpdateSubmoduleRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UserUpdateSubmoduleRequest.ProtoReflect.Descriptor instead. func (*UserUpdateSubmoduleRequest) Descriptor() ([]byte, []int) { - return file_operations_proto_rawDescGZIP(), []int{43} + return file_operations_proto_rawDescGZIP(), []int{42} } func (x *UserUpdateSubmoduleRequest) GetRepository() *Repository { @@ -4047,7 +3933,7 @@ type UserUpdateSubmoduleResponse struct { func (x *UserUpdateSubmoduleResponse) Reset() { *x = UserUpdateSubmoduleResponse{} if protoimpl.UnsafeEnabled { - mi := &file_operations_proto_msgTypes[44] + mi := &file_operations_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4060,7 +3946,7 @@ func (x *UserUpdateSubmoduleResponse) String() string { func (*UserUpdateSubmoduleResponse) ProtoMessage() {} func (x *UserUpdateSubmoduleResponse) ProtoReflect() protoreflect.Message { - mi := &file_operations_proto_msgTypes[44] + mi := &file_operations_proto_msgTypes[43] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4073,7 +3959,7 @@ func (x *UserUpdateSubmoduleResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UserUpdateSubmoduleResponse.ProtoReflect.Descriptor instead. func (*UserUpdateSubmoduleResponse) Descriptor() ([]byte, []int) { - return file_operations_proto_rawDescGZIP(), []int{44} + return file_operations_proto_rawDescGZIP(), []int{43} } func (x *UserUpdateSubmoduleResponse) GetBranchUpdate() *OperationBranchUpdate { @@ -4137,7 +4023,7 @@ type UserRebaseConfirmableRequest_Header struct { func (x *UserRebaseConfirmableRequest_Header) Reset() { *x = UserRebaseConfirmableRequest_Header{} if protoimpl.UnsafeEnabled { - mi := &file_operations_proto_msgTypes[45] + mi := &file_operations_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4150,7 +4036,7 @@ func (x *UserRebaseConfirmableRequest_Header) String() string { func (*UserRebaseConfirmableRequest_Header) ProtoMessage() {} func (x *UserRebaseConfirmableRequest_Header) ProtoReflect() protoreflect.Message { - mi := &file_operations_proto_msgTypes[45] + mi := &file_operations_proto_msgTypes[44] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4163,7 +4049,7 @@ func (x *UserRebaseConfirmableRequest_Header) ProtoReflect() protoreflect.Messag // Deprecated: Use UserRebaseConfirmableRequest_Header.ProtoReflect.Descriptor instead. func (*UserRebaseConfirmableRequest_Header) Descriptor() ([]byte, []int) { - return file_operations_proto_rawDescGZIP(), []int{35, 0} + return file_operations_proto_rawDescGZIP(), []int{34, 0} } func (x *UserRebaseConfirmableRequest_Header) GetRepository() *Repository { @@ -4260,7 +4146,7 @@ type UserApplyPatchRequest_Header struct { func (x *UserApplyPatchRequest_Header) Reset() { *x = UserApplyPatchRequest_Header{} if protoimpl.UnsafeEnabled { - mi := &file_operations_proto_msgTypes[46] + mi := &file_operations_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4273,7 +4159,7 @@ func (x *UserApplyPatchRequest_Header) String() string { func (*UserApplyPatchRequest_Header) ProtoMessage() {} func (x *UserApplyPatchRequest_Header) ProtoReflect() protoreflect.Message { - mi := &file_operations_proto_msgTypes[46] + mi := &file_operations_proto_msgTypes[45] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4286,7 +4172,7 @@ func (x *UserApplyPatchRequest_Header) ProtoReflect() protoreflect.Message { // Deprecated: Use UserApplyPatchRequest_Header.ProtoReflect.Descriptor instead. func (*UserApplyPatchRequest_Header) Descriptor() ([]byte, []int) { - return file_operations_proto_rawDescGZIP(), []int{41, 0} + return file_operations_proto_rawDescGZIP(), []int{40, 0} } func (x *UserApplyPatchRequest_Header) GetRepository() *Repository { @@ -4708,386 +4594,367 @@ var file_operations_proto_rawDesc = []byte{ 0x61, 0x74, 0x65, 0x54, 0x72, 0x65, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x4d, 0x50, 0x54, 0x59, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x4f, 0x4e, 0x46, 0x4c, 0x49, 0x43, 0x54, 0x10, 0x02, 0x22, - 0xb8, 0x02, 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x76, 0x65, 0x72, 0x74, 0x45, 0x72, - 0x72, 0x6f, 0x72, 0x12, 0x43, 0x0a, 0x0e, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, - 0x66, 0x6c, 0x69, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x69, - 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, - 0x63, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0d, 0x6d, 0x65, 0x72, 0x67, 0x65, - 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x12, 0x5c, 0x0a, 0x17, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x73, 0x5f, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x61, 0x70, 0x70, 0x6c, - 0x69, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x69, 0x74, 0x61, - 0x6c, 0x79, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x41, 0x6c, 0x72, 0x65, 0x61, 0x64, - 0x79, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, - 0x15, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x41, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x41, - 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x12, 0x3a, 0x0a, 0x0b, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, - 0x5f, 0x68, 0x6f, 0x6f, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x69, - 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x48, 0x6f, 0x6f, 0x6b, 0x45, - 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x48, 0x6f, - 0x6f, 0x6b, 0x12, 0x3d, 0x0a, 0x0c, 0x6e, 0x6f, 0x74, 0x5f, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x74, - 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, - 0x79, 0x2e, 0x4e, 0x6f, 0x74, 0x41, 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x45, 0x72, 0x72, - 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0b, 0x6e, 0x6f, 0x74, 0x41, 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, - 0x72, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xf5, 0x02, 0x0a, 0x1b, 0x55, - 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x46, 0x0a, 0x06, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2e, 0x2e, 0x67, 0x69, 0x74, - 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, - 0x6c, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2e, - 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, - 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x70, 0x61, 0x74, 0x68, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, - 0x50, 0x61, 0x74, 0x68, 0x12, 0x25, 0x0a, 0x0e, 0x62, 0x61, 0x73, 0x65, 0x36, 0x34, 0x5f, 0x63, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x62, 0x61, - 0x73, 0x65, 0x36, 0x34, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x65, - 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x6d, 0x6f, 0x64, 0x65, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x46, 0x69, - 0x6c, 0x65, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x66, 0x65, 0x72, 0x5f, - 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, - 0x6e, 0x66, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, 0x55, 0x0a, 0x0a, 0x41, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x52, 0x45, - 0x41, 0x54, 0x45, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x5f, - 0x44, 0x49, 0x52, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x10, - 0x02, 0x12, 0x08, 0x0a, 0x04, 0x4d, 0x4f, 0x56, 0x45, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x44, - 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x43, 0x48, 0x4d, 0x4f, 0x44, - 0x10, 0x05, 0x22, 0x96, 0x01, 0x0a, 0x15, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, - 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, 0x06, - 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x67, - 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, - 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x48, 0x00, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x07, 0x63, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, - 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x42, 0x22, 0x0a, 0x20, 0x75, 0x73, 0x65, 0x72, 0x5f, - 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0xa2, 0x04, 0x0a, 0x1c, - 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x0a, + 0xf5, 0x02, 0x0a, 0x1b, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, + 0x6c, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, + 0x46, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x2e, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, + 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, + 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, + 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, + 0x50, 0x61, 0x74, 0x68, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, + 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x70, 0x72, 0x65, + 0x76, 0x69, 0x6f, 0x75, 0x73, 0x50, 0x61, 0x74, 0x68, 0x12, 0x25, 0x0a, 0x0e, 0x62, 0x61, 0x73, + 0x65, 0x36, 0x34, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0d, 0x62, 0x61, 0x73, 0x65, 0x36, 0x34, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x12, 0x29, 0x0a, 0x10, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, + 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x65, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x69, + 0x6e, 0x66, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0c, 0x69, 0x6e, 0x66, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x22, 0x55, 0x0a, 0x0a, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, + 0x0a, 0x06, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x52, + 0x45, 0x41, 0x54, 0x45, 0x5f, 0x44, 0x49, 0x52, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x50, + 0x44, 0x41, 0x54, 0x45, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x4d, 0x4f, 0x56, 0x45, 0x10, 0x03, + 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, + 0x43, 0x48, 0x4d, 0x4f, 0x44, 0x10, 0x05, 0x22, 0x96, 0x01, 0x0a, 0x15, 0x55, 0x73, 0x65, 0x72, + 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x3d, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x23, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, + 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x12, 0x1a, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x42, 0x22, 0x0a, 0x20, + 0x75, 0x73, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x65, + 0x73, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, + 0x22, 0xa2, 0x04, 0x0a, 0x1c, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, + 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x12, 0x38, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, + 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x42, 0x04, 0x98, 0xc6, 0x2c, 0x01, 0x52, + 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x04, 0x75, + 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x69, 0x74, 0x61, + 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x1f, 0x0a, + 0x0b, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x0a, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, + 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, + 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x10, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x61, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x11, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x45, 0x6d, + 0x61, 0x69, 0x6c, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x62, 0x72, 0x61, + 0x6e, 0x63, 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x3d, 0x0a, 0x10, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x6f, 0x72, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69, 0x74, 0x61, + 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x0f, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x14, + 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, + 0x6f, 0x72, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x73, 0x68, + 0x61, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x61, 0x72, 0x74, 0x53, 0x68, + 0x61, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x0b, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x28, 0x0a, 0x10, 0x65, + 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x6c, 0x64, 0x5f, 0x6f, 0x69, 0x64, 0x18, + 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4f, + 0x6c, 0x64, 0x4f, 0x69, 0x64, 0x22, 0xb6, 0x01, 0x0a, 0x16, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x3e, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x24, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x12, 0x37, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, + 0x00, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x23, 0x0a, 0x21, 0x75, 0x73, 0x65, + 0x72, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0xaa, + 0x01, 0x0a, 0x17, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x0d, 0x62, 0x72, + 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x52, 0x0c, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1f, + 0x0a, 0x0b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, + 0x2a, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, 0x65, 0x52, + 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xd3, 0x01, 0x0a, 0x14, + 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x45, + 0x72, 0x72, 0x6f, 0x72, 0x12, 0x3d, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, + 0x68, 0x65, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x69, 0x74, + 0x61, 0x6c, 0x79, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x45, + 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x68, + 0x65, 0x63, 0x6b, 0x12, 0x37, 0x0a, 0x0c, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69, 0x74, 0x61, + 0x6c, 0x79, 0x2e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, + 0x0b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x3a, 0x0a, 0x0b, + 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x68, 0x6f, 0x6f, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, + 0x6d, 0x48, 0x6f, 0x6f, 0x6b, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0a, 0x63, 0x75, + 0x73, 0x74, 0x6f, 0x6d, 0x48, 0x6f, 0x6f, 0x6b, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x22, 0xb1, 0x04, 0x0a, 0x1c, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x45, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, + 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x62, 0x6c, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, + 0x00, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x05, 0x61, 0x70, 0x70, + 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x05, 0x61, 0x70, 0x70, 0x6c, + 0x79, 0x1a, 0x86, 0x03, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x42, 0x04, 0x98, 0xc6, 0x2c, 0x01, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, - 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x72, 0x61, 0x6e, - 0x63, 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x62, - 0x72, 0x61, 0x6e, 0x63, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, - 0x6d, 0x69, 0x74, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x0d, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, - 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x63, 0x6f, - 0x6d, 0x6d, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2e, - 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x5f, - 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x63, 0x6f, 0x6d, - 0x6d, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x2a, - 0x0a, 0x11, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3d, 0x0a, 0x10, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, - 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x0f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x52, - 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, - 0x63, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x12, - 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x73, 0x68, 0x61, 0x18, 0x0a, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x61, 0x72, 0x74, 0x53, 0x68, 0x61, 0x12, 0x38, 0x0a, 0x09, - 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x09, 0x72, 0x65, 0x62, 0x61, + 0x73, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, + 0x08, 0x72, 0x65, 0x62, 0x61, 0x73, 0x65, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x72, 0x61, + 0x6e, 0x63, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, + 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x73, 0x68, 0x61, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x53, 0x68, 0x61, + 0x12, 0x3f, 0x0a, 0x11, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69, + 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x52, + 0x10, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, + 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x62, 0x72, 0x61, 0x6e, + 0x63, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, + 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x28, 0x0a, 0x10, 0x67, 0x69, 0x74, 0x5f, 0x70, 0x75, + 0x73, 0x68, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x0e, 0x67, 0x69, 0x74, 0x50, 0x75, 0x73, 0x68, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x29, 0x0a, 0x27, 0x75, 0x73, + 0x65, 0x72, 0x5f, 0x72, 0x65, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, + 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0xbf, 0x01, 0x0a, 0x1d, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, + 0x62, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0a, 0x72, 0x65, 0x62, 0x61, 0x73, + 0x65, 0x5f, 0x73, 0x68, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x09, 0x72, + 0x65, 0x62, 0x61, 0x73, 0x65, 0x53, 0x68, 0x61, 0x12, 0x27, 0x0a, 0x0e, 0x72, 0x65, 0x62, 0x61, + 0x73, 0x65, 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, + 0x48, 0x00, 0x52, 0x0d, 0x72, 0x65, 0x62, 0x61, 0x73, 0x65, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, + 0x64, 0x42, 0x2a, 0x0a, 0x28, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x62, 0x61, 0x73, 0x65, + 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x4a, 0x04, 0x08, + 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x52, 0x11, 0x70, 0x72, 0x65, 0x5f, 0x72, + 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x09, 0x67, 0x69, + 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xc3, 0x02, 0x0a, 0x11, 0x55, 0x73, 0x65, 0x72, + 0x53, 0x71, 0x75, 0x61, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, + 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x6f, 0x72, 0x79, 0x42, 0x04, 0x98, 0xc6, 0x2c, 0x01, 0x52, 0x0a, 0x72, 0x65, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, + 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x5f, 0x73, 0x68, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x53, 0x68, 0x61, 0x12, 0x17, 0x0a, 0x07, 0x65, 0x6e, 0x64, 0x5f, 0x73, 0x68, + 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x6e, 0x64, 0x53, 0x68, 0x61, 0x12, + 0x24, 0x0a, 0x06, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x0c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x06, 0x61, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x63, + 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x38, 0x0a, 0x09, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x28, 0x0a, 0x10, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, - 0x65, 0x64, 0x5f, 0x6f, 0x6c, 0x64, 0x5f, 0x6f, 0x69, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4f, 0x6c, 0x64, 0x4f, 0x69, 0x64, - 0x22, 0xb6, 0x01, 0x0a, 0x16, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, - 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3e, 0x0a, 0x06, 0x68, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x69, - 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, - 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x48, 0x00, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x37, 0x0a, 0x06, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x69, - 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, - 0x69, 0x6c, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x06, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x23, 0x0a, 0x21, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6d, - 0x6d, 0x69, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0xaa, 0x01, 0x0a, 0x17, 0x55, 0x73, - 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x0d, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, - 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, - 0x72, 0x61, 0x6e, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x0c, 0x62, 0x72, 0x61, - 0x6e, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x64, - 0x65, 0x78, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, - 0x69, 0x6e, 0x64, 0x65, 0x78, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x72, - 0x65, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, - 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xd3, 0x01, 0x0a, 0x14, 0x55, 0x73, 0x65, 0x72, 0x43, - 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, - 0x3d, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x41, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, - 0x00, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x37, - 0x0a, 0x0c, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x49, 0x6e, - 0x64, 0x65, 0x78, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0b, 0x69, 0x6e, 0x64, 0x65, - 0x78, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x3a, 0x0a, 0x0b, 0x63, 0x75, 0x73, 0x74, 0x6f, - 0x6d, 0x5f, 0x68, 0x6f, 0x6f, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, - 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x48, 0x6f, 0x6f, 0x6b, - 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x48, - 0x6f, 0x6f, 0x6b, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xb1, 0x04, 0x0a, - 0x1c, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x72, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x45, 0x0a, - 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, - 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x62, 0x61, 0x73, - 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x06, 0x68, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x1a, 0x86, 0x03, 0x0a, - 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, - 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69, - 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x42, - 0x04, 0x98, 0xc6, 0x2c, 0x01, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, - 0x79, 0x12, 0x20, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x0c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, - 0x73, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x09, 0x72, 0x65, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x69, 0x64, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x08, 0x72, 0x65, 0x62, 0x61, - 0x73, 0x65, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x1d, 0x0a, 0x0a, - 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x73, 0x68, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x53, 0x68, 0x61, 0x12, 0x3f, 0x0a, 0x11, 0x72, - 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, - 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x10, 0x72, 0x65, 0x6d, 0x6f, - 0x74, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x23, 0x0a, 0x0d, - 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, - 0x68, 0x12, 0x28, 0x0a, 0x10, 0x67, 0x69, 0x74, 0x5f, 0x70, 0x75, 0x73, 0x68, 0x5f, 0x6f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x67, 0x69, 0x74, - 0x50, 0x75, 0x73, 0x68, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x38, 0x0a, 0x09, 0x74, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, + 0x10, 0x05, 0x52, 0x09, 0x73, 0x71, 0x75, 0x61, 0x73, 0x68, 0x5f, 0x69, 0x64, 0x22, 0x5d, 0x0a, + 0x12, 0x55, 0x73, 0x65, 0x72, 0x53, 0x71, 0x75, 0x61, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x71, 0x75, 0x61, 0x73, 0x68, 0x5f, 0x73, 0x68, + 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x71, 0x75, 0x61, 0x73, 0x68, 0x53, + 0x68, 0x61, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x52, 0x11, + 0x70, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x52, 0x09, 0x67, 0x69, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xab, 0x01, 0x0a, + 0x1a, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x72, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x45, 0x0a, 0x0f, 0x72, + 0x65, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4d, 0x65, + 0x72, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, + 0x48, 0x00, 0x52, 0x0e, 0x72, 0x65, 0x62, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, + 0x63, 0x74, 0x12, 0x3d, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x68, 0x65, + 0x63, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, + 0x79, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x45, 0x72, 0x72, + 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xac, 0x01, 0x0a, 0x0f, 0x55, + 0x73, 0x65, 0x72, 0x53, 0x71, 0x75, 0x61, 0x73, 0x68, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x49, + 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, + 0x79, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, + 0x65, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x45, 0x0a, 0x0f, 0x72, 0x65, 0x62, + 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4d, 0x65, 0x72, 0x67, + 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, + 0x52, 0x0e, 0x72, 0x65, 0x62, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, + 0x42, 0x07, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x87, 0x03, 0x0a, 0x15, 0x55, 0x73, + 0x65, 0x72, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x3e, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, + 0x72, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x06, 0x68, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x1a, + 0xed, 0x01, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x0a, 0x72, 0x65, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, + 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, + 0x72, 0x79, 0x42, 0x04, 0x98, 0xc6, 0x2c, 0x01, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x6f, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, + 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x5f, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x74, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x38, 0x0a, 0x09, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x29, 0x0a, 0x27, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x72, 0x65, - 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x62, 0x6c, 0x65, - 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, - 0x22, 0xbf, 0x01, 0x0a, 0x1d, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0a, 0x72, 0x65, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x73, 0x68, 0x61, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x09, 0x72, 0x65, 0x62, 0x61, 0x73, 0x65, - 0x53, 0x68, 0x61, 0x12, 0x27, 0x0a, 0x0e, 0x72, 0x65, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x61, 0x70, - 0x70, 0x6c, 0x69, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x0d, 0x72, - 0x65, 0x62, 0x61, 0x73, 0x65, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x42, 0x2a, 0x0a, 0x28, - 0x75, 0x73, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x72, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, - 0x08, 0x04, 0x10, 0x05, 0x52, 0x11, 0x70, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, - 0x65, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x09, 0x67, 0x69, 0x74, 0x5f, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x22, 0xc3, 0x02, 0x0a, 0x11, 0x55, 0x73, 0x65, 0x72, 0x53, 0x71, 0x75, 0x61, 0x73, - 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, - 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, - 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, - 0x42, 0x04, 0x98, 0xc6, 0x2c, 0x01, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, - 0x72, 0x79, 0x12, 0x20, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, - 0x75, 0x73, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x73, 0x68, - 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x61, 0x72, 0x74, 0x53, 0x68, - 0x61, 0x12, 0x17, 0x0a, 0x07, 0x65, 0x6e, 0x64, 0x5f, 0x73, 0x68, 0x61, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x65, 0x6e, 0x64, 0x53, 0x68, 0x61, 0x12, 0x24, 0x0a, 0x06, 0x61, 0x75, - 0x74, 0x68, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x69, 0x74, - 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x06, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, - 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x52, 0x09, 0x73, - 0x71, 0x75, 0x61, 0x73, 0x68, 0x5f, 0x69, 0x64, 0x22, 0x5d, 0x0a, 0x12, 0x55, 0x73, 0x65, 0x72, - 0x53, 0x71, 0x75, 0x61, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, - 0x0a, 0x0a, 0x73, 0x71, 0x75, 0x61, 0x73, 0x68, 0x5f, 0x73, 0x68, 0x61, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x73, 0x71, 0x75, 0x61, 0x73, 0x68, 0x53, 0x68, 0x61, 0x4a, 0x04, 0x08, - 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x52, 0x11, 0x70, 0x72, 0x65, 0x5f, 0x72, - 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x09, 0x67, 0x69, - 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xab, 0x01, 0x0a, 0x1a, 0x55, 0x73, 0x65, 0x72, - 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x62, 0x6c, - 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x45, 0x0a, 0x0f, 0x72, 0x65, 0x62, 0x61, 0x73, 0x65, - 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x43, 0x6f, - 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0e, 0x72, - 0x65, 0x62, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x12, 0x3d, 0x0a, - 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x41, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, - 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x42, 0x07, 0x0a, 0x05, - 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xac, 0x01, 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x53, 0x71, - 0x75, 0x61, 0x73, 0x68, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x49, 0x0a, 0x10, 0x72, 0x65, 0x73, - 0x6f, 0x6c, 0x76, 0x65, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x73, - 0x6f, 0x6c, 0x76, 0x65, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, - 0x72, 0x48, 0x00, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x52, 0x65, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x45, 0x0a, 0x0f, 0x72, 0x65, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, - 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, - 0x6c, 0x69, 0x63, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0e, 0x72, 0x65, 0x62, - 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x42, 0x07, 0x0a, 0x05, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x22, 0x87, 0x03, 0x0a, 0x15, 0x55, 0x73, 0x65, 0x72, 0x41, 0x70, 0x70, - 0x6c, 0x79, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3e, - 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, - 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x41, 0x70, 0x70, 0x6c, - 0x79, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x48, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x1a, - 0x0a, 0x07, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, - 0x00, 0x52, 0x07, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x1a, 0xed, 0x01, 0x0a, 0x06, 0x48, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, - 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69, 0x74, 0x61, - 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x42, 0x04, 0x98, - 0xc6, 0x2c, 0x01, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, - 0x20, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, - 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, - 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x62, 0x72, 0x61, 0x6e, - 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x12, 0x28, 0x0a, 0x10, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x6c, 0x64, - 0x5f, 0x6f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x65, 0x78, 0x70, 0x65, - 0x63, 0x74, 0x65, 0x64, 0x4f, 0x6c, 0x64, 0x4f, 0x69, 0x64, 0x42, 0x22, 0x0a, 0x20, 0x75, 0x73, - 0x65, 0x72, 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x5f, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x72, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x5c, - 0x0a, 0x16, 0x55, 0x73, 0x65, 0x72, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x50, 0x61, 0x74, 0x63, 0x68, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x0d, 0x62, 0x72, 0x61, 0x6e, - 0x63, 0x68, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x0c, - 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x22, 0xd8, 0x02, 0x0a, - 0x1a, 0x55, 0x73, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6d, 0x6f, - 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0a, 0x72, - 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x12, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, - 0x6f, 0x72, 0x79, 0x42, 0x04, 0x98, 0xc6, 0x2c, 0x01, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, - 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, - 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, - 0x74, 0x5f, 0x73, 0x68, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6d, - 0x6d, 0x69, 0x74, 0x53, 0x68, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x1c, - 0x0a, 0x09, 0x73, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x09, 0x73, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x25, 0x0a, 0x0e, - 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x28, 0x0a, - 0x10, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x6c, 0x64, 0x5f, 0x6f, 0x69, - 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, - 0x64, 0x4f, 0x6c, 0x64, 0x4f, 0x69, 0x64, 0x22, 0xc9, 0x01, 0x0a, 0x1b, 0x55, 0x73, 0x65, 0x72, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x0d, 0x62, 0x72, 0x61, 0x6e, 0x63, - 0x68, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, - 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x0c, 0x62, - 0x72, 0x61, 0x6e, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x70, - 0x72, 0x65, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, - 0x76, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, - 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, - 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, - 0x52, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x32, 0xc9, 0x0b, 0x0a, 0x10, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5d, 0x0a, 0x10, 0x55, 0x73, 0x65, 0x72, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x1f, 0x2e, 0x67, - 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, - 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x5d, 0x0a, 0x10, 0x55, 0x73, 0x65, 0x72, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x1f, 0x2e, 0x67, 0x69, - 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x42, - 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, - 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, - 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x5d, 0x0a, 0x10, 0x55, 0x73, 0x65, 0x72, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x1f, 0x2e, 0x67, 0x69, 0x74, - 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x72, - 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x69, - 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x42, - 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, - 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x54, 0x0a, 0x0d, 0x55, 0x73, 0x65, 0x72, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x12, 0x1c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, - 0x55, 0x73, 0x65, 0x72, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, - 0x65, 0x72, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x54, 0x0a, 0x0d, 0x55, - 0x73, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x67, 0x12, 0x1c, 0x2e, 0x67, - 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x54, 0x61, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x69, 0x74, - 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, - 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, - 0x01, 0x12, 0x57, 0x0a, 0x0e, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x54, 0x6f, - 0x52, 0x65, 0x66, 0x12, 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, - 0x72, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x54, 0x6f, 0x52, 0x65, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, - 0x4d, 0x65, 0x72, 0x67, 0x65, 0x54, 0x6f, 0x52, 0x65, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x5a, 0x0a, 0x0f, 0x55, 0x73, - 0x65, 0x72, 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, 0x54, 0x6f, 0x52, 0x65, 0x66, 0x12, 0x1e, 0x2e, - 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x62, 0x61, 0x73, - 0x65, 0x54, 0x6f, 0x52, 0x65, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, - 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x62, 0x61, 0x73, - 0x65, 0x54, 0x6f, 0x52, 0x65, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, - 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x5e, 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x65, - 0x72, 0x67, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x1e, 0x2e, 0x67, 0x69, 0x74, 0x61, - 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x42, 0x72, 0x61, 0x6e, - 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x69, 0x74, 0x61, - 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x42, 0x72, 0x61, 0x6e, - 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, - 0x08, 0x01, 0x28, 0x01, 0x30, 0x01, 0x12, 0x51, 0x0a, 0x0c, 0x55, 0x73, 0x65, 0x72, 0x46, 0x46, - 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x1b, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, - 0x55, 0x73, 0x65, 0x72, 0x46, 0x46, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, - 0x72, 0x46, 0x46, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x57, 0x0a, 0x0e, 0x55, 0x73, 0x65, - 0x72, 0x43, 0x68, 0x65, 0x72, 0x72, 0x79, 0x50, 0x69, 0x63, 0x6b, 0x12, 0x1d, 0x2e, 0x67, 0x69, - 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x68, 0x65, 0x72, 0x72, 0x79, 0x50, - 0x69, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x69, 0x74, - 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x68, 0x65, 0x72, 0x72, 0x79, 0x50, 0x69, - 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, - 0x08, 0x01, 0x12, 0x5c, 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, - 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, - 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, - 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x28, 0x01, - 0x12, 0x70, 0x0a, 0x15, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x24, 0x2e, 0x67, 0x69, 0x74, 0x61, - 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x72, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x25, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x62, - 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x28, 0x01, - 0x30, 0x01, 0x12, 0x4b, 0x0a, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x76, 0x65, 0x72, 0x74, - 0x12, 0x19, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, - 0x76, 0x65, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, 0x69, - 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x76, 0x65, 0x72, 0x74, 0x52, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x28, 0x0a, 0x10, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, + 0x64, 0x5f, 0x6f, 0x6c, 0x64, 0x5f, 0x6f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4f, 0x6c, 0x64, 0x4f, 0x69, 0x64, 0x42, + 0x22, 0x0a, 0x20, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x5f, 0x70, 0x61, + 0x74, 0x63, 0x68, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x22, 0x5c, 0x0a, 0x16, 0x55, 0x73, 0x65, 0x72, 0x41, 0x70, 0x70, 0x6c, 0x79, + 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, + 0x0d, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4f, 0x70, + 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x52, 0x0c, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x22, 0xd8, 0x02, 0x0a, 0x1a, 0x55, 0x73, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x53, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x38, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x42, 0x04, 0x98, 0xc6, 0x2c, 0x01, 0x52, 0x0a, + 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x04, 0x75, 0x73, + 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, + 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, + 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x73, 0x68, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x53, 0x68, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x62, + 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x62, 0x72, 0x61, + 0x6e, 0x63, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, + 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x63, 0x6f, 0x6d, 0x6d, 0x69, + 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x12, 0x28, 0x0a, 0x10, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x6f, + 0x6c, 0x64, 0x5f, 0x6f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x65, 0x78, + 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4f, 0x6c, 0x64, 0x4f, 0x69, 0x64, 0x22, 0xc9, 0x01, 0x0a, + 0x1b, 0x55, 0x73, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x0d, + 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4f, 0x70, 0x65, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x52, 0x0c, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, 0x65, + 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x21, 0x0a, 0x0c, + 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4a, + 0x04, 0x08, 0x03, 0x10, 0x04, 0x52, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x72, + 0x65, 0x65, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x32, 0xc9, 0x0b, 0x0a, 0x10, 0x4f, 0x70, 0x65, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5d, 0x0a, + 0x10, 0x55, 0x73, 0x65, 0x72, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, + 0x68, 0x12, 0x1f, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x5d, 0x0a, 0x10, + 0x55, 0x73, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, + 0x12, 0x1f, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x5d, 0x0a, 0x10, 0x55, + 0x73, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, + 0x1f, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x20, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x54, 0x0a, 0x0d, 0x55, 0x73, + 0x65, 0x72, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x12, 0x1c, 0x2e, 0x67, 0x69, + 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, + 0x61, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61, + 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, + 0x12, 0x54, 0x0a, 0x0d, 0x55, 0x73, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, + 0x67, 0x12, 0x1c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x54, 0x61, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, + 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x57, 0x0a, 0x0e, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x65, + 0x72, 0x67, 0x65, 0x54, 0x6f, 0x52, 0x65, 0x66, 0x12, 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, + 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x54, 0x6f, 0x52, 0x65, 0x66, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, + 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x54, 0x6f, 0x52, 0x65, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, - 0x4b, 0x0a, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x53, 0x71, 0x75, 0x61, 0x73, 0x68, 0x12, 0x19, 0x2e, + 0x5a, 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, 0x54, 0x6f, 0x52, + 0x65, 0x66, 0x12, 0x1e, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, + 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, 0x54, 0x6f, 0x52, 0x65, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, + 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, 0x54, 0x6f, 0x52, 0x65, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x5e, 0x0a, 0x0f, 0x55, + 0x73, 0x65, 0x72, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x1e, + 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x65, 0x72, 0x67, + 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, + 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x65, 0x72, 0x67, + 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x28, 0x01, 0x30, 0x01, 0x12, 0x51, 0x0a, 0x0c, 0x55, + 0x73, 0x65, 0x72, 0x46, 0x46, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x1b, 0x2e, 0x67, 0x69, + 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x46, 0x46, 0x42, 0x72, 0x61, 0x6e, 0x63, + 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, + 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x46, 0x46, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x57, + 0x0a, 0x0e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x68, 0x65, 0x72, 0x72, 0x79, 0x50, 0x69, 0x63, 0x6b, + 0x12, 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x68, + 0x65, 0x72, 0x72, 0x79, 0x50, 0x69, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1e, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x68, 0x65, + 0x72, 0x72, 0x79, 0x50, 0x69, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x5c, 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x43, + 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x69, 0x74, + 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, + 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x69, 0x74, + 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, + 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, + 0x02, 0x08, 0x01, 0x28, 0x01, 0x12, 0x70, 0x0a, 0x15, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x62, + 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x24, + 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x62, 0x61, + 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, + 0x65, 0x72, 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, + 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, + 0x02, 0x08, 0x01, 0x28, 0x01, 0x30, 0x01, 0x12, 0x4b, 0x0a, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x52, + 0x65, 0x76, 0x65, 0x72, 0x74, 0x12, 0x19, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, + 0x73, 0x65, 0x72, 0x52, 0x65, 0x76, 0x65, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1a, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, + 0x76, 0x65, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, + 0x28, 0x02, 0x08, 0x01, 0x12, 0x4b, 0x0a, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x53, 0x71, 0x75, 0x61, + 0x73, 0x68, 0x12, 0x19, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, + 0x53, 0x71, 0x75, 0x61, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x53, 0x71, 0x75, 0x61, 0x73, - 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, - 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x53, 0x71, 0x75, 0x61, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x59, 0x0a, 0x0e, - 0x55, 0x73, 0x65, 0x72, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x50, 0x61, 0x74, 0x63, 0x68, 0x12, 0x1d, - 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x41, 0x70, 0x70, 0x6c, - 0x79, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, - 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x41, 0x70, 0x70, 0x6c, 0x79, - 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, - 0x97, 0x28, 0x02, 0x08, 0x01, 0x28, 0x01, 0x12, 0x66, 0x0a, 0x13, 0x55, 0x73, 0x65, 0x72, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x22, - 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x53, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x42, - 0x34, 0x5a, 0x32, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x69, - 0x74, 0x6c, 0x61, 0x62, 0x2d, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2f, - 0x76, 0x31, 0x36, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x67, 0x69, 0x74, - 0x61, 0x6c, 0x79, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, + 0x01, 0x12, 0x59, 0x0a, 0x0e, 0x55, 0x73, 0x65, 0x72, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x50, 0x61, + 0x74, 0x63, 0x68, 0x12, 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, + 0x72, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, + 0x41, 0x70, 0x70, 0x6c, 0x79, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x28, 0x01, 0x12, 0x66, 0x0a, 0x13, + 0x55, 0x73, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6d, 0x6f, 0x64, + 0x75, 0x6c, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, + 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, + 0x2e, 0x55, 0x73, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, + 0x28, 0x02, 0x08, 0x01, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2d, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x69, + 0x74, 0x61, 0x6c, 0x79, 0x2f, 0x76, 0x31, 0x36, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, + 0x6f, 0x2f, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -5103,7 +4970,7 @@ func file_operations_proto_rawDescGZIP() []byte { } var file_operations_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_operations_proto_msgTypes = make([]protoimpl.MessageInfo, 47) +var file_operations_proto_msgTypes = make([]protoimpl.MessageInfo, 46) var file_operations_proto_goTypes = []interface{}{ (UserRevertResponse_CreateTreeError)(0), // 0: gitaly.UserRevertResponse.CreateTreeError (UserCommitFilesActionHeader_ActionType)(0), // 1: gitaly.UserCommitFilesActionHeader.ActionType @@ -5135,172 +5002,167 @@ var file_operations_proto_goTypes = []interface{}{ (*UserCherryPickError)(nil), // 27: gitaly.UserCherryPickError (*UserRevertRequest)(nil), // 28: gitaly.UserRevertRequest (*UserRevertResponse)(nil), // 29: gitaly.UserRevertResponse - (*UserRevertError)(nil), // 30: gitaly.UserRevertError - (*UserCommitFilesActionHeader)(nil), // 31: gitaly.UserCommitFilesActionHeader - (*UserCommitFilesAction)(nil), // 32: gitaly.UserCommitFilesAction - (*UserCommitFilesRequestHeader)(nil), // 33: gitaly.UserCommitFilesRequestHeader - (*UserCommitFilesRequest)(nil), // 34: gitaly.UserCommitFilesRequest - (*UserCommitFilesResponse)(nil), // 35: gitaly.UserCommitFilesResponse - (*UserCommitFilesError)(nil), // 36: gitaly.UserCommitFilesError - (*UserRebaseConfirmableRequest)(nil), // 37: gitaly.UserRebaseConfirmableRequest - (*UserRebaseConfirmableResponse)(nil), // 38: gitaly.UserRebaseConfirmableResponse - (*UserSquashRequest)(nil), // 39: gitaly.UserSquashRequest - (*UserSquashResponse)(nil), // 40: gitaly.UserSquashResponse - (*UserRebaseConfirmableError)(nil), // 41: gitaly.UserRebaseConfirmableError - (*UserSquashError)(nil), // 42: gitaly.UserSquashError - (*UserApplyPatchRequest)(nil), // 43: gitaly.UserApplyPatchRequest - (*UserApplyPatchResponse)(nil), // 44: gitaly.UserApplyPatchResponse - (*UserUpdateSubmoduleRequest)(nil), // 45: gitaly.UserUpdateSubmoduleRequest - (*UserUpdateSubmoduleResponse)(nil), // 46: gitaly.UserUpdateSubmoduleResponse - (*UserRebaseConfirmableRequest_Header)(nil), // 47: gitaly.UserRebaseConfirmableRequest.Header - (*UserApplyPatchRequest_Header)(nil), // 48: gitaly.UserApplyPatchRequest.Header - (*Repository)(nil), // 49: gitaly.Repository - (*User)(nil), // 50: gitaly.User - (*Branch)(nil), // 51: gitaly.Branch - (*CustomHookError)(nil), // 52: gitaly.CustomHookError - (*AccessCheckError)(nil), // 53: gitaly.AccessCheckError - (*ReferenceUpdateError)(nil), // 54: gitaly.ReferenceUpdateError - (*timestamppb.Timestamp)(nil), // 55: google.protobuf.Timestamp - (*Tag)(nil), // 56: gitaly.Tag - (*ReferenceExistsError)(nil), // 57: gitaly.ReferenceExistsError - (*MergeConflictError)(nil), // 58: gitaly.MergeConflictError - (*GitCommit)(nil), // 59: gitaly.GitCommit - (*NotAncestorError)(nil), // 60: gitaly.NotAncestorError - (*ChangesAlreadyAppliedError)(nil), // 61: gitaly.ChangesAlreadyAppliedError - (*IndexError)(nil), // 62: gitaly.IndexError - (*ResolveRevisionError)(nil), // 63: gitaly.ResolveRevisionError + (*UserCommitFilesActionHeader)(nil), // 30: gitaly.UserCommitFilesActionHeader + (*UserCommitFilesAction)(nil), // 31: gitaly.UserCommitFilesAction + (*UserCommitFilesRequestHeader)(nil), // 32: gitaly.UserCommitFilesRequestHeader + (*UserCommitFilesRequest)(nil), // 33: gitaly.UserCommitFilesRequest + (*UserCommitFilesResponse)(nil), // 34: gitaly.UserCommitFilesResponse + (*UserCommitFilesError)(nil), // 35: gitaly.UserCommitFilesError + (*UserRebaseConfirmableRequest)(nil), // 36: gitaly.UserRebaseConfirmableRequest + (*UserRebaseConfirmableResponse)(nil), // 37: gitaly.UserRebaseConfirmableResponse + (*UserSquashRequest)(nil), // 38: gitaly.UserSquashRequest + (*UserSquashResponse)(nil), // 39: gitaly.UserSquashResponse + (*UserRebaseConfirmableError)(nil), // 40: gitaly.UserRebaseConfirmableError + (*UserSquashError)(nil), // 41: gitaly.UserSquashError + (*UserApplyPatchRequest)(nil), // 42: gitaly.UserApplyPatchRequest + (*UserApplyPatchResponse)(nil), // 43: gitaly.UserApplyPatchResponse + (*UserUpdateSubmoduleRequest)(nil), // 44: gitaly.UserUpdateSubmoduleRequest + (*UserUpdateSubmoduleResponse)(nil), // 45: gitaly.UserUpdateSubmoduleResponse + (*UserRebaseConfirmableRequest_Header)(nil), // 46: gitaly.UserRebaseConfirmableRequest.Header + (*UserApplyPatchRequest_Header)(nil), // 47: gitaly.UserApplyPatchRequest.Header + (*Repository)(nil), // 48: gitaly.Repository + (*User)(nil), // 49: gitaly.User + (*Branch)(nil), // 50: gitaly.Branch + (*CustomHookError)(nil), // 51: gitaly.CustomHookError + (*AccessCheckError)(nil), // 52: gitaly.AccessCheckError + (*ReferenceUpdateError)(nil), // 53: gitaly.ReferenceUpdateError + (*timestamppb.Timestamp)(nil), // 54: google.protobuf.Timestamp + (*Tag)(nil), // 55: gitaly.Tag + (*ReferenceExistsError)(nil), // 56: gitaly.ReferenceExistsError + (*MergeConflictError)(nil), // 57: gitaly.MergeConflictError + (*GitCommit)(nil), // 58: gitaly.GitCommit + (*NotAncestorError)(nil), // 59: gitaly.NotAncestorError + (*ChangesAlreadyAppliedError)(nil), // 60: gitaly.ChangesAlreadyAppliedError + (*IndexError)(nil), // 61: gitaly.IndexError + (*ResolveRevisionError)(nil), // 62: gitaly.ResolveRevisionError } var file_operations_proto_depIdxs = []int32{ - 49, // 0: gitaly.UserCreateBranchRequest.repository:type_name -> gitaly.Repository - 50, // 1: gitaly.UserCreateBranchRequest.user:type_name -> gitaly.User - 51, // 2: gitaly.UserCreateBranchResponse.branch:type_name -> gitaly.Branch - 52, // 3: gitaly.UserCreateBranchError.custom_hook:type_name -> gitaly.CustomHookError - 49, // 4: gitaly.UserUpdateBranchRequest.repository:type_name -> gitaly.Repository - 50, // 5: gitaly.UserUpdateBranchRequest.user:type_name -> gitaly.User - 49, // 6: gitaly.UserDeleteBranchRequest.repository:type_name -> gitaly.Repository - 50, // 7: gitaly.UserDeleteBranchRequest.user:type_name -> gitaly.User - 53, // 8: gitaly.UserDeleteBranchError.access_check:type_name -> gitaly.AccessCheckError - 54, // 9: gitaly.UserDeleteBranchError.reference_update:type_name -> gitaly.ReferenceUpdateError - 52, // 10: gitaly.UserDeleteBranchError.custom_hook:type_name -> gitaly.CustomHookError - 49, // 11: gitaly.UserDeleteTagRequest.repository:type_name -> gitaly.Repository - 50, // 12: gitaly.UserDeleteTagRequest.user:type_name -> gitaly.User - 49, // 13: gitaly.UserCreateTagRequest.repository:type_name -> gitaly.Repository - 50, // 14: gitaly.UserCreateTagRequest.user:type_name -> gitaly.User - 55, // 15: gitaly.UserCreateTagRequest.timestamp:type_name -> google.protobuf.Timestamp - 56, // 16: gitaly.UserCreateTagResponse.tag:type_name -> gitaly.Tag - 53, // 17: gitaly.UserCreateTagError.access_check:type_name -> gitaly.AccessCheckError - 54, // 18: gitaly.UserCreateTagError.reference_update:type_name -> gitaly.ReferenceUpdateError - 52, // 19: gitaly.UserCreateTagError.custom_hook:type_name -> gitaly.CustomHookError - 57, // 20: gitaly.UserCreateTagError.reference_exists:type_name -> gitaly.ReferenceExistsError - 49, // 21: gitaly.UserMergeBranchRequest.repository:type_name -> gitaly.Repository - 50, // 22: gitaly.UserMergeBranchRequest.user:type_name -> gitaly.User - 55, // 23: gitaly.UserMergeBranchRequest.timestamp:type_name -> google.protobuf.Timestamp + 48, // 0: gitaly.UserCreateBranchRequest.repository:type_name -> gitaly.Repository + 49, // 1: gitaly.UserCreateBranchRequest.user:type_name -> gitaly.User + 50, // 2: gitaly.UserCreateBranchResponse.branch:type_name -> gitaly.Branch + 51, // 3: gitaly.UserCreateBranchError.custom_hook:type_name -> gitaly.CustomHookError + 48, // 4: gitaly.UserUpdateBranchRequest.repository:type_name -> gitaly.Repository + 49, // 5: gitaly.UserUpdateBranchRequest.user:type_name -> gitaly.User + 48, // 6: gitaly.UserDeleteBranchRequest.repository:type_name -> gitaly.Repository + 49, // 7: gitaly.UserDeleteBranchRequest.user:type_name -> gitaly.User + 52, // 8: gitaly.UserDeleteBranchError.access_check:type_name -> gitaly.AccessCheckError + 53, // 9: gitaly.UserDeleteBranchError.reference_update:type_name -> gitaly.ReferenceUpdateError + 51, // 10: gitaly.UserDeleteBranchError.custom_hook:type_name -> gitaly.CustomHookError + 48, // 11: gitaly.UserDeleteTagRequest.repository:type_name -> gitaly.Repository + 49, // 12: gitaly.UserDeleteTagRequest.user:type_name -> gitaly.User + 48, // 13: gitaly.UserCreateTagRequest.repository:type_name -> gitaly.Repository + 49, // 14: gitaly.UserCreateTagRequest.user:type_name -> gitaly.User + 54, // 15: gitaly.UserCreateTagRequest.timestamp:type_name -> google.protobuf.Timestamp + 55, // 16: gitaly.UserCreateTagResponse.tag:type_name -> gitaly.Tag + 52, // 17: gitaly.UserCreateTagError.access_check:type_name -> gitaly.AccessCheckError + 53, // 18: gitaly.UserCreateTagError.reference_update:type_name -> gitaly.ReferenceUpdateError + 51, // 19: gitaly.UserCreateTagError.custom_hook:type_name -> gitaly.CustomHookError + 56, // 20: gitaly.UserCreateTagError.reference_exists:type_name -> gitaly.ReferenceExistsError + 48, // 21: gitaly.UserMergeBranchRequest.repository:type_name -> gitaly.Repository + 49, // 22: gitaly.UserMergeBranchRequest.user:type_name -> gitaly.User + 54, // 23: gitaly.UserMergeBranchRequest.timestamp:type_name -> google.protobuf.Timestamp 22, // 24: gitaly.UserMergeBranchResponse.branch_update:type_name -> gitaly.OperationBranchUpdate - 53, // 25: gitaly.UserMergeBranchError.access_check:type_name -> gitaly.AccessCheckError - 54, // 26: gitaly.UserMergeBranchError.reference_update:type_name -> gitaly.ReferenceUpdateError - 52, // 27: gitaly.UserMergeBranchError.custom_hook:type_name -> gitaly.CustomHookError - 58, // 28: gitaly.UserMergeBranchError.merge_conflict:type_name -> gitaly.MergeConflictError - 49, // 29: gitaly.UserMergeToRefRequest.repository:type_name -> gitaly.Repository - 50, // 30: gitaly.UserMergeToRefRequest.user:type_name -> gitaly.User - 55, // 31: gitaly.UserMergeToRefRequest.timestamp:type_name -> google.protobuf.Timestamp - 49, // 32: gitaly.UserRebaseToRefRequest.repository:type_name -> gitaly.Repository - 50, // 33: gitaly.UserRebaseToRefRequest.user:type_name -> gitaly.User - 55, // 34: gitaly.UserRebaseToRefRequest.timestamp:type_name -> google.protobuf.Timestamp - 49, // 35: gitaly.UserFFBranchRequest.repository:type_name -> gitaly.Repository - 50, // 36: gitaly.UserFFBranchRequest.user:type_name -> gitaly.User + 52, // 25: gitaly.UserMergeBranchError.access_check:type_name -> gitaly.AccessCheckError + 53, // 26: gitaly.UserMergeBranchError.reference_update:type_name -> gitaly.ReferenceUpdateError + 51, // 27: gitaly.UserMergeBranchError.custom_hook:type_name -> gitaly.CustomHookError + 57, // 28: gitaly.UserMergeBranchError.merge_conflict:type_name -> gitaly.MergeConflictError + 48, // 29: gitaly.UserMergeToRefRequest.repository:type_name -> gitaly.Repository + 49, // 30: gitaly.UserMergeToRefRequest.user:type_name -> gitaly.User + 54, // 31: gitaly.UserMergeToRefRequest.timestamp:type_name -> google.protobuf.Timestamp + 48, // 32: gitaly.UserRebaseToRefRequest.repository:type_name -> gitaly.Repository + 49, // 33: gitaly.UserRebaseToRefRequest.user:type_name -> gitaly.User + 54, // 34: gitaly.UserRebaseToRefRequest.timestamp:type_name -> google.protobuf.Timestamp + 48, // 35: gitaly.UserFFBranchRequest.repository:type_name -> gitaly.Repository + 49, // 36: gitaly.UserFFBranchRequest.user:type_name -> gitaly.User 22, // 37: gitaly.UserFFBranchResponse.branch_update:type_name -> gitaly.OperationBranchUpdate - 49, // 38: gitaly.UserCherryPickRequest.repository:type_name -> gitaly.Repository - 50, // 39: gitaly.UserCherryPickRequest.user:type_name -> gitaly.User - 59, // 40: gitaly.UserCherryPickRequest.commit:type_name -> gitaly.GitCommit - 49, // 41: gitaly.UserCherryPickRequest.start_repository:type_name -> gitaly.Repository - 55, // 42: gitaly.UserCherryPickRequest.timestamp:type_name -> google.protobuf.Timestamp + 48, // 38: gitaly.UserCherryPickRequest.repository:type_name -> gitaly.Repository + 49, // 39: gitaly.UserCherryPickRequest.user:type_name -> gitaly.User + 58, // 40: gitaly.UserCherryPickRequest.commit:type_name -> gitaly.GitCommit + 48, // 41: gitaly.UserCherryPickRequest.start_repository:type_name -> gitaly.Repository + 54, // 42: gitaly.UserCherryPickRequest.timestamp:type_name -> google.protobuf.Timestamp 22, // 43: gitaly.UserCherryPickResponse.branch_update:type_name -> gitaly.OperationBranchUpdate - 58, // 44: gitaly.UserCherryPickError.cherry_pick_conflict:type_name -> gitaly.MergeConflictError - 60, // 45: gitaly.UserCherryPickError.target_branch_diverged:type_name -> gitaly.NotAncestorError - 61, // 46: gitaly.UserCherryPickError.changes_already_applied:type_name -> gitaly.ChangesAlreadyAppliedError - 53, // 47: gitaly.UserCherryPickError.access_check:type_name -> gitaly.AccessCheckError - 49, // 48: gitaly.UserRevertRequest.repository:type_name -> gitaly.Repository - 50, // 49: gitaly.UserRevertRequest.user:type_name -> gitaly.User - 59, // 50: gitaly.UserRevertRequest.commit:type_name -> gitaly.GitCommit - 49, // 51: gitaly.UserRevertRequest.start_repository:type_name -> gitaly.Repository - 55, // 52: gitaly.UserRevertRequest.timestamp:type_name -> google.protobuf.Timestamp + 57, // 44: gitaly.UserCherryPickError.cherry_pick_conflict:type_name -> gitaly.MergeConflictError + 59, // 45: gitaly.UserCherryPickError.target_branch_diverged:type_name -> gitaly.NotAncestorError + 60, // 46: gitaly.UserCherryPickError.changes_already_applied:type_name -> gitaly.ChangesAlreadyAppliedError + 52, // 47: gitaly.UserCherryPickError.access_check:type_name -> gitaly.AccessCheckError + 48, // 48: gitaly.UserRevertRequest.repository:type_name -> gitaly.Repository + 49, // 49: gitaly.UserRevertRequest.user:type_name -> gitaly.User + 58, // 50: gitaly.UserRevertRequest.commit:type_name -> gitaly.GitCommit + 48, // 51: gitaly.UserRevertRequest.start_repository:type_name -> gitaly.Repository + 54, // 52: gitaly.UserRevertRequest.timestamp:type_name -> google.protobuf.Timestamp 22, // 53: gitaly.UserRevertResponse.branch_update:type_name -> gitaly.OperationBranchUpdate 0, // 54: gitaly.UserRevertResponse.create_tree_error_code:type_name -> gitaly.UserRevertResponse.CreateTreeError - 58, // 55: gitaly.UserRevertError.merge_conflict:type_name -> gitaly.MergeConflictError - 61, // 56: gitaly.UserRevertError.changes_already_applied:type_name -> gitaly.ChangesAlreadyAppliedError - 52, // 57: gitaly.UserRevertError.custom_hook:type_name -> gitaly.CustomHookError - 60, // 58: gitaly.UserRevertError.not_ancestor:type_name -> gitaly.NotAncestorError - 1, // 59: gitaly.UserCommitFilesActionHeader.action:type_name -> gitaly.UserCommitFilesActionHeader.ActionType - 31, // 60: gitaly.UserCommitFilesAction.header:type_name -> gitaly.UserCommitFilesActionHeader - 49, // 61: gitaly.UserCommitFilesRequestHeader.repository:type_name -> gitaly.Repository - 50, // 62: gitaly.UserCommitFilesRequestHeader.user:type_name -> gitaly.User - 49, // 63: gitaly.UserCommitFilesRequestHeader.start_repository:type_name -> gitaly.Repository - 55, // 64: gitaly.UserCommitFilesRequestHeader.timestamp:type_name -> google.protobuf.Timestamp - 33, // 65: gitaly.UserCommitFilesRequest.header:type_name -> gitaly.UserCommitFilesRequestHeader - 32, // 66: gitaly.UserCommitFilesRequest.action:type_name -> gitaly.UserCommitFilesAction - 22, // 67: gitaly.UserCommitFilesResponse.branch_update:type_name -> gitaly.OperationBranchUpdate - 53, // 68: gitaly.UserCommitFilesError.access_check:type_name -> gitaly.AccessCheckError - 62, // 69: gitaly.UserCommitFilesError.index_update:type_name -> gitaly.IndexError - 52, // 70: gitaly.UserCommitFilesError.custom_hook:type_name -> gitaly.CustomHookError - 47, // 71: gitaly.UserRebaseConfirmableRequest.header:type_name -> gitaly.UserRebaseConfirmableRequest.Header - 49, // 72: gitaly.UserSquashRequest.repository:type_name -> gitaly.Repository - 50, // 73: gitaly.UserSquashRequest.user:type_name -> gitaly.User - 50, // 74: gitaly.UserSquashRequest.author:type_name -> gitaly.User - 55, // 75: gitaly.UserSquashRequest.timestamp:type_name -> google.protobuf.Timestamp - 58, // 76: gitaly.UserRebaseConfirmableError.rebase_conflict:type_name -> gitaly.MergeConflictError - 53, // 77: gitaly.UserRebaseConfirmableError.access_check:type_name -> gitaly.AccessCheckError - 63, // 78: gitaly.UserSquashError.resolve_revision:type_name -> gitaly.ResolveRevisionError - 58, // 79: gitaly.UserSquashError.rebase_conflict:type_name -> gitaly.MergeConflictError - 48, // 80: gitaly.UserApplyPatchRequest.header:type_name -> gitaly.UserApplyPatchRequest.Header - 22, // 81: gitaly.UserApplyPatchResponse.branch_update:type_name -> gitaly.OperationBranchUpdate - 49, // 82: gitaly.UserUpdateSubmoduleRequest.repository:type_name -> gitaly.Repository - 50, // 83: gitaly.UserUpdateSubmoduleRequest.user:type_name -> gitaly.User - 55, // 84: gitaly.UserUpdateSubmoduleRequest.timestamp:type_name -> google.protobuf.Timestamp - 22, // 85: gitaly.UserUpdateSubmoduleResponse.branch_update:type_name -> gitaly.OperationBranchUpdate - 49, // 86: gitaly.UserRebaseConfirmableRequest.Header.repository:type_name -> gitaly.Repository - 50, // 87: gitaly.UserRebaseConfirmableRequest.Header.user:type_name -> gitaly.User - 49, // 88: gitaly.UserRebaseConfirmableRequest.Header.remote_repository:type_name -> gitaly.Repository - 55, // 89: gitaly.UserRebaseConfirmableRequest.Header.timestamp:type_name -> google.protobuf.Timestamp - 49, // 90: gitaly.UserApplyPatchRequest.Header.repository:type_name -> gitaly.Repository - 50, // 91: gitaly.UserApplyPatchRequest.Header.user:type_name -> gitaly.User - 55, // 92: gitaly.UserApplyPatchRequest.Header.timestamp:type_name -> google.protobuf.Timestamp - 2, // 93: gitaly.OperationService.UserCreateBranch:input_type -> gitaly.UserCreateBranchRequest - 5, // 94: gitaly.OperationService.UserUpdateBranch:input_type -> gitaly.UserUpdateBranchRequest - 7, // 95: gitaly.OperationService.UserDeleteBranch:input_type -> gitaly.UserDeleteBranchRequest - 12, // 96: gitaly.OperationService.UserCreateTag:input_type -> gitaly.UserCreateTagRequest - 10, // 97: gitaly.OperationService.UserDeleteTag:input_type -> gitaly.UserDeleteTagRequest - 18, // 98: gitaly.OperationService.UserMergeToRef:input_type -> gitaly.UserMergeToRefRequest - 20, // 99: gitaly.OperationService.UserRebaseToRef:input_type -> gitaly.UserRebaseToRefRequest - 15, // 100: gitaly.OperationService.UserMergeBranch:input_type -> gitaly.UserMergeBranchRequest - 23, // 101: gitaly.OperationService.UserFFBranch:input_type -> gitaly.UserFFBranchRequest - 25, // 102: gitaly.OperationService.UserCherryPick:input_type -> gitaly.UserCherryPickRequest - 34, // 103: gitaly.OperationService.UserCommitFiles:input_type -> gitaly.UserCommitFilesRequest - 37, // 104: gitaly.OperationService.UserRebaseConfirmable:input_type -> gitaly.UserRebaseConfirmableRequest - 28, // 105: gitaly.OperationService.UserRevert:input_type -> gitaly.UserRevertRequest - 39, // 106: gitaly.OperationService.UserSquash:input_type -> gitaly.UserSquashRequest - 43, // 107: gitaly.OperationService.UserApplyPatch:input_type -> gitaly.UserApplyPatchRequest - 45, // 108: gitaly.OperationService.UserUpdateSubmodule:input_type -> gitaly.UserUpdateSubmoduleRequest - 3, // 109: gitaly.OperationService.UserCreateBranch:output_type -> gitaly.UserCreateBranchResponse - 6, // 110: gitaly.OperationService.UserUpdateBranch:output_type -> gitaly.UserUpdateBranchResponse - 8, // 111: gitaly.OperationService.UserDeleteBranch:output_type -> gitaly.UserDeleteBranchResponse - 13, // 112: gitaly.OperationService.UserCreateTag:output_type -> gitaly.UserCreateTagResponse - 11, // 113: gitaly.OperationService.UserDeleteTag:output_type -> gitaly.UserDeleteTagResponse - 19, // 114: gitaly.OperationService.UserMergeToRef:output_type -> gitaly.UserMergeToRefResponse - 21, // 115: gitaly.OperationService.UserRebaseToRef:output_type -> gitaly.UserRebaseToRefResponse - 16, // 116: gitaly.OperationService.UserMergeBranch:output_type -> gitaly.UserMergeBranchResponse - 24, // 117: gitaly.OperationService.UserFFBranch:output_type -> gitaly.UserFFBranchResponse - 26, // 118: gitaly.OperationService.UserCherryPick:output_type -> gitaly.UserCherryPickResponse - 35, // 119: gitaly.OperationService.UserCommitFiles:output_type -> gitaly.UserCommitFilesResponse - 38, // 120: gitaly.OperationService.UserRebaseConfirmable:output_type -> gitaly.UserRebaseConfirmableResponse - 29, // 121: gitaly.OperationService.UserRevert:output_type -> gitaly.UserRevertResponse - 40, // 122: gitaly.OperationService.UserSquash:output_type -> gitaly.UserSquashResponse - 44, // 123: gitaly.OperationService.UserApplyPatch:output_type -> gitaly.UserApplyPatchResponse - 46, // 124: gitaly.OperationService.UserUpdateSubmodule:output_type -> gitaly.UserUpdateSubmoduleResponse - 109, // [109:125] is the sub-list for method output_type - 93, // [93:109] is the sub-list for method input_type - 93, // [93:93] is the sub-list for extension type_name - 93, // [93:93] is the sub-list for extension extendee - 0, // [0:93] is the sub-list for field type_name + 1, // 55: gitaly.UserCommitFilesActionHeader.action:type_name -> gitaly.UserCommitFilesActionHeader.ActionType + 30, // 56: gitaly.UserCommitFilesAction.header:type_name -> gitaly.UserCommitFilesActionHeader + 48, // 57: gitaly.UserCommitFilesRequestHeader.repository:type_name -> gitaly.Repository + 49, // 58: gitaly.UserCommitFilesRequestHeader.user:type_name -> gitaly.User + 48, // 59: gitaly.UserCommitFilesRequestHeader.start_repository:type_name -> gitaly.Repository + 54, // 60: gitaly.UserCommitFilesRequestHeader.timestamp:type_name -> google.protobuf.Timestamp + 32, // 61: gitaly.UserCommitFilesRequest.header:type_name -> gitaly.UserCommitFilesRequestHeader + 31, // 62: gitaly.UserCommitFilesRequest.action:type_name -> gitaly.UserCommitFilesAction + 22, // 63: gitaly.UserCommitFilesResponse.branch_update:type_name -> gitaly.OperationBranchUpdate + 52, // 64: gitaly.UserCommitFilesError.access_check:type_name -> gitaly.AccessCheckError + 61, // 65: gitaly.UserCommitFilesError.index_update:type_name -> gitaly.IndexError + 51, // 66: gitaly.UserCommitFilesError.custom_hook:type_name -> gitaly.CustomHookError + 46, // 67: gitaly.UserRebaseConfirmableRequest.header:type_name -> gitaly.UserRebaseConfirmableRequest.Header + 48, // 68: gitaly.UserSquashRequest.repository:type_name -> gitaly.Repository + 49, // 69: gitaly.UserSquashRequest.user:type_name -> gitaly.User + 49, // 70: gitaly.UserSquashRequest.author:type_name -> gitaly.User + 54, // 71: gitaly.UserSquashRequest.timestamp:type_name -> google.protobuf.Timestamp + 57, // 72: gitaly.UserRebaseConfirmableError.rebase_conflict:type_name -> gitaly.MergeConflictError + 52, // 73: gitaly.UserRebaseConfirmableError.access_check:type_name -> gitaly.AccessCheckError + 62, // 74: gitaly.UserSquashError.resolve_revision:type_name -> gitaly.ResolveRevisionError + 57, // 75: gitaly.UserSquashError.rebase_conflict:type_name -> gitaly.MergeConflictError + 47, // 76: gitaly.UserApplyPatchRequest.header:type_name -> gitaly.UserApplyPatchRequest.Header + 22, // 77: gitaly.UserApplyPatchResponse.branch_update:type_name -> gitaly.OperationBranchUpdate + 48, // 78: gitaly.UserUpdateSubmoduleRequest.repository:type_name -> gitaly.Repository + 49, // 79: gitaly.UserUpdateSubmoduleRequest.user:type_name -> gitaly.User + 54, // 80: gitaly.UserUpdateSubmoduleRequest.timestamp:type_name -> google.protobuf.Timestamp + 22, // 81: gitaly.UserUpdateSubmoduleResponse.branch_update:type_name -> gitaly.OperationBranchUpdate + 48, // 82: gitaly.UserRebaseConfirmableRequest.Header.repository:type_name -> gitaly.Repository + 49, // 83: gitaly.UserRebaseConfirmableRequest.Header.user:type_name -> gitaly.User + 48, // 84: gitaly.UserRebaseConfirmableRequest.Header.remote_repository:type_name -> gitaly.Repository + 54, // 85: gitaly.UserRebaseConfirmableRequest.Header.timestamp:type_name -> google.protobuf.Timestamp + 48, // 86: gitaly.UserApplyPatchRequest.Header.repository:type_name -> gitaly.Repository + 49, // 87: gitaly.UserApplyPatchRequest.Header.user:type_name -> gitaly.User + 54, // 88: gitaly.UserApplyPatchRequest.Header.timestamp:type_name -> google.protobuf.Timestamp + 2, // 89: gitaly.OperationService.UserCreateBranch:input_type -> gitaly.UserCreateBranchRequest + 5, // 90: gitaly.OperationService.UserUpdateBranch:input_type -> gitaly.UserUpdateBranchRequest + 7, // 91: gitaly.OperationService.UserDeleteBranch:input_type -> gitaly.UserDeleteBranchRequest + 12, // 92: gitaly.OperationService.UserCreateTag:input_type -> gitaly.UserCreateTagRequest + 10, // 93: gitaly.OperationService.UserDeleteTag:input_type -> gitaly.UserDeleteTagRequest + 18, // 94: gitaly.OperationService.UserMergeToRef:input_type -> gitaly.UserMergeToRefRequest + 20, // 95: gitaly.OperationService.UserRebaseToRef:input_type -> gitaly.UserRebaseToRefRequest + 15, // 96: gitaly.OperationService.UserMergeBranch:input_type -> gitaly.UserMergeBranchRequest + 23, // 97: gitaly.OperationService.UserFFBranch:input_type -> gitaly.UserFFBranchRequest + 25, // 98: gitaly.OperationService.UserCherryPick:input_type -> gitaly.UserCherryPickRequest + 33, // 99: gitaly.OperationService.UserCommitFiles:input_type -> gitaly.UserCommitFilesRequest + 36, // 100: gitaly.OperationService.UserRebaseConfirmable:input_type -> gitaly.UserRebaseConfirmableRequest + 28, // 101: gitaly.OperationService.UserRevert:input_type -> gitaly.UserRevertRequest + 38, // 102: gitaly.OperationService.UserSquash:input_type -> gitaly.UserSquashRequest + 42, // 103: gitaly.OperationService.UserApplyPatch:input_type -> gitaly.UserApplyPatchRequest + 44, // 104: gitaly.OperationService.UserUpdateSubmodule:input_type -> gitaly.UserUpdateSubmoduleRequest + 3, // 105: gitaly.OperationService.UserCreateBranch:output_type -> gitaly.UserCreateBranchResponse + 6, // 106: gitaly.OperationService.UserUpdateBranch:output_type -> gitaly.UserUpdateBranchResponse + 8, // 107: gitaly.OperationService.UserDeleteBranch:output_type -> gitaly.UserDeleteBranchResponse + 13, // 108: gitaly.OperationService.UserCreateTag:output_type -> gitaly.UserCreateTagResponse + 11, // 109: gitaly.OperationService.UserDeleteTag:output_type -> gitaly.UserDeleteTagResponse + 19, // 110: gitaly.OperationService.UserMergeToRef:output_type -> gitaly.UserMergeToRefResponse + 21, // 111: gitaly.OperationService.UserRebaseToRef:output_type -> gitaly.UserRebaseToRefResponse + 16, // 112: gitaly.OperationService.UserMergeBranch:output_type -> gitaly.UserMergeBranchResponse + 24, // 113: gitaly.OperationService.UserFFBranch:output_type -> gitaly.UserFFBranchResponse + 26, // 114: gitaly.OperationService.UserCherryPick:output_type -> gitaly.UserCherryPickResponse + 34, // 115: gitaly.OperationService.UserCommitFiles:output_type -> gitaly.UserCommitFilesResponse + 37, // 116: gitaly.OperationService.UserRebaseConfirmable:output_type -> gitaly.UserRebaseConfirmableResponse + 29, // 117: gitaly.OperationService.UserRevert:output_type -> gitaly.UserRevertResponse + 39, // 118: gitaly.OperationService.UserSquash:output_type -> gitaly.UserSquashResponse + 43, // 119: gitaly.OperationService.UserApplyPatch:output_type -> gitaly.UserApplyPatchResponse + 45, // 120: gitaly.OperationService.UserUpdateSubmodule:output_type -> gitaly.UserUpdateSubmoduleResponse + 105, // [105:121] is the sub-list for method output_type + 89, // [89:105] is the sub-list for method input_type + 89, // [89:89] is the sub-list for extension type_name + 89, // [89:89] is the sub-list for extension extendee + 0, // [0:89] is the sub-list for field type_name } func init() { file_operations_proto_init() } @@ -5649,18 +5511,6 @@ func file_operations_proto_init() { } } file_operations_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UserRevertError); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_operations_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UserCommitFilesActionHeader); i { case 0: return &v.state @@ -5672,7 +5522,7 @@ func file_operations_proto_init() { return nil } } - file_operations_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + file_operations_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UserCommitFilesAction); i { case 0: return &v.state @@ -5684,7 +5534,7 @@ func file_operations_proto_init() { return nil } } - file_operations_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + file_operations_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UserCommitFilesRequestHeader); i { case 0: return &v.state @@ -5696,7 +5546,7 @@ func file_operations_proto_init() { return nil } } - file_operations_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + file_operations_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UserCommitFilesRequest); i { case 0: return &v.state @@ -5708,7 +5558,7 @@ func file_operations_proto_init() { return nil } } - file_operations_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + file_operations_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UserCommitFilesResponse); i { case 0: return &v.state @@ -5720,7 +5570,7 @@ func file_operations_proto_init() { return nil } } - file_operations_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { + file_operations_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UserCommitFilesError); i { case 0: return &v.state @@ -5732,7 +5582,7 @@ func file_operations_proto_init() { return nil } } - file_operations_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + file_operations_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UserRebaseConfirmableRequest); i { case 0: return &v.state @@ -5744,7 +5594,7 @@ func file_operations_proto_init() { return nil } } - file_operations_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + file_operations_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UserRebaseConfirmableResponse); i { case 0: return &v.state @@ -5756,7 +5606,7 @@ func file_operations_proto_init() { return nil } } - file_operations_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + file_operations_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UserSquashRequest); i { case 0: return &v.state @@ -5768,7 +5618,7 @@ func file_operations_proto_init() { return nil } } - file_operations_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { + file_operations_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UserSquashResponse); i { case 0: return &v.state @@ -5780,7 +5630,7 @@ func file_operations_proto_init() { return nil } } - file_operations_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { + file_operations_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UserRebaseConfirmableError); i { case 0: return &v.state @@ -5792,7 +5642,7 @@ func file_operations_proto_init() { return nil } } - file_operations_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { + file_operations_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UserSquashError); i { case 0: return &v.state @@ -5804,7 +5654,7 @@ func file_operations_proto_init() { return nil } } - file_operations_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { + file_operations_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UserApplyPatchRequest); i { case 0: return &v.state @@ -5816,7 +5666,7 @@ func file_operations_proto_init() { return nil } } - file_operations_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { + file_operations_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UserApplyPatchResponse); i { case 0: return &v.state @@ -5828,7 +5678,7 @@ func file_operations_proto_init() { return nil } } - file_operations_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { + file_operations_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UserUpdateSubmoduleRequest); i { case 0: return &v.state @@ -5840,7 +5690,7 @@ func file_operations_proto_init() { return nil } } - file_operations_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { + file_operations_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UserUpdateSubmoduleResponse); i { case 0: return &v.state @@ -5852,7 +5702,7 @@ func file_operations_proto_init() { return nil } } - file_operations_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { + file_operations_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UserRebaseConfirmableRequest_Header); i { case 0: return &v.state @@ -5864,7 +5714,7 @@ func file_operations_proto_init() { return nil } } - file_operations_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { + file_operations_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UserApplyPatchRequest_Header); i { case 0: return &v.state @@ -5903,42 +5753,36 @@ func file_operations_proto_init() { (*UserCherryPickError_ChangesAlreadyApplied)(nil), (*UserCherryPickError_AccessCheck)(nil), } - file_operations_proto_msgTypes[28].OneofWrappers = []interface{}{ - (*UserRevertError_MergeConflict)(nil), - (*UserRevertError_ChangesAlreadyApplied)(nil), - (*UserRevertError_CustomHook)(nil), - (*UserRevertError_NotAncestor)(nil), - } - file_operations_proto_msgTypes[30].OneofWrappers = []interface{}{ + file_operations_proto_msgTypes[29].OneofWrappers = []interface{}{ (*UserCommitFilesAction_Header)(nil), (*UserCommitFilesAction_Content)(nil), } - file_operations_proto_msgTypes[32].OneofWrappers = []interface{}{ + file_operations_proto_msgTypes[31].OneofWrappers = []interface{}{ (*UserCommitFilesRequest_Header)(nil), (*UserCommitFilesRequest_Action)(nil), } - file_operations_proto_msgTypes[34].OneofWrappers = []interface{}{ + file_operations_proto_msgTypes[33].OneofWrappers = []interface{}{ (*UserCommitFilesError_AccessCheck)(nil), (*UserCommitFilesError_IndexUpdate)(nil), (*UserCommitFilesError_CustomHook)(nil), } - file_operations_proto_msgTypes[35].OneofWrappers = []interface{}{ + file_operations_proto_msgTypes[34].OneofWrappers = []interface{}{ (*UserRebaseConfirmableRequest_Header_)(nil), (*UserRebaseConfirmableRequest_Apply)(nil), } - file_operations_proto_msgTypes[36].OneofWrappers = []interface{}{ + file_operations_proto_msgTypes[35].OneofWrappers = []interface{}{ (*UserRebaseConfirmableResponse_RebaseSha)(nil), (*UserRebaseConfirmableResponse_RebaseApplied)(nil), } - file_operations_proto_msgTypes[39].OneofWrappers = []interface{}{ + file_operations_proto_msgTypes[38].OneofWrappers = []interface{}{ (*UserRebaseConfirmableError_RebaseConflict)(nil), (*UserRebaseConfirmableError_AccessCheck)(nil), } - file_operations_proto_msgTypes[40].OneofWrappers = []interface{}{ + file_operations_proto_msgTypes[39].OneofWrappers = []interface{}{ (*UserSquashError_ResolveRevision)(nil), (*UserSquashError_RebaseConflict)(nil), } - file_operations_proto_msgTypes[41].OneofWrappers = []interface{}{ + file_operations_proto_msgTypes[40].OneofWrappers = []interface{}{ (*UserApplyPatchRequest_Header_)(nil), (*UserApplyPatchRequest_Patches)(nil), } @@ -5948,7 +5792,7 @@ func file_operations_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_operations_proto_rawDesc, NumEnums: 2, - NumMessages: 47, + NumMessages: 46, NumExtensions: 0, NumServices: 1, }, diff --git a/proto/operations.proto b/proto/operations.proto index 72df2f6d8..27a792572 100644 --- a/proto/operations.proto +++ b/proto/operations.proto @@ -745,20 +745,6 @@ message UserRevertResponse { CreateTreeError create_tree_error_code = 5; } -// UserRevertError is an error returned by the UserRevert RPC. -message UserRevertError { - oneof error { - // merge_conflict is returned if there is a conflict when applying the revert. - MergeConflictError merge_conflict = 1; - // changes_already_applied is returned if the result after applying the revert is empty. - ChangesAlreadyAppliedError changes_already_applied = 2; - // custom_hook contains the error message if the pre-receive hook failed. - CustomHookError custom_hook = 3; - // not_ancestor is returned if the old tip of the target branch is not an ancestor of the new commit. - NotAncestorError not_ancestor = 4; - } -} - // UserCommitFilesActionHeader contains the details of the action to be performed. message UserCommitFilesActionHeader { // ActionType is the type of action to perform. -- cgit v1.2.3 From 2625753feda7773bac2a4c3b4f6ef312c46bda3d Mon Sep 17 00:00:00 2001 From: James Liu Date: Tue, 9 Jan 2024 11:00:49 +1100 Subject: Revert "Merge branch 'jliu-track-restored-repos-2' into 'master'" This reverts commit 7de65fabe6eaf803771cf0dd5d53dbaf1e628d56, reversing changes made to 0ffc495be9bb5a8f4ff60764b545443d54210e56. --- internal/backup/backup.go | 54 ++------- internal/backup/backup_test.go | 115 ++----------------- internal/backup/pipeline.go | 34 ++---- internal/backup/pipeline_test.go | 61 ++-------- internal/backup/server_side.go | 56 ++------- internal/backup/server_side_test.go | 125 ++++----------------- internal/cli/gitalybackup/create.go | 2 +- internal/cli/gitalybackup/restore.go | 34 +----- internal/cli/gitalybackup/restore_test.go | 20 +++- .../gitaly/service/repository/remove_all_test.go | 1 - internal/praefect/datastore/repository_store.go | 27 ----- internal/praefect/remove_all.go | 1 - internal/praefect/remove_all_test.go | 1 - internal/praefect/server.go | 3 - internal/praefect/walkrepos.go | 47 -------- internal/praefect/walkrepos_test.go | 88 --------------- proto/go/gitalypb/repository.pb.go | 52 ++++----- proto/go/gitalypb/repository_grpc.pb.go | 5 - proto/repository.proto | 2 - 19 files changed, 113 insertions(+), 615 deletions(-) delete mode 100644 internal/praefect/walkrepos.go delete mode 100644 internal/praefect/walkrepos_test.go diff --git a/internal/backup/backup.go b/internal/backup/backup.go index a15f47abe..5eed0724a 100644 --- a/internal/backup/backup.go +++ b/internal/backup/backup.go @@ -211,56 +211,25 @@ func NewManagerLocal( } } -// RemoveRepository removes the specified repository from its storage. -func (mgr *Manager) RemoveRepository(ctx context.Context, req *RemoveRepositoryRequest) error { - if err := setContextServerInfo(ctx, &req.Server, req.Repo.StorageName); err != nil { - return fmt.Errorf("remove repo: set context: %w", err) +// RemoveAllRepositories removes all repositories in the specified storage name. +func (mgr *Manager) RemoveAllRepositories(ctx context.Context, req *RemoveAllRepositoriesRequest) error { + if err := setContextServerInfo(ctx, &req.Server, req.StorageName); err != nil { + return fmt.Errorf("manager: %w", err) } repoClient, err := mgr.newRepoClient(ctx, req.Server) if err != nil { - return fmt.Errorf("remove repo: create client: %w", err) + return fmt.Errorf("manager: %w", err) } - _, err = repoClient.RemoveRepository(ctx, &gitalypb.RemoveRepositoryRequest{Repository: req.Repo}) + _, err = repoClient.RemoveAll(ctx, &gitalypb.RemoveAllRequest{StorageName: req.StorageName}) if err != nil { - return fmt.Errorf("remove repo: remove: %w", err) + return fmt.Errorf("manager: %w", err) } return nil } -// ListRepositories returns a list of repositories found in the given storage. -func (mgr *Manager) ListRepositories(ctx context.Context, req *ListRepositoriesRequest) (repos []*gitalypb.Repository, err error) { - if err := setContextServerInfo(ctx, &req.Server, req.StorageName); err != nil { - return nil, fmt.Errorf("list repos: set context: %w", err) - } - - internalClient, err := mgr.newInternalClient(ctx, req.Server) - if err != nil { - return nil, fmt.Errorf("list repos: create client: %w", err) - } - - stream, err := internalClient.WalkRepos(ctx, &gitalypb.WalkReposRequest{StorageName: req.StorageName}) - if err != nil { - return nil, fmt.Errorf("list repos: walk: %w", err) - } - - for { - resp, err := stream.Recv() - if errors.Is(err, io.EOF) { - break - } - if err != nil { - return nil, fmt.Errorf("list repos: receiving messages: %w", err) - } - - repos = append(repos, &gitalypb.Repository{RelativePath: resp.RelativePath, StorageName: req.StorageName}) - } - - return repos, nil -} - // Create creates a repository backup. func (mgr *Manager) Create(ctx context.Context, req *CreateRequest) error { if req.VanityRepository == nil { @@ -604,12 +573,3 @@ func (mgr *Manager) newRepoClient(ctx context.Context, server storage.ServerInfo return gitalypb.NewRepositoryServiceClient(conn), nil } - -func (mgr *Manager) newInternalClient(ctx context.Context, server storage.ServerInfo) (gitalypb.InternalGitalyClient, error) { - conn, err := mgr.conns.Dial(ctx, server.Address, server.Token) - if err != nil { - return nil, err - } - - return gitalypb.NewInternalGitalyClient(conn), nil -} diff --git a/internal/backup/backup_test.go b/internal/backup/backup_test.go index 81e87a33a..14341ec6a 100644 --- a/internal/backup/backup_test.go +++ b/internal/backup/backup_test.go @@ -28,10 +28,13 @@ import ( "google.golang.org/protobuf/proto" ) -func TestManager_RemoveRepository(t *testing.T) { - if testhelper.IsPraefectEnabled() { - t.Skip("local backup manager expects to operate on the local filesystem so cannot operate through praefect") - } +func TestManager_RemoveAllRepositories(t *testing.T) { + testhelper.SkipWithWAL(t, ` +RemoveAll is removing the entire content of the storage. This would also remove the database's and +the transaction manager's disk state. The RPC needs to be updated to shut down all partitions and +the database and only then perform the removal. + +Issue: https://gitlab.com/gitlab-org/gitaly/-/issues/5269`) t.Parallel() @@ -55,109 +58,11 @@ func TestManager_RemoveRepository(t *testing.T) { require.NoError(t, err) fsBackup := backup.NewManager(sink, locator, pool) - err = fsBackup.RemoveRepository(ctx, &backup.RemoveRepositoryRequest{ - Server: storage.ServerInfo{Address: cfg.SocketPath, Token: cfg.Auth.Token}, - Repo: repo, + err = fsBackup.RemoveAllRepositories(ctx, &backup.RemoveAllRepositoriesRequest{ + Server: storage.ServerInfo{Address: cfg.SocketPath, Token: cfg.Auth.Token}, + StorageName: repo.StorageName, }) require.NoError(t, err) - require.NoDirExists(t, repoPath) - - // With an invalid repository - err = fsBackup.RemoveRepository(ctx, &backup.RemoveRepositoryRequest{ - Server: storage.ServerInfo{Address: cfg.SocketPath, Token: cfg.Auth.Token}, - Repo: &gitalypb.Repository{StorageName: "nonexistent", RelativePath: "nonexistent"}, - }) - - require.EqualError(t, err, "remove repo: remove: rpc error: code = InvalidArgument desc = storage name not found") -} - -func TestManager_ListRepositories(t *testing.T) { - t.Parallel() - - for _, tc := range []struct { - desc string - repos map[string][]*gitalypb.Repository - }{ - { - desc: "no repos", - repos: make(map[string][]*gitalypb.Repository), - }, - { - desc: "repos in a single storage", - repos: map[string][]*gitalypb.Repository{ - "storage-1": { - {RelativePath: "a", StorageName: "storage-1"}, - {RelativePath: "b", StorageName: "storage-1"}, - }, - }, - }, - { - desc: "repos in multiple storages", - repos: map[string][]*gitalypb.Repository{ - "storage-1": { - {RelativePath: "a", StorageName: "storage-1"}, - {RelativePath: "b", StorageName: "storage-1"}, - }, - "storage-2": { - {RelativePath: "c", StorageName: "storage-2"}, - {RelativePath: "d", StorageName: "storage-2"}, - }, - }, - }, - } { - t.Run(tc.desc, func(t *testing.T) { - if testhelper.IsPraefectEnabled() { - t.Skip("local backup manager expects to operate on the local filesystem so cannot operate through praefect") - } - - var storages []string - for storageName := range tc.repos { - storages = append(storages, storageName) - } - - // We don't really need a "default" storage, but this makes initialisation cleaner since - // WithStorages() takes at least one argument. - cfg := testcfg.Build(t, testcfg.WithStorages("default", storages...)) - cfg.SocketPath = testserver.RunGitalyServer(t, cfg, setup.RegisterAll) - - ctx := testhelper.Context(t) - - for storageName, repos := range tc.repos { - for _, repo := range repos { - storagePath, ok := cfg.StoragePath(storageName) - require.True(t, ok) - - _, _ = gittest.CreateRepository(t, ctx, cfg, gittest.CreateRepositoryConfig{ - SkipCreationViaService: true, - RelativePath: repo.RelativePath, - Storage: config.Storage{Name: storageName, Path: storagePath}, - }) - } - } - - pool := client.NewPool() - defer testhelper.MustClose(t, pool) - - backupRoot := testhelper.TempDir(t) - sink := backup.NewFilesystemSink(backupRoot) - defer testhelper.MustClose(t, sink) - - locator, err := backup.ResolveLocator("pointer", sink) - require.NoError(t, err) - - fsBackup := backup.NewManager(sink, locator, pool) - - for storageName, repos := range tc.repos { - actualRepos, err := fsBackup.ListRepositories(ctx, &backup.ListRepositoriesRequest{ - Server: storage.ServerInfo{Address: cfg.SocketPath, Token: cfg.Auth.Token}, - StorageName: storageName, - }) - - require.NoError(t, err) - require.EqualValues(t, repos, actualRepos) - } - }) - } } func TestManager_Create(t *testing.T) { diff --git a/internal/backup/pipeline.go b/internal/backup/pipeline.go index 3eb91de91..5672493ac 100644 --- a/internal/backup/pipeline.go +++ b/internal/backup/pipeline.go @@ -16,8 +16,7 @@ import ( type Strategy interface { Create(context.Context, *CreateRequest) error Restore(context.Context, *RestoreRequest) error - ListRepositories(context.Context, *ListRepositoriesRequest) ([]*gitalypb.Repository, error) - RemoveRepository(context.Context, *RemoveRepositoryRequest) error + RemoveAllRepositories(context.Context, *RemoveAllRepositoriesRequest) error } // CreateRequest is the request to create a backup @@ -53,14 +52,9 @@ type RestoreRequest struct { BackupID string } -// RemoveRepositoryRequest is a request to remove an individual repository from its storage. -type RemoveRepositoryRequest struct { - Server storage.ServerInfo - Repo *gitalypb.Repository -} - -// ListRepositoriesRequest is the request to list repositories in a given storage. -type ListRepositoriesRequest struct { +// RemoveAllRepositoriesRequest is the request to remove all repositories in the specified +// storage name. +type RemoveAllRepositoriesRequest struct { Server storage.ServerInfo StorageName string } @@ -187,9 +181,6 @@ type Pipeline struct { pipelineError error cmdErrors *commandErrors - - processedRepos map[string]map[*gitalypb.Repository]struct{} - processedReposMu sync.Mutex } // NewPipeline creates a pipeline that executes backup and restore jobs. @@ -204,7 +195,6 @@ func NewPipeline(log log.Logger, opts ...PipelineOption) (*Pipeline, error) { done: make(chan struct{}), workersByStorage: make(map[string]chan *contextCommand), cmdErrors: &commandErrors{}, - processedRepos: make(map[string]map[*gitalypb.Repository]struct{}), } for _, opt := range opts { @@ -262,19 +252,19 @@ func (p *Pipeline) Handle(ctx context.Context, cmd Command) { } // Done waits for any in progress jobs to complete then reports any accumulated errors -func (p *Pipeline) Done() (processedRepos map[string]map[*gitalypb.Repository]struct{}, err error) { +func (p *Pipeline) Done() error { close(p.done) p.workerWg.Wait() if p.pipelineError != nil { - return nil, fmt.Errorf("pipeline: %w", p.pipelineError) + return fmt.Errorf("pipeline: %w", p.pipelineError) } if len(p.cmdErrors.errs) > 0 { - return nil, fmt.Errorf("pipeline: %w", p.cmdErrors) + return fmt.Errorf("pipeline: %w", p.cmdErrors) } - return p.processedRepos, nil + return nil } // getWorker finds the channel associated with a storage. When no channel is @@ -335,14 +325,6 @@ func (p *Pipeline) processCommand(ctx context.Context, cmd Command) { return } - storageName := cmd.Repository().StorageName - p.processedReposMu.Lock() - if _, ok := p.processedRepos[storageName]; !ok { - p.processedRepos[storageName] = make(map[*gitalypb.Repository]struct{}) - } - p.processedRepos[storageName][cmd.Repository()] = struct{}{} - p.processedReposMu.Unlock() - log.Info(fmt.Sprintf("completed %s", cmd.Name())) } diff --git a/internal/backup/pipeline_test.go b/internal/backup/pipeline_test.go index 37f7c2e5b..04c539a5f 100644 --- a/internal/backup/pipeline_test.go +++ b/internal/backup/pipeline_test.go @@ -98,8 +98,7 @@ func TestPipeline(t *testing.T) { p.Handle(ctx, NewCreateCommand(strategy, CreateRequest{Repository: &gitalypb.Repository{StorageName: "storage1"}})) p.Handle(ctx, NewCreateCommand(strategy, CreateRequest{Repository: &gitalypb.Repository{StorageName: "storage2"}})) } - _, err = p.Done() - require.NoError(t, err) + require.NoError(t, p.Done()) }) } }) @@ -116,16 +115,14 @@ func TestPipeline(t *testing.T) { p.Handle(ctx, NewCreateCommand(strategy, CreateRequest{Repository: &gitalypb.Repository{StorageName: "default"}})) - _, err = p.Done() - require.EqualError(t, err, "pipeline: context canceled") + require.EqualError(t, p.Done(), "pipeline: context canceled") }) } type MockStrategy struct { - CreateFunc func(context.Context, *CreateRequest) error - RestoreFunc func(context.Context, *RestoreRequest) error - RemoveRepositoryFunc func(context.Context, *RemoveRepositoryRequest) error - ListRepositoriesFunc func(context.Context, *ListRepositoriesRequest) ([]*gitalypb.Repository, error) + CreateFunc func(context.Context, *CreateRequest) error + RestoreFunc func(context.Context, *RestoreRequest) error + RemoveAllRepositoriesFunc func(context.Context, *RemoveAllRepositoriesRequest) error } func (s MockStrategy) Create(ctx context.Context, req *CreateRequest) error { @@ -142,20 +139,13 @@ func (s MockStrategy) Restore(ctx context.Context, req *RestoreRequest) error { return nil } -func (s MockStrategy) RemoveRepository(ctx context.Context, req *RemoveRepositoryRequest) error { - if s.RemoveRepositoryFunc != nil { - return s.RemoveRepositoryFunc(ctx, req) +func (s MockStrategy) RemoveAllRepositories(ctx context.Context, req *RemoveAllRepositoriesRequest) error { + if s.RemoveAllRepositoriesFunc != nil { + return s.RemoveAllRepositoriesFunc(ctx, req) } return nil } -func (s MockStrategy) ListRepositories(ctx context.Context, req *ListRepositoriesRequest) ([]*gitalypb.Repository, error) { - if s.ListRepositoriesFunc != nil { - return s.ListRepositoriesFunc(ctx, req) - } - return nil, nil -} - func testPipeline(t *testing.T, init func() *Pipeline) { strategy := MockStrategy{ CreateFunc: func(_ context.Context, req *CreateRequest) error { @@ -232,7 +222,7 @@ func testPipeline(t *testing.T, init func() *Pipeline) { require.Equal(t, tc.level, logEntry.Level) } - _, err := p.Done() + err := p.Done() if tc.level == logrus.ErrorLevel { require.EqualError(t, err, "pipeline: 1 failures encountered:\n - c.git: assert.AnError general error for testing\n") @@ -268,7 +258,7 @@ func testPipeline(t *testing.T, init func() *Pipeline) { for _, cmd := range commands { p.Handle(ctx, cmd) } - _, err := p.Done() + err := p.Done() require.EqualError(t, err, "pipeline: 1 failures encountered:\n - c.git: assert.AnError general error for testing\n") }) } @@ -319,34 +309,3 @@ func TestPipelineError(t *testing.T) { }) } } - -func TestPipelineProcessedRepos(t *testing.T) { - strategy := MockStrategy{} - - repos := map[string]map[*gitalypb.Repository]struct{}{ - "storage1": { - &gitalypb.Repository{RelativePath: "a.git", StorageName: "storage1"}: struct{}{}, - &gitalypb.Repository{RelativePath: "b.git", StorageName: "storage1"}: struct{}{}, - }, - "storage2": { - &gitalypb.Repository{RelativePath: "c.git", StorageName: "storage2"}: struct{}{}, - }, - "storage3": { - &gitalypb.Repository{RelativePath: "d.git", StorageName: "storage3"}: struct{}{}, - }, - } - - p, err := NewPipeline(testhelper.SharedLogger(t)) - require.NoError(t, err) - - ctx := testhelper.Context(t) - for _, v := range repos { - for repo := range v { - p.Handle(ctx, NewRestoreCommand(strategy, RestoreRequest{Repository: repo})) - } - } - - processedRepos, err := p.Done() - require.NoError(t, err) - require.EqualValues(t, repos, processedRepos) -} diff --git a/internal/backup/server_side.go b/internal/backup/server_side.go index a1a9a37eb..35654f215 100644 --- a/internal/backup/server_side.go +++ b/internal/backup/server_side.go @@ -2,9 +2,7 @@ package backup import ( "context" - "errors" "fmt" - "io" "gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/storage" "gitlab.com/gitlab-org/gitaly/v16/internal/grpc/client" @@ -95,56 +93,25 @@ func (ss ServerSideAdapter) Restore(ctx context.Context, req *RestoreRequest) er return nil } -// RemoveRepository removes the specified repository from its storage. -func (ss ServerSideAdapter) RemoveRepository(ctx context.Context, req *RemoveRepositoryRequest) error { - if err := setContextServerInfo(ctx, &req.Server, req.Repo.StorageName); err != nil { - return fmt.Errorf("server-side remove repo: set context: %w", err) +// RemoveAllRepositories removes all repositories in the specified storage name. +func (ss ServerSideAdapter) RemoveAllRepositories(ctx context.Context, req *RemoveAllRepositoriesRequest) error { + if err := setContextServerInfo(ctx, &req.Server, req.StorageName); err != nil { + return fmt.Errorf("server-side remove all: %w", err) } repoClient, err := ss.newRepoClient(ctx, req.Server) if err != nil { - return fmt.Errorf("server-side remove repo: create client: %w", err) + return fmt.Errorf("server-side remove all: %w", err) } - _, err = repoClient.RemoveRepository(ctx, &gitalypb.RemoveRepositoryRequest{Repository: req.Repo}) + _, err = repoClient.RemoveAll(ctx, &gitalypb.RemoveAllRequest{StorageName: req.StorageName}) if err != nil { - return fmt.Errorf("server-side remove repo: remove: %w", err) + return fmt.Errorf("server-side remove all: %w", err) } return nil } -// ListRepositories returns a list of repositories found in the given storage. -func (ss ServerSideAdapter) ListRepositories(ctx context.Context, req *ListRepositoriesRequest) (repos []*gitalypb.Repository, err error) { - if err := setContextServerInfo(ctx, &req.Server, req.StorageName); err != nil { - return nil, fmt.Errorf("server-side list repos: set context: %w", err) - } - - internalClient, err := ss.newInternalClient(ctx, req.Server) - if err != nil { - return nil, fmt.Errorf("server-side list repos: create client: %w", err) - } - - stream, err := internalClient.WalkRepos(ctx, &gitalypb.WalkReposRequest{StorageName: req.StorageName}) - if err != nil { - return nil, fmt.Errorf("server-side list repos: walk: %w", err) - } - - for { - resp, err := stream.Recv() - if errors.Is(err, io.EOF) { - break - } - if err != nil { - return nil, err - } - - repos = append(repos, &gitalypb.Repository{RelativePath: resp.RelativePath, StorageName: req.StorageName}) - } - - return repos, nil -} - func (ss ServerSideAdapter) newRepoClient(ctx context.Context, server storage.ServerInfo) (gitalypb.RepositoryServiceClient, error) { conn, err := ss.pool.Dial(ctx, server.Address, server.Token) if err != nil { @@ -153,12 +120,3 @@ func (ss ServerSideAdapter) newRepoClient(ctx context.Context, server storage.Se return gitalypb.NewRepositoryServiceClient(conn), nil } - -func (ss ServerSideAdapter) newInternalClient(ctx context.Context, server storage.ServerInfo) (gitalypb.InternalGitalyClient, error) { - conn, err := ss.pool.Dial(ctx, server.Address, server.Token) - if err != nil { - return nil, err - } - - return gitalypb.NewInternalGitalyClient(conn), nil -} diff --git a/internal/backup/server_side_test.go b/internal/backup/server_side_test.go index 69669df60..2acc547c5 100644 --- a/internal/backup/server_side_test.go +++ b/internal/backup/server_side_test.go @@ -13,11 +13,9 @@ import ( "gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/service/setup" "gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/storage" "gitlab.com/gitlab-org/gitaly/v16/internal/grpc/client" - "gitlab.com/gitlab-org/gitaly/v16/internal/praefect/datastore" "gitlab.com/gitlab-org/gitaly/v16/internal/structerr" "gitlab.com/gitlab-org/gitaly/v16/internal/testhelper" "gitlab.com/gitlab-org/gitaly/v16/internal/testhelper/testcfg" - "gitlab.com/gitlab-org/gitaly/v16/internal/testhelper/testdb" "gitlab.com/gitlab-org/gitaly/v16/internal/testhelper/testserver" "gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb" ) @@ -259,15 +257,28 @@ func TestServerSideAdapter_Restore(t *testing.T) { } } -func TestServerSideAdapter_RemoveRepository(t *testing.T) { +func TestServerSideAdapter_RemoveAllRepositories(t *testing.T) { + testhelper.SkipWithWAL(t, ` +RemoveAll is removing the entire content of the storage. This would also remove the database's and +the transaction manager's disk state. The RPC needs to be updated to shut down all partitions and +the database and only then perform the removal. + +Issue: https://gitlab.com/gitlab-org/gitaly/-/issues/5269`) + t.Parallel() - db := testdb.New(t) - db.TruncateAll(t) - datastore.NewPostgresRepositoryStore(db, map[string][]string{"virtual-storage": {"default"}}) + backupRoot := testhelper.TempDir(t) + sink := backup.NewFilesystemSink(backupRoot) + defer testhelper.MustClose(t, sink) + + locator, err := backup.ResolveLocator("pointer", sink) + require.NoError(t, err) cfg := testcfg.Build(t) - cfg.SocketPath = testserver.RunGitalyServer(t, cfg, setup.RegisterAll) + cfg.SocketPath = testserver.RunGitalyServer(t, cfg, setup.RegisterAll, + testserver.WithBackupSink(sink), + testserver.WithBackupLocator(locator), + ) ctx := testhelper.Context(t) @@ -278,103 +289,9 @@ func TestServerSideAdapter_RemoveRepository(t *testing.T) { defer testhelper.MustClose(t, pool) adapter := backup.NewServerSideAdapter(pool) - err := adapter.RemoveRepository(ctx, &backup.RemoveRepositoryRequest{ - Server: storage.ServerInfo{Address: cfg.SocketPath, Token: cfg.Auth.Token}, - Repo: repo, + err = adapter.RemoveAllRepositories(ctx, &backup.RemoveAllRepositoriesRequest{ + Server: storage.ServerInfo{Address: cfg.SocketPath, Token: cfg.Auth.Token}, + StorageName: repo.StorageName, }) require.NoError(t, err) - require.NoDirExists(t, repoPath) - - // With an invalid repository - err = adapter.RemoveRepository(ctx, &backup.RemoveRepositoryRequest{ - Server: storage.ServerInfo{Address: cfg.SocketPath, Token: cfg.Auth.Token}, - Repo: &gitalypb.Repository{StorageName: "default", RelativePath: "nonexistent"}, - }) - - require.EqualError(t, err, "server-side remove repo: remove: rpc error: code = NotFound desc = repository does not exist") -} - -func TestServerSideAdapter_ListRepositories(t *testing.T) { - t.Parallel() - - for _, tc := range []struct { - desc string - repos map[string][]*gitalypb.Repository - }{ - { - desc: "no repos", - repos: make(map[string][]*gitalypb.Repository), - }, - { - desc: "repos in a single storage", - repos: map[string][]*gitalypb.Repository{ - "storage-1": { - {RelativePath: "a", StorageName: "storage-1"}, - {RelativePath: "b", StorageName: "storage-1"}, - }, - }, - }, - { - desc: "repos in multiple storages", - repos: map[string][]*gitalypb.Repository{ - "storage-1": { - {RelativePath: "a", StorageName: "storage-1"}, - {RelativePath: "b", StorageName: "storage-1"}, - }, - "storage-2": { - {RelativePath: "c", StorageName: "storage-2"}, - {RelativePath: "d", StorageName: "storage-2"}, - }, - }, - }, - } { - t.Run(tc.desc, func(t *testing.T) { - var storages []string - for storageName := range tc.repos { - storages = append(storages, storageName) - } - - db := testdb.New(t) - db.TruncateAll(t) - rs := datastore.NewPostgresRepositoryStore(db, map[string][]string{"virtual-storage": storages}) - - // We don't really need a "default" storage, but this makes initialisation cleaner since - // WithStorages() takes at least one argument. - cfg := testcfg.Build(t, testcfg.WithStorages("default", storages...)) - cfg.SocketPath = testserver.RunGitalyServer(t, cfg, setup.RegisterAll) - - ctx := testhelper.Context(t) - - repoID := 1 - for storageName, repos := range tc.repos { - for _, repo := range repos { - storagePath, ok := cfg.StoragePath(storageName) - require.True(t, ok) - - _, _ = gittest.CreateRepository(t, ctx, cfg, gittest.CreateRepositoryConfig{ - RelativePath: repo.RelativePath, - Storage: config.Storage{Name: storageName, Path: storagePath}, - }) - - require.NoError(t, rs.CreateRepository(ctx, int64(repoID), "virtual-storage", repo.RelativePath, repo.RelativePath, storageName, nil, nil, false, false)) - - repoID++ - } - } - - pool := client.NewPool() - defer testhelper.MustClose(t, pool) - - adapter := backup.NewServerSideAdapter(pool) - - for storageName, repos := range tc.repos { - actualRepos, err := adapter.ListRepositories(ctx, &backup.ListRepositoriesRequest{ - Server: storage.ServerInfo{Address: cfg.SocketPath, Token: cfg.Auth.Token}, - StorageName: storageName, - }) - require.NoError(t, err) - require.EqualValues(t, repos, actualRepos) - } - }) - } } diff --git a/internal/cli/gitalybackup/create.go b/internal/cli/gitalybackup/create.go index be7e75cdb..4ad653490 100644 --- a/internal/cli/gitalybackup/create.go +++ b/internal/cli/gitalybackup/create.go @@ -174,7 +174,7 @@ func (cmd *createSubcommand) run(ctx context.Context, logger log.Logger, stdin i })) } - if _, err := pipeline.Done(); err != nil { + if err := pipeline.Done(); err != nil { return fmt.Errorf("create: %w", err) } return nil diff --git a/internal/cli/gitalybackup/restore.go b/internal/cli/gitalybackup/restore.go index defffa223..de9e2cd3d 100644 --- a/internal/cli/gitalybackup/restore.go +++ b/internal/cli/gitalybackup/restore.go @@ -135,18 +135,15 @@ func (cmd *restoreSubcommand) run(ctx context.Context, logger log.Logger, stdin manager = backup.NewManager(sink, locator, pool) } - // Get the set of existing repositories keyed by storage. We'll later use this to determine any - // dangling repos that should be removed. - existingRepos := make(map[string][]*gitalypb.Repository) for _, storageName := range cmd.removeAllRepositories { - repos, err := manager.ListRepositories(ctx, &backup.ListRepositoriesRequest{ + err := manager.RemoveAllRepositories(ctx, &backup.RemoveAllRepositoriesRequest{ StorageName: storageName, }) if err != nil { - logger.WithError(err).WithField("storage_name", storageName).Warn("failed to list repositories") + // Treat RemoveAll failures as soft failures until we can determine + // how often it fails. + logger.WithError(err).WithField("storage_name", storageName).Warn("failed to remove all repositories") } - - existingRepos[storageName] = repos } var opts []backup.PipelineOption @@ -181,29 +178,8 @@ func (cmd *restoreSubcommand) run(ctx context.Context, logger log.Logger, stdin })) } - restoredRepos, err := pipeline.Done() - if err != nil { + if err := pipeline.Done(); err != nil { return fmt.Errorf("restore: %w", err) } - - var removalErrors []error - for storageName, repos := range existingRepos { - for _, repo := range repos { - if dangling := restoredRepos[storageName][repo]; dangling == struct{}{} { - // If we have dangling repos (those which exist in the storage but - // weren't part of the restore), they need to be deleted so the - // state of repos in Gitaly matches that in the Rails DB. - if err := manager.RemoveRepository(ctx, &backup.RemoveRepositoryRequest{Repo: repo}); err != nil { - removalErrors = append(removalErrors, fmt.Errorf("storage_name %q relative_path %q: %w", storageName, repo.RelativePath, err)) - } - } - } - } - - if len(removalErrors) > 0 { - return fmt.Errorf("remove dangling repositories: %d failures encountered: %w", - len(removalErrors), errors.Join(removalErrors...)) - } - return nil } diff --git a/internal/cli/gitalybackup/restore_test.go b/internal/cli/gitalybackup/restore_test.go index 6d235525c..4eae52e4c 100644 --- a/internal/cli/gitalybackup/restore_test.go +++ b/internal/cli/gitalybackup/restore_test.go @@ -75,6 +75,12 @@ Issue: https://gitlab.com/gitlab-org/gitaly/-/issues/5269`) })) } + require.NoError(t, encoder.Encode(map[string]string{ + "address": "invalid", + "token": "invalid", + "relative_path": "invalid", + })) + ctx = testhelper.MergeIncomingMetadata(ctx, testcfg.GitalyServersMetadataFromCfg(t, cfg)) args := []string{ @@ -97,7 +103,9 @@ Issue: https://gitlab.com/gitlab-org/gitaly/-/issues/5269`) require.DirExists(t, existRepoPath) - require.NoError(t, cmd.RunContext(ctx, args)) + require.EqualError(t, + cmd.RunContext(ctx, args), + "restore: pipeline: 1 failures encountered:\n - invalid: manager: could not dial source: invalid connection string: \"invalid\"\n") require.NoDirExists(t, existRepoPath) @@ -171,6 +179,12 @@ Issue: https://gitlab.com/gitlab-org/gitaly/-/issues/5269`) })) } + require.NoError(t, encoder.Encode(map[string]string{ + "address": "invalid", + "token": "invalid", + "relative_path": "invalid", + })) + ctx = testhelper.MergeIncomingMetadata(ctx, testcfg.GitalyServersMetadataFromCfg(t, cfg)) args := []string{ @@ -193,7 +207,9 @@ Issue: https://gitlab.com/gitlab-org/gitaly/-/issues/5269`) require.DirExists(t, existRepoPath) - require.NoError(t, cmd.RunContext(ctx, args)) + require.EqualError(t, + cmd.RunContext(ctx, args), + "restore: pipeline: 1 failures encountered:\n - invalid: server-side restore: could not dial source: invalid connection string: \"invalid\"\n") require.NoDirExists(t, existRepoPath) diff --git a/internal/gitaly/service/repository/remove_all_test.go b/internal/gitaly/service/repository/remove_all_test.go index fd913523a..785f54b01 100644 --- a/internal/gitaly/service/repository/remove_all_test.go +++ b/internal/gitaly/service/repository/remove_all_test.go @@ -28,7 +28,6 @@ Issue: https://gitlab.com/gitlab-org/gitaly/-/issues/5269`) require.DirExists(t, repo1Path) require.DirExists(t, repo1Path) - //nolint:staticcheck _, err := client.RemoveAll(ctx, &gitalypb.RemoveAllRequest{ StorageName: cfg.Storages[0].Name, }) diff --git a/internal/praefect/datastore/repository_store.go b/internal/praefect/datastore/repository_store.go index 4925fcb50..77dce6ba0 100644 --- a/internal/praefect/datastore/repository_store.go +++ b/internal/praefect/datastore/repository_store.go @@ -111,8 +111,6 @@ type RepositoryStore interface { MarkVirtualStorageUnverified(ctx context.Context, virtualStorage string) (int64, error) // MarkStorageUnverified marsk all replicas on the storage as unverified. MarkStorageUnverified(ctx context.Context, virtualStorage, storage string) (int64, error) - // ListRepositoryPaths retrieves the relative path for all repositories present on the given virtual storage. - ListRepositoryPaths(ctx context.Context, virtualStorage string) ([]string, error) } // PostgresRepositoryStore is a Postgres implementation of RepositoryStore. @@ -918,28 +916,3 @@ func (rs *PostgresRepositoryStore) GetReplicaPath(ctx context.Context, repositor return replicaPath, nil } - -// ListRepositoryPaths retrieves the relative path for all repositories present on the given virtual storage. -func (rs *PostgresRepositoryStore) ListRepositoryPaths(ctx context.Context, virtualStorage string) ([]string, error) { - rows, err := rs.db.QueryContext(ctx, ` -SELECT relative_path -FROM repositories -WHERE virtual_storage = $1 -`, virtualStorage) - if err != nil { - return nil, fmt.Errorf("query: %w", err) - } - defer rows.Close() - - var relativePaths []string - for rows.Next() { - var relativePath string - if err := rows.Scan(&relativePath); err != nil { - return nil, fmt.Errorf("scan: %w", err) - } - - relativePaths = append(relativePaths, relativePath) - } - - return relativePaths, rows.Err() -} diff --git a/internal/praefect/remove_all.go b/internal/praefect/remove_all.go index 9c383cd0e..9fa08206f 100644 --- a/internal/praefect/remove_all.go +++ b/internal/praefect/remove_all.go @@ -32,7 +32,6 @@ func RemoveAllHandler(rs datastore.RepositoryStore, conns Connections) grpc.Stre conn := conn group.Go(func() error { - //nolint:staticcheck _, err := gitalypb.NewRepositoryServiceClient(conn).RemoveAll(ctx, &gitalypb.RemoveAllRequest{ StorageName: rewrittenStorage, }) diff --git a/internal/praefect/remove_all_test.go b/internal/praefect/remove_all_test.go index 6b5e6f848..965ffb95b 100644 --- a/internal/praefect/remove_all_test.go +++ b/internal/praefect/remove_all_test.go @@ -96,7 +96,6 @@ Issue: https://gitlab.com/gitlab-org/gitaly/-/issues/5269`) _, err = client.RepositorySize(ctx, &gitalypb.RepositorySizeRequest{Repository: &gitalypb.Repository{}}) testhelper.RequireGrpcError(t, errServedByGitaly, err) - //nolint:staticcheck resp, err := client.RemoveAll(ctx, &gitalypb.RemoveAllRequest{StorageName: virtualStorage}) require.NoError(t, err) diff --git a/internal/praefect/server.go b/internal/praefect/server.go index c52b160f5..54cbee079 100644 --- a/internal/praefect/server.go +++ b/internal/praefect/server.go @@ -195,9 +195,6 @@ func NewGRPCServer( "DeleteObjectPool": DeleteObjectPoolHandler(deps.RepositoryStore, deps.Logger, deps.Conns), "GetObjectPool": GetObjectPoolHandler(deps.RepositoryStore, deps.Router), }) - proxy.RegisterStreamHandlers(srv, "gitaly.InternalGitaly", map[string]grpc.StreamHandler{ - "WalkRepos": WalkReposHandler(deps.RepositoryStore), - }) } return srv diff --git a/internal/praefect/walkrepos.go b/internal/praefect/walkrepos.go deleted file mode 100644 index 1f321a4c7..000000000 --- a/internal/praefect/walkrepos.go +++ /dev/null @@ -1,47 +0,0 @@ -package praefect - -import ( - "fmt" - - "gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/storage" - "gitlab.com/gitlab-org/gitaly/v16/internal/praefect/datastore" - "gitlab.com/gitlab-org/gitaly/v16/internal/structerr" - "gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb" - "google.golang.org/grpc" -) - -// WalkReposHandler implements an interceptor for the WalkRepos RPC, invoked when calling -// through Praefect. Instead of walking the storage directory in the filesystem, this Praefect -// implementation queries the database for all known repositories in the given virtual storage. -// As a consequence, the modification_time parameter can't be populated in the response. -func WalkReposHandler(rs datastore.RepositoryStore) grpc.StreamHandler { - return func(srv interface{}, stream grpc.ServerStream) error { - sendRepo := func(relPath string) error { - return stream.SendMsg(&gitalypb.WalkReposResponse{ - RelativePath: relPath, - }) - } - - var req gitalypb.WalkReposRequest - if err := stream.RecvMsg(&req); err != nil { - return fmt.Errorf("receive request: %w", err) - } - - if req.StorageName == "" { - return structerr.NewInvalidArgument("%w", storage.ErrStorageNotSet) - } - - repos, err := rs.ListRepositoryPaths(stream.Context(), req.StorageName) - if err != nil { - return structerr.NewInternal("list repository paths: %w", err) - } - - for _, repo := range repos { - if err := sendRepo(repo); err != nil { - return structerr.NewInternal("send repository path: %w", err) - } - } - - return nil - } -} diff --git a/internal/praefect/walkrepos_test.go b/internal/praefect/walkrepos_test.go deleted file mode 100644 index 63301caf5..000000000 --- a/internal/praefect/walkrepos_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package praefect - -import ( - "net" - "path/filepath" - "testing" - - "github.com/stretchr/testify/require" - "gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/storage" - "gitlab.com/gitlab-org/gitaly/v16/internal/grpc/protoregistry" - "gitlab.com/gitlab-org/gitaly/v16/internal/praefect/config" - "gitlab.com/gitlab-org/gitaly/v16/internal/praefect/datastore" - "gitlab.com/gitlab-org/gitaly/v16/internal/structerr" - "gitlab.com/gitlab-org/gitaly/v16/internal/testhelper" - "gitlab.com/gitlab-org/gitaly/v16/internal/testhelper/testdb" - "gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" -) - -func TestWalkReposHandler(t *testing.T) { - t.Parallel() - - db := testdb.New(t) - for _, tc := range []struct { - desc string - request *gitalypb.WalkReposRequest - responses []*gitalypb.WalkReposResponse - expectedErr error - }{ - { - desc: "missing storage name", - request: &gitalypb.WalkReposRequest{}, - expectedErr: structerr.NewInvalidArgument("%w", storage.ErrStorageNotSet), - }, - { - desc: "repositories found", - request: &gitalypb.WalkReposRequest{StorageName: "virtual-storage"}, - responses: []*gitalypb.WalkReposResponse{ - {RelativePath: "relative-path"}, - {RelativePath: "relative-path-2"}, - }, - }, - } { - t.Run(tc.desc, func(t *testing.T) { - db.TruncateAll(t) - rs := datastore.NewPostgresRepositoryStore(db, map[string][]string{"virtual-storage": {"storage"}}) - ctx := testhelper.Context(t) - - require.NoError(t, rs.CreateRepository(ctx, 0, "virtual-storage", "relative-path", "relative-path", "storage", nil, nil, false, false)) - require.NoError(t, rs.CreateRepository(ctx, 1, "virtual-storage", "relative-path-2", "relative-path-2", "storage", nil, nil, false, false)) - - tmp := testhelper.TempDir(t) - - ln, err := net.Listen("unix", filepath.Join(tmp, "praefect")) - require.NoError(t, err) - - srv := NewGRPCServer(&Dependencies{ - Config: config.Config{Failover: config.Failover{ElectionStrategy: config.ElectionStrategyPerRepository}}, - Logger: testhelper.SharedLogger(t), - RepositoryStore: rs, - Registry: protoregistry.GitalyProtoPreregistered, - }, nil) - defer srv.Stop() - - go testhelper.MustServe(t, srv, ln) - - clientConn, err := grpc.DialContext(ctx, "unix://"+ln.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials())) - require.NoError(t, err) - defer testhelper.MustClose(t, clientConn) - - client := gitalypb.NewInternalGitalyClient(clientConn) - - stream, err := client.WalkRepos(ctx, tc.request) - if tc.expectedErr != nil { - // Consume the first message and test for errors only if we're expecting an error. - _, err = stream.Recv() - testhelper.RequireGrpcError(t, tc.expectedErr, err) - return - } - require.NoError(t, err) - - actualRepos, err := testhelper.Receive(stream.Recv) - require.NoError(t, err) - testhelper.ProtoEqual(t, tc.responses, actualRepos) - }) - } -} diff --git a/proto/go/gitalypb/repository.pb.go b/proto/go/gitalypb/repository.pb.go index bdfd812ed..ca005e2b5 100644 --- a/proto/go/gitalypb/repository.pb.go +++ b/proto/go/gitalypb/repository.pb.go @@ -6163,7 +6163,7 @@ var file_repository_proto_rawDesc = []byte{ 0x1c, 0x0a, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x32, 0xb2, 0x20, 0x0a, 0x11, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, + 0x6c, 0x75, 0x65, 0x32, 0xaf, 0x20, 0x0a, 0x11, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5d, 0x0a, 0x10, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, @@ -6399,34 +6399,34 @@ var file_repository_proto_rawDesc = []byte{ 0x79, 0x2e, 0x46, 0x75, 0x6c, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x46, 0x75, 0x6c, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x09, 0xfa, 0x97, 0x28, - 0x02, 0x08, 0x02, 0x88, 0x02, 0x01, 0x12, 0x4d, 0x0a, 0x09, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, + 0x02, 0x08, 0x02, 0x88, 0x02, 0x01, 0x12, 0x4a, 0x0a, 0x09, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x6c, 0x6c, 0x12, 0x18, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x6c, 0x6c, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x0b, 0xfa, 0x97, 0x28, 0x04, 0x08, 0x01, - 0x10, 0x02, 0x88, 0x02, 0x01, 0x12, 0x5d, 0x0a, 0x10, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, - 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1f, 0x2e, 0x67, 0x69, 0x74, 0x61, - 0x6c, 0x79, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, - 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x69, 0x74, - 0x61, 0x6c, 0x79, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, - 0x28, 0x02, 0x08, 0x02, 0x12, 0x60, 0x0a, 0x11, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x52, - 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x20, 0x2e, 0x67, 0x69, 0x74, 0x61, - 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x69, - 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x70, 0x6f, - 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, - 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x60, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, - 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x69, - 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x41, 0x74, 0x74, 0x72, - 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, - 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x41, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x02, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, 0x74, 0x6c, - 0x61, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2d, 0x6f, 0x72, - 0x67, 0x2f, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2f, 0x76, 0x31, 0x36, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x70, 0x62, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x08, 0xfa, 0x97, 0x28, 0x04, 0x08, 0x01, + 0x10, 0x02, 0x12, 0x5d, 0x0a, 0x10, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1f, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, + 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, + 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, + 0x02, 0x12, 0x60, 0x0a, 0x11, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x20, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, + 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, + 0x79, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, + 0x02, 0x08, 0x01, 0x12, 0x60, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x41, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, + 0x79, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x69, 0x74, + 0x61, 0x6c, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, + 0x97, 0x28, 0x02, 0x08, 0x02, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2d, 0x6f, 0x72, 0x67, 0x2f, 0x67, + 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2f, 0x76, 0x31, 0x36, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, + 0x67, 0x6f, 0x2f, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( diff --git a/proto/go/gitalypb/repository_grpc.pb.go b/proto/go/gitalypb/repository_grpc.pb.go index b407b948c..d5af55b94 100644 --- a/proto/go/gitalypb/repository_grpc.pb.go +++ b/proto/go/gitalypb/repository_grpc.pb.go @@ -192,9 +192,7 @@ type RepositoryServiceClient interface { // FullPath reads the "gitlab.fullpath" configuration from the repository's // gitconfig. Returns an error in case the full path has not been configured. FullPath(ctx context.Context, in *FullPathRequest, opts ...grpc.CallOption) (*FullPathResponse, error) - // Deprecated: Do not use. // RemoveAll deletes all repositories on a specified storage. - // Deprecated in favour of individually removing repositories with RemoveRepository. RemoveAll(ctx context.Context, in *RemoveAllRequest, opts ...grpc.CallOption) (*RemoveAllResponse, error) // BackupRepository creates a full or incremental backup streamed directly to // object-storage. The backup is created synchronously. The destination must @@ -959,7 +957,6 @@ func (c *repositoryServiceClient) FullPath(ctx context.Context, in *FullPathRequ return out, nil } -// Deprecated: Do not use. func (c *repositoryServiceClient) RemoveAll(ctx context.Context, in *RemoveAllRequest, opts ...grpc.CallOption) (*RemoveAllResponse, error) { out := new(RemoveAllResponse) err := c.cc.Invoke(ctx, "/gitaly.RepositoryService/RemoveAll", in, out, opts...) @@ -1170,9 +1167,7 @@ type RepositoryServiceServer interface { // FullPath reads the "gitlab.fullpath" configuration from the repository's // gitconfig. Returns an error in case the full path has not been configured. FullPath(context.Context, *FullPathRequest) (*FullPathResponse, error) - // Deprecated: Do not use. // RemoveAll deletes all repositories on a specified storage. - // Deprecated in favour of individually removing repositories with RemoveRepository. RemoveAll(context.Context, *RemoveAllRequest) (*RemoveAllResponse, error) // BackupRepository creates a full or incremental backup streamed directly to // object-storage. The backup is created synchronously. The destination must diff --git a/proto/repository.proto b/proto/repository.proto index 4a5d8dbf8..25d446f61 100644 --- a/proto/repository.proto +++ b/proto/repository.proto @@ -381,13 +381,11 @@ service RepositoryService { } // RemoveAll deletes all repositories on a specified storage. - // Deprecated in favour of individually removing repositories with RemoveRepository. rpc RemoveAll(RemoveAllRequest) returns (RemoveAllResponse) { option (op_type) = { op: MUTATOR scope_level: STORAGE }; - option deprecated = true; } // BackupRepository creates a full or incremental backup streamed directly to -- cgit v1.2.3 From 85df4b0674709b4588426ab5c9d6df684fd7f730 Mon Sep 17 00:00:00 2001 From: GitLab Release Tools Bot Date: Wed, 10 Jan 2024 23:26:00 +0000 Subject: Update changelog for 16.7.2 [ci skip] --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc8802e30..f1d480119 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Gitaly changelog +## 16.7.2 (2024-01-10) + +### Security (1 change) + +- [commit: Fix bug in commit signature parsing](gitlab-org/security/gitaly@50ef27b6e9af04d821133dd6a11fbe6d8cee8e97) ([merge request](gitlab-org/security/gitaly!87)) + ## 16.7.1 (2023-12-23) No changes. -- cgit v1.2.3 From 3ccd3763c6e8cd99501cf418f1d33ee9cae3d373 Mon Sep 17 00:00:00 2001 From: GitLab Release Tools Bot Date: Wed, 10 Jan 2024 23:26:16 +0000 Subject: Update changelog for 16.6.4 [ci skip] --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1d480119..82a93204d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,12 @@ No changes. - [gitaly: Move asynchronous tasks to end of startup](gitlab-org/gitaly@dd4ea4388b4b8e7c49ea423126f8be5e067729cd) ([merge request](gitlab-org/gitaly!6532)) - [cgroups: Create repository cgroups on-demand](gitlab-org/gitaly@105f6dd81689b7819b844b8be1be844bca6f2a67) ([merge request](gitlab-org/gitaly!6499)) +## 16.6.4 (2024-01-10) + +### Security (1 change) + +- [commit: Fix bug in commit signature parsing](gitlab-org/security/gitaly@615b117d20f45b8dd2b91a8a1f01e12dc966f193) ([merge request](gitlab-org/security/gitaly!91)) + ## 16.6.3 (2023-12-23) No changes. -- cgit v1.2.3 From c61895c84b9716b5f6d769cbc1a86bb6a6ef1c89 Mon Sep 17 00:00:00 2001 From: GitLab Release Tools Bot Date: Thu, 11 Jan 2024 00:15:30 +0000 Subject: Update changelog for 16.5.6 [ci skip] --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82a93204d..4b48461f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,6 +73,12 @@ No changes. - [backup: Use --all when creating an incremental backup bundle](gitlab-org/gitaly@d874d8c3eb0d0c9b9362b583ba8ccc42221140cd) ([merge request](gitlab-org/gitaly!6421)) +## 16.5.6 (2024-01-11) + +### Security (1 change) + +- [commit: Fix bug in commit signature parsing](gitlab-org/security/gitaly@d410c35c0906cb75bc0d8d967744b12991cefaa0) ([merge request](gitlab-org/security/gitaly!89)) + ## 16.5.5 (2023-12-23) No changes. -- cgit v1.2.3 From 4551075b93e7fb449734dd84406157b068e53a0c Mon Sep 17 00:00:00 2001 From: GitLab Release Tools Bot Date: Thu, 11 Jan 2024 00:33:57 +0000 Subject: Update changelog for 16.4.5 [ci skip] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b48461f3..67651cfb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -125,6 +125,10 @@ No changes. - [gitaly: Don't block on preloading licensedb](gitlab-org/gitaly@d96964bf6fe4a17adfded56a28bc276377351bc8) ([merge request](gitlab-org/gitaly!6397)) - [packed_binaries: Extract binaries in parallel](gitlab-org/gitaly@2f5d4f93bdd27d3299f9ccc005ee7bcca35efcc9) ([merge request](gitlab-org/gitaly!6401)) +## 16.4.5 (2024-01-11) + +No changes. + ## 16.4.4 (2023-12-13) No changes. -- cgit v1.2.3 From 0a476bda11bfe8f91d49a3cbeb8e0aa71f274f63 Mon Sep 17 00:00:00 2001 From: GitLab Release Tools Bot Date: Thu, 11 Jan 2024 00:46:41 +0000 Subject: Update changelog for 16.3.7 [ci skip] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67651cfb8..2ffbc3027 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -187,6 +187,10 @@ No changes. - [backup: Improve time taken to create a full backup bundle](gitlab-org/gitaly@c43b91c07de39a4e32b3bb855787ab43e9c6a6c7) ([merge request](gitlab-org/gitaly!6305)) - [backup: Improve performance of writing ref lists](gitlab-org/gitaly@897aadac824654041b9c383b17ec8a5f217eac6b) ([merge request](gitlab-org/gitaly!6305)) +## 16.3.7 (2024-01-11) + +No changes. + ## 16.3.6 (2023-10-30) No changes. -- cgit v1.2.3 From be88ac1c5db1dfaeef43492ebf9a5ebbfe4f3740 Mon Sep 17 00:00:00 2001 From: GitLab Release Tools Bot Date: Thu, 11 Jan 2024 00:59:43 +0000 Subject: Update changelog for 16.2.9 [ci skip] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ffbc3027..75e88a048 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -258,6 +258,10 @@ No changes. - [backup: Write an empty ref list for empty repositories](gitlab-org/gitaly@7fbae125172570e5bef222908388ba0ca1c9e43a) ([merge request](gitlab-org/gitaly!6090)) +## 16.2.9 (2024-01-11) + +No changes. + ## 16.2.8 (2023-09-28) No changes. -- cgit v1.2.3 From 3cef841313043732f7003229d6cfda487ad43d79 Mon Sep 17 00:00:00 2001 From: GitLab Release Tools Bot Date: Thu, 11 Jan 2024 01:10:56 +0000 Subject: Update changelog for 16.1.6 [ci skip] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75e88a048..d835e6cf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -334,6 +334,10 @@ No changes. - [localrepo: Avoid getting all branches for default branch detection](gitlab-org/gitaly@bdd3c05a2cc2889260050f2714884cc24503ae1a) by @blanet ([merge request](gitlab-org/gitaly!5725)) +## 16.1.6 (2024-01-11) + +No changes. + ## 16.1.5 (2023-08-31) No changes. -- cgit v1.2.3