cob: Include commit timestamp in `Entry`
Signed-off-by: Alexis Sellier <alexis@radicle.xyz>
This commit is contained in:
parent
86beae2ebb
commit
d061afdab1
|
|
@ -9,6 +9,7 @@ use git_commit::{self as commit, Commit};
|
||||||
use git_ext::Oid;
|
use git_ext::Oid;
|
||||||
use git_trailers::OwnedTrailer;
|
use git_trailers::OwnedTrailer;
|
||||||
|
|
||||||
|
use crate::history::entry::Timestamp;
|
||||||
use crate::{
|
use crate::{
|
||||||
change::{self, store, Change},
|
change::{self, store, Change},
|
||||||
history::entry,
|
history::entry,
|
||||||
|
|
@ -114,7 +115,7 @@ impl change::Storage for git2::Repository {
|
||||||
Signature::from((*key, sig))
|
Signature::from((*key, sig))
|
||||||
};
|
};
|
||||||
|
|
||||||
let id = write_commit(self, resource, tips, message, signature.clone(), tree)?;
|
let (id, timestamp) = write_commit(self, resource, tips, message, signature.clone(), tree)?;
|
||||||
Ok(Change {
|
Ok(Change {
|
||||||
id,
|
id,
|
||||||
revision: revision.into(),
|
revision: revision.into(),
|
||||||
|
|
@ -122,11 +123,13 @@ impl change::Storage for git2::Repository {
|
||||||
resource,
|
resource,
|
||||||
manifest,
|
manifest,
|
||||||
contents,
|
contents,
|
||||||
|
timestamp,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load(&self, id: Self::ObjectId) -> Result<Change, Self::LoadError> {
|
fn load(&self, id: Self::ObjectId) -> Result<Change, Self::LoadError> {
|
||||||
let commit = Commit::read(self, id.into())?;
|
let commit = Commit::read(self, id.into())?;
|
||||||
|
let timestamp = git2::Time::from(commit.committer().time).seconds() as u64;
|
||||||
let resource = parse_resource_trailer(commit.trailers())?;
|
let resource = parse_resource_trailer(commit.trailers())?;
|
||||||
let mut signatures = Signatures::try_from(&commit)?
|
let mut signatures = Signatures::try_from(&commit)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
@ -149,6 +152,7 @@ impl change::Storage for git2::Repository {
|
||||||
resource,
|
resource,
|
||||||
manifest,
|
manifest,
|
||||||
contents,
|
contents,
|
||||||
|
timestamp,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -208,7 +212,7 @@ fn write_commit<O>(
|
||||||
message: String,
|
message: String,
|
||||||
signature: Signature,
|
signature: Signature,
|
||||||
tree: git2::Tree,
|
tree: git2::Tree,
|
||||||
) -> Result<Oid, error::Create>
|
) -> Result<(Oid, Timestamp), error::Create>
|
||||||
where
|
where
|
||||||
O: AsRef<git2::Oid>,
|
O: AsRef<git2::Oid>,
|
||||||
{
|
{
|
||||||
|
|
@ -221,14 +225,14 @@ where
|
||||||
|
|
||||||
{
|
{
|
||||||
let author = repo.signature()?;
|
let author = repo.signature()?;
|
||||||
|
let timestamp = author.when().seconds() as Timestamp;
|
||||||
let mut headers = commit::Headers::new();
|
let mut headers = commit::Headers::new();
|
||||||
headers.push(
|
headers.push(
|
||||||
"gpgsig",
|
"gpgsig",
|
||||||
&String::from_utf8(crypto::ssh::ExtendedSignature::from(signature).to_armored())?,
|
&String::from_utf8(crypto::ssh::ExtendedSignature::from(signature).to_armored())?,
|
||||||
);
|
);
|
||||||
let author = commit::Author::try_from(&author)?;
|
let author = commit::Author::try_from(&author)?;
|
||||||
|
let oid = Commit::new(
|
||||||
let commit = Commit::new(
|
|
||||||
tree.id(),
|
tree.id(),
|
||||||
parents,
|
parents,
|
||||||
author.clone(),
|
author.clone(),
|
||||||
|
|
@ -236,11 +240,10 @@ where
|
||||||
headers,
|
headers,
|
||||||
message,
|
message,
|
||||||
trailers,
|
trailers,
|
||||||
);
|
)
|
||||||
commit
|
.write(repo)?;
|
||||||
.write(repo)
|
|
||||||
.map(Oid::from)
|
Ok((Oid::from(oid), timestamp))
|
||||||
.map_err(error::Create::from)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,10 @@ use std::{error::Error, fmt};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{history::Contents, signatures, TypeName};
|
use crate::{
|
||||||
|
history::{Contents, Timestamp},
|
||||||
|
signatures, TypeName,
|
||||||
|
};
|
||||||
|
|
||||||
pub trait Storage {
|
pub trait Storage {
|
||||||
type CreateError: Error + Send + Sync + 'static;
|
type CreateError: Error + Send + Sync + 'static;
|
||||||
|
|
@ -59,6 +62,8 @@ pub struct Change<Resource, Id, Signature> {
|
||||||
pub manifest: Manifest,
|
pub manifest: Manifest,
|
||||||
/// The contents that describe `Change`.
|
/// The contents that describe `Change`.
|
||||||
pub contents: Contents,
|
pub contents: Contents,
|
||||||
|
/// Timestamp of change.
|
||||||
|
pub timestamp: Timestamp,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Resource, Id, S> fmt::Display for Change<Resource, Id, S>
|
impl<Resource, Id, S> fmt::Display for Change<Resource, Id, S>
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ fn evaluate_change(
|
||||||
change.resource,
|
change.resource,
|
||||||
child_commits.iter().cloned(),
|
child_commits.iter().cloned(),
|
||||||
change.contents().clone(),
|
change.contents().clone(),
|
||||||
|
change.timestamp,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ use radicle_crypto::PublicKey;
|
||||||
use crate::pruning_fold;
|
use crate::pruning_fold;
|
||||||
|
|
||||||
pub mod entry;
|
pub mod entry;
|
||||||
pub use entry::{Clock, Contents, Entry, EntryId, EntryWithClock};
|
pub use entry::{Clock, Contents, Entry, EntryId, EntryWithClock, Timestamp};
|
||||||
|
|
||||||
/// The DAG of changes making up the history of a collaborative object.
|
/// The DAG of changes making up the history of a collaborative object.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
@ -44,6 +44,7 @@ impl History {
|
||||||
actor: PublicKey,
|
actor: PublicKey,
|
||||||
resource: Oid,
|
resource: Oid,
|
||||||
contents: Contents,
|
contents: Contents,
|
||||||
|
timestamp: Timestamp,
|
||||||
) -> Self
|
) -> Self
|
||||||
where
|
where
|
||||||
Id: Into<EntryId>,
|
Id: Into<EntryId>,
|
||||||
|
|
@ -55,6 +56,7 @@ impl History {
|
||||||
resource,
|
resource,
|
||||||
children: vec![],
|
children: vec![],
|
||||||
contents,
|
contents,
|
||||||
|
timestamp,
|
||||||
};
|
};
|
||||||
let mut entries = HashMap::new();
|
let mut entries = HashMap::new();
|
||||||
entries.insert(id, EntryWithClock::from(root_entry));
|
entries.insert(id, EntryWithClock::from(root_entry));
|
||||||
|
|
@ -85,6 +87,16 @@ impl History {
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the current history timestamp.
|
||||||
|
/// This is the latest timestamp of any tip.
|
||||||
|
pub fn timestamp(&self) -> Clock {
|
||||||
|
self.graph
|
||||||
|
.externals(petgraph::Direction::Outgoing)
|
||||||
|
.map(|n| self.graph[n].timestamp())
|
||||||
|
.max()
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
/// A topological (parents before children) traversal of the dependency
|
/// A topological (parents before children) traversal of the dependency
|
||||||
/// graph of this history. This is analagous to
|
/// graph of this history. This is analagous to
|
||||||
/// [`std::iter::Iterator::fold`] in that it folds every change into an
|
/// [`std::iter::Iterator::fold`] in that it folds every change into an
|
||||||
|
|
@ -120,6 +132,7 @@ impl History {
|
||||||
new_actor: PublicKey,
|
new_actor: PublicKey,
|
||||||
new_resource: Oid,
|
new_resource: Oid,
|
||||||
new_contents: Contents,
|
new_contents: Contents,
|
||||||
|
new_timestamp: Timestamp,
|
||||||
) where
|
) where
|
||||||
Id: Into<EntryId>,
|
Id: Into<EntryId>,
|
||||||
{
|
{
|
||||||
|
|
@ -131,6 +144,7 @@ impl History {
|
||||||
new_resource,
|
new_resource,
|
||||||
std::iter::empty::<git2::Oid>(),
|
std::iter::empty::<git2::Oid>(),
|
||||||
new_contents,
|
new_contents,
|
||||||
|
new_timestamp,
|
||||||
);
|
);
|
||||||
let new_ix = self.graph.add_node(EntryWithClock {
|
let new_ix = self.graph.add_node(EntryWithClock {
|
||||||
entry: new_entry,
|
entry: new_entry,
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,9 @@ pub type Contents = Vec<u8>;
|
||||||
/// Logical clock used to track causality in change graph.
|
/// Logical clock used to track causality in change graph.
|
||||||
pub type Clock = u64;
|
pub type Clock = u64;
|
||||||
|
|
||||||
|
/// Local time in seconds since epoch.
|
||||||
|
pub type Timestamp = u64;
|
||||||
|
|
||||||
/// A unique identifier for a history entry.
|
/// A unique identifier for a history entry.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq, PartialOrd, Ord)]
|
#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq, PartialOrd, Ord)]
|
||||||
pub struct EntryId(Oid);
|
pub struct EntryId(Oid);
|
||||||
|
|
@ -51,6 +54,8 @@ pub struct Entry {
|
||||||
pub(super) children: Vec<EntryId>,
|
pub(super) children: Vec<EntryId>,
|
||||||
/// The contents of this entry.
|
/// The contents of this entry.
|
||||||
pub(super) contents: Contents,
|
pub(super) contents: Contents,
|
||||||
|
/// The entry timestamp, as seconds since epoch.
|
||||||
|
pub(super) timestamp: Timestamp,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Entry {
|
impl Entry {
|
||||||
|
|
@ -60,6 +65,7 @@ impl Entry {
|
||||||
resource: Oid,
|
resource: Oid,
|
||||||
children: ChildIds,
|
children: ChildIds,
|
||||||
contents: Contents,
|
contents: Contents,
|
||||||
|
timestamp: Timestamp,
|
||||||
) -> Self
|
) -> Self
|
||||||
where
|
where
|
||||||
Id1: Into<EntryId>,
|
Id1: Into<EntryId>,
|
||||||
|
|
@ -72,6 +78,7 @@ impl Entry {
|
||||||
resource,
|
resource,
|
||||||
children: children.into_iter().map(|id| id.into()).collect(),
|
children: children.into_iter().map(|id| id.into()).collect(),
|
||||||
contents,
|
contents,
|
||||||
|
timestamp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,6 +97,11 @@ impl Entry {
|
||||||
&self.actor
|
&self.actor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The entry timestamp.
|
||||||
|
pub fn timestamp(&self) -> Timestamp {
|
||||||
|
self.timestamp
|
||||||
|
}
|
||||||
|
|
||||||
/// The contents of this change
|
/// The contents of this change
|
||||||
pub fn contents(&self) -> &Contents {
|
pub fn contents(&self) -> &Contents {
|
||||||
&self.contents
|
&self.contents
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ where
|
||||||
init_change.signature.key,
|
init_change.signature.key,
|
||||||
resource.content_id(),
|
resource.content_id(),
|
||||||
contents.clone(),
|
contents.clone(),
|
||||||
|
init_change.timestamp,
|
||||||
);
|
);
|
||||||
|
|
||||||
let object_id = init_change.id().into();
|
let object_id = init_change.id().into();
|
||||||
|
|
|
||||||
|
|
@ -81,9 +81,13 @@ where
|
||||||
message,
|
message,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
object
|
object.history.extend(
|
||||||
.history
|
change.id,
|
||||||
.extend(change.id, change.signature.key, change.resource, changes);
|
change.signature.key,
|
||||||
|
change.resource,
|
||||||
|
changes,
|
||||||
|
change.timestamp,
|
||||||
|
);
|
||||||
storage
|
storage
|
||||||
.update(identifier, typename, &object_id, &change)
|
.update(identifier, typename, &object_id, &change)
|
||||||
.map_err(|err| error::Update::Refs { err: Box::new(err) })?;
|
.map_err(|err| error::Update::Refs { err: Box::new(err) })?;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue