Tags
[?]
Mar 10, 2021, 4:56 PM
QL6K2ZM35B3NIXEMMCJWUSFXOBQHAGXRDMO7ID5DCKTJH4QJVY7QCDependencies
- [2]
ZBNKSYA6Fixing a bus error when starting a transaction on a full disk - [3]
SXEYMYF7Fixing the bad changes in history (unfortunately, by rebooting). - [4]
SNZ3OAMCuse native external subcommand support instead of hand-rolled one - [5]
IIV3EL2XCleanup, formatting, and fixing the Git feature - [6]
BXD3IQYNFixing --features git - [7]
I24UEJQLVarious post-fire fixes - [8]
GHO6DWPIRefactoring iterators - [9]
YN63NUZOSanakirja 1.0 - [10]
H23LO7U7a few more clippy lints addressed - [11]
I52XSRUHMassive cleanup, and simplification - [12]
CCLLB7OIUpgrading to Sanakirja 0.15 + version bump - [13]
JL4WKA5PImplement the Sanakirja concurrency model in a cross-process way - [14]
7S4YD633Change in semantic of the new Sanakirja compared to the previous one (get returns Some(…) even if the key is not found) - [15]
7ZFRYVVQCargo.nix and formatting - [16]
VO5OQW4WRemoving anyhow in libpijul - [*]
TZ42DX3BProperly dropping a channel
Change contents
- replacement in pijul/src/main.rs at line 125
// Tag(Tag),Tag(Tag), - replacement in pijul/src/main.rs at line 213
// SubCommand::Tag(tag) => tag.run().await,SubCommand::Tag(tag) => tag.run().await, - file addition: tag.rs[3.93386]
use std::io::Write;use std::path::PathBuf;use std::sync::{Arc, RwLock};use crate::repository::Repository;use anyhow::bail;use clap::Clap;use libpijul::change::ChangeHeader;use libpijul::{Base32, ChannelMutTxnT, ChannelTxnT, MutTxnT, TxnT, TxnTExt};#[derive(Clap, Debug)]pub struct Tag {/// Set the repository where this command should run. Defaults to/// the first ancestor of the current directory that contains a/// `.pijul` directory.#[clap(long = "repository")]repo_path: Option<PathBuf>,#[clap(subcommand)]subcmd: SubCommand,}#[derive(Clap, Debug)]pub enum SubCommand {/// Create a tag.#[clap(name = "create")]Create {#[clap(short = 'm', long = "message")]message: Option<String>,/// Set the author field#[clap(long = "author")]author: Option<String>,/// Record the change in this channel instead of the current channel#[clap(long = "channel")]channel: Option<String>,#[clap(long = "timestamp")]timestamp: Option<i64>,},/// Restore a tag into a new channel.#[clap(name = "checkout")]Checkout {tag: String,/// Optional new channel name. If not given, the base32/// representation of the tag hash is used.#[clap(long = "to-channel")]to_channel: Option<String>,},}impl Tag {pub async fn run(self) -> Result<(), anyhow::Error> {let mut stdout = std::io::stdout();let mut repo = Repository::find_root(self.repo_path).await?;match self.subcmd {SubCommand::Create {message,author,channel,timestamp,} => {let channel_name = repo.config.get_current_channel(channel.as_deref()).0.to_string();try_record(&mut repo, &channel_name)?;let mut txn = repo.pristine.mut_txn_begin()?;let channel = txn.load_channel(&channel_name)?.unwrap();let last_t = if let Some(n) = txn.reverse_log(&*channel.read()?, None)?.next() {n?.0.into()} else {bail!("Channel {} is empty", channel_name);};log::debug!("last_t = {:?}", last_t);if txn.get_tags(&channel.read()?.tags, &last_t)?.is_some() {bail!("Current state is already tagged")}let mut tag_path = repo.path.join(libpijul::DOT_DIR);tag_path.push("tags");std::fs::create_dir_all(&tag_path)?;let mut temp_path = tag_path.clone();temp_path.push("tag");let mut w = std::fs::File::create(&temp_path)?;let header = header(author.as_deref(), message, timestamp)?;let h = libpijul::tag::from_channel(&txn, &channel_name, &header, &mut w)?;libpijul::changestore::filesystem::push_filename(&mut tag_path, &h);std::fs::create_dir_all(tag_path.parent().unwrap())?;std::fs::rename(&temp_path, &tag_path)?;txn.put_tags(&mut *channel.write()?, last_t.into(), &h)?;txn.commit()?;writeln!(stdout, "{}", h.to_base32())?;}SubCommand::Checkout { tag, to_channel } => {let h = if let Some(h) = libpijul::Hash::from_base32(tag.as_bytes()) {h} else {bail!("Invalid tag {:?}", tag)};let channel_name = if let Some(ref channel) = to_channel {channel.as_str()} else {tag.as_str()};let mut txn = repo.pristine.mut_txn_begin()?;if txn.load_channel(channel_name)?.is_some() {bail!("Channel {:?} already exists", channel_name)}let mut tag_path = repo.path.join(libpijul::DOT_DIR);tag_path.push("tags");libpijul::changestore::filesystem::push_filename(&mut tag_path, &h);let f = libpijul::tag::OpenTagFile::open(&tag)?;libpijul::tag::restore_channel(f, &mut txn, &channel_name)?;txn.commit()?;}}Ok(())}}fn header(author: Option<&str>,message: Option<String>,timestamp: Option<i64>,) -> Result<ChangeHeader, anyhow::Error> {let authors = if let Some(a) = author {vec![libpijul::change::Author {name: a.to_string(),full_name: None,email: None,}]} else if let Ok(global) = crate::config::Global::load() {vec![global.author]} else {Vec::new()};let header = ChangeHeader {message: message.clone().unwrap_or_else(String::new),authors,description: None,timestamp: if let Some(t) = timestamp {chrono::DateTime::from_utc(chrono::NaiveDateTime::from_timestamp(t, 0), chrono::Utc)} else {chrono::Utc::now()},};let toml = toml::to_string_pretty(&header)?;loop {let bytes = edit::edit_bytes(toml.as_bytes())?;if let Ok(header) = toml::from_slice(&bytes) {return Ok(header);}}}fn try_record(repo: &mut Repository, channel: &str) -> Result<(), anyhow::Error> {let txn = repo.pristine.mut_txn_begin()?;if let Some(channel) = txn.load_channel(channel)? {let mut state = libpijul::RecordBuilder::new();state.record(Arc::new(RwLock::new(txn)),libpijul::Algorithm::default(),channel,repo.working_copy.clone(),&repo.changes,"",num_cpus::get(),)?;let rec = state.finish();if !rec.actions.is_empty() {bail!("Cannot change channel, as there are unrecorded changes.")}} else {bail!("Channel not found: {}", channel)}Ok(())} - replacement in pijul/src/commands/mod.rs at line 54
// mod tag;// pub use tag::*;mod tag;pub use tag::*; - file addition: tag.rs[3.198146]
use crate::pristine::sanakirja::{Channel, MutTxn, SanakirjaError, P, UP};use crate::pristine::*;use crate::HashSet;use crate::TxnT;use log::*;use serde_derive::*;use std::io::Read;use std::path::Path;use std::sync::{Arc, RwLock};#[derive(Debug, Serialize, Deserialize, Default)]struct FileHeader {version: u64,header: u64,channel: u64,unhashed: u64,total: u64,offsets: DbOffsets,state: Merkle,}#[derive(Debug, Serialize, Deserialize, Default)]struct DbOffsets {internal: u64,external: u64,graph: u64,changes: u64,revchanges: u64,states: u64,tags: u64,apply_counter: u64,size: u64,}pub struct OpenTagFile {header: FileHeader,file: std::fs::File,}#[derive(Debug, Error)]pub enum TagError {#[error("Version mismatch")]VersionMismatch,#[error(transparent)]Io(#[from] std::io::Error),#[error(transparent)]Bincode(#[from] bincode::Error),#[error(transparent)]Zstd(#[from] zstd_seekable::Error),#[error(transparent)]Txn(SanakirjaError),#[error("Synchronisation error")]Sync,}impl From<TxnErr<SanakirjaError>> for TagError {fn from(e: TxnErr<SanakirjaError>) -> Self {TagError::Txn(e.0)}}impl OpenTagFile {pub fn open<P: AsRef<Path>>(p: P) -> Result<Self, TagError> {let mut file = std::fs::File::open(p)?;let mut off = [0u8; std::mem::size_of::<FileHeader>() as usize];file.read_exact(&mut off)?;let header = bincode::deserialize(&off)?;Ok(OpenTagFile { header, file })}}pub const VERSION: u64 = 5;const BLOCK_SIZE: usize = 4096;pub fn restore_channel(mut tag: OpenTagFile,txn: &mut MutTxn<()>,name: &str,) -> Result<ChannelRef<MutTxn<()>>, TagError> {use std::io::{Seek, SeekFrom};tag.file.seek(SeekFrom::Start(tag.header.channel))?;let mut comp = vec![0; (tag.header.unhashed - tag.header.channel) as usize];debug!("tag header {:?}", tag.header);tag.file.read_exact(&mut comp)?;debug!("{:?} {:?}", &comp[..20], comp.len());debug!("{:?}", &comp[comp.len() - 20..]);let mut buf = vec![0; tag.header.offsets.size as usize];zstd_seekable::Seekable::init_buf(&comp)?.decompress(&mut buf, 0).unwrap();let filetxn = Txn::from_slice(&mut buf);let external: ::sanakirja::btree::Db_<ChangeId, SerializedHash, UP<ChangeId, SerializedHash>> =::sanakirja::btree::Db_::from_page(tag.header.offsets.external);debug!("restoring graph");let graph = restore(&filetxn,txn,tag.header.offsets.graph,|file_txn, txn, k: &Vertex<ChangeId>, v: &SerializedEdge| {let k = if k.change.is_root() {*k} else {debug!("btree get: {:?}", k.change);let (kc, h) =::sanakirja::btree::get(file_txn, &external, &k.change, None)?.unwrap();assert_eq!(k.change, *kc);Vertex {change: crate::pristine::make_changeid(txn, &h.into())?,..*k}};let dest = v.dest();let dest = {if dest.change.is_root() {dest} else {let (vd, change) =::sanakirja::btree::get(file_txn, &external, &dest.change, None)?.unwrap();assert_eq!(v.dest().change, *vd);Position {change: crate::pristine::make_changeid(txn, &change.into())?,..v.dest()}}};let introduced_by = v.introduced_by();let introduced_by = if introduced_by.is_root() {introduced_by} else {let (vi, change) =::sanakirja::btree::get(file_txn, &external, &introduced_by, None)?.unwrap();assert_eq!(introduced_by, *vi);crate::pristine::make_changeid(txn, &change.into())?};let v = Edge {dest,introduced_by,..v.into()};Ok((k, v.into()))},)?;debug!("restoring changes");let changes = restore(&filetxn,txn,tag.header.offsets.changes,|file_txn, txn, k: &ChangeId, v: &L64| {let (k_, h) = ::sanakirja::btree::get(file_txn, &external, k, None)?.unwrap();assert_eq!(k, k_);let k = crate::pristine::make_changeid(txn, &h.into())?;Ok((k, *v))},)?;debug!("restoring revchanges");let revchanges = restore(&filetxn,txn,tag.header.offsets.revchanges,|file_txn, txn, k: &L64, v: &Pair<ChangeId, SerializedMerkle>| {let (v0, h) = ::sanakirja::btree::get(file_txn, &external, &v.a, None)?.unwrap();assert_eq!(v.a, *v0);let v_ = crate::pristine::make_changeid(txn, &h.into())?;Ok((*k,Pair {a: v_,b: v.b.clone(),},))},)?;debug!("restoring states");let states = restore(&filetxn,txn,tag.header.offsets.states,|_, _, k: &SerializedMerkle, v: &L64| Ok((k.clone(), *v)),)?;debug!("restoring states");let tags = restore(&filetxn,txn,tag.header.offsets.tags,|_, _, k: &L64, v: &SerializedHash| Ok((*k, *v)),)?;let name = crate::small_string::SmallString::from_str(name);let br = ChannelRef {r: Arc::new(RwLock::new(Channel {graph,changes,revchanges,states,tags,apply_counter: tag.header.offsets.apply_counter,name: name.clone(),last_modified: 0,})),};txn.open_channels.lock().unwrap().insert(name, br.clone());Ok(br)}struct Txn<'a> {data: *mut u8,marker: std::marker::PhantomData<&'a ()>,}impl<'a> Txn<'a> {fn from_slice(s: &'a mut [u8]) -> Self {Txn {data: s.as_mut_ptr(),marker: std::marker::PhantomData,}}}impl<'a> ::sanakirja::LoadPage for Txn<'a> {type Error = ::sanakirja::CRCError;fn load_page(&self, off: u64) -> Result<::sanakirja::CowPage, ::sanakirja::CRCError> {Ok(::sanakirja::CowPage {data: unsafe { self.data.add(off as usize) },offset: off,})}}fn restore<K: ::sanakirja::UnsizedStorable,V: ::sanakirja::UnsizedStorable,P: ::sanakirja::btree::BTreeMutPage<K, V>,F,>(file_txn: &Txn,txn: &mut crate::pristine::sanakirja::MutTxn<()>,pending: u64,f: F,) -> Result<::sanakirja::btree::Db_<K, V, P>, TxnErr<SanakirjaError>>whereF: Fn(&Txn,&mut crate::pristine::sanakirja::MutTxn<()>,&K,&V,) -> Result<(K, V), TxnErr<SanakirjaError>>,{use ::sanakirja::AllocPage;let mut dict = HashSet::default();let page = txn.txn.alloc_page()?;let result = page.0.offset;let mut pending = vec![(pending, page)];while let Some((offset, mut new_page_)) = pending.pop() {debug!("{:?}", offset);let page = ::sanakirja::CowPage {data: unsafe { file_txn.data.offset(offset as isize) },offset,};let mut curs = P::cursor_first(&page);let mut new_curs = P::cursor_first(&new_page_.0);P::init(&mut new_page_);unsafe {P::set_left_child(&mut new_page_,&new_curs,P::left_child(page.as_page(), &curs),);}while let Some((k, v, r)) = P::next(&txn.txn, page.as_page(), &mut curs) {let (k, v) = f(file_txn, txn, k, v)?;let r = if r > 0 {assert!(dict.insert(r));let new_page = txn.txn.alloc_page()?;let off = new_page.0.offset;pending.push((r, new_page));off} else {0};unsafe { P::put_mut(&mut new_page_, &new_curs, &k, &v, r) }P::move_next(&mut new_curs);}}Ok(::sanakirja::btree::Db_::from_page(result))}pub fn from_channel<W: std::io::Write, T: ::sanakirja::LoadPage<Error = ::sanakirja::Error>>(txn: &crate::pristine::sanakirja::GenericTxn<T>,channel: &str,header: &crate::change::ChangeHeader,mut w: W,) -> Result<Hash, TagError> {let out = Vec::with_capacity(1 << 16);let (out, offsets, state) = compress_channel(txn, channel, out)?;debug!("{:?} {:?}", &out[..20], out.len());debug!("{:?}", &out[out.len() - 20..]);let mut header_buf = Vec::with_capacity(1 << 10);bincode::serialize_into(&mut header_buf, header).unwrap();let mut off = FileHeader {version: VERSION,header: 0,channel: 0,unhashed: 0,total: 0,offsets,state,};off.header = bincode::serialized_size(&off)?;off.channel = off.header + header_buf.len() as u64;off.unhashed = off.channel + out.len() as u64;off.total = off.unhashed;let mut hasher = Hasher::default();let mut off_buf = Vec::with_capacity(off.header as usize);bincode::serialize_into(&mut off_buf, &off)?;debug!("off_buf = {:?}", off_buf.len());w.write_all(&off_buf)?;hasher.update(&off_buf);debug!("header_buf = {:?}", header_buf.len());w.write_all(&header_buf)?;hasher.update(&header_buf);debug!("out = {:?}", out.len());w.write_all(&out)?;hasher.update(&out);Ok(hasher.finish())}const LEVEL: usize = 10;const PIPE_LEN: usize = 10;fn compress_channel<W: std::io::Write + Send + 'static,T: ::sanakirja::LoadPage<Error = ::sanakirja::Error>,>(txn: &crate::pristine::sanakirja::GenericTxn<T>,channel: &str,mut to: W,) -> Result<(W, DbOffsets, Merkle), TagError> {debug!("int = {:?}", txn.internal.db);let (sender, receiver) = std::sync::mpsc::sync_channel::<Vec<u8>>(PIPE_LEN);let (bsender, breceiver) = std::sync::mpsc::sync_channel::<Vec<u8>>(PIPE_LEN);for _ in 0..PIPE_LEN {bsender.send(vec![0; 4096]).map_err(|_| TagError::Sync)?;}let t = std::thread::spawn(move || -> Result<(W, usize), TagError> {let mut comp = zstd_seekable::SeekableCStream::new(LEVEL, BLOCK_SIZE).unwrap();let mut out = [0; BLOCK_SIZE];let mut n = 0;while let Ok(input) = receiver.recv() {n += BLOCK_SIZE;let mut input_off = 0;let mut output_off = 0;while input_off < BLOCK_SIZE as usize {let (a, b) = comp.compress(&mut out[output_off..], &input[input_off..]).unwrap();output_off += a;input_off += b;}to.write_all(&out[..output_off]).unwrap();bsender.send(input).map_err(|_| TagError::Sync)?;}while let Ok(n) = comp.end_stream(&mut out) {if n == 0 {break;}to.write_all(&out[..n])?;}Ok((to, n))});let channel = txn.load_channel(channel)?.unwrap();let channel = channel.read().unwrap();let mut new = 0;debug!("copying internal");let internal = copy::<SerializedHash, ChangeId, UP<SerializedHash, ChangeId>, _>(txn,txn.internal.db,&mut new,&sender,&breceiver,)?;debug!("copying external");let external = copy::<ChangeId, SerializedHash, UP<ChangeId, SerializedHash>, _>(txn,txn.external.db,&mut new,&sender,&breceiver,)?;debug!("copying graph");let graph = copy::<Vertex<ChangeId>, SerializedEdge, P<Vertex<ChangeId>, SerializedEdge>, _>(txn,channel.graph.db,&mut new,&sender,&breceiver,)?;debug!("copying changes");let changes = copy::<ChangeId, L64, P<ChangeId, L64>, _>(txn,channel.changes.db,&mut new,&sender,&breceiver,)?;debug!("copying revchanges");let revchanges = copy::<L64,Pair<ChangeId, SerializedMerkle>,UP<L64, Pair<ChangeId, SerializedMerkle>>,_,>(&txn, channel.revchanges.db, &mut new, &sender, &breceiver)?;debug!("copying states");let states = copy::<SerializedMerkle, L64, UP<SerializedMerkle, L64>, _>(txn,channel.states.db,&mut new,&sender,&breceiver,)?;let tags = copy::<L64, SerializedHash, UP<L64, SerializedHash>, _>(&txn,channel.states.db,&mut new,&sender,&breceiver,)?;std::mem::drop(sender);let (w, n) = t.join().unwrap()?;let state = crate::pristine::current_state(txn, &channel)?;Ok((w,DbOffsets {internal,external,graph,changes,revchanges,states,tags: tags,apply_counter: channel.apply_counter,size: n as u64,},state,))}fn copy<K: ::sanakirja::UnsizedStorable,V: ::sanakirja::UnsizedStorable,P: ::sanakirja::btree::BTreeMutPage<K, V>,T: ::sanakirja::LoadPage<Error = ::sanakirja::Error>,>(txn: &crate::pristine::sanakirja::GenericTxn<T>,pending: u64,new_page: &mut u64,sender: &std::sync::mpsc::SyncSender<Vec<u8>>,buffers: &std::sync::mpsc::Receiver<Vec<u8>>,) -> Result<u64, TagError> {let mut dict = HashSet::default();let result = *new_page;let mut pending = vec![(pending, *new_page)];*new_page += BLOCK_SIZE as u64;while let Some((old_page_off, new_page_)) = pending.pop() {let page = txn.txn.load_page(old_page_off).unwrap();let mut memory = buffers.recv().map_err(|_| TagError::Sync)?;let mut new_page_ = ::sanakirja::MutPage(::sanakirja::CowPage {data: memory.as_mut_ptr(),offset: new_page_,});P::init(&mut new_page_);let mut curs = P::cursor_first(&page);let mut new_curs = P::cursor_first(&new_page_.0);unsafe {P::set_left_child(&mut new_page_,&new_curs,P::left_child(page.as_page(), &curs),);}while let Some((k, v, r)) = P::next(&txn.txn, page.as_page(), &mut curs) {let r = if r > 0 {assert!(dict.insert(r));let new = *new_page;*new_page += BLOCK_SIZE as u64;pending.push((r, new));unsafe {P::set_left_child(&mut new_page_, &curs, new);}new} else {0};debug!("put {:?} {:?}", k, v);unsafe { P::put_mut(&mut new_page_, &new_curs, k, v, r) }P::move_next(&mut new_curs);}sender.send(memory).unwrap();}Ok(result)} - replacement in libpijul/src/pristine/sanakirja.rs at line 13
type P<K, V> = btree::page::Page<K, V>;pub(crate) type P<K, V> = btree::page::Page<K, V>; - replacement in libpijul/src/pristine/sanakirja.rs at line 15
type UP<K, V> = btree::page_unsized::Page<K, V>;pub(crate) type UP<K, V> = btree::page_unsized::Page<K, V>; - edit in libpijul/src/pristine/sanakirja.rs at line 30[18.88][3.38798]
#[error("Pristine version mismatch. Cloning over the network can fix this.")]Version, - edit in libpijul/src/pristine/sanakirja.rs at line 100
Version, - edit in libpijul/src/pristine/sanakirja.rs at line 115
const VERSION: L64 = L64(1u64.to_le()); - edit in libpijul/src/pristine/sanakirja.rs at line 121
if L64(txn.root(Root::Version as usize)) != VERSION {return Err(SanakirjaError::Version);} - replacement in libpijul/src/pristine/sanakirja.rs at line 152
pub fn mut_txn_begin(&self) -> Result<MutTxn<()>, Error> {pub fn mut_txn_begin(&self) -> Result<MutTxn<()>, SanakirjaError> { - edit in libpijul/src/pristine/sanakirja.rs at line 154
if let Some(version) = txn.root(Root::Version as usize) {if L64(version) != VERSION {return Err(SanakirjaError::Version.into());}} else {txn.set_root(Root::Version as usize, VERSION.0);} - replacement in libpijul/src/pristine/sanakirja.rs at line 250
pub internal: Db<SerializedHash, ChangeId>,pub internal: UDb<SerializedHash, ChangeId>, - replacement in libpijul/src/pristine/sanakirja.rs at line 252
pub external: Db<ChangeId, SerializedHash>,pub external: UDb<ChangeId, SerializedHash>, - replacement in libpijul/src/pristine/sanakirja.rs at line 266
channels: UDb<SmallStr, T6>,channels: UDb<SmallStr, T8>, - edit in libpijul/src/pristine/sanakirja.rs at line 313
let tags: UDb<L64, SerializedHash> = UDb::from_page(tup.0[4].into()); - edit in libpijul/src/pristine/sanakirja.rs at line 322
debug!("check: tags 0x{:x}", tags.db);::sanakirja::debug::add_refs(&self.txn, &tags, &mut refs).unwrap(); - edit in libpijul/src/pristine/sanakirja.rs at line 660
pub tags: UDb<L64, SerializedHash>, - edit in libpijul/src/pristine/sanakirja.rs at line 848
}}type Tags = UDb<L64, SerializedHash>;fn get_tags(&self,channel: &Self::Tags,c: &L64,) -> Result<Option<&SerializedHash>, TxnErr<Self::GraphError>> {match btree::get(&self.txn, channel, c, None)? {Some((k, v)) if k == c => Ok(Some(v)),_ => Ok(None),}}type TagsCursor =::sanakirja::btree::cursor::Cursor<L64, SerializedHash, UP<L64, SerializedHash>>;fn cursor_tags<'txn>(&'txn self,channel: &Self::Tags,k: Option<L64>,) -> Result<crate::pristine::Cursor<Self, &'txn Self, Self::TagsCursor, L64, SerializedHash>,TxnErr<Self::GraphError>,> {let mut cursor = btree::cursor::Cursor::new(&self.txn, channel)?;if let Some(k) = k {cursor.set(&self.txn, &k, None)?;}Ok(Cursor {cursor,txn: self,k: std::marker::PhantomData,v: std::marker::PhantomData,t: std::marker::PhantomData,})}fn cursor_tags_next(&self,cursor: &mut Self::TagsCursor,) -> Result<Option<(&L64, &SerializedHash)>, TxnErr<Self::GraphError>> {if let Ok(x) = cursor.next(&self.txn) {Ok(x)} else {Err(TxnErr(SanakirjaError::PristineCorrupt))}}fn cursor_tags_prev(&self,cursor: &mut Self::TagsCursor,) -> Result<Option<(&L64, &SerializedHash)>, TxnErr<Self::GraphError>> {if let Ok(x) = cursor.prev(&self.txn) {Ok(x)} else {Err(TxnErr(SanakirjaError::PristineCorrupt)) - replacement in libpijul/src/pristine/sanakirja.rs at line 1063
apply_counter: tup.0[4].into(),last_modified: tup.0[5].into(),tags: UDb::from_page(tup.0[4].into()),apply_counter: tup.0[5].into(),last_modified: tup.0[6].into(), - replacement in libpijul/src/pristine/sanakirja.rs at line 1218
type Channels = UDb<SmallStr, T6>;type ChannelsCursor = ::sanakirja::btree::cursor::Cursor<SmallStr, T6, UP<SmallStr, T6>>;sanakirja_cursor!(channels, SmallStr, T6,);type Channels = UDb<SmallStr, T8>;type ChannelsCursor = ::sanakirja::btree::cursor::Cursor<SmallStr, T8, UP<SmallStr, T8>>;sanakirja_cursor!(channels, SmallStr, T8,); - edit in libpijul/src/pristine/sanakirja.rs at line 1634
btree::del(&mut self.txn, &mut channel.tags, &t.into(), None)?; - edit in libpijul/src/pristine/sanakirja.rs at line 1642
fn put_tags(&mut self,channel: &mut Self::Channel,t: ApplyTimestamp,h: &Hash,) -> Result<(), TxnErr<Self::GraphError>> {btree::put(&mut self.txn, &mut channel.tags, &t.into(), &h.into())?;Ok(())}fn del_tags(&mut self,channel: &mut Self::Channel,t: ApplyTimestamp,) -> Result<(), TxnErr<Self::GraphError>> {btree::del(&mut self.txn, &mut channel.tags, &t.into(), None)?;Ok(())} - replacement in libpijul/src/pristine/sanakirja.rs at line 1760
apply_counter: b.0[4].into(),tags: UDb::from_page(b.0[4].into()),apply_counter: b.0[5].into(),last_modified: b.0[6].into(), - edit in libpijul/src/pristine/sanakirja.rs at line 1764
last_modified: b.0[5].into(), - edit in libpijul/src/pristine/sanakirja.rs at line 1773
tags: btree::create_db_(&mut self.txn)?, - edit in libpijul/src/pristine/sanakirja.rs at line 1775
last_modified: 0, - edit in libpijul/src/pristine/sanakirja.rs at line 1777
last_modified: 0, - edit in libpijul/src/pristine/sanakirja.rs at line 1817
tags: btree::fork_db(&mut self.txn, &channel.tags).map_err(|e| ForkError::Txn(e.into()))?, - replacement in libpijul/src/pristine/sanakirja.rs at line 2020
apply_counter: c.0[4].into(),last_modified: c.0[5].into(),tags: UDb::from_page(c.0[4].into()),apply_counter: c.0[5].into(),last_modified: c.0[6].into(), - replacement in libpijul/src/pristine/sanakirja.rs at line 2046
let t6 = T6([let t8 = T8([ - edit in libpijul/src/pristine/sanakirja.rs at line 2051
channel.tags.db.into(), - edit in libpijul/src/pristine/sanakirja.rs at line 2054
0u64.into(), - replacement in libpijul/src/pristine/sanakirja.rs at line 2056
debug!("t6 = {:?}", t6);btree::put(&mut self.txn, &mut self.channels, &channel.name, &t6)?;btree::put(&mut self.txn, &mut self.channels, &channel.name, &t8)?; - replacement in libpijul/src/pristine/sanakirja.rs at line 2192
direct_repr!(T6);[3.97832]direct_repr!(T8); - replacement in libpijul/src/pristine/mod.rs at line 119
pub struct T6([L64; 6]);pub struct T8([L64; 8]); - edit in libpijul/src/pristine/mod.rs at line 342
type Tags;fn get_tags(&self,channel: &Self::Tags,c: &L64,) -> Result<Option<&SerializedHash>, TxnErr<Self::GraphError>>;type TagsCursor;fn cursor_tags<'txn>(&'txn self,channel: &Self::Tags,pos: Option<L64>,) -> Result<crate::pristine::Cursor<Self, &'txn Self, Self::TagsCursor, L64, SerializedHash>,TxnErr<Self::GraphError>,>;fn cursor_tags_next(&self,cursor: &mut Self::TagsCursor,) -> Result<Option<(&L64, &SerializedHash)>, TxnErr<Self::GraphError>>;fn cursor_tags_prev(&self,cursor: &mut Self::TagsCursor,) -> Result<Option<(&L64, &SerializedHash)>, TxnErr<Self::GraphError>>; - replacement in libpijul/src/pristine/mod.rs at line 490
cursor!(channels, SmallStr, T6);cursor!(channels, SmallStr, T8); - edit in libpijul/src/pristine/mod.rs at line 1520
fn put_tags(&mut self,channel: &mut Self::Channel,t: ApplyTimestamp,h: &Hash,) -> Result<(), TxnErr<Self::GraphError>>;fn del_tags(&mut self,channel: &mut Self::Channel,t: ApplyTimestamp,) -> Result<(), TxnErr<Self::GraphError>>; - edit in libpijul/src/lib.rs at line 31
pub mod tag; - replacement in libpijul/src/lib.rs at line 100
Base32, ChangeId, ChannelRef, ChannelTxnT, DepsTxnT, EdgeFlags, GraphTxnT, Hash, Inode, Merkle,MutTxnT, OwnedPathId, RemoteRef, TreeTxnT, TxnT, Vertex,Base32, ChangeId, ChannelMutTxnT, ChannelRef, ChannelTxnT, DepsTxnT, EdgeFlags, GraphTxnT,Hash, Inode, Merkle, MutTxnT, OwnedPathId, RemoteRef, TreeTxnT, TxnT, Vertex,