I24UEJQLCH2SOXA4UHIYWTRDCHSOPU7AFTRUOTX7HZIAV4AZKYEQC O4DNWMPDUWI6SKYOZTQKCSX6MSR73CTGCUSM65TSQYVOUSAAS6KAC IYJZVLETBAQDDELENH3FX7ZTOC3HY4UJ3AMC3MACW6O7ZCWZTR6AC NZIK34IMY3L5YMFISBLHUL5ATENBL35VOZJW4EHPINZEL6IQE4UQC YWUZQU3TBJJMUVMX2JJW2D2JLTN57YJA36YJXKHBF7UJ6GEBTWUAC VL7ZYKHBPKLNY5SA5QBW56SJ7LBBCKCGV5UAYLVF75KY6PPBOD4AC OU243LABJJZ3MQHYW2A2MYKW7KZLTZGHJJXDR2BIIIHXT5BSYN3AC PKIHBUGT3N4BUZ2QP2UWJI4ICOIF6EZVXBFKG753SOTYBAKSVTFAC NF4O25IELPL2JJBVM3UXMOWP2VIFWXII7PQD3PX5SUW3RYHKP5XQC I6DVZEFUMGH6BFOLGBPM6J4PL5I4PAAODJYG7REXYPDHPKPBLDTAC ADPAFSMYUBTKSK63EPCY5WQGROJQWFCWO4UFPWY3ZXD5ZNH26P2QC YTQS4ES362EJ27OE45CE5HLY7ZU57YLVKRMDRJ2OXT623VM5WOBQC VE2UWMW42VWMOYZWJCJZBX4NYJO37E6WNISFBUXZFLQ5HWN3B4VQC YDTN6BGI5TFRJFM3N3Y2J463GKFHU5L3DPBQXBDIQJDWXU5ELKMQC QV66H4YAO5ASPDY72R3FBFMSFE2DXTKU54W4OSNBY5KPLXNIFPIAC 2D7P2VKJASU7QDQZHGCLBIT6G2V5WUFYLWTCEVVEI2EZHGM6XYRAC X7OHUPL5VYT6ECER2KNGRNFLRX7SBZOM5QWSQ4PBO2UPIE7XM6MAC MFTN7GBWZNQAFHKER57MLZAJGVEAHV2GYAQN2QTDHTPCEURDVIGQC EEOOHGQQK43J36LQJDSS3UK56M54DXPYE4VB3K4A2XAYGOYDJAXAC SXEYMYF7P4RZMZ46WPL4IZUTSQ2ATBWYZX7QNVMS3SGOYXYOHAGQC JL4WKA5PBKXRNAMETYO4I52QKASQ3COYHH2JKGA7W5YLIRZZH53AC YN63NUZO4LVJ7XPMURDULTXBVJKW5MVCTZ24R7Z52QMHO3HPDUVQC CCLLB7OIFNFYJZTG3UCI7536TOCWSCSXR67VELSB466R24WLJSDAC TKEVOH7HXON7SOBGXTUDHAHO2U2GPTQRNESP6ERKUQAS526OZIRAC UDHP4ZVBQZT2VBURB2MDCU2IZDNMCAFSIUKWRBDQ5BWMFKSN2LYQC L4JXJHWXYNCL4QGJXNKKTOKKTAXKKXBJUUY7HFZGEUZ5A2V5H34QC 76PCXGML77EZWTRI5E6KHLVRAFTJ2AB5YRN5EKOYNAPKTWY2KCGAC MU5GSJAW65PEG3BRYUKZ7O37BPHW3MOX3S5E2RFOXKGUOJEEDQ5AC VMPAOJS2ZFOLNXALHWSVM5AFENWX6ZUACB45EJV3HXI7DQNAZPHQC LGEJSLTYI7Y2CYC3AN6ECMT3D3MTWCAKZPVQEG5MPM2OBW5FQ46AC 62XVBWPYCBULZ2IUWF36JVHAPMKCGQC53PQQ53AAGZGC2KT7VQRQC Y6EVFMTA6FOH3OQH6QCSWMI3F6SYZT2FSHO6GF4M3ICENDCWFM4QC ZBNKSYA6PW4DSPC3NCRUZLVHW2GNXMCSDSAGEIKHGHDGGZRBH7ZQC 5DVRL6MFXQOCPOZMYSKBERMRRVUTYRL2SRGRTU2MH4IEOFCDKM3QC YAJAXIV5VL263Z6FYLKFPROB3MQPRPH22P44GRGRVGEP56HOMBOAC EGSVRZJVIBSPYAI65A25CH5RYAGL4PUP3B24VSRUS3M4WIUCZWHAC SLJ3OHD4F6GJGZ3SV2D7DMR3PXYHPSI64X77KZ3RJ24EGEX6ZNQAC I52XSRUH5RVHQBFWVMAQPTUSPAJ4KNVID2RMI3UGCVKFLYUO6WZAC 3AMEP2Y5J6GA4AWQONF4JVA3XSR3ASLHHKMYG44R72SOUY3UQCDAC GHO6DWPILBBTL6CVZKERJBTFL3EY6ZT4YM4E5R4S6YPGVFKFHCVAC MHQBEHJDJ7MUW46HIS24AZFBC4DZDKZNBVBOBOBPML6GGFIS4LQAC 3KRGVQFUWFHPOGZOXVTJYNCM4XBRVYITAEOVPKBSAZ5GZIUO5KVQC XA23FMQM2AI7RMR36AYN7UNP2D5JWVJMJPHURWZO7URM7H46PU6AC SPA2OL5ITFMLB5P2WL342QAU2FXPKSFS4XHAMW6HYWOGSGLO2MJAC VNBLGT6GAN2AHKRFKTKED7WNDDRGNULY5H343ZYV3ETSDZZKGBTAC QMTANHVNRPQ5IX66FYQBFRBDCTN6YKMNCO6OHTQ6QCUASPWWXJKAC Y7YAFMFFJY3SQ3GYN3SS4V3FZWMH3B5L65AXQBXOR5XARSMF5JJQC WZVCLZKY34KQBQU6YBGJLQCDADBQ67LQVDNRVCMQVY3O3C3EIWSQC VQPAUKBQ2POZKL7CZFAZK5ZQKEBYL27XZYZWYUSH5AH25KK6DWKAC MDADYULS5AWVMTJDGYCGNQTN6T7XJDRUBDTFILDY5MLF6I2PE5NAC KWAGWB73AMLJFK2Z7SBKHHKKHFRX7AQKXCWDN2MBX72RYCNMB36QC ZHABNS3S6FSINO74FOI5KHYXYDTBPO4FQTTYTUS7NNKEVVNLYC4AC DJYHARZ7CSRMX6ZFM6P52SM2EC57VTSHWAIMFSD7Q3EL7UYZGLXQC MF3WAHBIH6Q2F7ZOKWPEJF6VGSKJITWLR3Z64GTD6YQZNA5EATWQC G6S6PWZEFJK7ARWBIFKDU6VYC5DCJ2YFJMWZOLLWWKU52R2QPXZAC HSVGP2G4D2F56DS3YKZLSYPS4A5BNGH4NTAXAOZ57OCXFM3E5AYAC 44BN7FWSIXKG75IJUTCXLJE7VANNQFPRHQXTPLQHFU7AKGLSPQRAC IIV3EL2XYI2X7HZWKXEXQFAE3R3KC2Q7SGOT3Q332HSENMYVF32QC OJZWJUF2TCGZ7RFVY6FPKBS5P3C4BGHZDPVH775OHVNVFMJICKNQC 4VWXL6KQGYGDUQRCVJCEVIV6CKJSEIYDX4YF33OX6EDNKJNEGD2AC ZRUPLBBTT4S6S7A3LOAHG4ONYEGPA5CFO4L2XBCNFKK45MWX3BDAC HMMMKONLCRAXVT7SO2ITTFDOJIQKKVSRIZPXYVPDC34RCBHWMHVAC 5BRU2RRWOQBMS2V3RQM7PRFR5UILYZ73GISHAKJA6KIZGC5M2MFAC BD5PC25AB5MKVIYDFSDGRZ4YGX4PKW4SMZ3YAYAPNA5HLDVJUR3QC 6DOXSHWGKJIMIPFCNLASGKBAJCJMJULW5HFRZAZ67EYSMXXGJ3KAC VIHXB7SGRETFPHPYZFSRGOFRXEO4RZY57WDZSU6IAUEJRU3HPKQAC 2K7JLB4Z7BS5VFNWD4DO3MKYU7VNPA5MTVHVSDI3FQZ5ICM6XM6QC SQVWP4LU7AAJSEIHK5CNNUK3XBUVT3FGIJIOPTKMR53PO2P4ARNQC HR3WK6A7KKILCHI2CD2BZSZQJUS44MJIT2K3WPKJBHUXGRKAQDRQC LYTVEPH3W5UHF7MAYFWBT6NVNC42HEVKJGGMFDKUDZDNDOI33YJQC B3QWIGDERNMB3M6P5WTWP5CN2DB3KCS7MVTEPC2XVJ237ZXVQGMAC Q45QHPO4HDTEZF2W4UDZSYYQ46BPEIWSW4GJILZR5HTJNLKXJABQC QE64ATLZWMKHYABCD3VA547PYXCK6YN3K7RE2TX3SCQNKG7XLVAQC VLPIKNFSMJXOG37QYRGUJC6YFMZXZUFDADQV4PYASKKDQOJ24MZAC A6R6SGCPLFM45QNWJLISFBR3EEXVITYHCWEUOPNH4UIGIWJRTZAQC R3H7D42UZ446V5TO2574BMAQQAYYJPEIMSZVDPAGVIYU2COJSWBAC JRENVH5DF2F4SOV7UNJENFA7VDI3H63XK76R3LFZK6QCW7JIBLSQC ENKQ3QZGH2QW246C7GSZRKYLODJOQHKZZSYV7QHB7VPOFP5PASVQC TPEH2XNBS5RO4IEVKENVF6P65AH7IX64KK2JAYMSJT3J5GXO67EAC VO5OQW4W2656DIYYRNZ3PO7TQ4JOKQ3GVWE5ALUTYVMX3WMXJOYQC 7FFFKQZU3TFXWL45TILYNX5A7AC7HBK526SD5DZGYCELN76YE7TAC 4OCC6D42GZYRDLH3NSKXMJTRKXP7UZ6Z3YNGCNUT7NT6WBDBCBIAC JP3BYVXXWFBVQ23MEHJ3LE36AN26P6OCZALKUXMNLHS2TSTM3NKAC X243Z3Y54ULINQMMRIKLHRV5T237B7VUOAHVJ7DMPOQ6A6GQXY2AC 7UPL3Y2A5QOBU6VIUHNWEFGSGQ5CMWGDGOVLRZ53UARXG3TDGLMAC NO2QPRFLGCYUDXYJTOY3S3NZJCCLAFOQUHITKDZ7LSZLRLOV5W3QC BZSC7VMYSFRXDHDDAMCDR6X67FN5VWIBOSE76BQLX7OCVOJFUA3AC 3M7WBE24JTPTHWQOU5PO2ZJYKPKEH2F6R4M6RWIRFG334PQEA55QC ZAEUSICJC3YOWGF6NZEQCQ34PHPRSBCJEP7FIWE6VIWJGVU734HQC CXM5CBS27BL35Z6TRCI7OS4AHWVJ4VFND7HECGAUC74ZQ5KFZXLAC EQLDTLXVCARE36EJE3S6SNEVTW2JJY4EYD36EX7WSIFLG2XMKKQAC CCFJ7VO3I73FE3MZRS5RSDRYNZVW7AXC345P4BXS7JIL2TU3LQJQC VYHHOEYHO67JNJEODX5L3CQFIV3DAXZBBIQUOMCWJDYF3VWICDNQC LLT3GY6ULCVHMO3VUSVI5H4O244Z3ULOWLTW2IGJXIA2TWIHJDSQC WKX5S4Z4DOB5S6A6X5V6ECZFCHQUMWRGX5XT4FBOG57P6HPWK7CAC VXZNQQHCDC6MBLUMNDJVPM4I7XWTDYBPZZNCPZCA6EJ3GS5WAQGQC 6YMDOZIB5LVYLFIDGN2WNT5JTHEAMS4TFPVDEZ3OWXWOKJOC5QDAC 7ZFRYVVQQGJYG3POPWJWL3CDW37YDXZYZQC3OSWFHWEUSEMYQ4EQC TZ42DX3BML5C3O5Z6OBVNBCHSIIHT6AOJPD6ICOLOP4LPYFXQN2QC DNQHXWRZF6EWII3RGQ6HPGFD47BOOFH4FCQHSCACBS3RLQO3DNIQC BXD3IQYNMKMI5BTANCF6NZHZP4CKPWADGJMBT2R3WTMKVKONT5QAC IM6UFPOZHZTBMESRXGBALOAWJWUUDGLP2TVLSZ3RZPSJITKB3R7QC ATZ3BWSEFJBLVGDUZFESRNHVCIO6ZRZ3ALPANZSVGVO7A5BUAFQQC NA5I4WYNE2O3LPSHXGWXW7XL4YNYFDREEGDOP6LJ5HJXTQDXM7BAC KVBLRDOUFRYB6BPOQJDD7OVBYMTTPDAUX7CJ5DC3U7WFRI5OLWRAC JRSBH6HTYXSIZKHW6SGWAF3JCEPMFTUG4JZUSUSF73ODEGFLAAJAC ZXTHL45OYLOICXTXXEQ6AMNSLMQFZ6BFUJWMO6LZOSDK7ERROC5AC 7PM25EXLNQ6JUUIZNTAOQYNNIZNG6TJREEBUSAIC3FIOE7FHETSAC KDF6FJRVF72L274BEUJCTUKRFMNL6BDZMTVKDPEYGFX4TC3YOVSQC BZCGXVS77ZS3N4QPLIHNWZ3YFVV7H4PXQD3U6RN5ZFVOC7QL7MBQC HQ56ADNSNBCCEBNK5PE5ZVBKKBGWY3ATGFWEYPXJKJBJDUJ2XKQQC 43AJ37IXON6PMMYRZM6OB2MJXPYCNIWW2XBDVNBXIRH5DD7JDTVQC NMX52UOGRCY2O7HT7Q45KWISOHNV4PEEMLDYDBJ4QPDIMTVKKJ6AC CZX6TRWR53F2BRLKSUTBPEONKS65IYNBO2FYB4HSWF6OK7DOEJGAC G734WNM64AR5BLAZMN5MDPKSFTYXTUQR6MAGB32NRBC5FXFRWSJAC HDGRZISM2SS4TK5BMNGDIYG22SOXAZRTTC6YFIOPY4LSO53QDWZQC RXNT67OTDNFTBYXS6ECDAZ26PRTDROASNYTR6IEXYQUO4K5YNXYQC SAGSYAPXQ2T6GC3B3TNRPNFTZMS7UMME6YQGSF5MOIM66S5NKB2QC HKEOO4QJ5EACX37IJG76GEUMNSZMFW4VRKA4IVBCGR52ZQSYTN6QC 3WIQYEISUMGOL5FY4LCWOJS55CTTQA7WMJZXAE3Q4GQUTBFLAE7QC OUWD436ATBTZJR53B6XDSI5SXRRNZV7YGRUEA5ACHZC2RUDP7G5QC 23LVKATNTT74YKHG7KJM6SBO2IVZEV24TQ46ZJIHQ2IXONWNVXJAC UFCZKKLXVYQYQBYENCAFHY3ZPPSDAJJAIREZSYNQM4QNXV6G6RXAC 33SQMZYXPV2A3F7P6WBFKFO2BTQV6THSQXOLV3PCZASC6OTKVHTQC txn.output_repository_no_pending(&mut repo, &store, &mut channel, "", true, None)?;
let txn = Arc::new(RwLock::new(txn));let wc = Arc::new(repo);libpijul::output::output_repository_no_pending(wc,&store,txn.clone(),channel.clone(),"",true,None,num_cpus::get(),)?;let txn = if let Ok(txn) = Arc::try_unwrap(txn) {txn.into_inner().unwrap()} else {unreachable!()};
let mut txn = repo.pristine.mut_txn_begin()?;if let Some(mut channel) = txn.load_channel(channel_name)? {let pending_hash = if self.reset {super::pending(&mut txn, &mut channel, &mut repo)?} else {None};
let txn = repo.pristine.mut_txn_begin()?;let channel = if let Some(channel) = txn.load_channel(channel_name)? {channel} else {bail!("No such channel: {:?}", channel_name);};let txn = Arc::new(RwLock::new(txn));let pending_hash = if self.reset {super::pending(txn.clone(), &channel, &mut repo)?} else {None};let mut txn = if let Ok(txn) = Arc::try_unwrap(txn) {txn.into_inner().unwrap()} else {unreachable!()};
if self.change_id.is_empty() {// No change ids were given, present a list for choosing// The number can be set in the global config or passed as a command-line optionlet number_of_changes = if let Some(n) = self.show_changes {n} else {let cfg = crate::config::Global::load()?;cfg.unrecord_changes.ok_or_else(|| {anyhow!("Can't determine how many changes to show. \Please set the `unrecord_changes` option in \your global config or run `pijul unrecord` \with the `--show-changes` option.")})?};let hashes_ = txn.reverse_log(&channel.borrow(), None)?.map(|h| (h.unwrap().1).0.into()).take(number_of_changes).collect::<Vec<_>>();let o = make_changelist(&repo.changes, &hashes_, "unrecord")?;for h in parse_changelist(&edit::edit_bytes(&o[..])?).iter() {hashes.push((*h, *txn.get_internal(&h.into())?.unwrap()))}
if self.change_id.is_empty() {// No change ids were given, present a list for choosing// The number can be set in the global config or passed as a command-line optionlet number_of_changes = if let Some(n) = self.show_changes {n
for c in self.change_id.iter() {let (hash, cid) = txn.hash_from_prefix(c)?;hashes.push((hash, cid))}
let cfg = crate::config::Global::load()?;cfg.unrecord_changes.ok_or_else(|| {anyhow!("Can't determine how many changes to show. \Please set the `unrecord_changes` option in \your global config or run `pijul unrecord` \with the `--show-changes` option.")})?
let channel_ = channel.borrow();let mut changes: Vec<(Hash, ChangeId, Option<u64>)> = Vec::new();for (hash, change_id) in hashes {let n = txn.get_changeset(txn.changes(&channel_), &change_id).unwrap();changes.push((hash, change_id, n.map(|&x| x.into())));
let hashes_ = txn.reverse_log(&*channel.read()?, None)?.map(|h| (h.unwrap().1).0.into()).take(number_of_changes).collect::<Vec<_>>();let o = make_changelist(&repo.changes, &hashes_, "unrecord")?;for h in parse_changelist(&edit::edit_bytes(&o[..])?).iter() {hashes.push((*h, *txn.get_internal(&h.into())?.unwrap()))}} else {for c in self.change_id.iter() {let (hash, cid) = txn.hash_from_prefix(c)?;hashes.push((hash, cid))
std::mem::drop(channel_);changes.sort_by(|a, b| b.2.cmp(&a.2));for (hash, change_id, _) in changes {let channel_ = channel.borrow();for p in txn.iter_revdep(&change_id)? {let (p, d) = p?;if p < &change_id {continue;} else if p > &change_id {break;
};let channel_ = channel.read()?;let mut changes: Vec<(Hash, ChangeId, Option<u64>)> = Vec::new();for (hash, change_id) in hashes {let n = txn.get_changeset(txn.changes(&channel_), &change_id).unwrap();changes.push((hash, change_id, n.map(|&x| x.into())));}std::mem::drop(channel_);changes.sort_by(|a, b| b.2.cmp(&a.2));for (hash, change_id, _) in changes {let channel_ = channel.read()?;for p in txn.iter_revdep(&change_id)? {let (p, d) = p?;if p < &change_id {continue;} else if p > &change_id {break;}if txn.get_changeset(txn.changes(&channel_), d)?.is_some() {let dep: Hash = txn.get_external(d)?.unwrap().into();if Some(dep) == pending_hash {bail!("Cannot unrecord change {} because unrecorded changes depend on it",hash.to_base32());} else {bail!("Cannot unrecord change {} because {} depend on it",hash.to_base32(),dep.to_base32());
if txn.get_changeset(txn.changes(&channel_), d)?.is_some() {let dep: Hash = txn.get_external(d)?.unwrap().into();if Some(dep) == pending_hash {bail!("Cannot unrecord change {} because unrecorded changes depend on it",hash.to_base32());} else {bail!("Cannot unrecord change {} because {} depend on it",hash.to_base32(),dep.to_base32());}}
if self.reset && is_current_channel {txn.output_repository_no_pending(&mut repo.working_copy,&repo.changes,&mut channel,"",true,None,)?;
std::mem::drop(channel_);txn.unrecord(&repo.changes, &channel, &hash)?;}let txn = Arc::new(RwLock::new(txn));if self.reset && is_current_channel {libpijul::output::output_repository_no_pending(repo.working_copy.clone(),&repo.changes,txn.clone(),channel.clone(),"",true,None,num_cpus::get(),)?;}if let Some(h) = pending_hash {txn.write().unwrap().unrecord(&repo.changes, &channel, &h)?;if cfg!(feature = "keep-changes") {repo.changes.del_change(&h)?;
if let Some(h) = pending_hash {txn.unrecord(&repo.changes, &mut channel, &h)?;if cfg!(feature = "keep-changes") {repo.changes.del_change(&h)?;}}
use libpijul::{ChannelTxnT, DepsTxnT, MutTxnT, MutTxnTExt, TxnT, TxnTExt};use log::debug;
use libpijul::{ChannelTxnT, DepsTxnT, MutTxnT, TxnT, TxnTExt};use log::*;
let mut repo = Repository::find_root(self.repo_path).await?;let mut txn = repo.pristine.mut_txn_begin()?;let (channel_name, _) = repo.config.get_current_channel(self.channel.as_deref());
let repo = Repository::find_root(self.repo_path).await?;let txn = repo.pristine.mut_txn_begin()?;let config_path = repo.config_path();let mut config = repo.config;let (channel_name, _) = config.get_current_channel(self.channel.as_deref());
} else {let (current_channel, _) = repo.config.get_current_channel(None);if self.channel.as_deref() == Some(current_channel) {if !overwrite_changes {return Ok(());}} else if self.channel.is_some() {if !self.files.is_empty() {bail!("Cannot use --channel with individual paths. Did you mean --dry-run?")
return Ok(());}let txn = Arc::new(RwLock::new(txn));let (current_channel, _) = config.get_current_channel(None);if self.channel.as_deref() == Some(current_channel) {if !overwrite_changes {return Ok(());}} else if self.channel.is_some() {if !self.files.is_empty() {bail!("Cannot use --channel with individual paths. Did you mean --dry-run?")}let channel = {let txn = txn.read().unwrap();txn.load_channel(current_channel)?};if let Some(channel) = channel {let mut state = libpijul::RecordBuilder::new();state.record(txn.clone(),libpijul::Algorithm::default(),channel.clone(),repo.working_copy.clone(),&repo.changes,"",num_cpus::get(),)?;let rec = state.finish();debug!("actions = {:?}", rec.actions);if !rec.actions.is_empty() {bail!("Cannot change channel, as there are unrecorded changes.")
if let Some(mut channel) = txn.load_channel(current_channel)? {let mut state = libpijul::RecordBuilder::new();txn.record(&mut state,libpijul::Algorithm::default(),&mut channel,&mut repo.working_copy,&repo.changes,"",)?;let rec = state.finish();debug!("actions = {:?}", rec.actions);if !rec.actions.is_empty() {bail!("Cannot change channel, as there are unrecorded changes.")}}
let now = std::time::Instant::now();if self.files.is_empty() {if self.channel.is_none() || self.channel.as_deref() == Some(current_channel) {let last_modified = last_modified(&txn, &channel.borrow());txn.output_repository_no_pending(&mut repo.working_copy,&repo.changes,&mut channel,"",true,Some(last_modified),)?;txn.touch_channel(&mut channel.borrow_mut(), None);txn.commit()?;return Ok(());}let mut inodes = HashSet::new();if let Some(cur) = txn.load_channel(current_channel)? {let mut changediff = HashSet::new();let (a, b, s) = libpijul::pristine::last_common_state(&txn,&cur.borrow(),&channel.borrow(),)?;debug!("last common state {:?}", s);changes_after(&txn, &cur.borrow(), a, &mut changediff, &mut inodes)?;changes_after(&txn, &channel.borrow(), b, &mut changediff, &mut inodes)?;}
let now = std::time::Instant::now();if self.files.is_empty() {if self.channel.is_none() || self.channel.as_deref() == Some(current_channel) {let last_modified = last_modified(&*txn.read().unwrap(), &*channel.read()?);libpijul::output::output_repository_no_pending(repo.working_copy.clone(),&repo.changes,txn.clone(),channel.clone(),"",true,Some(last_modified),num_cpus::get(),)?;let mut txn = if let Ok(txn) = Arc::try_unwrap(txn) {txn.into_inner().unwrap()} else {unreachable!()};txn.touch_channel(&mut *channel.write()?, None);txn.commit()?;return Ok(());}let mut inodes = HashSet::new();let txn_ = txn.read().unwrap();if let Some(cur) = txn_.load_channel(current_channel)? {let mut changediff = HashSet::new();let (a, b, s) = libpijul::pristine::last_common_state(&*txn_,&*cur.read()?,&*channel.read()?,)?;debug!("last common state {:?}", s);changes_after(&*txn_, &*cur.read()?, a, &mut changediff, &mut inodes)?;changes_after(&*txn_, &*channel.read()?, b, &mut changediff, &mut inodes)?;}
if self.channel.is_some() {repo.config.current_channel = self.channel;repo.save_config()?;
if self.channel.is_some() {config.current_channel = self.channel;config.save(&config_path)?;}let mut paths = Vec::with_capacity(inodes.len());for pos in inodes.iter() {if let Some((path, _)) =libpijul::fs::find_path(&repo.changes, &*txn_, &*channel.read()?, false, *pos)?{paths.push(path)} else {paths.clear();break;
let mut paths = Vec::with_capacity(inodes.len());for pos in inodes.iter() {if let Some((path, _)) = libpijul::fs::find_path(&repo.changes,&txn,&channel.borrow(),false,*pos,)? {paths.push(path)} else {paths.clear();break;}}PROGRESS.borrow_mut().unwrap().push(crate::progress::Cursor::Spin {i: 0,pre: "Outputting repository".into(),});for path in paths.iter() {debug!("resetting {:?}", path);txn.output_repository_no_pending(&mut repo.working_copy,&repo.changes,&mut channel,&path,true,None,)?;}if paths.is_empty() {txn.output_repository_no_pending(&mut repo.working_copy,&repo.changes,&mut channel,"",true,None,)?;}PROGRESS.join();} else {PROGRESS.borrow_mut().unwrap().push(crate::progress::Cursor::Spin {i: 0,pre: "Outputting repository".into(),});for root in self.files.iter() {let root = std::fs::canonicalize(&root)?;let path = root.strip_prefix(&repo_path)?.to_str().unwrap();txn.output_repository_no_pending(&mut repo.working_copy,&repo.changes,&mut channel,&path,true,None,)?;}PROGRESS.join();
}PROGRESS.borrow_mut().unwrap().push(crate::progress::Cursor::Spin {i: 0,pre: "Outputting repository".into(),});std::mem::drop(txn_);for path in paths.iter() {debug!("resetting {:?}", path);libpijul::output::output_repository_no_pending(repo.working_copy.clone(),&repo.changes,txn.clone(),channel.clone(),&path,true,None,num_cpus::get(),)?;}if paths.is_empty() {libpijul::output::output_repository_no_pending(repo.working_copy,&repo.changes,txn.clone(),channel,"",true,None,num_cpus::get(),)?;}PROGRESS.join();} else {PROGRESS.borrow_mut().unwrap().push(crate::progress::Cursor::Spin {i: 0,pre: "Outputting repository".into(),});for root in self.files.iter() {let root = std::fs::canonicalize(&root)?;let path = root.strip_prefix(&repo_path)?.to_str().unwrap();libpijul::output::output_repository_no_pending(repo.working_copy.clone(),&repo.changes,txn.clone(),channel.clone(),&path,true,None,num_cpus::get(),)?;
use libpijul::pristine::ChannelMutTxnT;use libpijul::{Base32, ChannelRef, ChannelTxnT, MutTxnT, MutTxnTExt, TxnT, TxnTExt};
use libpijul::{Base32, ChannelMutTxnT, ChannelRef, ChannelTxnT, MutTxnT, MutTxnTExt, TxnT, TxnTExt,};use libpijul::{HashMap, HashSet};
mut txn: T,channel: &mut ChannelRef<T>,working_copy: &mut libpijul::working_copy::FileSystem,
txn: Arc<RwLock<T>>,channel: ChannelRef<T>,working_copy: Arc<libpijul::working_copy::FileSystem>,
let hash = super::pending(&mut txn, &mut channel, &mut repo)?;
let txn = Arc::new(RwLock::new(txn));let hash = super::pending(txn.clone(), &mut channel, &mut repo)?;let mut txn = if let Ok(txn) = Arc::try_unwrap(txn) {txn.into_inner().unwrap()} else {unreachable!()};
txn.output_repository_no_pending(&mut repo.working_copy,&repo.changes,&mut channel,path,true,None,)?;
conflicts.extend(libpijul::output::output_repository_no_pending(repo.working_copy.clone(),&repo.changes,txn.clone(),channel.clone(),path,true,None,num_cpus::get(),)?.into_iter(),);
txn.output_repository_no_pending(&mut repo.working_copy,&repo.changes,&mut channel,"",true,None,)?;
conflicts.extend(libpijul::output::output_repository_no_pending(repo.working_copy.clone(),&repo.changes,txn.clone(),channel.clone(),"",true,None,num_cpus::get(),)?.into_iter(),);
fn pending<T: libpijul::MutTxnTExt + libpijul::TxnT>(txn: &mut T,channel: &mut libpijul::ChannelRef<T>,
fn pending<T: libpijul::MutTxnTExt + libpijul::TxnT + Send + Sync + 'static>(txn: std::sync::Arc<std::sync::RwLock<T>>,channel: &libpijul::ChannelRef<T>,
if let Some(root) = self.root {let (pos, _) = txn.follow_oldest_path(&repo.changes, &channel, &root).unwrap();libpijul::pristine::debug_root(&txn,&channel.borrow().graph,pos.inode_vertex(),std::io::stdout(),true,)?;} else {let channel = channel.borrow();libpijul::pristine::debug(&txn, &channel.graph, std::io::stdout())?;
if !self.sanakirja_only {libpijul::pristine::debug_inodes(&txn);libpijul::pristine::debug_revinodes(&txn);libpijul::pristine::debug_tree_print(&txn);libpijul::pristine::debug_revtree_print(&txn);if let Some(root) = self.root {let (pos, _) = txn.follow_oldest_path(&repo.changes, &channel, &root).unwrap();libpijul::pristine::debug_root(&txn,&channel.read()?.graph,pos.inode_vertex(),std::io::stdout(),true,)?;} else {let channel = channel.read()?;libpijul::pristine::debug(&txn, &channel.graph, std::io::stdout())?;}libpijul::pristine::check_alive_debug(&repo.changes, &txn, &*channel.read()?, 0)?;
txn.output_repository_no_pending(&mut repo.working_copy,
let config_path = repo.config_path();let mut config = repo.config;let txn = Arc::new(RwLock::new(txn));libpijul::output::output_repository_no_pending(repo.working_copy.clone(),
let channel = channel?;let channel = channel.borrow();let name = txn.name(&channel);
let (_, channel) = channel?;let channel = channel.read()?;let name = txn.name(&*channel);
fn remove_path(&mut self, name: &str) -> Result<(), Self::Error>;fn rename(&mut self, former: &str, new: &str) -> Result<(), Self::Error>;fn set_permissions(&mut self, name: &str, permissions: u16) -> Result<(), Self::Error>;fn write_file<A, E: std::error::Error, F: FnOnce(&mut dyn std::io::Write) -> Result<A, E>>(&mut self,file: &str,writer: F,) -> Result<A, WriteError<E>>;
fn remove_path(&self, name: &str) -> Result<(), Self::Error>;fn rename(&self, former: &str, new: &str) -> Result<(), Self::Error>;fn set_permissions(&self, name: &str, permissions: u16) -> Result<(), Self::Error>;type Writer: std::io::Write;fn write_file(&self, file: &str) -> Result<Self::Writer, Self::Error>;
fn create_dir_all(&mut self, file: &str) -> Result<(), Self::Error> {if self.get_file(file).is_none() {
fn create_dir_all(&self, file: &str) -> Result<(), Self::Error> {let m = self.0.lock().unwrap();if m.get_file(file).is_none() {
fn remove_path(&mut self, path: &str) -> Result<(), Self::Error> {self.remove_path_(path);
fn remove_path(&self, path: &str) -> Result<(), Self::Error> {self.0.lock().unwrap().remove_path_(path);
fn write_file<A, E: std::error::Error, F: FnOnce(&mut dyn std::io::Write) -> Result<A, E>>(&mut self,file: &str,writer: F,) -> Result<A, WriteError<E>> {match self.get_file_mut(file) {
type Writer = Writer;fn write_file(&self, file: &str) -> Result<Self::Writer, Self::Error> {let mut m = self.0.lock().unwrap();match m.get_file_mut(file) {
pub struct Writer {w: Arc<Mutex<Vec<u8>>>,}impl std::io::Write for Writer {fn write(&mut self, b: &[u8]) -> Result<usize, std::io::Error> {self.w.lock().unwrap().write(b)}fn flush(&mut self) -> Result<(), std::io::Error> {Ok(())}}
fn write_file<A, E: std::error::Error, F: FnOnce(&mut dyn std::io::Write) -> Result<A, E>>(&mut self,file: &str,writer: F,) -> Result<A, WriteError<E>> {
type Writer = std::io::BufWriter<std::fs::File>;fn write_file(&self, file: &str) -> Result<Self::Writer, Self::Error> {
let br = br?;let br = br.borrow();if txn.name(&br) != txn.name(&channel)&& txn.get_changeset(txn.changes(&br), &change_id)?.is_some(){
let (name, br) = br?;if name.as_str() == txn.name(&channel) {continue;}let br = br.read().unwrap();if txn.get_changeset(txn.changes(&br), &change_id)?.is_some() {
let mut txn = env.mut_txn_begin();let mut txn2 = env2.mut_txn_begin();let mut txn3 = env3.mut_txn_begin();
let mut txn = env.mut_txn_begin().unwrap();let mut txn2 = env2.mut_txn_begin().unwrap();let mut txn3 = env3.mut_txn_begin().unwrap();
fn record_all_output<T: MutTxnT, R: WorkingCopy, P: ChangeStore + Clone + Send + 'static>(repo: &mut R,changes: &P,txn: &mut T,channel: &mut ChannelRef<T>,
fn record_all_output<T: MutTxnT + Send + Sync + 'static,R: WorkingCopy + Send + Sync + 'static,P: ChangeStore + Clone + Send + Sync + 'static,>(repo: Arc<R>,changes: Arc<P>,txn: Arc<RwLock<T>>,channel: &ChannelRef<T>,
let hash = record_all(repo, changes, txn, channel, prefix)?;output::output_repository_no_pending(repo, changes, txn, channel, "", true, None).unwrap();
let hash = record_all(repo.as_ref(),changes.as_ref(),&mut *txn.write().unwrap(),channel,prefix,)?;output::output_repository_no_pending(repo, changes, txn, channel.clone(), "", true, None, 1).unwrap();
let mut txn = env.mut_txn_begin();txn.add_file("dir/file").unwrap();let mut channel = txn.open_or_create_channel("main").unwrap();
let mut txn = Arc::new(RwLock::new(env.mut_txn_begin().unwrap()));txn.write().unwrap().add_file("dir/file").unwrap();let mut channel = txn.write().unwrap().open_or_create_channel("main").unwrap();
record_all_output(&mut repo, &changes, &mut txn, &mut channel, "").unwrap();debug_to_file(&txn, &channel.borrow(), "debug0").unwrap();let files: Vec<_> = crate::fs::iter_working_copy(&txn, Inode::ROOT)
record_all_output(repo.clone(),changes.clone(),txn.clone(),&channel.clone(),"",).unwrap();debug_to_file(&*txn.read().unwrap(), &channel, "debug0").unwrap();let files: Vec<_> = crate::fs::iter_working_copy(&*txn.read().unwrap(), Inode::ROOT)
record_all_output(&mut repo, &changes, &mut txn, &mut channel, "").unwrap();debug_to_file(&txn, &channel.borrow(), "debug").unwrap();
record_all_output(repo, changes, txn, &channel, "").unwrap();debug_to_file(&*txn.read().unwrap(), &channel, "debug").unwrap();
txn.add_file("dir2/file").unwrap();txn.remove_file("dir2").unwrap();assert!(crate::fs::iter_working_copy(&txn, Inode::ROOT).all(|f| f.unwrap().1 != "dir2"));assert!(txn.remove_file("dir2").is_err());txn.commit().unwrap();
txn.write().unwrap().add_file("dir2/file").unwrap();txn.write().unwrap().remove_file("dir2").unwrap();assert!(crate::fs::iter_working_copy(&*txn.read().unwrap(), Inode::ROOT).all(|f| f.unwrap().1 != "dir2"));assert!(txn.write().unwrap().remove_file("dir2").is_err());txn.write().unwrap().commit().unwrap();
let mut txn = env.mut_txn_begin();txn.add_file("a/b/c/d/e")?;let mut channel = txn.open_or_create_channel("main")?;
let mut txn = Arc::new(RwLock::new(env.mut_txn_begin().unwrap()));txn.write().unwrap().add_file("a/b/c/d/e")?;let mut channel = txn.write().unwrap().open_or_create_channel("main")?;
record_all_output(&mut repo, &changes, &mut txn, &mut channel, "")?;debug_to_file(&txn, &channel.borrow(), "debug0").unwrap();let files: Vec<_> = crate::fs::iter_working_copy(&txn, Inode::ROOT)
record_all_output(repo.clone(), changes.clone(), txn.clone(), &channel, "")?;debug_to_file(&*txn.read().unwrap(), &channel, "debug0").unwrap();let files: Vec<_> = crate::fs::iter_working_copy(&*txn.read().unwrap(), Inode::ROOT)
record_all_output(&mut repo, &changes, &mut txn, &mut channel, "")?;debug_to_file(&txn, &channel.borrow(), "debug").unwrap();
record_all_output(repo.clone(), changes.clone(), txn.clone(), &channel, "")?;debug_to_file(&*txn.read().unwrap(), &channel, "debug").unwrap();
let mut txn = env.mut_txn_begin();txn.add_file("dir/file")?;let mut channel = txn.open_or_create_channel("main")?;
let mut txn = Arc::new(RwLock::new(env.mut_txn_begin().unwrap()));txn.write().unwrap().add_file("dir/file")?;let mut channel = txn.write().unwrap().open_or_create_channel("main")?;
record_all_output(&mut repo, &changes, &mut txn, &mut channel, "").unwrap();debug_to_file(&txn, &channel.borrow(), "debug").unwrap();
record_all_output(repo.clone(), changes.clone(), txn.clone(), &channel, "").unwrap();debug_to_file(&*txn.read().unwrap(), &channel, "debug").unwrap();
repo.write_file::<_, std::io::Error, _>("dir/file", |w| {w.write_all(b"a\nb\nc\n")?;Ok(())})?;record_all_output(&mut repo, &changes, &mut txn, &mut channel, "").unwrap();
repo.write_file("dir/file").unwrap().write_all(b"a\nb\nc\n").unwrap();record_all_output(repo.clone(), changes.clone(), txn.clone(), &channel, "").unwrap();
let mut txn = env.mut_txn_begin();txn.add_file("dir/file")?;let mut channel = txn.open_or_create_channel("main")?;
let mut txn = env.mut_txn_begin().unwrap();txn.write().unwrap().add_file("dir/file")?;let mut channel = txn.write().unwrap().open_or_create_channel("main")?;
let mut txn = env.mut_txn_begin();txn.add_file("dir/file")?;let mut channel = txn.open_or_create_channel("main")?;
let mut txn = env.mut_txn_begin().unwrap();txn.write().unwrap().add_file("dir/file")?;let mut channel = txn.write().unwrap().open_or_create_channel("main")?;
pub(crate) rec: Recorded,pub(crate) redundant: Vec<(Vertex<ChangeId>, SerializedEdge)>,recorded_inodes: HashMap<Inode, Position<Option<ChangeId>>>,deleted_vertices: HashSet<Position<ChangeId>>,former_parents: Vec<Parent>,
pub(crate) rec: Vec<Arc<Mutex<Recorded>>>,recorded_inodes: Arc<Mutex<HashMap<Inode, Position<Option<ChangeId>>>>>,deleted_vertices: Arc<Mutex<HashSet<Position<ChangeId>>>>,
rec: Recorded {contents: Vec::new(),actions: Vec::new(),updatables: HashMap::new(),largest_file: 0,has_binary_files: false,oldest_change: std::time::SystemTime::UNIX_EPOCH,},redundant: Vec::new(),recorded_inodes: HashMap::new(),deleted_vertices: HashSet::new(),former_parents: Vec::new(),
rec: Vec::new(),recorded_inodes: Arc::new(Mutex::new(HashMap::default())),
pub fn recorded(&mut self) -> Arc<Mutex<Recorded>> {let m = Arc::new(Mutex::new(self.recorded_()));self.rec.push(m.clone());m}fn recorded_(&self) -> Recorded {Recorded {contents: self.contents.clone(),actions: Vec::new(),updatables: HashMap::default(),largest_file: 0,has_binary_files: false,oldest_change: std::time::SystemTime::UNIX_EPOCH,redundant: Vec::new(),force_rediff: self.force_rediff,deleted_vertices: self.deleted_vertices.clone(),recorded_inodes: self.recorded_inodes.clone(),}}
pub fn finish(self) -> Recorded {self.rec
pub fn finish(mut self) -> Recorded {if self.rec.is_empty() {self.recorded();}let mut it = self.rec.into_iter();let mut result = if let Ok(rec) = Arc::try_unwrap(it.next().unwrap()) {rec.into_inner().unwrap()} else {unreachable!()};for rec in it {let rec = if let Ok(rec) = Arc::try_unwrap(rec) {rec.into_inner().unwrap()} else {unreachable!()};let off = result.actions.len();result.actions.extend(rec.actions.into_iter());for (a, b) in rec.updatables {result.updatables.insert(a + off, b);}result.largest_file = result.largest_file.max(rec.largest_file);result.has_binary_files |= rec.has_binary_files;result.oldest_change = result.oldest_change.min(rec.oldest_change);result.redundant.extend(rec.redundant.into_iter())}debug!("result = {:?}, updatables = {:?}",result.actions, result.updatables);result
fn get_inodes_<T: ChannelTxnT + TreeTxnT<TreeError = <T as GraphTxnT>::GraphError>>(txn: Arc<RwLock<T>>,channel: ChannelRef<T>,inode: &Inode,) -> Result<Option<Position<ChangeId>>, TxnErr<T::GraphError>> {let txn = txn.read().unwrap();let channel = channel.r.read().unwrap();Ok(get_inodes(&*txn, &*channel, inode)?.map(|x| *x))}
if txn.get_changeset(txn.changes(channel), &vertex.change)?.is_some()
if let Some(e) = iter_adjacent(txn,txn.graph(channel),vertex.inode_vertex(),EdgeFlags::PARENT,EdgeFlags::all(),)?.next()
let work = Arc::new(Mutex::new(Tasks {t: VecDeque::new(),stop: false,}));let mut workers: Vec<std::thread::JoinHandle<()>> = Vec::new();for t in 0..0 {// n_workers - 1 {let working_copy = working_copy.clone();let changes = changes.clone();let channel = channel.clone();let work = work.clone();let txn = txn.clone();workers.push(std::thread::spawn(move || loop {let (w, stop) = {let mut work = work.lock().unwrap();(work.t.pop_front(), work.stop)};if let Some((item, vertex, rec, new_papa)) = w {// This parent has changed.info!("record existing file {:?} on thread {:?}", item, t);rec.lock().unwrap().record_existing_file(txn.clone(),diff_algorithm,channel.clone(),working_copy.clone(),&changes,&item,new_papa,vertex,).unwrap();} else if stop {info!("stop {:?}", t);break;} else {info!("yield {:?}", t);std::thread::park_timeout(std::time::Duration::from_secs(1));}}))}
let vertex = if let Some(vertex) = self.recorded_inodes.get(&item.inode) {*vertex
let vertex: Option<Position<Option<ChangeId>>> = self.recorded_inodes.lock().unwrap().get(&item.inode).cloned();let vertex = if let Some(vertex) = vertex {vertex
txn,txn.graph(channel),working_copy,
&*txn,txn.graph(&channel),working_copy.as_ref(),
} else if let Some(&vertex) = get_inodes(txn, &channel, &item.inode)? {self.delete_obsolete_children(txn,txn.graph(channel),working_copy,changes,&item.full_path,vertex,)?;self.record_existing_file(txn,diff_algorithm,&channel,working_copy,changes,&item,vertex,)?;self.recorded_inodes.insert(item.inode, vertex.to_option());
} else if let Some(vertex) = get_inodes_(txn.clone(), channel.clone(), &item.inode)? {{let mut txn = txn.write().unwrap();let mut channel = channel.r.write().unwrap();let mut graph = txn.graph(&mut *channel);self.delete_obsolete_children(&mut *txn,&mut graph,working_copy.as_ref(),changes,&item.full_path,vertex,)?;}let rec = self.recorded();let new_papa = {let mut recorded = self.recorded_inodes.lock().unwrap();recorded.insert(item.inode, vertex.to_option());recorded.get(&item.papa).cloned()};let mut work = work.lock().unwrap();work.t.push_back((item.clone(), vertex, rec, new_papa));std::mem::drop(work);for t in workers.iter() {t.thread().unpark()}
}info!("stop work");work.lock().unwrap().stop = true;for t in workers.iter() {t.thread().unpark()}loop {let w = {let mut work = work.lock().unwrap();debug!("waiting, stop = {:?}", work.stop);work.t.pop_front()};if let Some((item, vertex, rec, new_papa)) = w {// This parent has changed.info!("record existing file {:?}", item);rec.lock().unwrap().record_existing_file(txn.clone(),diff_algorithm,channel.clone(),working_copy.clone(),changes,&item,new_papa,vertex,).unwrap();} else {break;}}for (n, t) in workers.into_iter().enumerate() {debug!("WAITING {:?}", n);t.join().unwrap()
fn delete_obsolete_children<T: GraphTxnT + TreeTxnT<TreeError = <T as GraphTxnT>::GraphError>,W: WorkingCopy,C: ChangeStore,>(&mut self,txn: &T,channel: &T::Graph,working_copy: &W,changes: &C,full_path: &str,v: Position<ChangeId>,) -> Result<(), RecordError<C::Error, W::Error, T::GraphError>>where<W as WorkingCopy>::Error: 'static,{if self.ignore_missing {return Ok(());}let f0 = EdgeFlags::FOLDER | EdgeFlags::BLOCK;let f1 = f0 | EdgeFlags::PSEUDO;debug!("delete_obsolete_children, v = {:?}", v);for child in iter_adjacent(txn, channel, v.inode_vertex(), f0, f1)? {let child = child?;let child = txn.find_block(channel, child.dest()).unwrap();for grandchild in iter_adjacent(txn, channel, *child, f0, f1)? {let grandchild = grandchild?;debug!("grandchild {:?}", grandchild);let needs_deletion =if let Some(inode) = txn.get_revinodes(&grandchild.dest(), None)? {debug!("inode = {:?} {:?}", inode, txn.get_revtree(inode, None));if let Some(path) = crate::fs::inode_filename(txn, *inode)? {working_copy.file_metadata(&path).is_err()} else {true}} else {true};if needs_deletion {let mut name = Vec::new();changes.get_contents(|p| txn.get_external(&p).unwrap().map(From::from),*child,&mut name,).map_err(RecordError::Changestore)?;let mut full_path = full_path.to_string();if name.len() > 2 {if let Ok(name) = std::str::from_utf8(&name[2..]) {if !full_path.is_empty() {full_path.push('/');}full_path.push_str(name);}}// delete recursively.let rec = self.recorded();let mut rec = rec.lock().unwrap();rec.record_deleted_file(txn,&channel,working_copy,&full_path,grandchild.dest(),)?}}}Ok(())}fn push_children<'a,T: ChannelTxnT + TreeTxnT<TreeError = <T as GraphTxnT>::GraphError>,W: WorkingCopy,C: ChangeStore,>(&mut self,txn: &T,channel: &T::Channel,working_copy: &W,item: &mut RecordItem,components: &mut Components<'a>,vertex: Position<Option<ChangeId>>,stack: &mut Vec<(RecordItem, Components<'a>)>,prefix: &str,) -> Result<(), RecordError<C::Error, W::Error, T::GraphError>>where<W as crate::working_copy::WorkingCopy>::Error: 'static,{debug!("push_children, item = {:?}", item);let comp = components.next();let full_path = item.full_path.clone();let fileid = OwnedPathId {parent_inode: item.inode,basename: SmallString::new(),};let mut has_matching_children = false;for x in txn.iter_tree(&fileid, None)? {let (fileid_, child_inode) = x?;debug!("push_children {:?} {:?}", fileid_, child_inode);if fileid_.parent_inode < fileid.parent_inode || fileid_.basename.is_empty() {continue;} else if fileid_.parent_inode > fileid.parent_inode {break;}if let Some(comp) = comp {if comp != fileid_.basename.as_str() {continue;}}has_matching_children = true;let basename = fileid_.basename.as_str().to_string();let full_path = if full_path.is_empty() {basename.clone()} else {full_path.clone() + "/" + &basename};debug!("fileid_ {:?} child_inode {:?}", fileid_, child_inode);if let Ok(meta) = working_copy.file_metadata(&full_path) {stack.push((RecordItem {papa: item.inode,inode: *child_inode,v_papa: vertex,basename,full_path,metadata: meta,},components.clone(),));} else if let Some(vertex) = get_inodes(txn, &channel, child_inode)? {let rec = self.recorded();let mut rec = rec.lock().unwrap();rec.record_deleted_file(txn, txn.graph(channel), working_copy, &full_path, *vertex)?}}if comp.is_some() && !has_matching_children {debug!("comp = {:?}", comp);return Err(RecordError::PathNotInRepo(prefix.to_string()));}Ok(())}}fn modified_since_last_commit<T: ChannelTxnT, W: WorkingCopy>(txn: &T,channel: &T::Channel,working_copy: &W,prefix: &str,) -> Result<bool, std::time::SystemTimeError> {if let Ok(last_modified) = working_copy.modified_time(prefix) {debug!("last_modified = {:?}, channel.last = {:?}",last_modified.duration_since(std::time::UNIX_EPOCH)?.as_secs(),txn.last_modified(channel));Ok(last_modified.duration_since(std::time::UNIX_EPOCH)?.as_secs()>= txn.last_modified(channel))} else {Ok(true)}}impl Recorded {
let name_start = ChangePosition(self.rec.contents.len().into());meta.write(&mut self.rec.contents).unwrap();self.rec.contents.extend(item.basename.as_bytes());let name_end = ChangePosition(self.rec.contents.len().into());self.rec.contents.push(0);let inode_pos = ChangePosition(self.rec.contents.len().into());self.rec.contents.push(0);
let mut contents = self.contents.lock().unwrap();let name_start = ChangePosition(contents.len().into());// Push the metadata, big-endian.contents.push((meta.0 >> 8) as u8);contents.push((meta.0 & 0xff) as u8);//contents.extend(item.basename.as_bytes());let name_end = ChangePosition(contents.len().into());contents.push(0);let inode_pos = ChangePosition(contents.len().into());contents.push(0);
let start = ChangePosition(self.rec.contents.len().into());working_copy.read_file(&item.full_path, &mut self.rec.contents)?;let end = ChangePosition(self.rec.contents.len().into());self.rec.largest_file = self.rec.largest_file.max(end.0.as_u64() - start.0.as_u64());self.rec.has_binary_files |= {
let start = ChangePosition(contents.len().into());working_copy.read_file(&item.full_path, &mut contents)?;let end = ChangePosition(contents.len().into());self.largest_file = self.largest_file.max(end.0.as_u64() - start.0.as_u64());self.has_binary_files |= {
for name_ in iter_adjacent(txn, txn.graph(channel), vertex.inode_vertex(), f0, f1)? {
let txn_ = txn.read().unwrap();let channel_ = channel.read().unwrap();for name_ in iter_adjacent(&*txn_,txn_.graph(&*channel_),vertex.inode_vertex(),f0,f1,)? {
if self.former_parents.len() > 1|| self.former_parents[0].basename != item.basename|| self.former_parents[0].metadata != item.metadata|| self.former_parents[0].parent != item.v_papa
if former_parents.len() > 1|| former_parents[0].basename != item.basename|| former_parents[0].metadata != item.metadata|| former_parents[0].parent != item.v_papa
fn delete_obsolete_children<T: GraphTxnT + TreeTxnT<TreeError = <T as GraphTxnT>::GraphError>,W: WorkingCopy,C: ChangeStore,>(&mut self,txn: &T,channel: &T::Graph,working_copy: &W,changes: &C,full_path: &str,v: Position<ChangeId>,) -> Result<(), RecordError<C::Error, W::Error, T::GraphError>>where<W as WorkingCopy>::Error: 'static,{if self.ignore_missing {return Ok(());}let f0 = EdgeFlags::FOLDER | EdgeFlags::BLOCK;let f1 = f0 | EdgeFlags::PSEUDO;debug!("delete_obsolete_children, v = {:?}", v);for child in iter_adjacent(txn, channel, v.inode_vertex(), f0, f1)? {let child = child?;let child = txn.find_block(channel, child.dest()).unwrap();for grandchild in iter_adjacent(txn, channel, *child, f0, f1)? {let grandchild = grandchild?;debug!("grandchild {:?}", grandchild);let needs_deletion =if let Some(inode) = txn.get_revinodes(&grandchild.dest(), None)? {debug!("inode = {:?} {:?}", inode, txn.get_revtree(inode, None));if let Some(path) = crate::fs::inode_filename(txn, *inode)? {working_copy.file_metadata(&path).is_err()} else {true}} else {true};if needs_deletion {let mut name = Vec::new();changes.get_contents(|p| txn.get_external(&p).unwrap().map(From::from),*child,&mut name,).map_err(RecordError::Changestore)?;let mut full_path = full_path.to_string();if name.len() > 2 {if let Ok(name) = std::str::from_utf8(&name[2..]) {if !full_path.is_empty() {full_path.push('/');}full_path.push_str(name);}}// delete recursively.self.record_deleted_file(txn,&channel,working_copy,&full_path,grandchild.dest(),)?}}}Ok(())}fn push_children<'a,T: ChannelTxnT + TreeTxnT<TreeError = <T as GraphTxnT>::GraphError>,W: WorkingCopy,C: ChangeStore,>(&mut self,txn: &T,channel: &T::Channel,working_copy: &W,item: &mut RecordItem<'a>,vertex: Position<Option<ChangeId>>,stack: &mut Vec<RecordItem<'a>>,prefix: &str,) -> Result<(), RecordError<C::Error, W::Error, T::GraphError>>where<W as crate::working_copy::WorkingCopy>::Error: 'static,{debug!("push_children, item = {:?}", item);let comp = item.components.next();let full_path = item.full_path.clone();let fileid = OwnedPathId {parent_inode: item.inode,basename: SmallString::new(),};let mut has_matching_children = false;for x in txn.iter_tree(&fileid, None)? {let (fileid_, child_inode) = x?;debug!("push_children {:?} {:?}", fileid_, child_inode);if fileid_.parent_inode < fileid.parent_inode || fileid_.basename.is_empty() {continue;} else if fileid_.parent_inode > fileid.parent_inode {break;}if let Some(comp) = comp {if comp != fileid_.basename.as_str() {continue;}}has_matching_children = true;let basename = fileid_.basename.as_str().to_string();let full_path = if full_path.is_empty() {basename.clone()} else {full_path.clone() + "/" + &basename};debug!("fileid_ {:?} child_inode {:?}", fileid_, child_inode);if let Ok(meta) = working_copy.file_metadata(&full_path) {stack.push(RecordItem {papa: item.inode,inode: *child_inode,v_papa: vertex,basename,full_path,metadata: meta,components: item.components.clone(),})} else if let Some(vertex) = get_inodes(txn, &channel, child_inode)? {self.record_deleted_file(txn,txn.graph(channel),working_copy,&full_path,*vertex,)?}}if comp.is_some() && !has_matching_children {debug!("comp = {:?}", comp);return Err(RecordError::PathNotInRepo(prefix.to_string()));}Ok(())}fn modified_since_last_commit<T: ChannelTxnT, W: WorkingCopy>(&mut self,txn: &T,channel: &T::Channel,working_copy: &W,prefix: &str,) -> Result<bool, std::time::SystemTimeError> {if let Ok(last_modified) = working_copy.modified_time(prefix) {debug!("last_modified = {:?}, channel.last = {:?}",last_modified.duration_since(std::time::UNIX_EPOCH)?.as_secs(),txn.last_modified(channel));Ok(last_modified.duration_since(std::time::UNIX_EPOCH)?.as_secs()>= txn.last_modified(channel))} else {Ok(true)}}}impl Builder {
let name_start = ChangePosition(self.rec.contents.len().into());item.metadata.write(&mut self.rec.contents).unwrap();self.rec.contents.extend(item.basename.as_bytes());let name_end = ChangePosition(self.rec.contents.len().into());self.rec.contents.push(0);let name = &self.rec.contents[name_start.0.as_usize()..name_end.0.as_usize()];
let mut contents = self.contents.lock().unwrap();let name_start = ChangePosition(contents.len().into());// Push the metadata, big-endian.contents.push((item.metadata.0 >> 8) as u8);contents.push((item.metadata.0 & 0xff) as u8);//contents.extend(item.basename.as_bytes());let name_end = ChangePosition(contents.len().into());contents.push(0);let name = &contents[name_start.0.as_usize()..name_end.0.as_usize()];
open_channels: RefCell::new(HashMap::new()),open_remotes: RefCell::new(HashMap::new()),
open_channels: Mutex::new(HashMap::default()),open_remotes: Mutex::new(HashMap::default()),
pub(crate) open_channels: RefCell<HashMap<SmallString, ChannelRef<Self>>>,open_remotes: RefCell<HashMap<SmallString, RemoteRef<Self>>>,
pub(crate) open_channels: Mutex<HashMap<SmallString, ChannelRef<Self>>>,open_remotes: Mutex<HashMap<SmallString, RemoteRef<Self>>>,counter: usize,
let mut k = if let Some((k, _)) = cursor.set(txn, &key, None).map_err(|x| BlockError::Txn(x.into()))?{k} else if let Some((k, _)) = cursor.prev(txn).map_err(|x| BlockError::Txn(x.into()))? {k} else {return Err(BlockError::Block { block: p });
let mut k = match cursor.set(txn, &key, None) {Ok(Some((k, _))) => k,Ok(None) => {if let Some((k, _)) = cursor.prev(txn).map_err(|x| BlockError::Txn(x.into()))? {k} else {debug!("find_block_end, no prev");return Err(BlockError::Block { block: p });}}Err(e) => {debug!("find_block_end: BLOCK ERROR");return Err(BlockError::Txn(e.into()));}
self.counter += 1;debug!("put_graph {:?} {:?}, counter = {:?}", k, e, self.counter);/*if self.counter >= 12965 {let mut x = None;let mut cursor = btree::cursor::Cursor::new(&self.txn, graph)?;let mut panic = None;while let Some((k, v)) = cursor.next(&self.txn)? {if let Some((ref k_, ref v_)) = x {if k_ > k || (k_== k && v_ > v) {panic = Some((*k, *v));break}}x = Some((*k, *v))}self.debug(graph, ".put");if panic.is_some() {panic!("{:?}", x);}}*/
Ok(btree::del(&mut self.txn, graph, k, e)?)
self.counter += 1;debug!("del_graph {:?} {:?}, counter = {:?}", k, e, self.counter);/*if self.counter >= 12965 {let mut x = None;let mut cursor = btree::cursor::Cursor::new(&self.txn, graph)?;let mut panic = None;while let Some((k, v)) = cursor.next(&self.txn)? {if let Some((ref k_, ref v_)) = x {if k_ > k || (k_== k && v_ > v) {panic = Some((*k, *v));break}}x = Some((*k, *v))}self.debug(graph, ".del");if panic.is_some() {panic!("{:?}", x);}}let change = ChangeId::from_base32(b"MM6XEY5S32WRA").unwrap();let mm6v = Vertex {change,start: ChangePosition(L64(1478218)),end: ChangePosition(L64(1478229)),};let mm6e = (Edge {flag: EdgeFlags::BLOCK | EdgeFlags::FOLDER | EdgeFlags::PARENT,dest: Position {change,pos: ChangePosition(L64(1466149)),},introduced_by: change,}).into();let has_mm6 = if let Some((v, e)) = btree::get(&self.txn, graph, &mm6v, Some(&mm6e)).unwrap() {v == &mm6v && e == &mm6e} else {false};*/let result = Ok(btree::del(&mut self.txn, graph, k, e)?);/*if has_mm6 && (k != &mm6v || e != Some(&mm6e)) {if let Some((v, e)) = btree::get(&self.txn, graph, &mm6v, Some(&mm6e)).unwrap() {assert_eq!(v, &mm6v);assert_eq!(e, &mm6e)} else {panic!("Not found")}}*/result}fn debug(&mut self, graph: &mut Self::Graph, extra: &str) {::sanakirja::debug::debug(&self.txn,&[graph],format!("debug{}{}", self.counter, extra),true,);
pub(crate) r: Rc<RefCell<T::Channel>>,
pub(crate) r: Arc<RwLock<T::Channel>>,}#[derive(Debug, Error)]#[error("Mutex poison error")]pub struct PoisonError {}impl<T: ChannelTxnT> ChannelRef<T> {pub fn read(&self) -> Result<std::sync::RwLockReadGuard<T::Channel>, PoisonError> {self.r.read().map_err(|_| PoisonError {})}pub fn write(&self) -> Result<std::sync::RwLockWriteGuard<T::Channel>, PoisonError> {self.r.write().map_err(|_| PoisonError {})}
impl<T: ChannelTxnT> ChannelRef<T> {pub fn borrow(&self) -> std::cell::Ref<T::Channel> {self.r.borrow()
pub fn lock(&self) -> Result<std::sync::MutexGuard<Remote<T>>, PoisonError> {self.db.lock().map_err(|_| PoisonError {})
let a = txn.del_graph(graph,&k0,Some(&SerializedEdge::new(flag,k1.change,k1.start,introduced_by,)),)?;let b = txn.del_graph(graph,&k1,Some(&SerializedEdge::new(flag | EdgeFlags::PARENT,k0.change,k0.end,introduced_by,)),)?;if (a && !b) || (!a && b) {
let v0 = SerializedEdge::new(flag, k1.change, k1.start, introduced_by);let a = txn.del_graph(graph, &k0, Some(&v0))?;{if let Some(&ee) = txn.get_graph(graph, &k0, Some(&v0))? {if ee == v0 {txn.debug(graph, ".3");panic!("Not deleted: {:?} {:?}", k0, v0);}}}let v1 = SerializedEdge::new(flag | EdgeFlags::PARENT, k0.change, k0.end, introduced_by);let b = txn.del_graph(graph, &k1, Some(&v1))?;{if let Some(&ee) = txn.get_graph(graph, &k1, Some(&v1))? {if ee == v1 {txn.debug(graph, ".3");panic!("Not deleted: {:?} {:?}", k1, v1);}}}if a != b {txn.debug(graph, ".2");
let gaeul = ChangeId::from_base32(b"GAEULYDRSLJSC").unwrap();let has_gaeul1 = {let v = Vertex {change: gaeul,start: ChangePosition(L64(1677892)),end: ChangePosition(L64(1677893)),};let e = SerializedEdge::new(EdgeFlags::PSEUDO | EdgeFlags::PARENT,gaeul,ChangePosition(L64(1677837)),ChangeId::ROOT,);if let Some(&ee) = txn.get_graph(graph, &v, Some(&e))? {ee == e} else {false}};
let has_gaeul1_ = {let v = Vertex {change: gaeul,start: ChangePosition(L64(1677892)),end: ChangePosition(L64(1677893)),};let e = SerializedEdge::new(EdgeFlags::PSEUDO | EdgeFlags::PARENT,gaeul,ChangePosition(L64(1677837)),ChangeId::ROOT,);if let Some(&ee) = txn.get_graph(graph, &v, Some(&e))? {ee == e} else {false}};if has_gaeul1 && !has_gaeul1_ {txn.debug(graph, ".GAEUL");panic!("GAEULT");}
impl SerializedHash {pub fn size(b: &[u8]) -> usize {if b[0] == HashAlgorithm::Blake3 as u8 {1 + BLAKE3_BYTES} else if b[0] == HashAlgorithm::None as u8 {1} else {panic!("Unknown hash algorithm {:?}", b[0])}}pub unsafe fn size_from_ptr(b: *const u8) -> usize {if *b == HashAlgorithm::Blake3 as u8 {1 + BLAKE3_BYTES} else if *b == HashAlgorithm::None as u8 {1} else {panic!("Unknown hash algorithm {:?}", *b)}}}
pub fn output_repository_no_pending<T: MutTxnT, R: WorkingCopy, P: ChangeStore>(repo: &mut R,
pub fn output_repository_no_pending<T: MutTxnT + Send + Sync + 'static,R: WorkingCopy + Send + Sync + 'static,P: ChangeStore + Send + Clone + 'static,>(repo: Arc<R>,
fn output_loop<T: TreeMutTxnT + ChannelMutTxnT + GraphMutTxnT<GraphError = <T as TreeTxnT>::TreeError>,R: WorkingCopy + 'static,P: ChangeStore + Clone + Send,>(repo: Arc<R>,changes: &P,txn: Arc<RwLock<T>>,channel: ChannelRef<T>,work: Arc<crossbeam_deque::Injector<(OutputItem, String)>>,stop: Arc<std::sync::atomic::AtomicBool>,t: usize,) -> Result<(), OutputError<P::Error, T::GraphError, R::Error>> {use crossbeam_deque::*;// let backoff = crossbeam_utils::Backoff::new();// let w: Worker<(OutputItem, String)> = Worker::new_fifo();loop {match work.steal() {Steal::Success((item, path)) => {let mut conflicts = Vec::new();info!("Outputting {:?}, on thread {}", path, t);output_item::<_, _, R>(txn.clone(),channel.clone(),changes,&item,&mut conflicts,&repo,&path,)?;debug!("setting permissions for {:?}", path);repo.set_permissions(&path, item.meta.permissions()).map_err(OutputError::WorkingCopy)?;debug!("output {:?}", path);}Steal::Retry => {}Steal::Empty => {if stop.load(std::sync::atomic::Ordering::Relaxed) {break;}}}}Ok(())}
T: TreeMutTxnT + ChannelMutTxnT + GraphMutTxnT<GraphError = <T as TreeTxnT>::TreeError>,R: WorkingCopy,P: ChangeStore,
T: TreeMutTxnT+ ChannelMutTxnT+ GraphMutTxnT<GraphError = <T as TreeTxnT>::TreeError>+ Send+ Sync+ 'static,R: WorkingCopy + Send + Sync + 'static,P: ChangeStore + Send + Clone + 'static,
) -> Result<Vec<Conflict>, OutputError<P::Error, T::TreeError, R::Error>> {
n_workers: usize,) -> Result<Vec<Conflict>, OutputError<P::Error, T::TreeError, R::Error>>whereT::Channel: Send + Sync + 'static,{let work = Arc::new(crossbeam_deque::Injector::new());let stop = Arc::new(std::sync::atomic::AtomicBool::new(false));let mut threads = Vec::new();for t in 0..n_workers - 1 {let repo = repo.clone();let work = work.clone();let stop = stop.clone();let txn = txn.clone();let channel = channel.clone();let changes = changes.clone();threads.push(std::thread::spawn(move || {output_loop(repo, &changes, txn, channel, work, stop, t + 1)}))}
let dead = collect_dead_files(txn, txn.graph(channel), pending_change_id, Inode::ROOT)?;
let dead = {let txn_ = txn.read().unwrap();let channel = channel.read().unwrap();let graph = txn_.graph(&*channel);collect_dead_files(&*txn_, graph, pending_change_id, Inode::ROOT)?};
collect_children(txn,changes,txn.graph(channel),Position::ROOT,Inode::ROOT,"",next_prefix_basename,&mut files,)?;
{let txn = txn.read().unwrap();let channel = channel.read().unwrap();collect_children(&*txn,&*changes,txn.graph(&*channel),Position::ROOT,Inode::ROOT,"",next_prefix_basename,&mut files,)?;}
b.sort_unstable_by(|u, v| {txn.get_changeset(txn.changes(&channel), &u.0.change).unwrap().cmp(&txn.get_changeset(txn.changes(&channel), &v.0.change).unwrap(),)});
{let txn = txn.read().unwrap();let channel = channel.read().unwrap();b.sort_unstable_by(|u, v| {txn.get_changeset(txn.changes(&channel), &u.0.change).unwrap().cmp(&txn.get_changeset(txn.changes(&channel), &v.0.change).unwrap(),)});}
let dead =collect_dead_files(txn, txn.graph(channel), pending_change_id, inode)?;
let dead = {let txn_ = txn.read().unwrap();let channel = channel.read().unwrap();collect_dead_files(&*txn_, txn_.graph(&*channel), pending_change_id, inode)?};
collect_children(txn,changes,txn.graph(channel),output_item.pos,inode,&path,next_prefix_basename,&mut next_files,)?;
{let txn = txn.read().unwrap();let channel = channel.read().unwrap();collect_children(&*txn,&*changes,txn.graph(&*channel),output_item.pos,inode,&path,next_prefix_basename,&mut next_files,)?;}debug!("setting permissions for {:?}", path);repo.set_permissions(&path, output_item.meta.permissions()).map_err(OutputError::WorkingCopy)?;
if needs_output(repo, if_modified_after, &path) {repo.write_file(&path, |w: &mut dyn std::io::Write| {output_file::<_, _, R>(txn,channel,changes,&output_item,&mut conflicts,w,)}).map_err(OutputError::from)?
if needs_output(repo.as_ref(), if_modified_after, &path) {work.push((output_item.clone(), path.clone()));
if let Some(&inode) = txn.get_tree(&file_id, None)? {crate::fs::rec_delete(txn, &file_id, inode, true)
std::mem::drop(txn_);let mut txn_ = txn.write().unwrap();if let Some(&inode) = txn_.get_tree(&file_id, None)? {crate::fs::rec_delete(&mut *txn_, &file_id, inode, true)
if let Some(&inode) = txn.get_tree(&file_id, None)? {crate::fs::rec_delete(txn, &file_id, inode, true).map_err(PristineOutputError::Fs)?;
let mut txn_ = txn.write().unwrap();if let Some(&inode) = txn_.get_tree(&file_id, None)? {crate::fs::rec_delete(&mut *txn_, &file_id, inode, true).map_err(PristineOutputError::Fs)?;
fn output_file<T: ChannelMutTxnT + GraphMutTxnT, P: ChangeStore, W: WorkingCopy>(txn: &mut T,channel: &mut T::Channel,
fn output_item<T: ChannelMutTxnT + GraphMutTxnT, P: ChangeStore, W: WorkingCopy>(txn: Arc<RwLock<T>>,channel: ChannelRef<T>,
let mut l = retrieve(txn, txn.graph(channel), output_item.pos)?;let mut f = vertex_buffer::ConflictsWriter::new(w, &output_item.path, conflicts);
alive::output_graph(changes, txn, channel, &mut f, &mut l, &mut forward).map_err(PristineOutputError::from)?;
{let txn = txn.read().unwrap();let channel = channel.read().unwrap();let mut l = retrieve(&*txn, txn.graph(&*channel), output_item.pos)?;let w = repo.write_file(&path).map_err(OutputError::WorkingCopy)?;let mut f = vertex_buffer::ConflictsWriter::new(w, &output_item.path, conflicts);alive::output_graph(changes, &*txn, &*channel, &mut f, &mut l, &mut forward).map_err(PristineOutputError::from)?;}if forward.is_empty() {return Ok(());}let mut txn = txn.write().unwrap();let mut channel = channel.write().unwrap();
fn collect_dead_files<T: TreeMutTxnT + GraphTxnT<GraphError = <T as TreeTxnT>::TreeError>>(txn: &mut T,
fn collect_dead_files<T: TreeTxnT + GraphTxnT<GraphError = <T as TreeTxnT>::TreeError>>(txn: &T,
}use crate::working_copy::WriteError;impl<C: std::error::Error, T: std::error::Error + 'static, W: std::error::Error>OutputError<C, T, W>{fn from(e: WriteError<Self>) -> Self {match e {WriteError::Io(e) => OutputError::Pristine(PristineOutputError::Io(e)),WriteError::E(e) => e,}}
impl<C: std::error::Error, T: std::error::Error + 'static, W: std::error::Error> From<TxnErr<T>>for OutputError<C, T, W>
impl<C: std::error::Error, T: std::error::Error + 'static, W: std::error::Error + Send>From<TxnErr<T>> for OutputError<C, T, W>
}pub fn output_file<T: TreeTxnT + ChannelTxnT,C: crate::changestore::ChangeStore,V: crate::vertex_buffer::VertexBuffer,>(changes: &C,txn: &T,channel: &T::Channel,v0: Position<ChangeId>,out: &mut V,) -> Result<(), FileError<C::Error, T::GraphError>> {let mut forward = Vec::new();let mut graph = crate::alive::retrieve(&*txn, txn.graph(&*channel), v0)?;crate::alive::output_graph(changes, &*txn, &*channel, out, &mut graph, &mut forward)?;Ok(())
}}}}Ok(())}pub(crate) fn collect_zombie_context<T: GraphMutTxnT, K>(txn: &mut T,channel: &mut T::Graph,ws: &mut Workspace,inode: Position<Option<Hash>>,n: &NewEdge<Option<Hash>>,change_id: ChangeId,mut known: K,) -> Result<(), MissingError<T::GraphError>>whereK: FnMut(Hash) -> bool,{if n.flag.contains(EdgeFlags::FOLDER) {return Ok(());}let mut pos = internal_pos(txn, &n.to.start_pos(), change_id)?;let end_pos = internal_pos(txn, &n.to.end_pos(), change_id)?;let mut unknown_parents = Vec::new();while let Ok(&dest_vertex) = txn.find_block(&channel, pos) {debug!("collect zombie context: {:?}", dest_vertex);for v in iter_adjacent(txn,channel,dest_vertex,EdgeFlags::empty(),EdgeFlags::all() - EdgeFlags::DELETED,)? {let v = v?;if v.introduced_by() == change_id || v.dest().change.is_root() {continue;}if v.introduced_by().is_root() {ws.pseudo.push((dest_vertex, *v));continue;}if v.flag().contains(EdgeFlags::PARENT) {// Unwrap ok, since `v` is in the channel.let intro = txn.get_external(&v.introduced_by())?.unwrap().into();if !known(intro) {debug!("unknown: {:?}", v);unknown_parents.push((dest_vertex, *v))
}}zombify(txn, channel, ws, change_id, inode, n.flag, &unknown_parents)?;if dest_vertex.end < end_pos.pos {pos.pos = dest_vertex.end} else {break;}}Ok(())}fn zombify<T: GraphMutTxnT>(txn: &mut T,channel: &mut T::Graph,ws: &mut Workspace,change_id: ChangeId,inode: Position<Option<Hash>>,flag: EdgeFlags,unknown: &[(Vertex<ChangeId>, SerializedEdge)],) -> Result<(), MissingError<T::GraphError>> {for &(dest_vertex, edge) in unknown.iter() {let p = *txn.find_block_end(channel, edge.dest())?;ws.unknown_parents.push((dest_vertex, p, inode, edge.flag()));let fold = flag & EdgeFlags::FOLDER;debug!("zombify p {:?}, dest_vertex {:?}", p, dest_vertex);let mut v = p;while let Ok(&u) = txn.find_block_end(channel, v.start_pos()) {if u != v {debug!("u = {:?}, v = {:?}", u, v);put_graph_with_rev(txn,channel,EdgeFlags::DELETED | EdgeFlags::BLOCK | fold,u,v,change_id,)?;v = u} else {break;
// Zombify the first chunk of the split.for parent in iter_adjacent(txn,channel,v,EdgeFlags::PARENT,EdgeFlags::all() - EdgeFlags::DELETED,)? {let parent = parent?;if !parent.flag().contains(EdgeFlags::PSEUDO) {ws.parents.insert(*parent);}}debug!("ws.parents = {:?}", ws.parents);for parent in ws.parents.drain() {let parent_dest = *txn.find_block_end(channel, parent.dest())?;let mut flag = EdgeFlags::DELETED | EdgeFlags::BLOCK;if parent.flag().contains(EdgeFlags::FOLDER) {flag |= EdgeFlags::FOLDER}put_graph_with_rev(txn, channel, flag, parent_dest, v, change_id)?;}
use std::collections::HashMap;
// Making hashmaps deterministic (for testing)pub type HashMap<K, V> =std::collections::HashMap<K, V, std::hash::BuildHasherDefault<twox_hash::XxHash64>>;pub type HashSet<K> =std::collections::HashSet<K, std::hash::BuildHasherDefault<twox_hash::XxHash64>>;// pub type HashMap<K, V> = std::collections::HashMap<K, V, std::collections::hash_map::RandomState>;// pub type HashSet<K> = std::collections::HashSet<K, std::collections::hash_map::RandomState>;
}/*#[cfg(feature = "dump")]fn channel_from_dump<'a>(&'a mut self,name: &str,) -> Result<pristine::channel_dump::ChannelFromDump<'a, Self>,pristine::channel_dump::ChannelDumpError<Self::GraphError>,> {use pristine::channel_dump::*;if self.load_channel(name)?.is_none() {let channel = pristine::MutTxnT::open_or_create_channel(self, name).map_err(ChannelDumpError::Txn)?;Ok(ChannelFromDump::new(self, channel))} else {Err(ChannelDumpError::ChannelNameExists(name.to_string()))}
fs::follow_oldest_path(changes, self, &channel.borrow(), path)}fn output_file<C: changestore::ChangeStore, V: vertex_buffer::VertexBuffer>(&self,changes: &C,channel: &pristine::ChannelRef<Self>,v0: pristine::Position<pristine::ChangeId>,out: &mut V,) -> Result<(), output::FileError<C::Error, Self::GraphError>> {let mut forward = Vec::new();let channel = channel.borrow();let mut graph = alive::retrieve(self, self.graph(&channel), v0)?;alive::output_graph(changes, self, &channel, out, &mut graph, &mut forward)?;Ok(())
fs::follow_oldest_path(changes, self, &channel.read().unwrap(), path)
let pos = self.rec.contents.len();self.rec.contents.extend_from_slice(&b[..]);let pos_end = self.rec.contents.len();self.rec.contents.push(0);
let mut contents = self.contents.lock().unwrap();let pos = contents.len();contents.extend_from_slice(&b[..]);let pos_end = contents.len();contents.push(0);std::mem::drop(contents);
self.rec.contents.push(0);let pos = ChangePosition(self.rec.contents.len().into());self.rec.contents.push(0);
let mut contents = self.contents.lock().unwrap();contents.push(0);let pos = ChangePosition(contents.len().into());contents.push(0);let contents_len = contents.len();std::mem::drop(contents);
#[derive(Clone)]pub struct FileSystem(Arc<FileSystem_>);struct FileSystem_ {change_cache: Mutex<lru_cache::LruCache<ChangeId, Arc<Mutex<ChangeFile<'static>>>>>,
pub struct FileSystem {change_cache: RefCell<lru_cache::LruCache<ChangeId, ChangeFile<'static>>>,
let mut cache = self.0.change_cache.lock().unwrap();let mut poisoned = false;if let Some(c) = cache.get_mut(change_id) {if let Ok(l) = c.lock() {return l.has_contents();} else {poisoned = true}}if poisoned {cache.remove(change_id);
if let Some(l) = self.change_cache.borrow_mut().get_mut(change_id) {return l.has_contents();
crate::missing_context::collect_zombie_context(txn,T::graph_mut(channel),&mut ws.missing_context,n.inode,edge,change_id,|h| change.knows(&h),).map_err(LocalApplyError::from_missing)?
}}}Ok(())}fn put_newvertex<T: GraphMutTxnT>(txn: &mut T,graph: &mut T::Graph,ch: &Change,ws: &mut Workspace,change: ChangeId,n: &NewVertex<Option<Hash>>,) -> Result<(), LocalApplyError<T::GraphError>> {let vertex = Vertex {change,start: n.start,end: n.end,};if txn.find_block_end(graph, vertex.end_pos()).is_ok()|| txn.find_block(graph, vertex.start_pos()).is_ok(){error!("Invalid change: {:?}", vertex);return Err(LocalApplyError::InvalidChange);}debug!("put_newvertex {:?} {:?} {:?} {:?} {:?}",vertex, n.up_context, n.down_context, n.flag, change);assert!(ws.deleted_by.is_empty());for up in n.up_context.iter() {let up = internal_pos(txn, up, change)?;if put_up_context(txn, graph, ch, ws, up)? && n.flag.contains(EdgeFlags::FOLDER) {return Err(LocalApplyError::InvalidChange);}}for down in n.down_context.iter() {let down = internal_pos(txn, down, change)?;if down.change == change {return Err(LocalApplyError::InvalidChange);}if put_down_context(txn, graph, ch, ws, down)? && !n.flag.contains(EdgeFlags::FOLDER) {return Err(LocalApplyError::InvalidChange);}}debug!("deleted by: {:?}", ws.deleted_by);let up_flag = n.flag | EdgeFlags::BLOCK | EdgeFlags::DELETED;for up in ws.up_context.drain(..) {assert_ne!(up, vertex);if !n.flag.contains(EdgeFlags::FOLDER) {for change in ws.deleted_by.iter() {put_graph_with_rev(txn, graph, up_flag, up, vertex, *change)?;
debug!("down_context {:?}", ws.down_context);let mut down_flag = n.flag;if !n.flag.is_folder() {down_flag -= EdgeFlags::BLOCK}for down in ws.down_context.drain(..) {assert_ne!(down, vertex);put_graph_with_rev(txn, graph, down_flag, vertex, down, change)?;if n.flag.is_folder() {ws.missing_context.files.insert(down);}}ws.deleted_by.clear();
}fn put_up_context<T: GraphMutTxnT>(txn: &mut T,graph: &mut T::Graph,ch: &Change,ws: &mut Workspace,up: Position<ChangeId>,) -> Result<bool, LocalApplyError<T::GraphError>> {let up_vertex = if up.change.is_root() {Vertex::ROOT} else {debug!("put_up_context {:?}", up);let k = *txn.find_block_end(graph, up)?;assert_eq!(k.change, up.change);assert!(k.start <= up.pos);debug!("k = {:?}", k);if k.start < up.pos && k.end > up.pos {// The missing context "graphs" are only used at the// DELETION stage, check that:assert!(ws.missing_context.graphs.0.is_empty());txn.split_block(graph, &k, up.pos, &mut ws.adjbuf)?}Vertex {change: k.change,start: k.start,end: up.pos,}};debug!("up_vertex {:?}", up_vertex);let flag0 = EdgeFlags::PARENT | EdgeFlags::BLOCK;let flag1 = flag0 | EdgeFlags::DELETED | EdgeFlags::FOLDER;let mut is_non_folder = false;for parent in iter_adjacent(txn, graph, up_vertex, flag0, flag1)? {let parent = parent?;is_non_folder |=parent.flag() & (EdgeFlags::PARENT | EdgeFlags::FOLDER) == EdgeFlags::PARENT;if parent.flag().contains(EdgeFlags::PARENT | EdgeFlags::DELETED | EdgeFlags::BLOCK){let introduced_by = txn.get_external(&parent.introduced_by())?.unwrap().into();if !ch.knows(&introduced_by) {ws.deleted_by.insert(parent.introduced_by());}}}ws.up_context.push(up_vertex);Ok(is_non_folder)}fn put_down_context<T: GraphMutTxnT>(txn: &mut T,graph: &mut T::Graph,ch: &Change,ws: &mut Workspace,down: Position<ChangeId>,) -> Result<bool, LocalApplyError<T::GraphError>> {let k = *txn.find_block(&graph, down)?;assert_eq!(k.change, down.change);assert!(k.end >= down.pos);if k.start < down.pos && k.end > down.pos {// The missing context "graphs" are only used at the// DELETION stage, check that:assert!(ws.missing_context.graphs.0.is_empty());txn.split_block(graph, &k, down.pos, &mut ws.adjbuf)?}let down_vertex = Vertex {change: k.change,start: down.pos,end: k.end,};debug!("down_vertex {:?}", down_vertex);let flag0 = EdgeFlags::PARENT;let flag1 = flag0 | EdgeFlags::FOLDER | EdgeFlags::BLOCK | EdgeFlags::DELETED;let mut is_folder = false;for parent in iter_adjacent(txn, &graph, down_vertex, flag0, flag1)? {let parent = parent?;is_folder |= parent.flag().contains(EdgeFlags::PARENT | EdgeFlags::FOLDER);if parent.flag().contains(EdgeFlags::PARENT | EdgeFlags::BLOCK) {if parent.flag().contains(EdgeFlags::DELETED) {let introduced_by = txn.get_external(&parent.introduced_by())?.unwrap().into();if !ch.knows(&introduced_by) {ws.deleted_by.insert(parent.introduced_by());}}}}ws.down_context.push(down_vertex);Ok(is_folder)
pub(crate) fn put_newedge<T, E, F>(txn: &mut T,graph: &mut T::Graph,ws: &mut Workspace,change: ChangeId,inode: Position<Option<Hash>>,n: &NewEdge<Option<Hash>>,apply_check: F,) -> Result<(), E>whereT: GraphMutTxnT,E: From<LocalApplyError<T::GraphError>> + From<TxnErr<T::GraphError>>,F: Fn(&mut T, &mut T::Graph, Vertex<ChangeId>, Vertex<ChangeId>) -> Result<bool, E>,{debug!("put_newedge {:?} {:?}", n, change);if n.flag.contains(EdgeFlags::DELETED) {ws.missing_context.load_graph(txn, graph, inode).map_err(|_| LocalApplyError::InvalidChange)?;}if (n.previous.is_block() && !n.flag.is_block())|| (n.previous.is_folder() != n.flag.is_folder()){return Err(LocalApplyError::InvalidChange.into());}debug_assert!(ws.children.is_empty());debug_assert!(ws.parents.is_empty());let n_introduced_by = if let Some(n) = internal(txn, &n.introduced_by, change)? {n} else {return Err(LocalApplyError::InvalidChange.into());};let mut source = find_source_vertex(txn, graph, &n.from, change, inode, n.flag, ws)?;let mut target = find_target_vertex(txn, graph, &n.to, change, inode, n.flag, ws)?;if n.flag.contains(EdgeFlags::FOLDER) {ws.missing_context.files.insert(target);}loop {if target.end > n.to.end {assert!(!n.flag.contains(EdgeFlags::FOLDER));ws.missing_context.graphs.split(inode, target, n.to.end);txn.split_block(graph, &target, n.to.end, &mut ws.adjbuf)?;target.end = n.to.end}if n.flag.contains(EdgeFlags::DELETED) {debug_assert!(ws.children.is_empty());debug_assert!(ws.parents.is_empty());collect_pseudo_edges(txn, graph, ws, inode, target)?;if !n.flag.contains(EdgeFlags::FOLDER) {reconnect_pseudo_edges(txn, graph, inode, ws, target)?;}ws.children.clear();ws.parents.clear();}del_graph_with_rev(txn, graph, n.previous, source, target, n_introduced_by)?;if apply_check(txn, graph, source, target)? {put_graph_with_rev(txn, graph, n.flag, source, target, change)?;}if target.end >= n.to.end {debug!("{:?} {:?}", target, n.to);debug_assert_eq!(target.end, n.to.end);break;}source = target;target = *txn.find_block(graph, target.end_pos()).map_err(LocalApplyError::from)?;assert_ne!(source, target);}Ok(())}fn find_source_vertex<T: GraphMutTxnT>(txn: &mut T,channel: &mut T::Graph,from: &Position<Option<Hash>>,change: ChangeId,inode: Position<Option<Hash>>,flag: EdgeFlags,ws: &mut Workspace,) -> Result<Vertex<ChangeId>, LocalApplyError<T::GraphError>> {debug!("find_source_vertex");let mut source = *txn.find_block_end(&channel, internal_pos(txn, &from, change)?)?;debug!("source = {:?}", source);if source.start < from.pos && source.end > from.pos {assert!(!flag.contains(EdgeFlags::FOLDER));ws.missing_context.graphs.split(inode, source, from.pos);txn.split_block(channel, &source, from.pos, &mut ws.adjbuf)?;source.end = from.pos;}Ok(source)}fn find_target_vertex<T: GraphMutTxnT>(txn: &mut T,channel: &mut T::Graph,to: &Vertex<Option<Hash>>,change: ChangeId,inode: Position<Option<Hash>>,flag: EdgeFlags,ws: &mut Workspace,) -> Result<Vertex<ChangeId>, LocalApplyError<T::GraphError>> {let to_pos = internal_pos(txn, &to.start_pos(), change)?;debug!("find_target_vertex, to = {:?}", to);let mut target = *txn.find_block(channel, to_pos)?;debug!("target = {:?}", target);if target.start < to.start {assert!(!flag.contains(EdgeFlags::FOLDER));ws.missing_context.graphs.split(inode, target, to.start);txn.split_block(channel, &target, to.start, &mut ws.adjbuf)?;target.start = to.start;}Ok(target)}fn collect_pseudo_edges<T: GraphMutTxnT>(txn: &mut T,channel: &mut T::Graph,apply: &mut Workspace,inode: Position<Option<Hash>>,v: Vertex<ChangeId>,) -> Result<(), LocalApplyError<T::GraphError>> {for e in iter_adjacent(txn,&channel,v,EdgeFlags::empty(),EdgeFlags::all() - EdgeFlags::DELETED,)? {let e = e?;debug!("collect_pseudo_edges {:?} {:?}", v, e);if !e.flag().contains(EdgeFlags::FOLDER) {if e.flag().contains(EdgeFlags::PARENT) {let p = txn.find_block_end(channel, e.dest())?;if is_alive(txn, channel, p)? {apply.parents.insert(*p);}} else {let p = txn.find_block(channel, e.dest())?;if e.flag().contains(EdgeFlags::BLOCK)|| p.is_empty()|| is_alive(txn, channel, p).unwrap(){apply.children.insert(*p);}}}if e.flag().contains(EdgeFlags::PSEUDO) {apply.pseudo.push((v, *e, inode));}}Ok(())}fn reconnect_pseudo_edges<T: GraphMutTxnT>(txn: &mut T,channel: &mut T::Graph,inode: Position<Option<Hash>>,ws: &mut Workspace,target: Vertex<ChangeId>,) -> Result<(), LocalApplyError<T::GraphError>> {if ws.parents.is_empty() || ws.children.is_empty() {return Ok(());}let (graph, vids) = if let Some(x) = ws.missing_context.graphs.get(inode) {x} else {return Err(LocalApplyError::InvalidChange.into());};crate::alive::remove_redundant_parents(&graph,&vids,&mut ws.parents,&mut ws.missing_context.covered_parents,target,);for &p in ws.parents.iter() {ws.missing_context.covered_parents.insert((p, target));}crate::alive::remove_redundant_children(&graph, &vids, &mut ws.children, target);for &p in ws.parents.iter() {debug_assert!(is_alive(txn, channel, &p).unwrap());for &c in ws.children.iter() {if p != c {debug_assert!(is_alive(txn, channel, &c).unwrap());put_graph_with_rev(txn, channel, EdgeFlags::PSEUDO, p, c, ChangeId::ROOT)?;}}}Ok(())}
use super::{LocalApplyError, Workspace};use crate::change::{Change, NewVertex};use crate::pristine::*;use crate::{ChangeId, EdgeFlags, Hash, Vertex};pub fn put_newvertex<T: GraphMutTxnT>(txn: &mut T,graph: &mut T::Graph,ch: &Change,ws: &mut Workspace,change: ChangeId,n: &NewVertex<Option<Hash>>,) -> Result<(), LocalApplyError<T::GraphError>> {let vertex = Vertex {change,start: n.start,end: n.end,};if txn.find_block_end(graph, vertex.end_pos()).is_ok()|| txn.find_block(graph, vertex.start_pos()).is_ok(){error!("Invalid change: {:?}", vertex);return Err(LocalApplyError::InvalidChange);}debug!("put_newvertex {:?} {:?} {:?} {:?} {:?}",vertex, n.up_context, n.down_context, n.flag, change);assert!(ws.deleted_by.is_empty());for up in n.up_context.iter() {let up = internal_pos(txn, up, change)?;if put_up_context(txn, graph, ch, ws, up)? && n.flag.contains(EdgeFlags::FOLDER) {return Err(LocalApplyError::InvalidChange);}}for down in n.down_context.iter() {let down = internal_pos(txn, down, change)?;if down.change == change {return Err(LocalApplyError::InvalidChange);}if put_down_context(txn, graph, ch, ws, down)? && !n.flag.contains(EdgeFlags::FOLDER) {return Err(LocalApplyError::InvalidChange);}}debug!("deleted by: {:?}", ws.deleted_by);let up_flag = n.flag | EdgeFlags::BLOCK | EdgeFlags::DELETED;for up in ws.up_context.drain(..) {assert_ne!(up, vertex);if !n.flag.contains(EdgeFlags::FOLDER) {for change in ws.deleted_by.iter() {put_graph_with_rev(txn, graph, up_flag, up, vertex, *change)?;}}put_graph_with_rev(txn, graph, n.flag | EdgeFlags::BLOCK, up, vertex, change)?;}debug!("down_context {:?}", ws.down_context);let mut down_flag = n.flag;if !n.flag.is_folder() {down_flag -= EdgeFlags::BLOCK}for down in ws.down_context.drain(..) {assert_ne!(down, vertex);put_graph_with_rev(txn, graph, down_flag, vertex, down, change)?;if n.flag.is_folder() {ws.missing_context.files.insert(down);}}ws.deleted_by.clear();Ok(())}fn put_up_context<T: GraphMutTxnT>(txn: &mut T,graph: &mut T::Graph,ch: &Change,ws: &mut Workspace,up: Position<ChangeId>,) -> Result<bool, LocalApplyError<T::GraphError>> {let up_vertex = if up.change.is_root() {Vertex::ROOT} else {debug!("put_up_context {:?}", up);let k = *txn.find_block_end(graph, up)?;assert_eq!(k.change, up.change);assert!(k.start <= up.pos);debug!("k = {:?}", k);if k.start < up.pos && k.end > up.pos {// The missing context "graphs" are only used at the// DELETION stage, check that:assert!(ws.missing_context.graphs.0.is_empty());txn.split_block(graph, &k, up.pos, &mut ws.adjbuf)?}Vertex {change: k.change,start: k.start,end: up.pos,}};debug!("up_vertex {:?}", up_vertex);let flag0 = EdgeFlags::PARENT | EdgeFlags::BLOCK;let flag1 = flag0 | EdgeFlags::DELETED | EdgeFlags::FOLDER;let mut is_non_folder = false;for parent in iter_adjacent(txn, graph, up_vertex, flag0, flag1)? {let parent = parent?;is_non_folder |=parent.flag() & (EdgeFlags::PARENT | EdgeFlags::FOLDER) == EdgeFlags::PARENT;if parent.flag().contains(EdgeFlags::PARENT | EdgeFlags::DELETED | EdgeFlags::BLOCK){let introduced_by = txn.get_external(&parent.introduced_by())?.unwrap().into();if !ch.knows(&introduced_by) {ws.deleted_by.insert(parent.introduced_by());}}}ws.up_context.push(up_vertex);Ok(is_non_folder)}fn put_down_context<T: GraphMutTxnT>(txn: &mut T,graph: &mut T::Graph,ch: &Change,ws: &mut Workspace,down: Position<ChangeId>,) -> Result<bool, LocalApplyError<T::GraphError>> {let k = *txn.find_block(&graph, down)?;assert_eq!(k.change, down.change);assert!(k.end >= down.pos);if k.start < down.pos && k.end > down.pos {// The missing context "graphs" are only used at the// DELETION stage, check that:assert!(ws.missing_context.graphs.0.is_empty());txn.split_block(graph, &k, down.pos, &mut ws.adjbuf)?}let down_vertex = Vertex {change: k.change,start: down.pos,end: k.end,};debug!("down_vertex {:?}", down_vertex);let flag0 = EdgeFlags::PARENT;let flag1 = flag0 | EdgeFlags::FOLDER | EdgeFlags::BLOCK | EdgeFlags::DELETED;let mut is_folder = false;for parent in iter_adjacent(txn, &graph, down_vertex, flag0, flag1)? {let parent = parent?;is_folder |= parent.flag().contains(EdgeFlags::PARENT | EdgeFlags::FOLDER);if parent.flag().contains(EdgeFlags::PARENT | EdgeFlags::BLOCK) {if parent.flag().contains(EdgeFlags::DELETED) {let introduced_by = txn.get_external(&parent.introduced_by())?.unwrap().into();if !ch.knows(&introduced_by) {ws.deleted_by.insert(parent.introduced_by());}}}}ws.down_context.push(down_vertex);Ok(is_folder)}
use super::LocalApplyError;use crate::change::NewEdge;use crate::missing_context::*;use crate::pristine::*;pub fn put_newedge<T, E, F, K>(txn: &mut T,graph: &mut T::Graph,ws: &mut super::Workspace,change: ChangeId,inode: Position<Option<Hash>>,n: &NewEdge<Option<Hash>>,apply_check: F,mut known: K,) -> Result<(), E>whereT: GraphMutTxnT,E: From<LocalApplyError<T::GraphError>> + From<TxnErr<T::GraphError>>,F: Fn(&mut T, &mut T::Graph, Vertex<ChangeId>, Vertex<ChangeId>) -> Result<bool, E>,K: FnMut(&Hash) -> bool,{debug!("put_newedge {:?} {:?}", n, change);check_valid(txn, graph, inode, n, ws)?;let n_introduced_by = if let Some(n) = internal(txn, &n.introduced_by, change)? {n} else {return Err(LocalApplyError::InvalidChange.into());};let mut source = find_source_vertex(txn, graph, &n.from, change, inode, n.flag, ws)?;let mut target = find_target_vertex(txn, graph, &n.to, change, inode, n.flag, ws)?;if n.flag.contains(EdgeFlags::FOLDER) {ws.missing_context.files.insert(target);}let mut zombies = Vec::new();loop {if !n.flag.contains(EdgeFlags::DELETED) {collect_nondeleted_zombies::<_, E, _>(txn,graph,&mut known,source,target,&mut zombies,)?;}if target.end > n.to.end {assert!(!n.flag.contains(EdgeFlags::FOLDER));ws.missing_context.graphs.split(inode, target, n.to.end);txn.split_block(graph, &target, n.to.end, &mut ws.adjbuf)?;target.end = n.to.end}if n.flag.contains(EdgeFlags::DELETED) {debug_assert!(ws.children.is_empty());debug_assert!(ws.parents.is_empty());collect_pseudo_edges(txn, graph, ws, inode, target)?;if !n.flag.contains(EdgeFlags::FOLDER) {reconnect_pseudo_edges(txn, graph, inode, ws, target)?;}ws.children.clear();ws.parents.clear();}del_graph_with_rev(txn, graph, n.previous, source, target, n_introduced_by)?;if apply_check(txn, graph, source, target)? {put_graph_with_rev(txn, graph, n.flag, source, target, change)?;for intro in zombies.drain(..) {put_graph_with_rev(txn, graph, EdgeFlags::DELETED, source, target, intro)?;}}if target.end >= n.to.end {debug!("{:?} {:?}", target, n.to);debug_assert_eq!(target.end, n.to.end);break;}source = target;target = *txn.find_block(graph, target.end_pos()).map_err(LocalApplyError::from)?;assert_ne!(source, target);}if n.flag.contains(EdgeFlags::DELETED) {collect_zombie_context(txn, graph, &mut ws.missing_context, inode, n, change, known).map_err(LocalApplyError::from_missing)?;}Ok(())}fn collect_nondeleted_zombies<T, E, K>(txn: &mut T,graph: &mut T::Graph,mut known: K,source: Vertex<ChangeId>,target: Vertex<ChangeId>,zombies: &mut Vec<ChangeId>,) -> Result<(), E>whereT: GraphMutTxnT,E: From<LocalApplyError<T::GraphError>> + From<TxnErr<T::GraphError>>,K: FnMut(&Hash) -> bool,{for v in iter_deleted_parents(txn, graph, source)? {let v = v?;let intro = v.introduced_by();if !known(&txn.get_external(&intro)?.unwrap().into()) {zombies.push(intro)}}for v in iter_adjacent(txn, graph, target, EdgeFlags::empty(), EdgeFlags::all())? {let v = v?;if v.flag().contains(EdgeFlags::PARENT) {continue;}for v in iter_deleted_parents(txn, graph, target)? {let v = v?;let intro = v.introduced_by();if !known(&txn.get_external(&intro)?.unwrap().into()) {zombies.push(intro)}}}Ok(())}fn check_valid<T: GraphMutTxnT>(txn: &mut T,graph: &mut T::Graph,inode: Position<Option<Hash>>,n: &NewEdge<Option<Hash>>,ws: &mut super::Workspace,) -> Result<(), LocalApplyError<T::GraphError>> {if n.flag.contains(EdgeFlags::DELETED) {ws.missing_context.load_graph(txn, graph, inode).map_err(|_| LocalApplyError::InvalidChange)?;}if (n.previous.is_block() && !n.flag.is_block())|| (n.previous.is_folder() != n.flag.is_folder()){return Err(LocalApplyError::InvalidChange.into());}debug_assert!(ws.children.is_empty());debug_assert!(ws.parents.is_empty());Ok(())}fn find_source_vertex<T: GraphMutTxnT>(txn: &mut T,channel: &mut T::Graph,from: &Position<Option<Hash>>,change: ChangeId,inode: Position<Option<Hash>>,flag: EdgeFlags,ws: &mut super::Workspace,) -> Result<Vertex<ChangeId>, LocalApplyError<T::GraphError>> {debug!("find_source_vertex");let mut source = *txn.find_block_end(&channel, internal_pos(txn, &from, change)?)?;debug!("source = {:?}", source);if source.start < from.pos && source.end > from.pos {assert!(!flag.contains(EdgeFlags::FOLDER));ws.missing_context.graphs.split(inode, source, from.pos);txn.split_block(channel, &source, from.pos, &mut ws.adjbuf)?;source.end = from.pos;}Ok(source)}fn find_target_vertex<T: GraphMutTxnT>(txn: &mut T,channel: &mut T::Graph,to: &Vertex<Option<Hash>>,change: ChangeId,inode: Position<Option<Hash>>,flag: EdgeFlags,ws: &mut super::Workspace,) -> Result<Vertex<ChangeId>, LocalApplyError<T::GraphError>> {let to_pos = internal_pos(txn, &to.start_pos(), change)?;debug!("find_target_vertex, to = {:?}", to);let mut target = *txn.find_block(channel, to_pos)?;debug!("target = {:?}", target);if target.start < to.start {assert!(!flag.contains(EdgeFlags::FOLDER));ws.missing_context.graphs.split(inode, target, to.start);txn.split_block(channel, &target, to.start, &mut ws.adjbuf)?;target.start = to.start;}Ok(target)}fn collect_pseudo_edges<T: GraphMutTxnT>(txn: &mut T,channel: &mut T::Graph,apply: &mut super::Workspace,inode: Position<Option<Hash>>,v: Vertex<ChangeId>,) -> Result<(), LocalApplyError<T::GraphError>> {for e in iter_adjacent(txn,&channel,v,EdgeFlags::empty(),EdgeFlags::all() - EdgeFlags::DELETED,)? {let e = e?;debug!("collect_pseudo_edges {:?} {:?}", v, e);if !e.flag().contains(EdgeFlags::FOLDER) {if e.flag().contains(EdgeFlags::PARENT) {let p = txn.find_block_end(channel, e.dest())?;if is_alive(txn, channel, p)? {apply.parents.insert(*p);}} else {let p = txn.find_block(channel, e.dest())?;if e.flag().contains(EdgeFlags::BLOCK)|| p.is_empty()|| is_alive(txn, channel, p).unwrap(){apply.children.insert(*p);}}}if e.flag().contains(EdgeFlags::PSEUDO) {apply.pseudo.push((v, *e, inode));}}Ok(())}fn reconnect_pseudo_edges<T: GraphMutTxnT>(txn: &mut T,channel: &mut T::Graph,inode: Position<Option<Hash>>,ws: &mut super::Workspace,target: Vertex<ChangeId>,) -> Result<(), LocalApplyError<T::GraphError>> {if ws.parents.is_empty() || ws.children.is_empty() {return Ok(());}let (graph, vids) = if let Some(x) = ws.missing_context.graphs.get(inode) {x} else {return Err(LocalApplyError::InvalidChange.into());};crate::alive::remove_redundant_parents(&graph,&vids,&mut ws.parents,&mut ws.missing_context.covered_parents,target,);for &p in ws.parents.iter() {ws.missing_context.covered_parents.insert((p, target));}crate::alive::remove_redundant_children(&graph, &vids, &mut ws.children, target);for &p in ws.parents.iter() {debug_assert!(is_alive(txn, channel, &p).unwrap());for &c in ws.children.iter() {if p != c {debug_assert!(is_alive(txn, channel, &c).unwrap());put_graph_with_rev(txn, channel, EdgeFlags::PSEUDO, p, c, ChangeId::ROOT)?;}}}Ok(())}fn collect_zombie_context<T: GraphMutTxnT, K>(txn: &mut T,channel: &mut T::Graph,ws: &mut crate::missing_context::Workspace,inode: Position<Option<Hash>>,n: &NewEdge<Option<Hash>>,change_id: ChangeId,mut known: K,) -> Result<(), MissingError<T::GraphError>>whereK: FnMut(&Hash) -> bool,{if n.flag.contains(EdgeFlags::FOLDER) {return Ok(());}let mut pos = internal_pos(txn, &n.to.start_pos(), change_id)?;let end_pos = internal_pos(txn, &n.to.end_pos(), change_id)?;let mut unknown_parents = Vec::new();while let Ok(&dest_vertex) = txn.find_block(&channel, pos) {debug!("collect zombie context: {:?}", dest_vertex);for v in iter_adjacent(txn,channel,dest_vertex,EdgeFlags::empty(),EdgeFlags::all() - EdgeFlags::DELETED,)? {let v = v?;if v.introduced_by() == change_id || v.dest().change.is_root() {continue;}if v.introduced_by().is_root() {ws.pseudo.push((dest_vertex, *v));continue;}if v.flag().contains(EdgeFlags::PARENT) {// Unwrap ok, since `v` is in the channel.let intro = txn.get_external(&v.introduced_by())?.unwrap().into();if !known(&intro) {debug!("unknown: {:?}", v);unknown_parents.push((dest_vertex, *v))}}}zombify(txn, channel, ws, change_id, inode, n.flag, &unknown_parents)?;if dest_vertex.end < end_pos.pos {pos.pos = dest_vertex.end} else {break;}}Ok(())}fn zombify<T: GraphMutTxnT>(txn: &mut T,channel: &mut T::Graph,ws: &mut crate::missing_context::Workspace,change_id: ChangeId,inode: Position<Option<Hash>>,flag: EdgeFlags,unknown: &[(Vertex<ChangeId>, SerializedEdge)],) -> Result<(), MissingError<T::GraphError>> {for &(dest_vertex, edge) in unknown.iter() {let p = *txn.find_block_end(channel, edge.dest())?;ws.unknown_parents.push((dest_vertex, p, inode, edge.flag()));let fold = flag & EdgeFlags::FOLDER;debug!("zombify p {:?}, dest_vertex {:?}", p, dest_vertex);let mut v = p;while let Ok(&u) = txn.find_block_end(channel, v.start_pos()) {if u != v {debug!("u = {:?}, v = {:?}", u, v);put_graph_with_rev(txn,channel,EdgeFlags::DELETED | EdgeFlags::BLOCK | fold,u,v,change_id,)?;v = u} else {break;}}// Zombify the first chunk of the split.for parent in iter_adjacent(txn,channel,v,EdgeFlags::PARENT,EdgeFlags::all() - EdgeFlags::DELETED,)? {let parent = parent?;if !parent.flag().contains(EdgeFlags::PSEUDO) {ws.parents.insert(*parent);}}debug!("ws.parents = {:?}", ws.parents);for parent in ws.parents.drain() {let parent_dest = *txn.find_block_end(channel, parent.dest())?;let mut flag = EdgeFlags::DELETED | EdgeFlags::BLOCK;if parent.flag().contains(EdgeFlags::FOLDER) {flag |= EdgeFlags::FOLDER}put_graph_with_rev(txn, channel, flag, parent_dest, v, change_id)?;}}Ok(())}
};"crossbeam-deque" = rec {crateName = "crossbeam-deque";version = "0.8.0";edition = "2018";sha256 = "1ad995vzq74k7jd1pgn9zxbacyzj9ii6l0svhlb2dxzy8vxnxbwl";authors = ["The Crossbeam Project Developers"];dependencies = [{name = "cfg-if";packageId = "cfg-if 1.0.0";}{name = "crossbeam-epoch";packageId = "crossbeam-epoch";optional = true;usesDefaultFeatures = false;}{name = "crossbeam-utils";packageId = "crossbeam-utils";optional = true;usesDefaultFeatures = false;}];features = {"default" = [ "std" ];"std" = [ "crossbeam-epoch/std" "crossbeam-utils/std" ];};resolvedDefaultFeatures = [ "crossbeam-epoch" "crossbeam-utils" "default" "std" ];};"crossbeam-epoch" = rec {crateName = "crossbeam-epoch";version = "0.9.3";edition = "2018";sha256 = "04jbrwrm6ibmd83anc3difsvk0fgjyr1aqs9k33sizlmxcwzd115";authors = ["The Crossbeam Project Developers"];dependencies = [{name = "cfg-if";packageId = "cfg-if 1.0.0";}{name = "crossbeam-utils";packageId = "crossbeam-utils";usesDefaultFeatures = false;}{name = "lazy_static";packageId = "lazy_static";optional = true;}{name = "memoffset";packageId = "memoffset";}{name = "scopeguard";packageId = "scopeguard";usesDefaultFeatures = false;}];features = {"default" = [ "std" ];"loom" = [ "loom-crate" "crossbeam-utils/loom" ];"nightly" = [ "crossbeam-utils/nightly" "const_fn" ];"std" = [ "alloc" "crossbeam-utils/std" "lazy_static" ];};resolvedDefaultFeatures = [ "alloc" "lazy_static" "std" ];
};"memoffset" = rec {crateName = "memoffset";version = "0.6.1";edition = "2015";sha256 = "11yxgw330cf8g4wy0fnb20ag8gg1b33fsnfmg2g8z6h5wc444yqm";authors = ["Gilad Naaman <gilad.naaman@gmail.com>"];buildDependencies = [{name = "autocfg";packageId = "autocfg";}];features = {};resolvedDefaultFeatures = [ "default" ];
"rand" = rec {
"rand 0.7.3" = rec {crateName = "rand";version = "0.7.3";edition = "2018";sha256 = "00sdaimkbz491qgi6qxkv582yivl32m2jd401kzbn94vsiwicsva";authors = ["The Rand Project Developers""The Rust Project Developers"];dependencies = [{name = "getrandom";packageId = "getrandom 0.1.16";rename = "getrandom_package";optional = true;}{name = "libc";packageId = "libc";optional = true;usesDefaultFeatures = false;target = { target, features }: (target."unix" or false);}{name = "rand_chacha";packageId = "rand_chacha 0.2.2";usesDefaultFeatures = false;target = { target, features }: (!(target."os" == "emscripten"));}{name = "rand_core";packageId = "rand_core 0.5.1";}{name = "rand_hc";packageId = "rand_hc 0.2.0";target = { target, features }: (target."os" == "emscripten");}];devDependencies = [{name = "rand_hc";packageId = "rand_hc 0.2.0";}];features = {"alloc" = [ "rand_core/alloc" ];"default" = [ "std" ];"getrandom" = [ "getrandom_package" "rand_core/getrandom" ];"nightly" = [ "simd_support" ];"simd_support" = [ "packed_simd" ];"small_rng" = [ "rand_pcg" ];"std" = [ "rand_core/std" "rand_chacha/std" "alloc" "getrandom" "libc" ];"stdweb" = [ "getrandom_package/stdweb" ];"wasm-bindgen" = [ "getrandom_package/wasm-bindgen" ];};resolvedDefaultFeatures = [ "alloc" "default" "getrandom" "getrandom_package" "libc" "std" ];};"rand 0.8.3" = rec {
version = "0.2.2";edition = "2018";sha256 = "00il36fkdbsmpr99p9ksmmp6dn1md7rmnwmz0rr77jbrca2yvj7l";authors = ["The Rand Project Developers""The Rust Project Developers""The CryptoCorrosion Contributors"];dependencies = [{name = "ppv-lite86";packageId = "ppv-lite86";usesDefaultFeatures = false;features = [ "simd" ];}{name = "rand_core";packageId = "rand_core 0.5.1";}];features = {"default" = [ "std" "simd" ];"std" = [ "ppv-lite86/std" ];};resolvedDefaultFeatures = [ "std" ];};"rand_chacha 0.3.0" = rec {crateName = "rand_chacha";
"rand_hc" = rec {
"rand_hc 0.2.0" = rec {crateName = "rand_hc";version = "0.2.0";edition = "2018";sha256 = "0g31sqwpmsirdlwr0svnacr4dbqyz339im4ssl9738cjgfpjjcfa";authors = ["The Rand Project Developers"];dependencies = [{name = "rand_core";packageId = "rand_core 0.5.1";}];};"rand_hc 0.3.0" = rec {
};"twox-hash" = rec {crateName = "twox-hash";version = "1.6.0";edition = "2018";crateBin = [];sha256 = "0ndb4pil758kn0av83jjgq8kkfkwc5lhi5ii7fk5yw96h1wapy04";authors = ["Jake Goulding <jake.goulding@gmail.com>"];dependencies = [{name = "cfg-if";packageId = "cfg-if 0.1.10";usesDefaultFeatures = false;}{name = "rand";packageId = "rand 0.7.3";optional = true;}{name = "static_assertions";packageId = "static_assertions";usesDefaultFeatures = false;}];features = {"default" = [ "std" ];"serialize" = [ "serde" ];"std" = [ "rand" ];};resolvedDefaultFeatures = [ "default" "rand" "std" ];
name = "crossbeam-epoch"version = "0.9.3"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "2584f639eb95fea8c798496315b297cf81b9b58b6d30ab066a75455333cf4b12"dependencies = ["cfg-if 1.0.0","crossbeam-utils","lazy_static","memoffset","scopeguard",][[package]]
version = "0.7.3"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"dependencies = ["getrandom 0.1.16","libc","rand_chacha 0.2.2","rand_core 0.5.1","rand_hc 0.2.0",][[package]]name = "rand"
"rand_hc",
"rand_hc 0.3.0",][[package]]name = "rand_chacha"version = "0.2.2"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"dependencies = ["ppv-lite86","rand_core 0.5.1",