pijul 1.0.0-alpha.9

The sound distributed version control system.
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;

use clap::Clap;
use libpijul::changestore::ChangeStore;
use libpijul::{Hash, MutTxnT, MutTxnTExt, TxnT, TxnTExt};

use crate::repository::Repository;

#[derive(Clap, Debug)]
pub struct Upgrade {
    /// Set the repository where this command should run Defaults to the first ancestor of the current directory that contains a `.pijul` directory.
    #[clap(long = "repository")]
    repo_path: Option<PathBuf>,
}

impl Upgrade {
    pub fn run(self) -> Result<(), anyhow::Error> {
        let mut channels = HashMap::new();
        {
            let repo = Repository::find_root(self.repo_path.clone())?;
            let txn = repo.pristine.txn_begin()?;
            let mut hashes = HashSet::new();
            for channel in txn.iter_channels("") {
                let channel = channel.borrow();
                let name = channel.name();
                let e = channels.entry(name.to_string()).or_insert(Vec::new());
                hashes.clear();
                for (_, (h, _)) in txn.reverse_log(&channel, None) {
                    if !hashes.insert(h) {
                        continue;
                    }
                    let path = repo.changes.filename(&h);
                    let change = libpijul::change::v3::LocalChange3::deserialize(
                        path.to_str().unwrap(),
                        Some(&h),
                    )
                    .unwrap();
                    e.push((h, change))
                }
            }
            std::fs::rename(repo.path.join(".pijul"), repo.path.join(".pijul.old"))?;
        }
        let repo2 = Repository::init(self.repo_path)?;
        let mut txn2 = repo2.pristine.mut_txn_begin();
        let mut translations = HashMap::new();
        translations.insert(None, None);
        translations.insert(Some(Hash::None), Some(Hash::None));
        for (channel_name, mut changes) in channels {
            let mut channel = txn2.open_or_create_channel(&channel_name)?;
            while let Some((old_h, c)) = changes.pop() {
                let h = repo2.changes.save_change(&c.to_v4(&translations))?;
                translations.insert(Some(old_h), Some(h));
                txn2.apply_change(&repo2.changes, &mut channel, h)?;
            }
        }
        txn2.commit()?;
        Ok(())
    }
}