detached 86d856e

Merge pull request 'feat(api): add GitService.ListPaths' (#4) from feat/file-finder into main

Reviewed-on: https://git.bnema.dev/bnema/ermite-api/pulls/4
commit
86d856ecea6953c5040023a98a8e87f6b12100e2
author
bnema · · 2026-10-12 05:25 UTC
merge of
e9394b7 + cb56994 · diff against the first parent
signature
SSH UnverifiedSHA256:VOCA5lYARHcbKDZ7GhTV45VmcjWnJ6ZQIBazj17LivA The key is unknown
5 files · +297 -84
M README.md +1 -1
@@ -17,7 +17,7 @@ Handlers are mounted under `/-/api/` by the server. Errors are Connect codes.
1717 | Service | RPCs |
1818 |---|---|
1919 | `RepoService` | `ListRepos`, `GetRepo`, `CreateRepo`, `UpdateRepo` (field mask, merge settings), `DeleteRepo` |
20-| `GitService` | `ListRefs`, `GetTree`, `GetBlob` (streamed), `ListCommits`, `GetCommit` (structured diff), `ResolveRevision` |
20+| `GitService` | `ListRefs`, `GetTree`, `ListPaths`, `GetBlob` (streamed), `ListCommits`, `GetCommit` (structured diff), `ResolveRevision` |
2121 | `TokenService` | `CreateToken` (secret shown once), `ListTokens`, `RevokeToken` |
2222 | `MergeRequestService` | `CreateMergeRequest`, `ListMergeRequests`, `GetMergeRequest` (commits, diff, comments), `AddComment`, `ResolveComment`, `CloseMergeRequest`, `MergeMergeRequest`, `MarkRead` |
2323 | `SessionService` | `CreateLoginLink`, `SignOutEverywhere` (owner only) |
@@ -17,7 +17,7 @@ Handlers are mounted under `/-/api/` by the server. Errors are Connect codes.
17 | Service | RPCs |17 | Service | RPCs |
18 |---|---|18 |---|---|
19 | `RepoService` | `ListRepos`, `GetRepo`, `CreateRepo`, `UpdateRepo` (field mask, merge settings), `DeleteRepo` |19 | `RepoService` | `ListRepos`, `GetRepo`, `CreateRepo`, `UpdateRepo` (field mask, merge settings), `DeleteRepo` |
20-| `GitService` | `ListRefs`, `GetTree`, `GetBlob` (streamed), `ListCommits`, `GetCommit` (structured diff), `ResolveRevision` |20+| `GitService` | `ListRefs`, `GetTree`, `ListPaths`, `GetBlob` (streamed), `ListCommits`, `GetCommit` (structured diff), `ResolveRevision` |
21 | `TokenService` | `CreateToken` (secret shown once), `ListTokens`, `RevokeToken` |21 | `TokenService` | `CreateToken` (secret shown once), `ListTokens`, `RevokeToken` |
22 | `MergeRequestService` | `CreateMergeRequest`, `ListMergeRequests`, `GetMergeRequest` (commits, diff, comments), `AddComment`, `ResolveComment`, `CloseMergeRequest`, `MergeMergeRequest`, `MarkRead` |22 | `MergeRequestService` | `CreateMergeRequest`, `ListMergeRequests`, `GetMergeRequest` (commits, diff, comments), `AddComment`, `ResolveComment`, `CloseMergeRequest`, `MergeMergeRequest`, `MarkRead` |
23 | `SessionService` | `CreateLoginLink`, `SignOutEverywhere` (owner only) |23 | `SessionService` | `CreateLoginLink`, `SignOutEverywhere` (owner only) |
M gen/ermite/ermiteconnect/git.connect.go +40 -0
@@ -39,6 +39,8 @@ const (
3939 GitServiceListRefsProcedure = "/ermite.GitService/ListRefs"
4040 // GitServiceGetTreeProcedure is the fully-qualified name of the GitService's GetTree RPC.
4141 GitServiceGetTreeProcedure = "/ermite.GitService/GetTree"
42+ // GitServiceListPathsProcedure is the fully-qualified name of the GitService's ListPaths RPC.
43+ GitServiceListPathsProcedure = "/ermite.GitService/ListPaths"
4244 // GitServiceGetBlobProcedure is the fully-qualified name of the GitService's GetBlob RPC.
4345 GitServiceGetBlobProcedure = "/ermite.GitService/GetBlob"
4446 // GitServiceListCommitsProcedure is the fully-qualified name of the GitService's ListCommits RPC.
@@ -59,6 +61,13 @@ type GitServiceClient interface {
5961 // not_found if the path does not exist and invalid_argument if it is not a
6062 // directory.
6163 GetTree(context.Context, *connect.Request[ermite.GetTreeRequest]) (*connect.Response[ermite.GetTreeResponse], error)
64+ // ListPaths lists the paths of every file and symbolic link of a revision,
65+ // recursively, in Git tree order (the order of `git ls-tree -r`). Directories
66+ // and submodules are not listed, nor paths GetBlob cannot address (not valid
67+ // UTF-8, or with a control character). Fails with not_found for an unknown
68+ // revision or an empty repository. Servers that have it advertise the
69+ // feature "git.list_paths" in GetVersion.
70+ ListPaths(context.Context, *connect.Request[ermite.ListPathsRequest]) (*connect.Response[ermite.ListPathsResponse], error)
6271 // GetBlob streams the content of a file at a revision, in chunks of at most
6372 // 64 KiB. The first message carries the metadata in info, then the chunks
6473 // follow; the memory used by the server does not depend on the file size.
@@ -99,6 +108,12 @@ func NewGitServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...
99108 connect.WithSchema(gitServiceMethods.ByName("GetTree")),
100109 connect.WithClientOptions(opts...),
101110 ),
111+ listPaths: connect.NewClient[ermite.ListPathsRequest, ermite.ListPathsResponse](
112+ httpClient,
113+ baseURL+GitServiceListPathsProcedure,
114+ connect.WithSchema(gitServiceMethods.ByName("ListPaths")),
115+ connect.WithClientOptions(opts...),
116+ ),
102117 getBlob: connect.NewClient[ermite.GetBlobRequest, ermite.GetBlobResponse](
103118 httpClient,
104119 baseURL+GitServiceGetBlobProcedure,
@@ -130,6 +145,7 @@ func NewGitServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...
130145 type gitServiceClient struct {
131146 listRefs *connect.Client[ermite.ListRefsRequest, ermite.ListRefsResponse]
132147 getTree *connect.Client[ermite.GetTreeRequest, ermite.GetTreeResponse]
148+ listPaths *connect.Client[ermite.ListPathsRequest, ermite.ListPathsResponse]
133149 getBlob *connect.Client[ermite.GetBlobRequest, ermite.GetBlobResponse]
134150 listCommits *connect.Client[ermite.ListCommitsRequest, ermite.ListCommitsResponse]
135151 getCommit *connect.Client[ermite.GetCommitRequest, ermite.GetCommitResponse]
@@ -146,6 +162,11 @@ func (c *gitServiceClient) GetTree(ctx context.Context, req *connect.Request[erm
146162 return c.getTree.CallUnary(ctx, req)
147163 }
148164
165+// ListPaths calls ermite.GitService.ListPaths.
166+func (c *gitServiceClient) ListPaths(ctx context.Context, req *connect.Request[ermite.ListPathsRequest]) (*connect.Response[ermite.ListPathsResponse], error) {
167+ return c.listPaths.CallUnary(ctx, req)
168+}
169+
149170 // GetBlob calls ermite.GitService.GetBlob.
150171 func (c *gitServiceClient) GetBlob(ctx context.Context, req *connect.Request[ermite.GetBlobRequest]) (*connect.ServerStreamForClient[ermite.GetBlobResponse], error) {
151172 return c.getBlob.CallServerStream(ctx, req)
@@ -175,6 +196,13 @@ type GitServiceHandler interface {
175196 // not_found if the path does not exist and invalid_argument if it is not a
176197 // directory.
177198 GetTree(context.Context, *connect.Request[ermite.GetTreeRequest]) (*connect.Response[ermite.GetTreeResponse], error)
199+ // ListPaths lists the paths of every file and symbolic link of a revision,
200+ // recursively, in Git tree order (the order of `git ls-tree -r`). Directories
201+ // and submodules are not listed, nor paths GetBlob cannot address (not valid
202+ // UTF-8, or with a control character). Fails with not_found for an unknown
203+ // revision or an empty repository. Servers that have it advertise the
204+ // feature "git.list_paths" in GetVersion.
205+ ListPaths(context.Context, *connect.Request[ermite.ListPathsRequest]) (*connect.Response[ermite.ListPathsResponse], error)
178206 // GetBlob streams the content of a file at a revision, in chunks of at most
179207 // 64 KiB. The first message carries the metadata in info, then the chunks
180208 // follow; the memory used by the server does not depend on the file size.
@@ -211,6 +239,12 @@ func NewGitServiceHandler(svc GitServiceHandler, opts ...connect.HandlerOption)
211239 connect.WithSchema(gitServiceMethods.ByName("GetTree")),
212240 connect.WithHandlerOptions(opts...),
213241 )
242+ gitServiceListPathsHandler := connect.NewUnaryHandler(
243+ GitServiceListPathsProcedure,
244+ svc.ListPaths,
245+ connect.WithSchema(gitServiceMethods.ByName("ListPaths")),
246+ connect.WithHandlerOptions(opts...),
247+ )
214248 gitServiceGetBlobHandler := connect.NewServerStreamHandler(
215249 GitServiceGetBlobProcedure,
216250 svc.GetBlob,
@@ -241,6 +275,8 @@ func NewGitServiceHandler(svc GitServiceHandler, opts ...connect.HandlerOption)
241275 gitServiceListRefsHandler.ServeHTTP(w, r)
242276 case GitServiceGetTreeProcedure:
243277 gitServiceGetTreeHandler.ServeHTTP(w, r)
278+ case GitServiceListPathsProcedure:
279+ gitServiceListPathsHandler.ServeHTTP(w, r)
244280 case GitServiceGetBlobProcedure:
245281 gitServiceGetBlobHandler.ServeHTTP(w, r)
246282 case GitServiceListCommitsProcedure:
@@ -266,6 +302,10 @@ func (UnimplementedGitServiceHandler) GetTree(context.Context, *connect.Request[
266302 return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.GitService.GetTree is not implemented"))
267303 }
268304
305+func (UnimplementedGitServiceHandler) ListPaths(context.Context, *connect.Request[ermite.ListPathsRequest]) (*connect.Response[ermite.ListPathsResponse], error) {
306+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.GitService.ListPaths is not implemented"))
307+}
308+
269309 func (UnimplementedGitServiceHandler) GetBlob(context.Context, *connect.Request[ermite.GetBlobRequest], *connect.ServerStream[ermite.GetBlobResponse]) error {
270310 return connect.NewError(connect.CodeUnimplemented, errors.New("ermite.GitService.GetBlob is not implemented"))
271311 }
@@ -39,6 +39,8 @@ const (
39 GitServiceListRefsProcedure = "/ermite.GitService/ListRefs"39 GitServiceListRefsProcedure = "/ermite.GitService/ListRefs"
40 // GitServiceGetTreeProcedure is the fully-qualified name of the GitService's GetTree RPC.40 // GitServiceGetTreeProcedure is the fully-qualified name of the GitService's GetTree RPC.
41 GitServiceGetTreeProcedure = "/ermite.GitService/GetTree"41 GitServiceGetTreeProcedure = "/ermite.GitService/GetTree"
42+ // GitServiceListPathsProcedure is the fully-qualified name of the GitService's ListPaths RPC.
43+ GitServiceListPathsProcedure = "/ermite.GitService/ListPaths"
42 // GitServiceGetBlobProcedure is the fully-qualified name of the GitService's GetBlob RPC.44 // GitServiceGetBlobProcedure is the fully-qualified name of the GitService's GetBlob RPC.
43 GitServiceGetBlobProcedure = "/ermite.GitService/GetBlob"45 GitServiceGetBlobProcedure = "/ermite.GitService/GetBlob"
44 // GitServiceListCommitsProcedure is the fully-qualified name of the GitService's ListCommits RPC.46 // GitServiceListCommitsProcedure is the fully-qualified name of the GitService's ListCommits RPC.
@@ -59,6 +61,13 @@ type GitServiceClient interface {
59 // not_found if the path does not exist and invalid_argument if it is not a61 // not_found if the path does not exist and invalid_argument if it is not a
60 // directory.62 // directory.
61 GetTree(context.Context, *connect.Request[ermite.GetTreeRequest]) (*connect.Response[ermite.GetTreeResponse], error)63 GetTree(context.Context, *connect.Request[ermite.GetTreeRequest]) (*connect.Response[ermite.GetTreeResponse], error)
64+ // ListPaths lists the paths of every file and symbolic link of a revision,
65+ // recursively, in Git tree order (the order of `git ls-tree -r`). Directories
66+ // and submodules are not listed, nor paths GetBlob cannot address (not valid
67+ // UTF-8, or with a control character). Fails with not_found for an unknown
68+ // revision or an empty repository. Servers that have it advertise the
69+ // feature "git.list_paths" in GetVersion.
70+ ListPaths(context.Context, *connect.Request[ermite.ListPathsRequest]) (*connect.Response[ermite.ListPathsResponse], error)
62 // GetBlob streams the content of a file at a revision, in chunks of at most71 // GetBlob streams the content of a file at a revision, in chunks of at most
63 // 64 KiB. The first message carries the metadata in info, then the chunks72 // 64 KiB. The first message carries the metadata in info, then the chunks
64 // follow; the memory used by the server does not depend on the file size.73 // follow; the memory used by the server does not depend on the file size.
@@ -99,6 +108,12 @@ func NewGitServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...
99 connect.WithSchema(gitServiceMethods.ByName("GetTree")),108 connect.WithSchema(gitServiceMethods.ByName("GetTree")),
100 connect.WithClientOptions(opts...),109 connect.WithClientOptions(opts...),
101 ),110 ),
111+ listPaths: connect.NewClient[ermite.ListPathsRequest, ermite.ListPathsResponse](
112+ httpClient,
113+ baseURL+GitServiceListPathsProcedure,
114+ connect.WithSchema(gitServiceMethods.ByName("ListPaths")),
115+ connect.WithClientOptions(opts...),
116+ ),
102 getBlob: connect.NewClient[ermite.GetBlobRequest, ermite.GetBlobResponse](117 getBlob: connect.NewClient[ermite.GetBlobRequest, ermite.GetBlobResponse](
103 httpClient,118 httpClient,
104 baseURL+GitServiceGetBlobProcedure,119 baseURL+GitServiceGetBlobProcedure,
@@ -130,6 +145,7 @@ func NewGitServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...
130 type gitServiceClient struct {145 type gitServiceClient struct {
131 listRefs *connect.Client[ermite.ListRefsRequest, ermite.ListRefsResponse]146 listRefs *connect.Client[ermite.ListRefsRequest, ermite.ListRefsResponse]
132 getTree *connect.Client[ermite.GetTreeRequest, ermite.GetTreeResponse]147 getTree *connect.Client[ermite.GetTreeRequest, ermite.GetTreeResponse]
148+ listPaths *connect.Client[ermite.ListPathsRequest, ermite.ListPathsResponse]
133 getBlob *connect.Client[ermite.GetBlobRequest, ermite.GetBlobResponse]149 getBlob *connect.Client[ermite.GetBlobRequest, ermite.GetBlobResponse]
134 listCommits *connect.Client[ermite.ListCommitsRequest, ermite.ListCommitsResponse]150 listCommits *connect.Client[ermite.ListCommitsRequest, ermite.ListCommitsResponse]
135 getCommit *connect.Client[ermite.GetCommitRequest, ermite.GetCommitResponse]151 getCommit *connect.Client[ermite.GetCommitRequest, ermite.GetCommitResponse]
@@ -146,6 +162,11 @@ func (c *gitServiceClient) GetTree(ctx context.Context, req *connect.Request[erm
146 return c.getTree.CallUnary(ctx, req)162 return c.getTree.CallUnary(ctx, req)
147 }163 }
148 164
165+// ListPaths calls ermite.GitService.ListPaths.
166+func (c *gitServiceClient) ListPaths(ctx context.Context, req *connect.Request[ermite.ListPathsRequest]) (*connect.Response[ermite.ListPathsResponse], error) {
167+ return c.listPaths.CallUnary(ctx, req)
168+}
169+
149 // GetBlob calls ermite.GitService.GetBlob.170 // GetBlob calls ermite.GitService.GetBlob.
150 func (c *gitServiceClient) GetBlob(ctx context.Context, req *connect.Request[ermite.GetBlobRequest]) (*connect.ServerStreamForClient[ermite.GetBlobResponse], error) {171 func (c *gitServiceClient) GetBlob(ctx context.Context, req *connect.Request[ermite.GetBlobRequest]) (*connect.ServerStreamForClient[ermite.GetBlobResponse], error) {
151 return c.getBlob.CallServerStream(ctx, req)172 return c.getBlob.CallServerStream(ctx, req)
@@ -175,6 +196,13 @@ type GitServiceHandler interface {
175 // not_found if the path does not exist and invalid_argument if it is not a196 // not_found if the path does not exist and invalid_argument if it is not a
176 // directory.197 // directory.
177 GetTree(context.Context, *connect.Request[ermite.GetTreeRequest]) (*connect.Response[ermite.GetTreeResponse], error)198 GetTree(context.Context, *connect.Request[ermite.GetTreeRequest]) (*connect.Response[ermite.GetTreeResponse], error)
199+ // ListPaths lists the paths of every file and symbolic link of a revision,
200+ // recursively, in Git tree order (the order of `git ls-tree -r`). Directories
201+ // and submodules are not listed, nor paths GetBlob cannot address (not valid
202+ // UTF-8, or with a control character). Fails with not_found for an unknown
203+ // revision or an empty repository. Servers that have it advertise the
204+ // feature "git.list_paths" in GetVersion.
205+ ListPaths(context.Context, *connect.Request[ermite.ListPathsRequest]) (*connect.Response[ermite.ListPathsResponse], error)
178 // GetBlob streams the content of a file at a revision, in chunks of at most206 // GetBlob streams the content of a file at a revision, in chunks of at most
179 // 64 KiB. The first message carries the metadata in info, then the chunks207 // 64 KiB. The first message carries the metadata in info, then the chunks
180 // follow; the memory used by the server does not depend on the file size.208 // follow; the memory used by the server does not depend on the file size.
@@ -211,6 +239,12 @@ func NewGitServiceHandler(svc GitServiceHandler, opts ...connect.HandlerOption)
211 connect.WithSchema(gitServiceMethods.ByName("GetTree")),239 connect.WithSchema(gitServiceMethods.ByName("GetTree")),
212 connect.WithHandlerOptions(opts...),240 connect.WithHandlerOptions(opts...),
213 )241 )
242+ gitServiceListPathsHandler := connect.NewUnaryHandler(
243+ GitServiceListPathsProcedure,
244+ svc.ListPaths,
245+ connect.WithSchema(gitServiceMethods.ByName("ListPaths")),
246+ connect.WithHandlerOptions(opts...),
247+ )
214 gitServiceGetBlobHandler := connect.NewServerStreamHandler(248 gitServiceGetBlobHandler := connect.NewServerStreamHandler(
215 GitServiceGetBlobProcedure,249 GitServiceGetBlobProcedure,
216 svc.GetBlob,250 svc.GetBlob,
@@ -241,6 +275,8 @@ func NewGitServiceHandler(svc GitServiceHandler, opts ...connect.HandlerOption)
241 gitServiceListRefsHandler.ServeHTTP(w, r)275 gitServiceListRefsHandler.ServeHTTP(w, r)
242 case GitServiceGetTreeProcedure:276 case GitServiceGetTreeProcedure:
243 gitServiceGetTreeHandler.ServeHTTP(w, r)277 gitServiceGetTreeHandler.ServeHTTP(w, r)
278+ case GitServiceListPathsProcedure:
279+ gitServiceListPathsHandler.ServeHTTP(w, r)
244 case GitServiceGetBlobProcedure:280 case GitServiceGetBlobProcedure:
245 gitServiceGetBlobHandler.ServeHTTP(w, r)281 gitServiceGetBlobHandler.ServeHTTP(w, r)
246 case GitServiceListCommitsProcedure:282 case GitServiceListCommitsProcedure:
@@ -266,6 +302,10 @@ func (UnimplementedGitServiceHandler) GetTree(context.Context, *connect.Request[
266 return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.GitService.GetTree is not implemented"))302 return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.GitService.GetTree is not implemented"))
267 }303 }
268 304
305+func (UnimplementedGitServiceHandler) ListPaths(context.Context, *connect.Request[ermite.ListPathsRequest]) (*connect.Response[ermite.ListPathsResponse], error) {
306+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.GitService.ListPaths is not implemented"))
307+}
308+
269 func (UnimplementedGitServiceHandler) GetBlob(context.Context, *connect.Request[ermite.GetBlobRequest], *connect.ServerStream[ermite.GetBlobResponse]) error {309 func (UnimplementedGitServiceHandler) GetBlob(context.Context, *connect.Request[ermite.GetBlobRequest], *connect.ServerStream[ermite.GetBlobResponse]) error {
270 return connect.NewError(connect.CodeUnimplemented, errors.New("ermite.GitService.GetBlob is not implemented"))310 return connect.NewError(connect.CodeUnimplemented, errors.New("ermite.GitService.GetBlob is not implemented"))
271 }311 }
M gen/ermite/git.pb.go +226 -83 generated file: collapsed
M gen_test.go +1 -0
@@ -22,6 +22,7 @@ func TestProcedures(t *testing.T) {
2222 {ermiteconnect.RepoServiceDeleteRepoProcedure, "/ermite.RepoService/DeleteRepo"},
2323 {ermiteconnect.GitServiceListRefsProcedure, "/ermite.GitService/ListRefs"},
2424 {ermiteconnect.GitServiceGetTreeProcedure, "/ermite.GitService/GetTree"},
25+ {ermiteconnect.GitServiceListPathsProcedure, "/ermite.GitService/ListPaths"},
2526 {ermiteconnect.GitServiceGetBlobProcedure, "/ermite.GitService/GetBlob"},
2627 {ermiteconnect.GitServiceListCommitsProcedure, "/ermite.GitService/ListCommits"},
2728 {ermiteconnect.GitServiceGetCommitProcedure, "/ermite.GitService/GetCommit"},
@@ -22,6 +22,7 @@ func TestProcedures(t *testing.T) {
22 {ermiteconnect.RepoServiceDeleteRepoProcedure, "/ermite.RepoService/DeleteRepo"},22 {ermiteconnect.RepoServiceDeleteRepoProcedure, "/ermite.RepoService/DeleteRepo"},
23 {ermiteconnect.GitServiceListRefsProcedure, "/ermite.GitService/ListRefs"},23 {ermiteconnect.GitServiceListRefsProcedure, "/ermite.GitService/ListRefs"},
24 {ermiteconnect.GitServiceGetTreeProcedure, "/ermite.GitService/GetTree"},24 {ermiteconnect.GitServiceGetTreeProcedure, "/ermite.GitService/GetTree"},
25+ {ermiteconnect.GitServiceListPathsProcedure, "/ermite.GitService/ListPaths"},
25 {ermiteconnect.GitServiceGetBlobProcedure, "/ermite.GitService/GetBlob"},26 {ermiteconnect.GitServiceGetBlobProcedure, "/ermite.GitService/GetBlob"},
26 {ermiteconnect.GitServiceListCommitsProcedure, "/ermite.GitService/ListCommits"},27 {ermiteconnect.GitServiceListCommitsProcedure, "/ermite.GitService/ListCommits"},
27 {ermiteconnect.GitServiceGetCommitProcedure, "/ermite.GitService/GetCommit"},28 {ermiteconnect.GitServiceGetCommitProcedure, "/ermite.GitService/GetCommit"},
M proto/ermite/git.proto +29 -0
@@ -81,6 +81,13 @@ service GitService {
8181 // not_found if the path does not exist and invalid_argument if it is not a
8282 // directory.
8383 rpc GetTree(GetTreeRequest) returns (GetTreeResponse);
84+ // ListPaths lists the paths of every file and symbolic link of a revision,
85+ // recursively, in Git tree order (the order of `git ls-tree -r`). Directories
86+ // and submodules are not listed, nor paths GetBlob cannot address (not valid
87+ // UTF-8, or with a control character). Fails with not_found for an unknown
88+ // revision or an empty repository. Servers that have it advertise the
89+ // feature "git.list_paths" in GetVersion.
90+ rpc ListPaths(ListPathsRequest) returns (ListPathsResponse);
8491 // GetBlob streams the content of a file at a revision, in chunks of at most
8592 // 64 KiB. The first message carries the metadata in info, then the chunks
8693 // follow; the memory used by the server does not depend on the file size.
@@ -199,6 +206,28 @@ message GetTreeResponse {
199206 bool truncated = 5;
200207 }
201208
209+// ListPathsRequest selects a revision.
210+message ListPathsRequest {
211+ // Repo is the repository name.
212+ string repo = 1;
213+ // Revision is a branch, a tag or a commit id. Empty means HEAD.
214+ string revision = 2;
215+}
216+
217+// ListPathsResponse lists the file paths of a revision.
218+message ListPathsResponse {
219+ // CommitId is the id of the commit the revision resolved to.
220+ string commit_id = 1;
221+ // TreeId is the object id of its root tree.
222+ string tree_id = 2;
223+ // Paths are the paths of the files and symbolic links, in Git tree order. A
224+ // response holds at most 100,000 paths and 8 MiB of path bytes (server limits
225+ // that may change; rely on truncated, not on the numbers).
226+ repeated string paths = 3;
227+ // Truncated is true when more paths exist past the first ones returned.
228+ bool truncated = 4;
229+}
230+
202231 // BlobInfo describes a file.
203232 message BlobInfo {
204233 // Id is the object id of the blob.
@@ -81,6 +81,13 @@ service GitService {
81 // not_found if the path does not exist and invalid_argument if it is not a81 // not_found if the path does not exist and invalid_argument if it is not a
82 // directory.82 // directory.
83 rpc GetTree(GetTreeRequest) returns (GetTreeResponse);83 rpc GetTree(GetTreeRequest) returns (GetTreeResponse);
84+ // ListPaths lists the paths of every file and symbolic link of a revision,
85+ // recursively, in Git tree order (the order of `git ls-tree -r`). Directories
86+ // and submodules are not listed, nor paths GetBlob cannot address (not valid
87+ // UTF-8, or with a control character). Fails with not_found for an unknown
88+ // revision or an empty repository. Servers that have it advertise the
89+ // feature "git.list_paths" in GetVersion.
90+ rpc ListPaths(ListPathsRequest) returns (ListPathsResponse);
84 // GetBlob streams the content of a file at a revision, in chunks of at most91 // GetBlob streams the content of a file at a revision, in chunks of at most
85 // 64 KiB. The first message carries the metadata in info, then the chunks92 // 64 KiB. The first message carries the metadata in info, then the chunks
86 // follow; the memory used by the server does not depend on the file size.93 // follow; the memory used by the server does not depend on the file size.
@@ -199,6 +206,28 @@ message GetTreeResponse {
199 bool truncated = 5;206 bool truncated = 5;
200 }207 }
201 208
209+// ListPathsRequest selects a revision.
210+message ListPathsRequest {
211+ // Repo is the repository name.
212+ string repo = 1;
213+ // Revision is a branch, a tag or a commit id. Empty means HEAD.
214+ string revision = 2;
215+}
216+
217+// ListPathsResponse lists the file paths of a revision.
218+message ListPathsResponse {
219+ // CommitId is the id of the commit the revision resolved to.
220+ string commit_id = 1;
221+ // TreeId is the object id of its root tree.
222+ string tree_id = 2;
223+ // Paths are the paths of the files and symbolic links, in Git tree order. A
224+ // response holds at most 100,000 paths and 8 MiB of path bytes (server limits
225+ // that may change; rely on truncated, not on the numbers).
226+ repeated string paths = 3;
227+ // Truncated is true when more paths exist past the first ones returned.
228+ bool truncated = 4;
229+}
230+
202 // BlobInfo describes a file.231 // BlobInfo describes a file.
203 message BlobInfo {232 message BlobInfo {
204 // Id is the object id of the blob.233 // Id is the object id of the blob.

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 2

Tags 18

↑↓ moveenter openesc close