diff --git a/Cargo.lock b/Cargo.lock index 051831ae..0fcf17d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1724,6 +1724,7 @@ dependencies = [ "gix", "line-numbers", "prost", + "prost-types", "rustc-hash", "tempfile", "tokio", @@ -4110,9 +4111,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" +checksum = "528ac67416ff8646872a3c02cad9cc4ee5dc9f9540c9b10771855c95cb2e5ae1" dependencies = [ "bytes", "prost-derive", @@ -4141,9 +4142,9 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" +checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" dependencies = [ "anyhow", "itertools 0.14.0", @@ -4221,9 +4222,9 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8991c4cbdb8bc5b11f0b074ffe286c30e523de90fee5ba8132f1399f23cb3dd7" +checksum = "f94967dc7688f3054c7fac87473ffae4cc4c3904800e2d9f5b857246d8963b0a" dependencies = [ "prost", ] diff --git a/gitmirror/Cargo.toml b/gitmirror/Cargo.toml index 908bb1ee..d0b65979 100644 --- a/gitmirror/Cargo.toml +++ b/gitmirror/Cargo.toml @@ -18,6 +18,7 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] } line-numbers = "0.4.0" rustc-hash = "2.1.2" gix = { version = "0.84", features = ["parallel", "blob-diff", "merge", "sha1", "sha256", "revision", "tree-editor"] } +prost-types = "0.14.4" [build-dependencies] tonic-prost-build = { workspace = true } diff --git a/gitmirror/proto/gen/gitmirror.pb.go b/gitmirror/proto/gen/gitmirror.pb.go index 569923fe..9c10cec9 100644 --- a/gitmirror/proto/gen/gitmirror.pb.go +++ b/gitmirror/proto/gen/gitmirror.pb.go @@ -9,6 +9,7 @@ package gitmirrorv1 import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" unsafe "unsafe" @@ -75,6 +76,154 @@ func (x *GetBlobRequest) GetOid() string { return "" } +type CommitLogRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // repo DID + Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"` + // Ranges to include in the git log (revspec, "A..B", "A...B", etc.). + // At least one range, or all_refs must be specified. + Ranges [][]byte `protobuf:"bytes,2,rep,name=ranges,proto3" json:"ranges,omitempty"` + // If true, all refs are searched for commits. + // Must not be true when ranges are given. + AllRefs bool `protobuf:"varint,3,opt,name=all_refs,json=allRefs,proto3" json:"all_refs,omitempty"` + // After is an optional parameter to specify the earliest commit to consider. + After *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=after,proto3" json:"after,omitempty"` + // Before is an optional parameter to specify the latest commit to consider. + Before *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=before,proto3" json:"before,omitempty"` + // MaxCommits is an optional parameter to specify the maximum number of commits + // to return. If max_commits is 0, all commits that match the criteria will be + // returned. + MaxCommits uint32 `protobuf:"varint,6,opt,name=max_commits,json=maxCommits,proto3" json:"max_commits,omitempty"` + // Skip is an optional parameter to specify the number of commits to skip. + // This can be used to implement a poor mans pagination. + Skip uint32 `protobuf:"varint,7,opt,name=skip,proto3" json:"skip,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CommitLogRequest) Reset() { + *x = CommitLogRequest{} + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CommitLogRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CommitLogRequest) ProtoMessage() {} + +func (x *CommitLogRequest) ProtoReflect() protoreflect.Message { + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CommitLogRequest.ProtoReflect.Descriptor instead. +func (*CommitLogRequest) Descriptor() ([]byte, []int) { + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{1} +} + +func (x *CommitLogRequest) GetRepo() string { + if x != nil { + return x.Repo + } + return "" +} + +func (x *CommitLogRequest) GetRanges() [][]byte { + if x != nil { + return x.Ranges + } + return nil +} + +func (x *CommitLogRequest) GetAllRefs() bool { + if x != nil { + return x.AllRefs + } + return false +} + +func (x *CommitLogRequest) GetAfter() *timestamppb.Timestamp { + if x != nil { + return x.After + } + return nil +} + +func (x *CommitLogRequest) GetBefore() *timestamppb.Timestamp { + if x != nil { + return x.Before + } + return nil +} + +func (x *CommitLogRequest) GetMaxCommits() uint32 { + if x != nil { + return x.MaxCommits + } + return 0 +} + +func (x *CommitLogRequest) GetSkip() uint32 { + if x != nil { + return x.Skip + } + return 0 +} + +type CommitLogResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Commits []*GitCommit `protobuf:"bytes,1,rep,name=commits,proto3" json:"commits,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CommitLogResponse) Reset() { + *x = CommitLogResponse{} + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CommitLogResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CommitLogResponse) ProtoMessage() {} + +func (x *CommitLogResponse) ProtoReflect() protoreflect.Message { + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CommitLogResponse.ProtoReflect.Descriptor instead. +func (*CommitLogResponse) Descriptor() ([]byte, []int) { + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{2} +} + +func (x *CommitLogResponse) GetCommits() []*GitCommit { + if x != nil { + return x.Commits + } + return nil +} + // One chunk of a blob's raw bytes; concatenate in arrival order. type BlobChunk struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -85,7 +234,7 @@ type BlobChunk struct { func (x *BlobChunk) Reset() { *x = BlobChunk{} - mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[1] + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -97,7 +246,7 @@ func (x *BlobChunk) String() string { func (*BlobChunk) ProtoMessage() {} func (x *BlobChunk) ProtoReflect() protoreflect.Message { - mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[1] + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -110,7 +259,7 @@ func (x *BlobChunk) ProtoReflect() protoreflect.Message { // Deprecated: Use BlobChunk.ProtoReflect.Descriptor instead. func (*BlobChunk) Descriptor() ([]byte, []int) { - return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{1} + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{3} } func (x *BlobChunk) GetData() []byte { @@ -131,7 +280,7 @@ type CommitsRequest struct { func (x *CommitsRequest) Reset() { *x = CommitsRequest{} - mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[2] + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -143,7 +292,7 @@ func (x *CommitsRequest) String() string { func (*CommitsRequest) ProtoMessage() {} func (x *CommitsRequest) ProtoReflect() protoreflect.Message { - mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[2] + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -156,7 +305,7 @@ func (x *CommitsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CommitsRequest.ProtoReflect.Descriptor instead. func (*CommitsRequest) Descriptor() ([]byte, []int) { - return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{2} + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{4} } func (x *CommitsRequest) GetRepo() string { @@ -181,7 +330,7 @@ type CommitsOptions struct { func (x *CommitsOptions) Reset() { *x = CommitsOptions{} - mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[3] + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -193,7 +342,7 @@ func (x *CommitsOptions) String() string { func (*CommitsOptions) ProtoMessage() {} func (x *CommitsOptions) ProtoReflect() protoreflect.Message { - mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[3] + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -206,7 +355,7 @@ func (x *CommitsOptions) ProtoReflect() protoreflect.Message { // Deprecated: Use CommitsOptions.ProtoReflect.Descriptor instead. func (*CommitsOptions) Descriptor() ([]byte, []int) { - return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{3} + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{5} } type CommitsResponse struct { @@ -219,7 +368,7 @@ type CommitsResponse struct { func (x *CommitsResponse) Reset() { *x = CommitsResponse{} - mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[4] + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -231,7 +380,7 @@ func (x *CommitsResponse) String() string { func (*CommitsResponse) ProtoMessage() {} func (x *CommitsResponse) ProtoReflect() protoreflect.Message { - mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[4] + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -244,7 +393,7 @@ func (x *CommitsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CommitsResponse.ProtoReflect.Descriptor instead. func (*CommitsResponse) Descriptor() ([]byte, []int) { - return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{4} + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{6} } func (x *CommitsResponse) GetCommits() []string { @@ -267,7 +416,7 @@ type DiffRequest struct { func (x *DiffRequest) Reset() { *x = DiffRequest{} - mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[5] + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -279,7 +428,7 @@ func (x *DiffRequest) String() string { func (*DiffRequest) ProtoMessage() {} func (x *DiffRequest) ProtoReflect() protoreflect.Message { - mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[5] + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -292,7 +441,7 @@ func (x *DiffRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DiffRequest.ProtoReflect.Descriptor instead. func (*DiffRequest) Descriptor() ([]byte, []int) { - return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{5} + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{7} } func (x *DiffRequest) GetRepo() string { @@ -331,7 +480,7 @@ type InterdiffRequest struct { func (x *InterdiffRequest) Reset() { *x = InterdiffRequest{} - mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[6] + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -343,7 +492,7 @@ func (x *InterdiffRequest) String() string { func (*InterdiffRequest) ProtoMessage() {} func (x *InterdiffRequest) ProtoReflect() protoreflect.Message { - mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[6] + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -356,7 +505,7 @@ func (x *InterdiffRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use InterdiffRequest.ProtoReflect.Descriptor instead. func (*InterdiffRequest) Descriptor() ([]byte, []int) { - return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{6} + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{8} } func (x *InterdiffRequest) GetRepo() string { @@ -407,7 +556,7 @@ type FileContent struct { func (x *FileContent) Reset() { *x = FileContent{} - mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[7] + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -419,7 +568,7 @@ func (x *FileContent) String() string { func (*FileContent) ProtoMessage() {} func (x *FileContent) ProtoReflect() protoreflect.Message { - mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[7] + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -432,7 +581,7 @@ func (x *FileContent) ProtoReflect() protoreflect.Message { // Deprecated: Use FileContent.ProtoReflect.Descriptor instead. func (*FileContent) Descriptor() ([]byte, []int) { - return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{7} + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{9} } func (x *FileContent) GetPath() string { @@ -481,7 +630,7 @@ type LinePair struct { func (x *LinePair) Reset() { *x = LinePair{} - mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[8] + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -493,7 +642,7 @@ func (x *LinePair) String() string { func (*LinePair) ProtoMessage() {} func (x *LinePair) ProtoReflect() protoreflect.Message { - mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[8] + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -506,7 +655,7 @@ func (x *LinePair) ProtoReflect() protoreflect.Message { // Deprecated: Use LinePair.ProtoReflect.Descriptor instead. func (*LinePair) Descriptor() ([]byte, []int) { - return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{8} + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{10} } func (x *LinePair) GetLhs() uint32 { @@ -535,7 +684,7 @@ type Hunk struct { func (x *Hunk) Reset() { *x = Hunk{} - mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[9] + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -547,7 +696,7 @@ func (x *Hunk) String() string { func (*Hunk) ProtoMessage() {} func (x *Hunk) ProtoReflect() protoreflect.Message { - mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[9] + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -560,7 +709,7 @@ func (x *Hunk) ProtoReflect() protoreflect.Message { // Deprecated: Use Hunk.ProtoReflect.Descriptor instead. func (*Hunk) Descriptor() ([]byte, []int) { - return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{9} + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{11} } func (x *Hunk) GetNovelLhs() []uint32 { @@ -595,7 +744,7 @@ type ByteChanges struct { func (x *ByteChanges) Reset() { *x = ByteChanges{} - mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[10] + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -607,7 +756,7 @@ func (x *ByteChanges) String() string { func (*ByteChanges) ProtoMessage() {} func (x *ByteChanges) ProtoReflect() protoreflect.Message { - mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[10] + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -620,7 +769,7 @@ func (x *ByteChanges) ProtoReflect() protoreflect.Message { // Deprecated: Use ByteChanges.ProtoReflect.Descriptor instead. func (*ByteChanges) Descriptor() ([]byte, []int) { - return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{10} + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{12} } func (x *ByteChanges) GetLhs() uint64 { @@ -650,7 +799,7 @@ type FileDiff struct { func (x *FileDiff) Reset() { *x = FileDiff{} - mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[11] + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -662,7 +811,7 @@ func (x *FileDiff) String() string { func (*FileDiff) ProtoMessage() {} func (x *FileDiff) ProtoReflect() protoreflect.Message { - mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[11] + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -675,7 +824,7 @@ func (x *FileDiff) ProtoReflect() protoreflect.Message { // Deprecated: Use FileDiff.ProtoReflect.Descriptor instead. func (*FileDiff) Descriptor() ([]byte, []int) { - return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{11} + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{13} } func (x *FileDiff) GetLhsSrc() *FileContent { @@ -713,14 +862,161 @@ func (x *FileDiff) GetHasSyntacticChanges() bool { return false } +type GitCommit struct { + state protoimpl.MessageState `protogen:"open.v1"` + Oid string `protobuf:"bytes,1,opt,name=oid,proto3" json:"oid,omitempty"` + Author *GitSignature `protobuf:"bytes,2,opt,name=author,proto3" json:"author,omitempty"` + Committer *GitSignature `protobuf:"bytes,3,opt,name=committer,proto3" json:"committer,omitempty"` + Message []byte `protobuf:"bytes,4,opt,name=message,proto3" json:"message,omitempty"` + Parents []string `protobuf:"bytes,5,rep,name=parents,proto3" json:"parents,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GitCommit) Reset() { + *x = GitCommit{} + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GitCommit) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GitCommit) ProtoMessage() {} + +func (x *GitCommit) ProtoReflect() protoreflect.Message { + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[14] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GitCommit.ProtoReflect.Descriptor instead. +func (*GitCommit) Descriptor() ([]byte, []int) { + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{14} +} + +func (x *GitCommit) GetOid() string { + if x != nil { + return x.Oid + } + return "" +} + +func (x *GitCommit) GetAuthor() *GitSignature { + if x != nil { + return x.Author + } + return nil +} + +func (x *GitCommit) GetCommitter() *GitSignature { + if x != nil { + return x.Committer + } + return nil +} + +func (x *GitCommit) GetMessage() []byte { + if x != nil { + return x.Message + } + return nil +} + +func (x *GitCommit) GetParents() []string { + if x != nil { + return x.Parents + } + return nil +} + +type GitSignature struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name []byte `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Email []byte `protobuf:"bytes,2,opt,name=email,proto3" json:"email,omitempty"` + Date *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=date,proto3" json:"date,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GitSignature) Reset() { + *x = GitSignature{} + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GitSignature) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GitSignature) ProtoMessage() {} + +func (x *GitSignature) ProtoReflect() protoreflect.Message { + mi := &file_gitmirror_v1_gitmirror_proto_msgTypes[15] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GitSignature.ProtoReflect.Descriptor instead. +func (*GitSignature) Descriptor() ([]byte, []int) { + return file_gitmirror_v1_gitmirror_proto_rawDescGZIP(), []int{15} +} + +func (x *GitSignature) GetName() []byte { + if x != nil { + return x.Name + } + return nil +} + +func (x *GitSignature) GetEmail() []byte { + if x != nil { + return x.Email + } + return nil +} + +func (x *GitSignature) GetDate() *timestamppb.Timestamp { + if x != nil { + return x.Date + } + return nil +} + var File_gitmirror_v1_gitmirror_proto protoreflect.FileDescriptor const file_gitmirror_v1_gitmirror_proto_rawDesc = "" + "\n" + - "\x1cgitmirror/v1/gitmirror.proto\x12\fgitmirror.v1\"6\n" + + "\x1cgitmirror/v1/gitmirror.proto\x12\fgitmirror.v1\x1a\x1fgoogle/protobuf/timestamp.proto\"6\n" + "\x0eGetBlobRequest\x12\x12\n" + "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x10\n" + - "\x03oid\x18\x02 \x01(\tR\x03oid\"\x1f\n" + + "\x03oid\x18\x02 \x01(\tR\x03oid\"\xf4\x01\n" + + "\x10CommitLogRequest\x12\x12\n" + + "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" + + "\x06ranges\x18\x02 \x03(\fR\x06ranges\x12\x19\n" + + "\ball_refs\x18\x03 \x01(\bR\aallRefs\x120\n" + + "\x05after\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\x05after\x122\n" + + "\x06before\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\x06before\x12\x1f\n" + + "\vmax_commits\x18\x06 \x01(\rR\n" + + "maxCommits\x12\x12\n" + + "\x04skip\x18\a \x01(\rR\x04skip\"F\n" + + "\x11CommitLogResponse\x121\n" + + "\acommits\x18\x01 \x03(\v2\x17.gitmirror.v1.GitCommitR\acommits\"\x1f\n" + "\tBlobChunk\x12\x12\n" + "\x04data\x18\x01 \x01(\fR\x04data\"\\\n" + "\x0eCommitsRequest\x12\x12\n" + @@ -763,12 +1059,23 @@ const file_gitmirror_v1_gitmirror_proto_rawDesc = "" + "\x05hunks\x18\x03 \x03(\v2\x12.gitmirror.v1.HunkR\x05hunks\x12H\n" + "\x10has_byte_changes\x18\x04 \x01(\v2\x19.gitmirror.v1.ByteChangesH\x00R\x0ehasByteChanges\x88\x01\x01\x122\n" + "\x15has_syntactic_changes\x18\x05 \x01(\bR\x13hasSyntacticChangesB\x13\n" + - "\x11_has_byte_changes2\xa2\x02\n" + + "\x11_has_byte_changes\"\xbf\x01\n" + + "\tGitCommit\x12\x10\n" + + "\x03oid\x18\x01 \x01(\tR\x03oid\x122\n" + + "\x06author\x18\x02 \x01(\v2\x1a.gitmirror.v1.GitSignatureR\x06author\x128\n" + + "\tcommitter\x18\x03 \x01(\v2\x1a.gitmirror.v1.GitSignatureR\tcommitter\x12\x18\n" + + "\amessage\x18\x04 \x01(\fR\amessage\x12\x18\n" + + "\aparents\x18\x05 \x03(\tR\aparents\"h\n" + + "\fGitSignature\x12\x12\n" + + "\x04name\x18\x01 \x01(\fR\x04name\x12\x14\n" + + "\x05email\x18\x02 \x01(\fR\x05email\x12.\n" + + "\x04date\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\x04date2\xf2\x02\n" + "\x10GitMirrorService\x12F\n" + "\aCommits\x12\x1c.gitmirror.v1.CommitsRequest\x1a\x1d.gitmirror.v1.CommitsResponse\x12;\n" + "\x04Diff\x12\x19.gitmirror.v1.DiffRequest\x1a\x16.gitmirror.v1.FileDiff0\x01\x12E\n" + "\tInterdiff\x12\x1e.gitmirror.v1.InterdiffRequest\x1a\x16.gitmirror.v1.FileDiff0\x01\x12B\n" + - "\aGetBlob\x12\x1c.gitmirror.v1.GetBlobRequest\x1a\x17.gitmirror.v1.BlobChunk0\x01B2Z0tangled.org/core/gitmirror/proto/gen;gitmirrorv1b\x06proto3" + "\aGetBlob\x12\x1c.gitmirror.v1.GetBlobRequest\x1a\x17.gitmirror.v1.BlobChunk0\x01\x12N\n" + + "\tCommitLog\x12\x1e.gitmirror.v1.CommitLogRequest\x1a\x1f.gitmirror.v1.CommitLogResponse0\x01B2Z0tangled.org/core/gitmirror/proto/gen;gitmirrorv1b\x06proto3" var ( file_gitmirror_v1_gitmirror_proto_rawDescOnce sync.Once @@ -782,41 +1089,54 @@ func file_gitmirror_v1_gitmirror_proto_rawDescGZIP() []byte { return file_gitmirror_v1_gitmirror_proto_rawDescData } -var file_gitmirror_v1_gitmirror_proto_msgTypes = make([]protoimpl.MessageInfo, 12) +var file_gitmirror_v1_gitmirror_proto_msgTypes = make([]protoimpl.MessageInfo, 16) var file_gitmirror_v1_gitmirror_proto_goTypes = []any{ - (*GetBlobRequest)(nil), // 0: gitmirror.v1.GetBlobRequest - (*BlobChunk)(nil), // 1: gitmirror.v1.BlobChunk - (*CommitsRequest)(nil), // 2: gitmirror.v1.CommitsRequest - (*CommitsOptions)(nil), // 3: gitmirror.v1.CommitsOptions - (*CommitsResponse)(nil), // 4: gitmirror.v1.CommitsResponse - (*DiffRequest)(nil), // 5: gitmirror.v1.DiffRequest - (*InterdiffRequest)(nil), // 6: gitmirror.v1.InterdiffRequest - (*FileContent)(nil), // 7: gitmirror.v1.FileContent - (*LinePair)(nil), // 8: gitmirror.v1.LinePair - (*Hunk)(nil), // 9: gitmirror.v1.Hunk - (*ByteChanges)(nil), // 10: gitmirror.v1.ByteChanges - (*FileDiff)(nil), // 11: gitmirror.v1.FileDiff + (*GetBlobRequest)(nil), // 0: gitmirror.v1.GetBlobRequest + (*CommitLogRequest)(nil), // 1: gitmirror.v1.CommitLogRequest + (*CommitLogResponse)(nil), // 2: gitmirror.v1.CommitLogResponse + (*BlobChunk)(nil), // 3: gitmirror.v1.BlobChunk + (*CommitsRequest)(nil), // 4: gitmirror.v1.CommitsRequest + (*CommitsOptions)(nil), // 5: gitmirror.v1.CommitsOptions + (*CommitsResponse)(nil), // 6: gitmirror.v1.CommitsResponse + (*DiffRequest)(nil), // 7: gitmirror.v1.DiffRequest + (*InterdiffRequest)(nil), // 8: gitmirror.v1.InterdiffRequest + (*FileContent)(nil), // 9: gitmirror.v1.FileContent + (*LinePair)(nil), // 10: gitmirror.v1.LinePair + (*Hunk)(nil), // 11: gitmirror.v1.Hunk + (*ByteChanges)(nil), // 12: gitmirror.v1.ByteChanges + (*FileDiff)(nil), // 13: gitmirror.v1.FileDiff + (*GitCommit)(nil), // 14: gitmirror.v1.GitCommit + (*GitSignature)(nil), // 15: gitmirror.v1.GitSignature + (*timestamppb.Timestamp)(nil), // 16: google.protobuf.Timestamp } var file_gitmirror_v1_gitmirror_proto_depIdxs = []int32{ - 3, // 0: gitmirror.v1.CommitsRequest.options:type_name -> gitmirror.v1.CommitsOptions - 8, // 1: gitmirror.v1.Hunk.lines:type_name -> gitmirror.v1.LinePair - 7, // 2: gitmirror.v1.FileDiff.lhs_src:type_name -> gitmirror.v1.FileContent - 7, // 3: gitmirror.v1.FileDiff.rhs_src:type_name -> gitmirror.v1.FileContent - 9, // 4: gitmirror.v1.FileDiff.hunks:type_name -> gitmirror.v1.Hunk - 10, // 5: gitmirror.v1.FileDiff.has_byte_changes:type_name -> gitmirror.v1.ByteChanges - 2, // 6: gitmirror.v1.GitMirrorService.Commits:input_type -> gitmirror.v1.CommitsRequest - 5, // 7: gitmirror.v1.GitMirrorService.Diff:input_type -> gitmirror.v1.DiffRequest - 6, // 8: gitmirror.v1.GitMirrorService.Interdiff:input_type -> gitmirror.v1.InterdiffRequest - 0, // 9: gitmirror.v1.GitMirrorService.GetBlob:input_type -> gitmirror.v1.GetBlobRequest - 4, // 10: gitmirror.v1.GitMirrorService.Commits:output_type -> gitmirror.v1.CommitsResponse - 11, // 11: gitmirror.v1.GitMirrorService.Diff:output_type -> gitmirror.v1.FileDiff - 11, // 12: gitmirror.v1.GitMirrorService.Interdiff:output_type -> gitmirror.v1.FileDiff - 1, // 13: gitmirror.v1.GitMirrorService.GetBlob:output_type -> gitmirror.v1.BlobChunk - 10, // [10:14] is the sub-list for method output_type - 6, // [6:10] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name + 16, // 0: gitmirror.v1.CommitLogRequest.after:type_name -> google.protobuf.Timestamp + 16, // 1: gitmirror.v1.CommitLogRequest.before:type_name -> google.protobuf.Timestamp + 14, // 2: gitmirror.v1.CommitLogResponse.commits:type_name -> gitmirror.v1.GitCommit + 5, // 3: gitmirror.v1.CommitsRequest.options:type_name -> gitmirror.v1.CommitsOptions + 10, // 4: gitmirror.v1.Hunk.lines:type_name -> gitmirror.v1.LinePair + 9, // 5: gitmirror.v1.FileDiff.lhs_src:type_name -> gitmirror.v1.FileContent + 9, // 6: gitmirror.v1.FileDiff.rhs_src:type_name -> gitmirror.v1.FileContent + 11, // 7: gitmirror.v1.FileDiff.hunks:type_name -> gitmirror.v1.Hunk + 12, // 8: gitmirror.v1.FileDiff.has_byte_changes:type_name -> gitmirror.v1.ByteChanges + 15, // 9: gitmirror.v1.GitCommit.author:type_name -> gitmirror.v1.GitSignature + 15, // 10: gitmirror.v1.GitCommit.committer:type_name -> gitmirror.v1.GitSignature + 16, // 11: gitmirror.v1.GitSignature.date:type_name -> google.protobuf.Timestamp + 4, // 12: gitmirror.v1.GitMirrorService.Commits:input_type -> gitmirror.v1.CommitsRequest + 7, // 13: gitmirror.v1.GitMirrorService.Diff:input_type -> gitmirror.v1.DiffRequest + 8, // 14: gitmirror.v1.GitMirrorService.Interdiff:input_type -> gitmirror.v1.InterdiffRequest + 0, // 15: gitmirror.v1.GitMirrorService.GetBlob:input_type -> gitmirror.v1.GetBlobRequest + 1, // 16: gitmirror.v1.GitMirrorService.CommitLog:input_type -> gitmirror.v1.CommitLogRequest + 6, // 17: gitmirror.v1.GitMirrorService.Commits:output_type -> gitmirror.v1.CommitsResponse + 13, // 18: gitmirror.v1.GitMirrorService.Diff:output_type -> gitmirror.v1.FileDiff + 13, // 19: gitmirror.v1.GitMirrorService.Interdiff:output_type -> gitmirror.v1.FileDiff + 3, // 20: gitmirror.v1.GitMirrorService.GetBlob:output_type -> gitmirror.v1.BlobChunk + 2, // 21: gitmirror.v1.GitMirrorService.CommitLog:output_type -> gitmirror.v1.CommitLogResponse + 17, // [17:22] is the sub-list for method output_type + 12, // [12:17] is the sub-list for method input_type + 12, // [12:12] is the sub-list for extension type_name + 12, // [12:12] is the sub-list for extension extendee + 0, // [0:12] is the sub-list for field type_name } func init() { file_gitmirror_v1_gitmirror_proto_init() } @@ -824,15 +1144,15 @@ func file_gitmirror_v1_gitmirror_proto_init() { if File_gitmirror_v1_gitmirror_proto != nil { return } - file_gitmirror_v1_gitmirror_proto_msgTypes[8].OneofWrappers = []any{} - file_gitmirror_v1_gitmirror_proto_msgTypes[11].OneofWrappers = []any{} + file_gitmirror_v1_gitmirror_proto_msgTypes[10].OneofWrappers = []any{} + file_gitmirror_v1_gitmirror_proto_msgTypes[13].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_gitmirror_v1_gitmirror_proto_rawDesc), len(file_gitmirror_v1_gitmirror_proto_rawDesc)), NumEnums: 0, - NumMessages: 12, + NumMessages: 16, NumExtensions: 0, NumServices: 1, }, diff --git a/gitmirror/proto/gen/gitmirror_grpc.pb.go b/gitmirror/proto/gen/gitmirror_grpc.pb.go index 4e6ad768..e6b1ab6c 100644 --- a/gitmirror/proto/gen/gitmirror_grpc.pb.go +++ b/gitmirror/proto/gen/gitmirror_grpc.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.6.0 +// - protoc-gen-go-grpc v1.6.2 // - protoc (unknown) // source: gitmirror/v1/gitmirror.proto @@ -23,6 +23,7 @@ const ( GitMirrorService_Diff_FullMethodName = "/gitmirror.v1.GitMirrorService/Diff" GitMirrorService_Interdiff_FullMethodName = "/gitmirror.v1.GitMirrorService/Interdiff" GitMirrorService_GetBlob_FullMethodName = "/gitmirror.v1.GitMirrorService/GetBlob" + GitMirrorService_CommitLog_FullMethodName = "/gitmirror.v1.GitMirrorService/CommitLog" ) // GitMirrorServiceClient is the client API for GitMirrorService service. @@ -34,6 +35,7 @@ type GitMirrorServiceClient interface { Interdiff(ctx context.Context, in *InterdiffRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[FileDiff], error) // GetBlob streams a blob's raw bytes by OID, in chunks. GetBlob(ctx context.Context, in *GetBlobRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[BlobChunk], error) + CommitLog(ctx context.Context, in *CommitLogRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[CommitLogResponse], error) } type gitMirrorServiceClient struct { @@ -111,6 +113,25 @@ func (c *gitMirrorServiceClient) GetBlob(ctx context.Context, in *GetBlobRequest // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type GitMirrorService_GetBlobClient = grpc.ServerStreamingClient[BlobChunk] +func (c *gitMirrorServiceClient) CommitLog(ctx context.Context, in *CommitLogRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[CommitLogResponse], error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &GitMirrorService_ServiceDesc.Streams[3], GitMirrorService_CommitLog_FullMethodName, cOpts...) + if err != nil { + return nil, err + } + x := &grpc.GenericClientStream[CommitLogRequest, CommitLogResponse]{ClientStream: stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type GitMirrorService_CommitLogClient = grpc.ServerStreamingClient[CommitLogResponse] + // GitMirrorServiceServer is the server API for GitMirrorService service. // All implementations must embed UnimplementedGitMirrorServiceServer // for forward compatibility. @@ -120,6 +141,7 @@ type GitMirrorServiceServer interface { Interdiff(*InterdiffRequest, grpc.ServerStreamingServer[FileDiff]) error // GetBlob streams a blob's raw bytes by OID, in chunks. GetBlob(*GetBlobRequest, grpc.ServerStreamingServer[BlobChunk]) error + CommitLog(*CommitLogRequest, grpc.ServerStreamingServer[CommitLogResponse]) error mustEmbedUnimplementedGitMirrorServiceServer() } @@ -142,6 +164,9 @@ func (UnimplementedGitMirrorServiceServer) Interdiff(*InterdiffRequest, grpc.Ser func (UnimplementedGitMirrorServiceServer) GetBlob(*GetBlobRequest, grpc.ServerStreamingServer[BlobChunk]) error { return status.Error(codes.Unimplemented, "method GetBlob not implemented") } +func (UnimplementedGitMirrorServiceServer) CommitLog(*CommitLogRequest, grpc.ServerStreamingServer[CommitLogResponse]) error { + return status.Error(codes.Unimplemented, "method CommitLog not implemented") +} func (UnimplementedGitMirrorServiceServer) mustEmbedUnimplementedGitMirrorServiceServer() {} func (UnimplementedGitMirrorServiceServer) testEmbeddedByValue() {} @@ -214,6 +239,17 @@ func _GitMirrorService_GetBlob_Handler(srv interface{}, stream grpc.ServerStream // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type GitMirrorService_GetBlobServer = grpc.ServerStreamingServer[BlobChunk] +func _GitMirrorService_CommitLog_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(CommitLogRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(GitMirrorServiceServer).CommitLog(m, &grpc.GenericServerStream[CommitLogRequest, CommitLogResponse]{ServerStream: stream}) +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type GitMirrorService_CommitLogServer = grpc.ServerStreamingServer[CommitLogResponse] + // GitMirrorService_ServiceDesc is the grpc.ServiceDesc for GitMirrorService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -242,6 +278,11 @@ var GitMirrorService_ServiceDesc = grpc.ServiceDesc{ Handler: _GitMirrorService_GetBlob_Handler, ServerStreams: true, }, + { + StreamName: "CommitLog", + Handler: _GitMirrorService_CommitLog_Handler, + ServerStreams: true, + }, }, Metadata: "gitmirror/v1/gitmirror.proto", } diff --git a/gitmirror/proto/gitmirror/v1/gitmirror.proto b/gitmirror/proto/gitmirror/v1/gitmirror.proto index aea16488..69263dbc 100644 --- a/gitmirror/proto/gitmirror/v1/gitmirror.proto +++ b/gitmirror/proto/gitmirror/v1/gitmirror.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package gitmirror.v1; +import "google/protobuf/timestamp.proto"; + option go_package = "tangled.org/core/gitmirror/proto/gen;gitmirrorv1"; service GitMirrorService { @@ -10,6 +12,7 @@ service GitMirrorService { rpc Interdiff(InterdiffRequest) returns (stream FileDiff); // GetBlob streams a blob's raw bytes by OID, in chunks. rpc GetBlob(GetBlobRequest) returns (stream BlobChunk); + rpc CommitLog(CommitLogRequest) returns (stream CommitLogResponse); } message GetBlobRequest { @@ -19,6 +22,31 @@ message GetBlobRequest { string oid = 2; } +message CommitLogRequest { + // repo DID + string repo = 1; + // Ranges to include in the git log (revspec, "A..B", "A...B", etc.). + // At least one range, or all_refs must be specified. + repeated bytes ranges = 2; + // If true, all refs are searched for commits. + // Must not be true when ranges are given. + bool all_refs = 3; + // After is an optional parameter to specify the earliest commit to consider. + google.protobuf.Timestamp after = 4; + // Before is an optional parameter to specify the latest commit to consider. + google.protobuf.Timestamp before = 5; + // MaxCommits is an optional parameter to specify the maximum number of commits + // to return. If max_commits is 0, all commits that match the criteria will be + // returned. + uint32 max_commits = 6; + // Skip is an optional parameter to specify the number of commits to skip. + // This can be used to implement a poor mans pagination. + uint32 skip = 7; +} +message CommitLogResponse { + repeated GitCommit commits = 1; +} + // One chunk of a blob's raw bytes; concatenate in arrival order. message BlobChunk { bytes data = 1; @@ -95,3 +123,17 @@ message FileDiff { bool has_syntactic_changes = 5; // TODO: lhs_positions / rhs_positions (MatchedPos) — AST, added later. } + +message GitCommit { + string oid = 1; + GitSignature author = 2; + GitSignature committer = 3; + bytes message = 4; + repeated string parents = 5; +} + +message GitSignature { + bytes name = 1; + bytes email = 2; + google.protobuf.Timestamp date = 3; +} diff --git a/gitmirror/src/main.rs b/gitmirror/src/main.rs index 6b28b206..0a4fb1db 100644 --- a/gitmirror/src/main.rs +++ b/gitmirror/src/main.rs @@ -6,6 +6,7 @@ use tonic::{Request, Response, Status}; use tracing::info; mod diff; +mod protocol; pub mod gitmirror { pub mod v1 { @@ -18,20 +19,27 @@ use std::pin::Pin; use gix::bstr::ByteSlice as _; use tokio::sync::mpsc; -use tokio_stream::Stream; use tokio_stream::wrappers::ReceiverStream; +use tokio_stream::Stream; + +use gix::revision::plumbing::Spec as RevSpec; +use gix::revision::walk::Sorting; use gitmirror::v1::git_mirror_service_server::{GitMirrorService, GitMirrorServiceServer}; use gitmirror::v1::{ - BlobChunk, ByteChanges, CommitsRequest, CommitsResponse, DiffRequest, FileContent, FileDiff, - GetBlobRequest, Hunk, InterdiffRequest, LinePair, + BlobChunk, ByteChanges, CommitLogRequest, CommitLogResponse, CommitsRequest, CommitsResponse, + DiffRequest, FileContent, FileDiff, GetBlobRequest, GitCommit, GitSignature, Hunk, + InterdiffRequest, LinePair, }; type FileDiffStream = Pin> + Send>>; type BlobChunkStream = Pin> + Send>>; +type CommitLogResponseStream = Pin> + Send>>; /// Blob bytes are streamed in chunks of this size. const BLOB_CHUNK_SIZE: usize = 64 * 1024; +/// GitCommits are streamed in batches of this size. +const COMMIT_LOG_BATCH_SIZE: usize = 128; #[derive(Parser)] #[command(name = "gitmirror", about = "Git mirror gRPC service")] @@ -57,6 +65,47 @@ impl GitMirror { gix::open(self.repo_base.join(did)) .map_err(|e| Status::not_found(format!("repo not found: {e}"))) } + +} + +/// Resolve tips/hidden commits from a [`CommitLogRequest`] and return the resulting gix walk, +/// sorted newest-first to match `git log`'s default order. +fn commit_log_walk<'repo>( + repo: &'repo gix::Repository, + req: &CommitLogRequest, +) -> anyhow::Result> { + let mut tips = Vec::new(); + let mut hidden = Vec::new(); + + if req.all_refs { + for r in repo.references()?.all()? { + let mut r = r.map_err(|e| anyhow::anyhow!(e))?; + if let Ok(commit) = r.peel_to_commit() { + tips.push(commit.id); + } + } + } else { + for range in &req.ranges { + let revspec = repo.rev_parse(range.as_bstr())?; + let spec = revspec.detach(); + match spec { + RevSpec::Include(id) => tips.push(id), + RevSpec::Range { from, to } => { + tips.push(to); + hidden.push(from); + } + _ => { + anyhow::bail!("The spec isn't currently supported: {spec:?}") + } + } + } + } + + Ok(repo + .rev_walk(tips) + .sorting(Sorting::ByCommitTime(Default::default())) + .with_hidden(hidden) + .all()?) } #[tonic::async_trait] @@ -147,6 +196,72 @@ impl GitMirrorService for GitMirror { Ok(Response::new(Box::pin(ReceiverStream::new(rx)))) } + + type CommitLogStream = CommitLogResponseStream; + + async fn commit_log( + &self, + request: Request, + ) -> Result, Status> { + let req = request.into_inner(); + if req.all_refs == req.ranges.is_empty() { + return Err(Status::invalid_argument( + "specify exactly one of `ranges` or `all_refs`", + )); + } + let repo = self.open_repo(&req.repo)?; + let safe = repo.into_sync(); + + let (tx, rx) = mpsc::channel::>(16); + tokio::task::spawn_blocking(move || { + let repo = safe.to_thread_local(); + let run = || -> anyhow::Result<()> { + let after = req.after.map(|t| t.seconds); + let before = req.before.map(|t| t.seconds); + let max_commits = req.max_commits as usize; + let mut skip = req.skip as usize; + let mut sent = 0usize; + let mut batch = Vec::with_capacity(COMMIT_LOG_BATCH_SIZE); + + for info in commit_log_walk(&repo, &req)? { + let info = info?; + let commit_time = info.commit_time(); + if before.is_some_and(|b| commit_time > b) { + continue; + } + if after.is_some_and(|a| commit_time < a) { + break; // newest-first order: nothing older will match either + } + if skip > 0 { + skip -= 1; + continue; + } + if max_commits != 0 && sent >= max_commits { + break; + } + batch.push(GitCommit::try_from(&info.object()?)?); + sent += 1; + if batch.len() == COMMIT_LOG_BATCH_SIZE { + let msg = CommitLogResponse { + commits: std::mem::take(&mut batch), + }; + if tx.blocking_send(Ok(msg)).is_err() { + return Ok(()); // client hung up + } + } + } + if !batch.is_empty() { + let _ = tx.blocking_send(Ok(CommitLogResponse { commits: batch })); + } + Ok(()) + }; + if let Err(e) = run() { + let _ = tx.blocking_send(Err(Status::internal(e.to_string()))); + } + }); + + Ok(Response::new(Box::pin(ReceiverStream::new(rx)))) + } } /// Resolve a commit-ish ref string (oid, short oid, branch/tag) to its tree oid. @@ -184,6 +299,36 @@ fn file_content(f: diff::FileContent) -> FileContent { } } +impl TryFrom<&gix::Commit<'_>> for GitCommit { + type Error = anyhow::Error; + + fn try_from(commit: &gix::Commit<'_>) -> anyhow::Result { + Ok(GitCommit { + oid: commit.id.to_string(), + author: Some(commit.author()?.try_into()?), + committer: Some(commit.committer()?.try_into()?), + message: commit.message_raw()?.to_vec(), + parents: commit.parent_ids().map(|id| id.to_string()).collect(), + }) + } +} + +impl TryFrom> for GitSignature { + type Error = anyhow::Error; + + fn try_from(sig: gix::actor::SignatureRef<'_>) -> anyhow::Result { + let time = sig.time()?; + Ok(GitSignature { + name: sig.name.to_vec(), + email: sig.email.to_vec(), + date: Some(prost_types::Timestamp { + seconds: time.seconds, + nanos: 0, + }), + }) + } +} + fn hunk(h: diff::Hunk) -> Hunk { // Sort the novel line sets for deterministic output. let sorted = |set: rustc_hash::FxHashSet| -> Vec { diff --git a/gitmirror/src/protocol.rs b/gitmirror/src/protocol.rs new file mode 100644 index 00000000..93a7e028 --- /dev/null +++ b/gitmirror/src/protocol.rs @@ -0,0 +1,3 @@ +// pub mod v1 { +// tonic::include_proto!("gitmirror.v1"); +// }