}
}
#[allow(
clippy::await_holding_lock,
reason = "imposed by sanakirja API for txn"
)]
async fn pull(repo_path: &Path) -> Result<(), anyhow::Error> {
let mut repo = pijul::Repository::find_root(Some(repo_path))?;
let channel_name = current_channel(&repo)?;
let txn = repo.pristine.arc_txn_begin()?;
let mut channel = txn.write().open_or_create_channel(&channel_name)?;
let remote_name = if let Some(ref def) = repo.config.default_remote {
def
} else {
bail!("Missing remote")
};
// TODO why no same as current channel_name?
let from_channel = libpijul::DEFAULT_CHANNEL;
let no_cert_check = false;
let mut remote = pijul_remote::repository(
&repo,
Some(&repo.path),
None,
remote_name,
from_channel,
no_cert_check,
true,
)
.await?;
let RemoteDelta {
inodes,
remote_ref,
to_download,
remote_unrecs: _,
..
} = to_download(&mut txn.write(), &mut channel, &mut repo, &mut remote)
.await?;
let hash = pending(txn.clone(), &channel, &mut repo)?;
if let Some(ref r) = remote_ref {
remote.update_identities(&mut repo, r).await?;
}
// TODO: Show remote unrecords
// notify_remote_unrecords(&repo, remote_unrecs.as_slice());
if to_download.is_empty() {
if let Some(ref h) = hash {
txn.write().unrecord(&repo.changes, &channel, h, 0)?;
}
txn.commit()?;
bail!("Nothing to pull");
}
{
// Now that .pull is always given `false` for `do_apply`...
let mut ws = libpijul::ApplyWorkspace::new();
debug!("to_download = {:#?}", to_download);
let mut channel = channel.write();
let mut txn = txn.write();
for h in to_download.iter().rev() {
match h {
pijul_remote::CS::Change(h) => {
txn.apply_change_rec_ws(
&repo.changes,
&mut channel,
h,
&mut ws,
)?;
}
pijul_remote::CS::State(s) => {
if let Some(n) =
txn.channel_has_state(&channel.states, &s.into())?
{
txn.put_tags(&mut channel.tags, n.into(), s)?;
} else {
bail!(
"Cannot add tag {}: channel {:?} does not have that state",
libpijul::Base32::to_base32(s),
channel.name
)
}
}
}
}
}
let full = false;
remote
.complete_changes(&repo, &*txn.read(), &mut channel, &to_download, full)
.await?;
remote.finish().await?;
debug!("inodes = {:?}", inodes);
debug!("to_download: {:?}", to_download.len());
let mut touched = HashSet::new();
let txn_ = txn.read();
for d in to_download.iter() {
debug!("to_download {:?}", d);
match d {
pijul_remote::CS::Change(d) => {
use pijul::GraphTxnT;
if let Some(int) = txn_.get_internal(&d.into())? {
use pijul::DepsTxnT;
for inode in txn_.iter_rev_touched(int)? {
let (int_, inode) = inode?;
if int_ < int {
continue;
} else if int_ > int {
break;
}
use pijul::GraphTxnT;
let ext = libpijul::pristine::Position {
change: txn_
.get_external(&inode.change)?
.unwrap()
.into(),
pos: inode.pos,
};
if inodes.is_empty() || inodes.contains(&ext) {
touched.insert(*inode);
}
}
}
}
pijul_remote::CS::State(_) => {
// No need to do anything for now here, we don't
// output after downloading a tag.
}
}
}
std::mem::drop(txn_);
let is_current_channel = true;
if is_current_channel {
let mut touched_paths = BTreeSet::new();
{
let txn_ = txn.read();
for &i in touched.iter() {
if let Some((path, _)) = libpijul::fs::find_path(
&repo.changes,
&*txn_,
&*channel.read(),
false,
i,
)? {
touched_paths.insert(path);
} else {
touched_paths.clear();
break;
}
}
}
if touched_paths.is_empty() {
touched_paths.insert(String::from(""));
}
let mut last: Option<&str> = None;
let mut conflicts = Vec::new();
for path in touched_paths.iter() {
if let Some(last_path) = last {
// If `last_path` is a prefix (in the path sense) of `path`,
// skip.
if last_path.len() < path.len() {
let (pre_last, post_last) = path.split_at(last_path.len());
if pre_last == last_path && post_last.starts_with("/") {
continue;
}
}
}
debug!("path = {:?}", path);
conflicts.extend(
libpijul::output::output_repository_no_pending(
&repo.working_copy,
&repo.changes,
&txn,
&channel,
path,
true,
None,
std::thread::available_parallelism()?.get(),
0,
)?
.into_iter(),
);
last = Some(path)
}
// TODO: handle conflicts
// print_conflicts(&conflicts)?;
}
if let Some(h) = hash {
txn.write().unrecord(&repo.changes, &channel, &h, 0)?;
repo.changes.del_change(&h)?;