1// SPDX-License-Identifier: MIT
2
3syntax = "proto3";
4
5package ermite;
6
7import "ermite/types.proto";
8
9// RefKind is the kind of a Git ref.
10enum RefKind {
11 // Not set. In ListRefsRequest.kinds it is rejected; never returned.
12 REF_KIND_UNSPECIFIED = 0;
13 // A branch (refs/heads/*).
14 REF_KIND_BRANCH = 1;
15 // A tag (refs/tags/*), lightweight or annotated.
16 REF_KIND_TAG = 2;
17 // The symbolic ref HEAD, that is the default branch.
18 REF_KIND_HEAD = 3;
19}
20
21// EntryKind is the kind of an entry of a Git tree.
22enum EntryKind {
23 // Not set. Never returned.
24 ENTRY_KIND_UNSPECIFIED = 0;
25 // A regular, non-executable file (mode 100644).
26 ENTRY_KIND_FILE = 1;
27 // An executable file (mode 100755).
28 ENTRY_KIND_EXECUTABLE = 2;
29 // A directory (mode 040000).
30 ENTRY_KIND_DIRECTORY = 3;
31 // A symbolic link (mode 120000). The blob content is the link target.
32 ENTRY_KIND_SYMLINK = 4;
33 // A submodule commit (mode 160000). The id is a commit of another
34 // repository, which this one does not contain.
35 ENTRY_KIND_SUBMODULE = 5;
36}
37
38// FileStatus tells what a commit did to a file.
39enum FileStatus {
40 // Not set. Never returned.
41 FILE_STATUS_UNSPECIFIED = 0;
42 // The file was added.
43 FILE_STATUS_ADDED = 1;
44 // The file was deleted.
45 FILE_STATUS_DELETED = 2;
46 // The content or the mode of the file changed.
47 FILE_STATUS_MODIFIED = 3;
48 // The file was moved, possibly with changes. Rename detection is done by Git.
49 FILE_STATUS_RENAMED = 4;
50 // The file was copied from another file, possibly with changes.
51 FILE_STATUS_COPIED = 5;
52 // The type of the entry changed, for example a file became a symbolic link.
53 FILE_STATUS_TYPE_CHANGED = 6;
54}
55
56// FileOmission tells why a changed file has no hunks although it changed.
57enum FileOmission {
58 // The file is not omitted: its hunks are present, or it has none to show
59 // (binary, pure rename, mode change), or it is outside the requested window.
60 FILE_OMISSION_UNSPECIFIED = 0;
61 // The file is generated or vendored, by its path: its hunks were not read.
62 // It can be read on its own.
63 FILE_OMISSION_GENERATED = 1;
64 // The file changes more lines than the per-file limit. It can be read on its own.
65 FILE_OMISSION_LARGE = 2;
66 // The file did not fit in what remained of the total limits of the diff.
67 // It can be read on its own.
68 FILE_OMISSION_BUDGET = 3;
69 // The file's text exceeds the memory limit of a diff: it cannot be shown,
70 // even on its own. Clone the repository to read it.
71 FILE_OMISSION_UNAVAILABLE = 4;
72}
73
74// LineKind tells how a line of a hunk relates to the two sides of a diff.
75enum LineKind {
76 // Not set. Never returned.
77 LINE_KIND_UNSPECIFIED = 0;
78 // The line is present, unchanged, on both sides.
79 LINE_KIND_CONTEXT = 1;
80 // The line exists only on the new side.
81 LINE_KIND_ADDITION = 2;
82 // The line exists only on the old side.
83 LINE_KIND_DELETION = 3;
84}
85
86// GitService reads the Git data of repositories. It is read-only: pushes go
87// through Git itself.
88//
89// Every RPC takes the repository name. A repository that does not exist or
90// that the caller cannot read gives not_found. A revision is a branch name, a
91// tag name or a commit id (full, or an unambiguous abbreviation of at least 7
92// characters); empty means HEAD. A revision or path that does not exist gives
93// not_found; so does an empty repository, which has no commit to resolve.
94service GitService {
95 // ListRefs lists the branches, tags and HEAD of a repository. Refs are
96 // ordered by kind (HEAD, branches, tags) then by name.
97 rpc ListRefs(ListRefsRequest) returns (ListRefsResponse);
98 // GetTree lists the entries of a directory at a revision. Fails with
99 // not_found if the path does not exist and invalid_argument if it is not a
100 // directory.
101 rpc GetTree(GetTreeRequest) returns (GetTreeResponse);
102 // ListPaths lists the paths of every file and symbolic link of a revision,
103 // recursively, in Git tree order (the order of `git ls-tree -r`). Directories
104 // and submodules are not listed, nor paths GetBlob cannot address (not valid
105 // UTF-8, or with a control character). Fails with not_found for an unknown
106 // revision or an empty repository. Servers that have it advertise the
107 // feature "git.list_paths" in GetVersion.
108 rpc ListPaths(ListPathsRequest) returns (ListPathsResponse);
109 // GetBlob streams the content of a file at a revision, in chunks of at most
110 // 64 KiB. The first message carries the metadata in info, then the chunks
111 // follow; the memory used by the server does not depend on the file size.
112 // Fails with not_found if the path does not exist and invalid_argument if it
113 // is not a file.
114 rpc GetBlob(GetBlobRequest) returns (stream GetBlobResponse);
115 // ListCommits lists the history reachable from a revision, newest first
116 // (Git's default order), optionally limited to the commits that touched a
117 // path. A page_token that points deeper than 100,000 commits into the
118 // history is invalid_argument.
119 rpc ListCommits(ListCommitsRequest) returns (ListCommitsResponse);
120 // GetCommit returns one commit with its statistics and its diff.
121 rpc GetCommit(GetCommitRequest) returns (GetCommitResponse);
122 // ResolveRevision turns a revision into the id of the commit it designates.
123 rpc ResolveRevision(ResolveRevisionRequest) returns (ResolveRevisionResponse);
124}
125
126// Ref is a branch, a tag or HEAD.
127message Ref {
128 // Name is the full ref name, for example "refs/heads/main" or
129 // "refs/tags/v1.0.0". For HEAD it is "HEAD".
130 string name = 1;
131 // ShortName is the name without its "refs/heads/" or "refs/tags/" prefix. For
132 // HEAD it is the short name of the default branch, or empty if HEAD points
133 // to a branch that does not exist yet.
134 string short_name = 2;
135 // Kind is the kind of the ref.
136 RefKind kind = 3;
137 // Id is the id of the object the ref points to. For an annotated tag it is
138 // the id of the tag object.
139 string id = 4;
140 // CommitId is the id of the commit the ref designates once annotated tags are
141 // followed. It equals id for branches and lightweight tags. It is empty for
142 // a tag that points to something other than a commit.
143 string commit_id = 5;
144}
145
146// ListRefsRequest selects the refs to list.
147message ListRefsRequest {
148 // Repo is the repository name.
149 string repo = 1;
150 // Kinds restricts the result to these kinds. Empty means all kinds.
151 repeated RefKind kinds = 2;
152 // PageSize is the maximum number of refs to return. 0 means the server
153 // default; values above the server maximum are lowered to it.
154 int32 page_size = 3;
155 // PageToken is the next_page_token of a previous response. Empty for the
156 // first page.
157 string page_token = 4;
158}
159
160// ListRefsResponse is one page of refs.
161message ListRefsResponse {
162 // Refs are the refs of this page.
163 repeated Ref refs = 1;
164 // NextPageToken is passed as page_token to get the next page. Empty when
165 // there is no next page.
166 string next_page_token = 2;
167}
168
169// TreeEntry is one entry of a directory.
170message TreeEntry {
171 // Name is the name of the entry in its directory, without slash.
172 string name = 1;
173 // Path is the path of the entry from the repository root, with "/"
174 // separators and no leading slash.
175 string path = 2;
176 // Kind is the kind of the entry.
177 EntryKind kind = 3;
178 // Mode is the Git file mode, for example 0o100644 (33188 in decimal).
179 uint32 mode = 4;
180 // Id is the object id of the blob, tree or submodule commit.
181 string id = 5;
182 // Size is the size in bytes of a file or symbolic link. It is 0 for
183 // directories and submodules.
184 int64 size = 6;
185 // LastCommit is the latest commit that changed the entry. Set only when the
186 // request asked for it with include_last_commit.
187 Commit last_commit = 7;
188}
189
190// GetTreeRequest selects a directory.
191message GetTreeRequest {
192 // Repo is the repository name.
193 string repo = 1;
194 // Revision is a branch, a tag or a commit id. Empty means HEAD.
195 string revision = 2;
196 // Path is the directory, from the repository root, with "/" separators.
197 // Empty means the root.
198 string path = 3;
199 // IncludeLastCommit asks for the last commit of every entry in last_commit.
200 // It costs one history walk, so it is off by default and the server may be
201 // slow on large directories.
202 bool include_last_commit = 4;
203}
204
205// GetTreeResponse lists a directory.
206message GetTreeResponse {
207 // CommitId is the id of the commit the revision resolved to.
208 string commit_id = 1;
209 // TreeId is the object id of the listed directory.
210 string tree_id = 2;
211 // Entries are the entries of the directory: directories first, then the other
212 // entries, each group ordered by name. A response holds at most 5,000
213 // entries (a server limit that may change; rely on truncated, not on the
214 // number): past that, the first 5,000 in this order are returned and
215 // truncated is set. Paging may be added later, additively.
216 repeated TreeEntry entries = 3;
217 // LastCommitsTruncated is true when include_last_commit was set and the
218 // server gave a last_commit to the first entries only (at most 200 of them,
219 // in the order of entries), because each one costs a history lookup. The
220 // other entries have no last_commit although they have one in Git.
221 bool last_commits_truncated = 4;
222 // Truncated is true when the directory has more entries than the response
223 // holds; entries are then the first ones in the documented order.
224 bool truncated = 5;
225}
226
227// ListPathsRequest selects a revision.
228message ListPathsRequest {
229 // Repo is the repository name.
230 string repo = 1;
231 // Revision is a branch, a tag or a commit id. Empty means HEAD.
232 string revision = 2;
233}
234
235// ListPathsResponse lists the file paths of a revision.
236message ListPathsResponse {
237 // CommitId is the id of the commit the revision resolved to.
238 string commit_id = 1;
239 // TreeId is the object id of its root tree.
240 string tree_id = 2;
241 // Paths are the paths of the files and symbolic links, in Git tree order. A
242 // response holds at most 100,000 paths and 8 MiB of path bytes (server limits
243 // that may change; rely on truncated, not on the numbers).
244 repeated string paths = 3;
245 // Truncated is true when more paths exist past the first ones returned.
246 bool truncated = 4;
247}
248
249// BlobInfo describes a file.
250message BlobInfo {
251 // Id is the object id of the blob.
252 string id = 1;
253 // Path is the path of the file from the repository root.
254 string path = 2;
255 // Size is the size of the whole content in bytes.
256 int64 size = 3;
257 // Binary is true if the content is binary, with Git's rule: a NUL byte in the
258 // first 8,000 bytes.
259 bool binary = 4;
260 // MediaType is the detected media type, for example "text/plain" or
261 // "image/png", from the file name and the first bytes. It is
262 // "application/octet-stream" when unknown.
263 string media_type = 5;
264 // Mode is the Git file mode, for example 0o100644 (33188 in decimal).
265 uint32 mode = 6;
266}
267
268// GetBlobRequest selects a file.
269message GetBlobRequest {
270 // Repo is the repository name.
271 string repo = 1;
272 // Revision is a branch, a tag or a commit id. Empty means HEAD.
273 string revision = 2;
274 // Path is the file, from the repository root. It is required.
275 string path = 3;
276}
277
278// GetBlobResponse is one message of the content stream.
279message GetBlobResponse {
280 // Info describes the file. Set in the first message of the stream only.
281 BlobInfo info = 1;
282 // Chunk is the next part of the content, at most 64 KiB, in order. The first
283 // message may have an empty chunk. The chunks of a stream add up to
284 // info.size bytes.
285 bytes chunk = 2;
286}
287
288// ListCommitsRequest selects a page of history.
289message ListCommitsRequest {
290 // Repo is the repository name.
291 string repo = 1;
292 // Revision is where the history starts: a branch, a tag or a commit id.
293 // Empty means HEAD.
294 string revision = 2;
295 // Path restricts the result to the commits that changed this file or
296 // directory. Empty means the whole history.
297 string path = 3;
298 // PageSize is the maximum number of commits to return. 0 means the server
299 // default; values above 100 are lowered to 100.
300 int32 page_size = 4;
301 // PageToken is the next_page_token of a previous response, to continue the
302 // same listing. Empty for the first page. Other fields must be unchanged.
303 string page_token = 5;
304}
305
306// ListCommitsResponse is one page of commits.
307message ListCommitsResponse {
308 // Commits are the commits of this page, newest first.
309 repeated Commit commits = 1;
310 // NextPageToken is passed as page_token to get the next page. Empty when
311 // there is no next page.
312 string next_page_token = 2;
313}
314
315// GetCommitRequest selects a commit.
316message GetCommitRequest {
317 // Repo is the repository name.
318 string repo = 1;
319 // Revision is a branch, a tag or a commit id. Empty means HEAD.
320 string revision = 2;
321}
322
323// GetCommitResponse holds a commit, its statistics and its diff.
324message GetCommitResponse {
325 // Commit is the commit metadata.
326 Commit commit = 1;
327 // Stats are the totals over all the changed files, including files that the
328 // diff omits or truncates.
329 DiffStats stats = 2;
330 // Diff is the structured diff against the first parent, or against the empty
331 // tree for a root commit. For a merge commit this is the change relative to
332 // the first parent.
333 Diff diff = 3;
334}
335
336// DiffStats are the totals of a diff.
337message DiffStats {
338 // FilesChanged is the number of changed files.
339 int32 files_changed = 1;
340 // Additions is the number of added lines. Binary files count for 0.
341 int32 additions = 2;
342 // Deletions is the number of deleted lines. Binary files count for 0.
343 int32 deletions = 3;
344}
345
346// Diff is the structured difference introduced by a commit.
347//
348// The server limits its size: at most 300 files, at most 2,000 lines in one
349// file and at most 20,000 lines in total (hunk lines, context included). These
350// numbers are server behaviour and may change; clients must rely on the
351// truncated flags, not on the numbers.
352message Diff {
353 // Files are the changed files, ordered by path. Files past the file limit are
354 // omitted.
355 repeated FileDiff files = 1;
356 // Truncated is true when the diff is incomplete: files were omitted past the
357 // file limit, or at least one listed file has omitted set. DiffStats still count everything.
358 bool truncated = 2;
359}
360
361// FileDiff is the change of one file.
362message FileDiff {
363 // OldPath is the path before the commit. Empty for an added file.
364 string old_path = 1;
365 // NewPath is the path after the commit. Empty for a deleted file.
366 string new_path = 2;
367 // Status tells what happened to the file.
368 FileStatus status = 3;
369 // OldMode is the Git file mode before the commit, or 0 for an added file.
370 uint32 old_mode = 4;
371 // NewMode is the Git file mode after the commit, or 0 for a deleted file.
372 uint32 new_mode = 5;
373 // OldId is the blob id before the commit. Empty for an added file.
374 string old_id = 6;
375 // NewId is the blob id after the commit. Empty for a deleted file.
376 string new_id = 7;
377 // Binary is true if Git treats the file as binary (numstat reports "-"). A
378 // binary file has no hunks and additions and deletions are 0.
379 bool binary = 8;
380 // Additions is the number of added lines.
381 int32 additions = 9;
382 // Deletions is the number of deleted lines.
383 int32 deletions = 10;
384 // Similarity is the percentage (0-100) of unchanged content for a renamed or
385 // copied file, and 0 otherwise.
386 int32 similarity = 11;
387 // Truncated is true when the file exceeded a diff limit: hunks is then empty
388 // although the file changed. Additions and deletions remain accurate. It is
389 // kept for compatibility and equals omitted != FILE_OMISSION_UNSPECIFIED.
390 bool truncated = 12;
391 // Hunks are the changed regions, in file order. Empty for a binary file, a
392 // truncated file, a pure rename and a mode-only change.
393 repeated Hunk hunks = 13;
394 // Omitted tells why hunks is empty although the file changed. truncated is
395 // true exactly when omitted is not FILE_OMISSION_UNSPECIFIED.
396 FileOmission omitted = 14;
397}
398
399// Hunk is a contiguous changed region with its context.
400message Hunk {
401 // OldStart is the 1-based first line of the region in the old file. 0 when
402 // old_lines is 0 and the file was added.
403 int32 old_start = 1;
404 // OldLines is the number of old-side lines in the hunk.
405 int32 old_lines = 2;
406 // NewStart is the 1-based first line of the region in the new file. 0 when
407 // new_lines is 0 and the file was deleted.
408 int32 new_start = 3;
409 // NewLines is the number of new-side lines in the hunk.
410 int32 new_lines = 4;
411 // Header is the text after the second "@@" of the hunk header, usually the
412 // enclosing function. It may be empty.
413 string header = 5;
414 // Lines are the lines of the hunk, in order.
415 repeated Line lines = 6;
416}
417
418// Line is one line of a hunk.
419message Line {
420 // Kind tells which side the line belongs to.
421 LineKind kind = 1;
422 // OldNumber is the 1-based line number on the old side. 0 for an addition.
423 int32 old_number = 2;
424 // NewNumber is the 1-based line number on the new side. 0 for a deletion.
425 int32 new_number = 3;
426 // Text is the content without the line terminator. Bytes that are not valid
427 // UTF-8 are replaced by the replacement character.
428 string text = 4;
429 // NoNewlineAtEof is true when this is the last line of its side and the file
430 // does not end with a line break there.
431 bool no_newline_at_eof = 5;
432}
433
434// ResolveRevisionRequest names a revision.
435message ResolveRevisionRequest {
436 // Repo is the repository name.
437 string repo = 1;
438 // Revision is a branch, a tag or a commit id. Empty means HEAD.
439 string revision = 2;
440}
441
442// ResolveRevisionResponse holds the resolved commit.
443message ResolveRevisionResponse {
444 // CommitId is the full id of the commit the revision designates, after
445 // following annotated tags.
446 string commit_id = 1;
447 // Ref is the full name of the ref the revision matched, for example
448 // "refs/heads/main". Empty when the revision was a commit id or for HEAD
449 // when it is resolved by default.
450 string ref = 2;
451}