pijul 0.12.2

A patch-based distributed version control system, easy to use and fast. Command-line interface.
use clap::ArgMatches;
use commands::BasicOptions;
use error::Error;
use libpijul;
use libpijul::fs_representation::RepoRoot;
use rand;
use std::fs::{canonicalize, metadata};
use std::path::{Path, PathBuf};

#[derive(Debug, Clone, Copy)]
pub enum Operation {
    Add,
    Remove,
}

pub fn run(args: &ArgMatches, op: Operation) -> Result<(), Error> {
    debug!("fs_operation {:?}", op);
    let opts = BasicOptions::from_args(args)?;

    debug!("repo {:?}", &opts.repo_root);
    let mut extra_space = 409600;
    let recursive = args.is_present("recursive");
    loop {
        let touched_files = match args.values_of("files") {
            Some(l) => l.map(|p| Path::new(p).to_owned()).collect(),
            None => vec![],
        };
        match really_run(
            &opts.repo_root,
            &opts.cwd,
            touched_files,
            recursive,
            op,
            extra_space,
        ) {
            Err(ref e) if e.lacks_space() => extra_space *= 2,
            e => return e,
        }
    }
}

fn really_run(
    //    repo_dir: &RepoRoot<&'static Path>,
    repo_dir: &RepoRoot<PathBuf>,
    wd: &Path,
    mut files: Vec<PathBuf>,
    recursive: bool,
    op: Operation,
    extra_space: u64,
) -> Result<(), Error> {
    debug!("files {:?}", files);
    let mut rng = rand::thread_rng();
    let repo = repo_dir.open_repo(Some(extra_space))?;
    let mut txn = repo.mut_txn_begin(&mut rng)?;
    match op {
        Operation::Add => {
            for file_ in files.drain(..) {
                let p = canonicalize(wd.join(&file_))?;
                if recursive {
                    debug!("adding from {:?}", p);
                    let mut files = Vec::new();
                    for file in repo_dir.untracked_files(&txn, &p) {
                        debug!("untracked {:?}", file);
                        let m = metadata(repo_dir.absolutize(&file))?;
                        files.push((file, m.is_dir()));
                    }
                    for (file, is_dir) in files {
                        match txn.add_file(&file, is_dir) {
                            Ok(()) => {}
                            Err(libpijul::Error::AlreadyAdded) => {
                                eprintln!("{:?} is already in the repository", file_)
                            }
                            Err(e) => return Err(e.into()),
                        }
                    }
                    continue;
                } else {
                    let m = metadata(&p)?;
                    if let Ok(file) = repo_dir.relativize(&p) {
                        match txn.add_file(&file, m.is_dir()) {
                            Ok(()) => {}
                            Err(libpijul::Error::AlreadyAdded) => {
                                eprintln!("{:?} is already in the repository", file_)
                            }
                            Err(e) => return Err(e.into()),
                        }
                        continue;
                    }
                }
                return Err(Error::InvalidPath { path: file_ });
            }
        }
        Operation::Remove => {
            for file in &files[..] {
                debug!("file: {:?} {:?}", file, wd.join(file));
                let p = wd.join(file).canonicalize()?;
                debug!("p: {:?}", p);
                if let Ok(file) = repo_dir.relativize(&p) {
                    debug!("remove_file {:?}", file);
                    txn.remove_file(&file)?
                } else {
                    return Err(Error::InvalidPath {
                        path: file.to_path_buf(),
                    });
                }
            }
        }
    }
    txn.commit()?;
    Ok(())
}