Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions lore-proto/proto/lore/thin_client/v1/model.proto
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,23 @@ message DiffPartition {
bytes link_partition = 2;
}

// The revision that last modified a tree entry. Directories report the
// revision of the most recent change anywhere beneath them.
message TreeCommit {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of introducing a new type I would suggest using the Revision type in the thin client proto model

// Content signature of the revision.
bytes signature = 1;
// Free-form commit message.
string commit_message = 2;
// Commit timestamp (Unix epoch milliseconds).
uint64 timestamp = 3;
// Resolved (branch, number) of the revision. Carried in full rather than as
// a bare number because the number is per-branch and this is not enough to
// describe its' provenance
lore.model.v1.RevisionIdentifier identifier = 4;
// Identity that committed the revision.
string committed_by = 5;
}

// A single entry in a revision tree listing.
message TreeNode {
// Repository-relative path of this entry.
Expand All @@ -132,6 +149,10 @@ message TreeNode {
// True when a link entry tracks its parent's branch; false for pinned links
// and non-link entries.
bool tracking = 6;
// Revision that last modified this entry, set only when the request asks
// for it. Absent wherever the server cannot attribute an entry - most
// commonly the repository root.
optional TreeCommit last_commit = 7;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this field will be the same for potentially a large number of files I would rather have it in a separate list of unique revisions, and the tree node refer to the revision by index into the list.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the revision list could be arbitrarily long, it should go as a separate Revision type in the RevisionTreeResponse so it can be streamed. And requirement that any Revision indexed by a TreeNode must have appeared before the tree node in the stream.

}

// Self-describing revision record. Carries the resolved RevisionIdentifier
Expand Down
4 changes: 4 additions & 0 deletions lore-proto/proto/lore/thin_client/v1/thin_client.proto
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ message RevisionTreeRequest {
// emits only direct children of the prefix root; 2 emits direct
// children plus grandchildren; etc. 0 or unset means unbounded.
optional uint32 max_depth = 4;
// If true, populate `TreeNode.last_commit`. Off by default.
// Note: Attribution costs one delta-block read per state plus one
// file-metadata-block read per entry.
bool include_last_commit = 5;
}

// Header for a RevisionTree stream. Echoes the resolved revision so
Expand Down
42 changes: 42 additions & 0 deletions lore-proto/src/grpc/lore.thin_client.v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,38 @@ impl ::prost::Name for DiffPartition {
"/lore.thin_client.v1.DiffPartition".into()
}
}
/// The revision that last modified a tree entry. Directories report the
/// revision of the most recent change anywhere beneath them.
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct TreeCommit {
/// Content signature of the revision.
#[prost(bytes = "bytes", tag = "1")]
pub signature: ::prost::bytes::Bytes,
/// Free-form commit message.
#[prost(string, tag = "2")]
pub commit_message: ::prost::alloc::string::String,
/// Commit timestamp (Unix epoch milliseconds).
#[prost(uint64, tag = "3")]
pub timestamp: u64,
/// Resolved (branch, number) of the revision. Carried in full rather than as
/// a bare number because the number is per-branch and this is not enough to
/// describe its' provenance
#[prost(message, optional, tag = "4")]
pub identifier: ::core::option::Option<crate::lore::model::v1::RevisionIdentifier>,
/// Identity that committed the revision.
#[prost(string, tag = "5")]
pub committed_by: ::prost::alloc::string::String,
}
impl ::prost::Name for TreeCommit {
const NAME: &'static str = "TreeCommit";
const PACKAGE: &'static str = "lore.thin_client.v1";
fn full_name() -> ::prost::alloc::string::String {
"lore.thin_client.v1.TreeCommit".into()
}
fn type_url() -> ::prost::alloc::string::String {
"/lore.thin_client.v1.TreeCommit".into()
}
}
/// A single entry in a revision tree listing.
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct TreeNode {
Expand All @@ -126,6 +158,11 @@ pub struct TreeNode {
/// and non-link entries.
#[prost(bool, tag = "6")]
pub tracking: bool,
/// Revision that last modified this entry, set only when the request asks
/// for it. Absent wherever the server cannot attribute an entry - most
/// commonly the repository root.
#[prost(message, optional, tag = "7")]
pub last_commit: ::core::option::Option<TreeCommit>,
}
impl ::prost::Name for TreeNode {
const NAME: &'static str = "TreeNode";
Expand Down Expand Up @@ -714,6 +751,11 @@ pub struct RevisionTreeRequest {
/// children plus grandchildren; etc. 0 or unset means unbounded.
#[prost(uint32, optional, tag = "4")]
pub max_depth: ::core::option::Option<u32>,
/// If true, populate `TreeNode.last_commit`. Off by default.
/// Note: Attribution costs one delta-block read per state plus one
/// file-metadata-block read per entry.
#[prost(bool, tag = "5")]
pub include_last_commit: bool,
/// Revision specifier.
#[prost(oneof = "revision_tree_request::Query", tags = "1, 2")]
pub query: ::core::option::Option<revision_tree_request::Query>,
Expand Down
10 changes: 10 additions & 0 deletions lore-proto/tests/v1_thin_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use lore_proto::lore::thin_client::v1::RevisionInfoResponse;
use lore_proto::lore::thin_client::v1::RevisionTreeHeader;
use lore_proto::lore::thin_client::v1::RevisionTreeRequest;
use lore_proto::lore::thin_client::v1::RevisionTreeResponse;
use lore_proto::lore::thin_client::v1::TreeCommit;
use lore_proto::lore::thin_client::v1::TreeNode;
use lore_proto::lore::thin_client::v1::content_diff_response::Payload as ContentDiffPayload;
use lore_proto::lore::thin_client::v1::revision::Parent as RevisionParent;
Expand Down Expand Up @@ -119,7 +120,15 @@ fn v1_thin_client_field_shapes() {
size: _,
mode: _,
tracking: _,
last_commit: _,
} = TreeNode::default();
let TreeCommit {
signature: _,
commit_message: _,
timestamp: _,
identifier: _,
committed_by: _,
} = TreeCommit::default();

// Revision + nested Parent + Metadata
let Revision {
Expand Down Expand Up @@ -179,6 +188,7 @@ fn v1_thin_client_field_shapes() {
query: _,
path_prefix: _,
max_depth: _,
include_last_commit: _,
} = RevisionTreeRequest::default();
let _ = RevisionTreeQuery::Identifier(Default::default());
let _ = RevisionTreeQuery::Signature(Default::default());
Expand Down
16 changes: 15 additions & 1 deletion lore-revision/src/revision.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1125,12 +1125,18 @@ pub struct TreeResult {
pub paths: Vec<TreePath>,
}

/// Walk the tree at `revision`, optionally attributing each entry with the
/// revision that last modified it.
///
/// `include_last_commit` populates [`TreePath::last_revision`] and
/// [`TreePath::last_revision_repository`]. Off by default.
pub async fn tree(
repository: Arc<RepositoryContext>,
revision: Hash,
path: RelativePath,
max_depth: usize,
can_read: crate::state::CanReadRepository,
include_last_commit: bool,
) -> Result<TreeResult, StateError> {
lore_debug!(
"Gathering tree in repository {} revision: {} path: {}",
Expand All @@ -1139,7 +1145,15 @@ pub async fn tree(
path.as_str()
);
let state = State::deserialize(repository.clone(), revision).await?;
let paths = gather_tree_paths(state, repository, path, max_depth, can_read).await?;
let paths = gather_tree_paths(
state,
repository,
path,
max_depth,
can_read,
include_last_commit,
)
.await?;
Ok(TreeResult { paths })
}

Expand Down
Loading