use super::default_explain;
use super::record;
use chrono;
use clap::{ArgMatches, SubCommand};
use commands::hooks::run_hook;
use commands::record::{decide_authors, decide_patch_message, record_args};
use commands::{BasicOptions, StaticSubcommand};
use error::Error;
use libpijul::patch::PatchFlags;
use libpijul::Hash;
use meta::{load_signing_key, Global, Meta};
use std::collections::HashSet;
use std::mem::drop;
pub fn invocation() -> StaticSubcommand {
record_args(
SubCommand::with_name("tag").about(
"Create a tag, i.e. an empty patch with all patches on the branch as dependencies",
),
)
}
pub fn run(args: &ArgMatches) -> Result<Option<Hash>, Error> {
let opts = BasicOptions::from_args(args)?;
let patch_name_arg = args.value_of("message");
let patch_descr_arg = args.value_of("description");
let authors_arg = args.values_of("author").map(|x| x.collect::<Vec<_>>());
let branch_name = opts.branch();
let mut save_meta = false;
let (mut global, save_global) = Global::load().map(|g| (g, false)).unwrap_or_else(|e| {
info!("loading global key, error {:?}", e);
(Global::new(), true)
});
let mut meta = match Meta::load(&opts.repo_root) {
Ok(m) => m,
Err(_) => {
save_meta = true;
Meta::new()
}
};
let repo = opts.open_repo()?;
let patch = {
let txn = repo.txn_begin()?;
debug!("meta:{:?}", meta);
let authors = decide_authors(authors_arg, &meta, &global)?;
if meta.authors.len() == 0 {
meta.authors = authors.clone();
save_meta = true;
}
if global.author.is_none() {
global.author = Some(authors[0].clone());
}
debug!("authors:{:?}", authors);
let (patch_name, description) = decide_patch_message(
patch_name_arg,
patch_descr_arg,
String::from(""),
!args.is_present("no-editor"),
&opts.repo_root,
&meta,
&global,
)?;
run_hook(&opts.repo_root, "patch-name", Some(&patch_name))?;
debug!("patch_name:{:?}", patch_name);
if save_meta {
meta.save(&opts.repo_root)?
}
if save_global {
global.save()?
}
debug!("new");
let branch = txn.get_branch(&branch_name).unwrap();
let mut included = HashSet::new();
let mut patches = Vec::new();
for (_, patch) in txn.rev_iter_applied(&branch, None) {
let mut already_in = false;
for (p, revdep) in txn.iter_revdep(Some((patch, None))) {
if p == patch {
if included.contains(&revdep) {
already_in = true
}
} else {
break;
}
}
if !already_in {
let patch = txn.get_external(patch).unwrap();
patches.push(patch.to_owned());
}
included.insert(patch.to_owned());
}
txn.new_patch(
&branch,
authors,
patch_name,
description,
chrono::Utc::now(),
Vec::new(),
patches.into_iter(),
PatchFlags::TAG,
)
};
drop(repo);
let patches_dir = opts.repo_root.patches_dir();
let mut key = meta
.signing_key
.or(global.signing_key)
.and_then(|s| load_signing_key(s).ok());
let hash = if let Some(ref mut key) = key {
key.check_author(&patch.header().authors)?;
patch.save(&patches_dir, key.keys.get_mut(0))?
} else {
patch.save(&patches_dir, None)?
};
let pristine_dir = opts.pristine_dir();
let mut increase = 40960;
loop {
match record::record_no_resize(
&pristine_dir,
&opts.repo_root,
&branch_name,
&hash,
&patch,
&HashSet::new(),
increase,
) {
Err(ref e) if e.lacks_space() => increase *= 2,
_ => break,
}
}
Ok(Some(hash))
}
pub fn explain(res: Result<Option<Hash>, Error>) {
default_explain(res)
}