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 option
let 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 option
let 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 {})