MU5GSJAW65PEG3BRYUKZ7O37BPHW3MOX3S5E2RFOXKGUOJEEDQ5AC VO5OQW4W2656DIYYRNZ3PO7TQ4JOKQ3GVWE5ALUTYVMX3WMXJOYQC X6YFD4WVMUYJCR5IYPJH6UKYVWSA7DKBRVJ6XQFXHOE2TRYUTAHAC FE5ES6Q46FMWYPNNNJLORY377QGDE57LBBDIVWDTC6Z7U4U73NEQC SLJ3OHD4F6GJGZ3SV2D7DMR3PXYHPSI64X77KZ3RJ24EGEX6ZNQAC SXEYMYF7P4RZMZ46WPL4IZUTSQ2ATBWYZX7QNVMS3SGOYXYOHAGQC L4JXJHWXYNCL4QGJXNKKTOKKTAXKKXBJUUY7HFZGEUZ5A2V5H34QC I52XSRUH5RVHQBFWVMAQPTUSPAJ4KNVID2RMI3UGCVKFLYUO6WZAC UDHP4ZVBQZT2VBURB2MDCU2IZDNMCAFSIUKWRBDQ5BWMFKSN2LYQC 5QTMRUXNE2XNJCMLN6MQN24UEZ55EFC3LIR4PO6OPNTT5KEL7WXQC 76PCXGML77EZWTRI5E6KHLVRAFTJ2AB5YRN5EKOYNAPKTWY2KCGAC K6GWUOD55G377RVEEMMRPZ4EUAHCM2BGXNRJTE5UZJFFMJGFCEZQC Q45QHPO4HDTEZF2W4UDZSYYQ46BPEIWSW4GJILZR5HTJNLKXJABQC FBXYP7QM7SG6P2JDJVQPPCRKJE3GVYXNQ5GVV4GRDUNG6Q4ZRDJQC 367UBQ6KNAKUEWG32R4QRJ6H7IE7NAZFOPTC3ZOE4Z6E44RV3ISQC AN7IDX26RK33ZXASXLJMD4GTFWHCTHMJ6Y5C4ROCPIH33VUT2EYQC AEPEFS7O3YT7CRRFYQVJWUXUUSRGJ6K6XZQVK62B6N74UXOIFWYAC 5HF7C67M4DZMYTCIG32XEQQ662AHQMIHTHUK7TAVSO52XLMFBZPAC KDF6FJRVF72L274BEUJCTUKRFMNL6BDZMTVKDPEYGFX4TC3YOVSQC WZVCLZKY34KQBQU6YBGJLQCDADBQ67LQVDNRVCMQVY3O3C3EIWSQC BAUL3WR2ACY2HCJIM7K6HJOJ3UXDJISGLMDCSPH3WMPGJPL5AR4QC BBKV6VMN4EVBCBSAQMTL2TARBBSQEZGRCXMTKYUIDOJ3HZISUP7AC M5FK3ABTKBDG6HHW32G7UKRJEJQKD2U7BPXNZ3HVHBKULWVV6CTQC 5DVRL6MFXQOCPOZMYSKBERMRRVUTYRL2SRGRTU2MH4IEOFCDKM3QC debug!("waiting ssh");while let Some(Some((n, h, m))) = receiver.recv().await {txn.put_remote(remote, n, (h, m))?;
debug!("waiting ssh, command: {:?}", std::str::from_utf8(&command));let mut result = HashSet::new();while let Some(Some(m)) = receiver.recv().await {match m {super::ListLine::Change { n, h, m } => {txn.put_remote(remote, n, (h, m))?;}super::ListLine::Position(pos) => {result.insert(pos);}}
RemoteRepo::Http(ref h) => {let url = h.url.clone() + "/" + DOT_DIR;let from_ = from.to_string();let mut query = vec![("changelist", &from_), ("channel", &h.channel)];for p in paths.iter() {query.push(("path", p));}let res = h.client.get(&url).query(&query).send().await?;if !res.status().is_success() {return Err((crate::Error::Http {status: res.status(),}).into());}let resp = res.bytes().await?;if let Ok(data) = std::str::from_utf8(&resp) {for l in data.lines() {if !l.is_empty() {let (n, h, m) = parse_line(l)?;txn.put_remote(remote, n, (h, m))?;} else {break;}}}Ok(())}RemoteRepo::LocalChannel(_) => Ok(()),
RemoteRepo::Http(ref h) => h.download_changelist(txn, remote, from, paths).await,RemoteRepo::LocalChannel(_) => Ok(HashSet::new()),
let touches_inodes = inodes.is_empty()|| {debug!("inodes = {:?}", inodes);use libpijul::changestore::ChangeStore;let changes = repo.changes.get_changes(h)?;changes.iter().any(|c| {c.iter().any(|c| {let inode = c.inode();debug!("inode = {:?}", inode);if let Some(h) = inode.change {inodes.contains(&Position {change: h,pos: inode.pos,})} else {false}})})}|| { inodes.iter().any(|i| i.change == *h) };if touches_inodes {to_apply_inodes.push(*h);} else {continue;}
fn parse_line(data: &str) -> Result<(u64, Hash, Merkle), anyhow::Error> {
use libpijul::pristine::{ChangePosition, Position};use regex::Regex;lazy_static! {static ref CHANGELIST_LINE: Regex =Regex::new(r#"(?P<num>[0-9]+)\.(?P<hash>[A-Za-z0-9]+)\.(?P<merkle>[A-Za-z0-9]+)"#).unwrap();static ref PATHS_LINE: Regex =Regex::new(r#"(?P<hash>[A-Za-z0-9]+)\.(?P<num>[0-9]+)"#).unwrap();}enum ListLine {Change { n: u64, h: Hash, m: Merkle },Position(Position<Hash>),}fn parse_line(data: &str) -> Result<ListLine, anyhow::Error> {
let mut it = data.split('.');let n = if let Some(n) = it.next().and_then(|n| n.parse().ok()) {n} else {return Err((Error::ProtocolError {line: data.as_bytes().to_vec(),}).into());};debug!("n = {:?}", n);let h = if let Some(h) = it.next().and_then(|h| Hash::from_base32(h.as_bytes())) {h} else {return Err((Error::ProtocolError {line: data.as_bytes().to_vec(),}).into());};debug!("h = {:?}", h);let m = if let Some(m) = it.next().and_then(|m| {debug!("m = {:?}", m);Merkle::from_base32(m.as_bytes())}) {m} else {return Err((Error::ProtocolError {line: data.as_bytes().to_vec(),}).into());};debug!("m = {:?}", m);if it.next().is_some() {return Err((Error::ProtocolError {line: data.as_bytes().to_vec(),}).into());
if let Some(caps) = CHANGELIST_LINE.captures(data) {if let (Some(h), Some(m)) = (Hash::from_base32(caps.name("hash").unwrap().as_str().as_bytes()),Merkle::from_base32(caps.name("merkle").unwrap().as_str().as_bytes()),) {return Ok(ListLine::Change {n: caps.name("num").unwrap().as_str().parse()?,h,m,});}}if let Some(caps) = PATHS_LINE.captures(data) {return Ok(ListLine::Position(Position {change: Hash::from_base32(caps.name("hash").unwrap().as_str().as_bytes()).unwrap(),pos: ChangePosition(caps.name("num").unwrap().as_str().parse().unwrap()),}));
pub async fn download_changelist<T: MutTxnT>(&self,txn: &mut T,remote: &mut RemoteRef<T>,from: u64,paths: &[String],) -> Result<HashSet<Position<Hash>>, anyhow::Error> {let url = self.url.clone() + "/" + super::DOT_DIR;let from_ = from.to_string();let mut query = vec![("changelist", &from_), ("channel", &self.channel)];for p in paths.iter() {query.push(("path", p));}let res = self.client.get(&url).query(&query).send().await?;if !res.status().is_success() {return Err((crate::Error::Http {status: res.status(),}).into());}let resp = res.bytes().await?;let mut result = HashSet::new();if let Ok(data) = std::str::from_utf8(&resp) {for l in data.lines() {if !l.is_empty() {match super::parse_line(l)? {super::ListLine::Change { n, m, h } => {txn.put_remote(remote, n, (h, m))?;}super::ListLine::Position(pos) => {result.insert(pos);}}} else {break;}}}Ok(result)}
let mut paths = if let Some(p) = self.path {vec![p]} else {vec![]};let remote_changes = remote.update_changelist(&mut txn, &paths).await?;
let remote_changes = remote.update_changelist(&mut txn, &self.path).await?;
let path = if let Some(path) = paths.pop() {let (p, ambiguous) = txn.follow_oldest_path(&repo.changes, &channel, &path)?;
let mut paths = HashSet::new();for path in self.path.iter() {let (p, ambiguous) = txn.follow_oldest_path(&repo.changes, &channel, path)?;
if let Some(ref p) = path {if txn.get_touched_files(*p, Some(h_int)).is_some() {to_upload.push(h)
if paths.is_empty() {to_upload.push(h)} else {for p in paths.iter() {if txn.get_touched_files(*p, Some(h_int)).is_some() {to_upload.push(h);break;}
if let Some(ref p) = path {if txn.get_touched_files(*p, Some(h_int)).is_some() {to_upload.push(h)
if paths.is_empty() {to_upload.push(h)} else {for p in paths.iter() {if txn.get_touched_files(*p, Some(h_int)).is_some() {to_upload.push(h);break;}
let to_download = if self.changes.is_empty() {let mut paths = if let Some(p) = self.path {vec![p]} else {vec![]};let remote_changes = remote.update_changelist(&mut txn, &paths).await?;
let mut inodes: HashSet<libpijul::pristine::Position<libpijul::Hash>> = HashSet::new();let mut to_download = if self.changes.is_empty() {let remote_changes = remote.update_changelist(&mut txn, &self.path).await?;
let path = if let Some(path) = paths.pop() {let (p, ambiguous) = txn.follow_oldest_path(&repo.changes, &channel, &path)?;
let mut inodes_ = HashSet::new();for path in self.path.iter() {let (p, ambiguous) = txn.follow_oldest_path(&repo.changes, &channel, path)?;
Some(p)} else {None};
inodes_.insert(p);inodes_.extend(libpijul::fs::iter_graph_descendants(&txn,&channel.borrow(),p,));}inodes.extend(inodes_.iter().map(|x| libpijul::pristine::Position {change: txn.get_external(x.change).unwrap(),pos: x.pos,}));
debug!("recording");let hash = super::pending(&mut txn, &mut channel, &mut repo)?;remote.pull(&mut repo, &mut txn, &mut channel, &to_download, self.all).await?;
txn.output_repository_no_pending(&mut repo.working_copy,&repo.changes,&mut channel,"",true,)?;
debug!("inodes = {:?}", inodes);if inodes.is_empty() {txn.output_repository_no_pending(&mut repo.working_copy,&repo.changes,&mut channel,"",true,)?;} else {for i in inodes {let i = if let Some(change) = txn.get_internal(i.change) {libpijul::pristine::Position { change, pos: i.pos }} else {continue;};let (path, _) =libpijul::fs::find_path(&repo.changes, &txn, &channel.borrow(), false, i)?;debug!("path = {:?}", path);txn.output_repository_no_pending(&mut repo.working_copy,&repo.changes,&mut channel,&path,true,)?;}}
let inode = match *hunk {Atom::NewVertex(NewVertex { ref inode, .. }) => inode,Atom::EdgeMap(EdgeMap { ref inode, .. }) => inode,
let (inode, pos) = match *hunk {Atom::NewVertex(NewVertex {ref inode,ref flag,ref start,ref end,..}) => {if flag.contains(EdgeFlags::FOLDER) && start == end {(inode, Some(*start))} else {(inode, None)}}Atom::EdgeMap(EdgeMap { ref inode, .. }) => (inode, None),
}/// An iterator over the descendants of an/// inode key in the graph.////// Constructed using/// [`iter_graph_descendants`](fn.iter_graph_descendants.html).pub struct GraphDescendants<'txn, 'channel, T: TxnT> {txn: &'txn T,channel: &'channel Channel<T>,stack: Vec<AdjacentIterator<'txn, T>>,visited: HashSet<Position<ChangeId>>,
impl<'txn, 'channel, T: TxnT> Iterator for GraphDescendants<'txn, 'channel, T> {type Item = Position<ChangeId>;fn next(&mut self) -> Option<Self::Item> {if let Some(mut adj) = self.stack.pop() {if let Some(child) = adj.next() {self.stack.push(adj);let dest = find_block(self.txn, &self.channel, child.dest).unwrap();let grandchild = iter_adjacent(self.txn,&self.channel,dest,EdgeFlags::FOLDER,EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,).next().unwrap();if self.visited.insert(grandchild.dest) {self.stack.push(iter_adjacent(self.txn,&self.channel,grandchild.dest.inode_vertex(),EdgeFlags::FOLDER,EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,))}return Some(grandchild.dest);} else {// No child left, actually pop.}}None}}/// Returns a list of files under the given key. The root key is/// [`pristine::Vertex::ROOT`](../pristine/constant.Vertex::ROOT.html).pub fn iter_graph_descendants<'txn, 'channel, T: TxnT>(txn: &'txn T,channel: &'channel Channel<T>,key: Position<ChangeId>,) -> GraphDescendants<'txn, 'channel, T> {GraphDescendants {stack: vec![iter_adjacent(txn,&channel,key.inode_vertex(),EdgeFlags::FOLDER,EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,)],visited: HashSet::new(),txn,channel,}}