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,
}
}