@@ -9,29 +9,27 @@ can use it to create a GitHub-like UI for a git repo:
9
9
10
10
1 . Code browsing: given a specific commit/ref, browse files and directories.
11
11
2 . Diff between two revisions that resolve into two commits.
12
- 3 . Retrieve the history of the commits.
13
- 4 . Retrieve a specific object: all its metadata.
14
- 5 . Retrieve the refs: Branches, Tags, Remotes, Notes and user-defined
15
- "categories", where a category is: refs/<category >/<...>.
12
+ 3 . Retrieve the history of commits with a given head, and optionally a file.
13
+ 4 . List and retrieve metadata of refs and objects: Branches, Tags, Remotes,
14
+ Notes and user-defined "categories", where a category is: refs/<category >/<...>.
16
15
17
16
## Motivation
18
17
19
18
The ` radicle-surf ` crate aims to provide a safe and easy-to-use API that
20
19
supports the features listed in [ Introduction] . Based on the existing API,
21
20
the main goals of the refactoring are:
22
21
23
- - API review: make API simpler whenever possible .
24
- - Address open issues in the original ` radicle-surf ` repo as much as possible .
25
- - Not to shy away from being ` git ` specific. (i.e. not to consider supporting
26
- other VCS systems)
27
- - Hide away ` git2 ` from the API. The use of ` git2 ` should be an implementation
28
- detail.
22
+ - API review: identify the issues with the current API .
23
+ - New API: propose a new API that could reuse parts of the existing API .
24
+ - Address open issues in the original ` radicle-surf ` repo.
25
+ - Be ` git ` specific. (i.e. no need to support other VCS systems)
26
+ - Remove ` git2 ` from the public API. The use of ` git2 ` should be an
27
+ implementation detail.
29
28
30
29
## API review
31
30
32
- The current API has quite a bit accidental complexity that is not inherent with
33
- the requirements, especially when we can be git specific and don't care about
34
- other VCS systems.
31
+ In this section, we review some core types in the current API and propose
32
+ changes to them. The main theme is to make the API simpler and easier to use.
35
33
36
34
### Remove the ` Browser `
37
35
@@ -80,39 +78,92 @@ We no longer need another layer of indirection defined by `Vcs` trait.
80
78
With the changes proposed in the previous section, we describe what the new API
81
79
would look like and how they meet the requirements.
82
80
83
- ### Common principles
81
+ ### Basic types
84
82
85
- #### How to identify things that resolve into a commit
83
+ #### Revision and Commit
86
84
87
- In our API, it will be good to have a single way to identify all refs and
88
- objects that resolve into commits. In other words, we try to avoid using
89
- different ways at different places. Currently there are multiple types in the
90
- API for this purpose:
85
+ In Git, ` Revision ` commonly resolves into a ` Commit ` but could refers to other
86
+ objects for example a ` Blob ` . Hence we need to keep both concepts in the API.
87
+ Currently we have multiple types to identify a ` Commit ` or ` Revision ` .
91
88
92
89
- Commit
93
90
- Oid
94
91
- Rev
95
92
96
- Because ` Rev ` is the most high level among those and supports refs already ,
97
- I think we should use ` Rev ` in our API as much as possible .
93
+ The relations between them are: all ` Rev ` and ` Commit ` can resolve into ` Oid ` ,
94
+ and most ` Rev ` s can resolve into ` Commit ` .
98
95
99
- #### How to identify History
96
+ On one hand, ` Oid ` is the ultimate unique identifer but it is more machine-
97
+ friendly than human-friendly. On the other hand, ` Revision ` is most human-
98
+ friendly and better suited in the API interface. A conversion from ` Revision `
99
+ to ` Oid ` will be useful.
100
100
101
- TBD
101
+ For the places where ` Commit ` is required, we should explicitly ask for
102
+ ` Commit ` instead of ` Revision ` .
103
+
104
+ In conclusion, we define two new traits to support the use of ` Revision ` and
105
+ ` Commit ` :
106
+
107
+ ``` Rust
108
+ pub trait FromRevision {
109
+ /// Resolves a revision into an object id in `repo`.
110
+ fn object_id (& self , repo : & RepositoryRef ) -> Result <Oid , Error >;
111
+ }
112
+
113
+ pub trait ToCommit {
114
+ /// Converts to a commit in `repo`.
115
+ fn to_commit (self , repo : & RepositoryRef ) -> Result <Commit , Error >;
116
+ }
117
+ ```
118
+
119
+ These two traits will be implemented for most common representations of
120
+ ` Revision ` and ` Commit ` , for example ` &str ` , refs like ` Branch ` , ` Tag ` , etc.
121
+ Our API will use these traits where we expect a ` Revision ` or a ` Commit ` .
122
+
123
+ #### History
124
+
125
+ The current ` History ` is generic over VCS types and also retrieves the full list
126
+ of commits when the history is created. The VCS part can be removed and the
127
+ history can lazy-load the list of commits by implmenting ` Iterator ` to support
128
+ potentially very long histories.
129
+
130
+ We can also store the head commit with the history so that it's easy to get
131
+ the start point and it helps to identify the history.
132
+
133
+ To support getting the history of a file, we provide methods to modify a
134
+ ` History ` to filter by a file path.
135
+
136
+ The new ` History ` type would look like this:
137
+
138
+ ``` Rust
139
+ pub struct History <'a > {
140
+ repo : RepositoryRef <'a >,
141
+ head : Commit ,
142
+ revwalk : git2 :: Revwalk <'a >,
143
+ filter_by : Option <FilterBy >,
144
+ }
145
+
146
+ enum FilterBy {
147
+ File { path : file_system :: Path },
148
+ }
149
+ ```
150
+
151
+ For the methods provided by ` History ` , please see section [ Retrieve the history]
152
+ (#retrieve-the-history) below.
102
153
103
154
### Code browsing
104
155
105
156
The user should be able to browse the files and directories for any given
106
- commits or references . The core API is:
157
+ commit . The core API is:
107
158
108
159
- Create a root Directory:
109
160
``` Rust
110
- imp RepositoryRef {
111
- pub fn snapshot (& self , rev : & Rev ) -> Result <Directory , Error >;
161
+ impl RepositoryRef {
162
+ pub fn snapshot < C : ToCommit > (& self , commit : C ) -> Result <Directory , Error >;
112
163
}
113
164
```
114
165
115
- - Browse a Directory:
166
+ - Browse a Directory's contents :
116
167
``` Rust
117
168
impl Directory {
118
169
pub fn contents (& self ) -> impl Iterator <Item = & DirectoryContents >;
@@ -135,26 +186,84 @@ pub enum DirectoryContents {
135
186
136
187
### Diffs
137
188
138
- The user would be able to create a diff between any two revisions that resolve
139
- into two commits.
189
+ The user would be able to create a diff between any two revisions. In the first
190
+ implementation, these revisions have to resolve into commits. But in future,
191
+ the revisions could refer to other objects, e.g. files (blobs).
140
192
141
- The main change is to use ` Rev ` instead of ` Oid ` to identify ` from ` and ` to ` .
142
193
The core API is:
143
194
144
195
``` Rust
145
- imp RepositoryRef {
146
- pub fn diff (& self , from : & Rev , to : & Rev ) -> Result <Diff , Error >;
196
+ impl RepositoryRef {
197
+ /// Returns the diff between two revisions.
198
+ pub fn diff <R : FromRevision >(& self , from : R , to : R ) -> Result <Diff , Error >;
199
+
200
+ /// Returns the diff between a revision and the initial state of the repo.
201
+ pub fn initial_diff <R : FromRevision >(& self , rev : R ) -> Result <Diff , Error >;
202
+
203
+ /// Returns the diff of a specific commit.
204
+ pub fn diff_from_parent <C : ToCommit >(& self , commit : C ) -> Result <Diff , Error >;
147
205
}
148
206
```
207
+ TODO: can we use ` diff() ` to also support what ` initial_diff() ` does?
208
+
209
+ ### Retrieve the history
210
+
211
+ The user would be able to get the list of previous commits reachable from a
212
+ particular commit.
149
213
150
- To help convert from ` Oid ` to ` Rev ` , we provide a helper method :
214
+ To create a ` History ` from a repo with a given head :
151
215
``` Rust
152
- imp RepositoryRef {
153
- /// Returns the Oid of `rev`.
154
- pub fn rev_oid (& self , rev : & Rev ) -> Result <Oid , Error >;
216
+ impl RepositoryRef {
217
+ pub fn history <C : ToCommit >(& self , head : C ) -> Result <History , Error >;
155
218
}
156
219
```
157
220
221
+ ` History ` implements ` Iterator ` that produces ` Result<Commit, Error> ` , and
222
+ also provides these methods:
223
+
224
+ ``` Rust
225
+ impl <'a > History <'a > {
226
+ pub fn new <C : ToCommit >(repo : RepositoryRef <'a >, head : C ) -> Result <Self , Error >;
227
+
228
+ pub fn head (& self ) -> & Commit ;
229
+
230
+ // Modifies a history with a filter by `path`.
231
+ // This is to support getting the history of a file.
232
+ pub fn by_path (mut self , path : file_system :: Path ) -> Self ;
233
+ ```
234
+
235
+ - Alternative design:
236
+
237
+ One potential downside of define ` History ` as an iterator is that:
238
+ ` history.next() ` takes a mutable history object. A different design is to use
239
+ ` History ` as immutable object that produces an iterator on-demand:
240
+
241
+ ``` Rust
242
+ pub struct History <'a > {
243
+ repo : RepositoryRef <'a >,
244
+ head : Commit ,
245
+ }
246
+
247
+ impl <'a > History <'a > {
248
+ /// This method creats a new `RevWalk` internally and return an
249
+ /// iterator for all commits in a history.
250
+ pub fn iter (& self ) -> impl Iterator <Item = Commit >;
251
+ }
252
+ ```
253
+
254
+ In this design, ` History ` does not keep ` RevWalk ` in its state. It will create
255
+ a new one when ` iter() ` is called. I like the immutable interface of this design
256
+ but did not implement it in the current code mainly because the libgit2 doc says
257
+ [ creating a new ` RevWalk ` is relatively expensive] ( https://libgit2.org/libgit2/#HEAD/group/revwalk/git_revwalk_new ) .
258
+
259
+ ### List and retrieve metadata of refs
260
+
261
+ TODO: this section is work-in-progress.
262
+
263
+ Git has four different types of objects: Blob, Tree, Commit and Tag. The ` blob `
264
+ and ` tree ` are low-level to git and so I don't think we should expose them in
265
+ our API.
266
+
158
267
## Error handling
159
268
160
269
TBD
0 commit comments