pijul 0.12.2

A patch-based distributed version control system, easy to use and fast. Command-line interface.
use clap::{Arg, ArgMatches, SubCommand};
use commands::{default_explain, BasicOptions, StaticSubcommand};
use error::Error;
use libpijul::fs_representation::{RepoPath, RepoRoot};
use libpijul::status::{unrecorded_changes, ChangeType};
use rand;

use std::path::{Path, PathBuf};
use std::rc::Rc;

const UNRECORDED_FILES: &'static str = r#"
Changes not yet recorded:
  (use "pijul record ..." to record a new patch)
"#;

const UNTRACKED_FILES: &'static str = r#"
Untracked files:
  (use "pijul add <file>..." to track them)
"#;

const CONFLICTED_FILES: &'static str = r#"
Unresolved conflicts:
  (fix conflicts and record the resolution with "pijul record ...")
"#;

pub fn invocation() -> StaticSubcommand {
    SubCommand::with_name("status")
        .about("Show working tree status")
        .arg(
            Arg::with_name("repository")
                .long("repository")
                .takes_value(true)
                .help("Local repository."),
        )
        .arg(
            Arg::with_name("short")
                .long("short")
                .short("s")
                .help("Output in short format"),
        )
}

pub fn explain(r: Result<(), Error>) {
    default_explain(r)
}

pub fn run(args: &ArgMatches) -> Result<(), Error> {
    let opts = BasicOptions::from_args(args)?;
    let current_branch = opts.branch();
    let repo = opts.open_and_grow_repo(409600)?;
    let short = args.is_present("short");

    let (unrecorded, untracked, conflicts) = {
        let mut txn = repo.mut_txn_begin(rand::thread_rng())?;

        let mut unrecorded = unrecorded_changes(&mut txn, &opts.repo_root, &current_branch)?;
        unrecorded.sort_unstable();

        let mut untracked: Vec<RepoPath<PathBuf>> = opts
            .repo_root
            .untracked_files(&txn, &opts.repo_root.repo_root)
            .collect();
        untracked.sort_unstable();

        let mut conflicts = txn.list_conflict_files(&current_branch, &[])?;
        conflicts.sort_unstable();

        (unrecorded, untracked, conflicts)
    };

    if short {
        print_shortstatus(&opts.cwd, &opts.repo_root, unrecorded, untracked, conflicts);
    } else {
        print_longstatus(&current_branch, unrecorded, untracked, conflicts);
    }
    Ok(())
}

fn print_longstatus(
    branch: &str,
    changed: Vec<(Rc<RepoPath<PathBuf>>, ChangeType)>,
    untracked: Vec<RepoPath<PathBuf>>,
    conflicts: Vec<RepoPath<PathBuf>>,
) {
    println!("On branch {}", branch);
    if changed.is_empty() && untracked.is_empty() && conflicts.is_empty() {
        println!("Nothing to record, working tree clean");
    }

    if !conflicts.is_empty() {
        println!("{}", CONFLICTED_FILES);
        for f in conflicts {
            println!("        {}", f.display());
        }
    }

    if !changed.is_empty() {
        println!("{}", UNRECORDED_FILES);
        for (f, t) in changed {
            println!("        {:10} {}", t.long(), f.display());
        }
    }

    if !untracked.is_empty() {
        println!("{}", UNTRACKED_FILES);
        for f in untracked {
            println!("        {}", f.display());
        }
    }
}

fn print_shortstatus(
    cwd: &Path,
    repo_root: &RepoRoot<impl AsRef<Path>>,
    changed: Vec<(Rc<RepoPath<PathBuf>>, ChangeType)>,
    untracked: Vec<RepoPath<PathBuf>>,
    conflicts: Vec<RepoPath<PathBuf>>,
) {
    for f in conflicts {
        debug!("{:?} {:?}", repo_root.repo_root.as_ref(), f.as_path());
        println!(
            "C {}",
            pathdiff::diff_paths(&repo_root.repo_root.as_ref().join(f.as_path()), &cwd)
                .unwrap()
                .display()
        );
    }
    for (f, t) in changed {
        debug!("{:?} {:?}", repo_root.repo_root.as_ref(), f.as_path());
        println!(
            "{} {}",
            t.short(),
            pathdiff::diff_paths(&repo_root.repo_root.as_ref().join(f.as_path()), &cwd)
                .unwrap()
                .display()
        )
    }
    for f in untracked {
        debug!("{:?} {:?}", f.as_path(), cwd);
        println!(
            "? {}",
            pathdiff::diff_paths(&repo_root.repo_root.as_ref().join(f.as_path()), &cwd)
                .unwrap()
                .display()
        )
    }
}