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