| |
# An updated design for radicle-surf
|
| |
|
| + |
This is a design blueprint for the new `radicle-git/radicle-surf` crate. The
|
| + |
actual design details and implemenation are described and updated in its
|
| + |
documentation comments, viewable via `cargo doc`.
|
| + |
|
| |
## Introduction
|
| |
|
| - |
Now we have ported the `radicle-surf` crate from its own github repo to be
|
| - |
part of the `radicle-git` repo. We are taking this opportunity to refactor
|
| - |
its design as well. Intuitively, `radicle-surf` provides an API so that one
|
| - |
can use it to create a GitHub-like UI for a git repo:
|
| + |
In September 2022, we have ported the [`radicle-surf` crate](https://github.com/radicle-dev/radicle-surf)
|
| + |
from its own github repo to be part of the [`radicle-git` repo](https://github.com/radicle-dev/radicle-git).
|
| + |
We are taking this opportunity to refactor its design as well. Intuitively,
|
| + |
`radicle-surf` provides an API so that one can use it to create a GitHub-like
|
| + |
UI for a git repo:
|
| |
|
| |
1. Code browsing: given a specific commit/ref, browse files and directories.
|
| |
2. Diff between two revisions that resolve into two commits.
|
| |
3. Retrieve the history of commits with a given head, and optionally a file.
|
| |
4. List refs and retrieve their metadata: Branches, Tags, Remotes,
|
| - |
Notes and user-defined "categories", where a category is: refs/<category>/<...>.
|
| + |
Notes and user-defined "categories", where a category is: `refs/<category>/<...>`.
|
| |
|
| |
## Motivation
|
| |
|
| |
The `radicle-surf` crate aims to provide a safe and easy-to-use API that
|
| - |
supports the features listed in [Introduction]. Based on the existing API,
|
| - |
the main goals of the refactoring are:
|
| + |
supports the features listed in [Introduction](#introduction). Based on the
|
| + |
existing API, the main goals of the refactoring are:
|
| |
|
| |
- API review: identify the issues with the current API.
|
| - |
- New API: propose a new API that could reuse parts of the existing API.
|
| + |
- Updated API: propose an updated API that reuses parts of the existing API.
|
| |
- Address open issues in the original `radicle-surf` repo.
|
| |
- Be `git` specific. (i.e. no need to support other VCS systems)
|
| |
- Remove `git2` from the public API. The use of `git2` should be an
|
| |
In this section, we review some core types in the current API and propose
|
| |
changes to them. The main theme is to make the API simpler and easier to use.
|
| |
|
| - |
### Remove the `Browser`
|
| + |
### Remove the `Browser` type
|
| |
|
| - |
The type `Browser` is awkward as of today:
|
| + |
The type [`Browser`](https://github.com/radicle-dev/radicle-surf/blob/b85d2183d786e5fa447aab9d2f420a32f1061bfa/surf/src/vcs.rs#L145) is awkward:
|
| |
|
| |
- it is not a source of truth of any information. For example, `list_branches`
|
| |
method is just a wrapper of `Repository::list_branches`.
|
| - |
- it takes in `History`, but really works at the `Snapshot` level.
|
| - |
- it is mutable but its state does not help much.
|
| + |
- it takes in `History`, but really operates at the `Snapshot` level.
|
| + |
- it is mutable but its state mutations are not used much.
|
| |
|
| |
Can we just remove `Browser` and implement its functionalities using other
|
| |
types?
|
| |
- For generating `Directory`, use `Repository` directly given a `Rev`.
|
| |
- For accessing `Branch`, `Tag` or `Commit`, use `Repository`.
|
| |
|
| - |
## Remove the `Snapshot` type
|
| + |
### Remove the `Snapshot` type
|
| |
|
| - |
A `Snapshot` should be really just a tree (or `Directory`) of a `Commit` in
|
| - |
git. Currently it is a function that returns a `Directory`. Because it is OK
|
| - |
to be git specific, we don't need to have this generic function to create a
|
| - |
snapshot across different VCS systems.
|
| + |
A [`Snapshot`](https://github.com/radicle-dev/radicle-surf/blob/b85d2183d786e5fa447aab9d2f420a32f1061bfa/surf/src/vcs.rs#L140) should be really
|
| + |
just a tree (or `Directory`) of a `Commit` in git. Currently it is a function
|
| + |
that returns a `Directory`. As we are moving to be git specific, we don't need
|
| + |
to have this generic function to create a snapshot across different VCS systems.
|
| |
|
| |
The snapshot function can be easily implement as a method of `RepositoryRef`.
|
| |
|
| - |
## Simplify `Directory` and remove the `Tree` and `Forest` types
|
| + |
### Simplify `Directory` and remove the `Tree` and `Forest` types
|
| |
|
| - |
The `Directory` type represents the file system view of a snapshot. Its field
|
| + |
The [`Directory`](https://github.com/radicle-dev/radicle-surf/blob/b85d2183d786e5fa447aab9d2f420a32f1061bfa/surf/src/file_system/directory.rs#L144)
|
| + |
type represents the file system view of a snapshot. Its field
|
| |
`sub_directories` is defined a `Forest` based on `Tree`. The types are
|
| |
over-engineered from such a simple concept. We could refactor `Directory` to
|
| |
use `DirectoryContents` for its items and not to use `Tree` or `Forest` at all.
|
| |
}
|
| |
```
|
| |
|
| - |
We used to have the following method:
|
| - |
```Rust
|
| - |
/// Returns the diff between a revision and the initial state of the repo.
|
| - |
pub fn initial_diff<R: Revision>(&self, rev: R) -> Result<Diff, Error>;
|
| - |
```
|
| - |
|
| - |
However, it is not comparing with any other commit so the output is basically
|
| - |
the snapshot of `R`. I am not sure if it is necessary. My take is that we can
|
| - |
remove this method.
|
| - |
|
| - |
We also have the following method:
|
| + |
To obtain the diff of a particular commit, we will have:
|
| |
```Rust
|
| - |
/// Returns the diff of a specific commit.
|
| - |
pub fn diff_from_parent<C: ToCommit>(&self, commit: C) -> Result<Diff, Error>;
|
| + |
impl RepositoryRef {
|
| + |
pub fn diff_commit(&self, commit: impl ToCommit) -> Result<Diff, Error>;
|
| + |
}
|
| |
```
|
| |
|
| - |
I think it is probably better to instead define the above method as
|
| - |
`Commit::diff()` as its output is associated with a `Commit`.
|
| - |
|
| |
### Retrieve the history
|
| |
|
| |
The user would be able to get the list of previous commits reachable from a
|
| |
|
| |
### List refs and retrieve their metadata
|
| |
|
| - |
Git refs are simple names that point to objects using object IDs. `radicle-surf`
|
| - |
support refs by its `Ref` type.
|
| + |
Git refs are simple names that point to objects using object IDs. In this new
|
| + |
design, we no longer group different refs into a single enum. Instead, each kind
|
| + |
of ref would be their own type, e.g. `Tag`, `Branch`, `Namespace`, etc.
|
| + |
|
| + |
To retrieve the refs, the user would call the `<ref_type>s()` method of a
|
| + |
repo, e.g. `branches()`, `tags()`. The result is an iterator of available refs,
|
| + |
e.g. `Branches`.
|
| |
|
| |
```Rust
|
| - |
pub enum Ref {
|
| - |
/// A git tag, which can be found under `.git/refs/tags/`.
|
| - |
Tag {
|
| - |
/// The name of the tag, e.g. `v1.0.0`.
|
| - |
name: TagName,
|
| - |
},
|
| - |
/// A git branch, which can be found under `.git/refs/heads/`.
|
| - |
LocalBranch {
|
| - |
/// The name of the branch, e.g. `master`.
|
| - |
name: BranchName,
|
| - |
},
|
| - |
/// A git branch, which can be found under `.git/refs/remotes/`.
|
| - |
RemoteBranch {
|
| - |
/// The remote name, e.g. `origin`.
|
| - |
remote: String,
|
| - |
/// The name of the branch, e.g. `master`.
|
| - |
name: BranchName,
|
| - |
},
|
| - |
/// A git namespace, which can be found under `.git/refs/namespaces/`.
|
| - |
///
|
| - |
/// Note that namespaces can be nested.
|
| - |
Namespace {
|
| - |
/// The name value of the namespace.
|
| - |
namespace: String,
|
| - |
/// The reference under that namespace, e.g. The
|
| - |
/// `refs/remotes/origin/master/ portion of `refs/namespaces/
|
| - |
/// moi/refs/remotes/origin/master`.
|
| - |
reference: Box<Ref>,
|
| - |
},
|
| - |
/// A git notes, which can be found under `.git/refs/notes`
|
| - |
Notes {
|
| - |
/// The default name is "commits".
|
| - |
name: String,
|
| - |
}
|
| + |
impl RepositoryRef {
|
| + |
/// Lists branch names with `filter`.
|
| + |
pub fn branches<G>(&self, pattern: G) -> Result<Branches, Error>
|
| + |
where
|
| + |
G: Into<Glob<Branch>>
|
| |
}
|
| |
```
|
| |
|
| - |
### Git Objects
|
| + |
### Git Tree and Blob
|
| + |
|
| + |
In Git, a `Blob` object represents the content of a file, and a `Tree` object
|
| + |
represents the content of a listing (in a directory). A `Tree` or a `Blob` does
|
| + |
not have its own name because there could be different names (paths) pointing
|
| + |
to the same `Tree` or `Blob`. In contrast, a `Directory` or a `File` has names
|
| + |
included and possible other attributes.
|
| |
|
| - |
Git has four kinds of objects: Blob, Tree, Commit and Tag. We have already
|
| - |
discussed `Commit` (a struct) and `Tag` (`Ref::Tag`) types. For `blob`, we
|
| - |
use `File` type to represent, and for `tree`, we use `Directory` type to
|
| - |
represent. The motivation is to let the user "surf" a repo as a file system
|
| - |
as much as possible, and to avoid Git internal concepts in our API if possible.
|
| + |
In `radicle-surf` API, we would expose `Tree` and `Blob` as defined in Git, i.e.
|
| + |
as the content only, with some extra helper methods, for example `commit()` that
|
| + |
returns the commit that created the object.
|
| |
|
| - |
Open question: there could be some cases where the names `blob` and `tree`
|
| - |
shall be used. We need to define such cases clearly if they exist.
|
| + |
The `Repository` provides methods to retrieve `Tree` or `Blob` for a given path.
|
| |
|
| - |
## Error handling
|
| + |
## Summary
|
| |
|
| - |
TBD
|
| + |
This design document was created as a guideline for refactoring the
|
| + |
`radicle-surf` crate when we move it into the `radicle-git` repo. As the code
|
| + |
evolves, this document would not be matching the actual code exactly. However
|
| + |
we can still come back to this document to learn about the history of the
|
| + |
design and update it when needed.
|