pijul 1.0.0-alpha.9

The sound distributed version control system.
use std::path::PathBuf;

use clap::Clap;
use libpijul::{MutTxnT, MutTxnTExt, TxnT};
use log::debug;

use crate::repository::Repository;

#[derive(Clap, Debug)]
pub struct Unrecord {
    /// 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>,
    /// Unrecord changes from this channel instead of the current channel
    #[clap(long = "channel")]
    channel: Option<String>,
    /// Identifier of the change (unambiguous prefixes are accepted)
    #[clap(multiple = true)]
    change_id: Vec<String>,
}

impl Unrecord {
    pub fn run(self) -> Result<(), anyhow::Error> {
        let repo = Repository::find_root(self.repo_path)?;
        debug!("{:?}", repo.config);
        let channel_name = repo.config.get_current_channel(self.channel.as_ref());
        let mut txn = repo.pristine.mut_txn_begin();
        if let Some(mut channel) = txn.load_channel(channel_name) {
            let mut changes = Vec::new();
            let channel_ = channel.borrow();
            for c in self.change_id {
                let (hash, change_id) = txn.hash_from_prefix(&c)?;
                let n = txn
                    .get_changeset(&channel_.changes, change_id, None)
                    .unwrap();
                changes.push((hash, change_id, n, c))
            }
            std::mem::drop(channel_);
            changes.sort_by(|a, b| b.2.cmp(&a.2));
            for (hash, change_id, _, c) in changes {
                let channel_ = channel.borrow();
                for (p, d) in txn.iter_revdep(change_id) {
                    if p < change_id {
                        continue;
                    } else if p > change_id {
                        break;
                    }
                    if txn.get_changeset(&channel_.changes, d, None).is_some() {
                        return Err((crate::Error::CannotUnrecord {
                            change: c,
                            dep: txn.get_external(d).unwrap(),
                        })
                        .into());
                    }
                }
                std::mem::drop(channel_);
                txn.unrecord(&repo.changes, &mut channel, &hash)?;
            }
        }
        txn.commit()?;
        Ok(())
    }
}