mirror of
https://github.com/facebook/sapling.git
synced 2024-10-10 08:47:12 +03:00
a4599001be
Summary: Let's make it a bit more manageable. Reviewed By: StanislavGlebik Differential Revision: D15407978 fbshipit-source-id: 490e4d48c36349cf65171f38df0ef0372883281d
292 lines
9.7 KiB
Rust
292 lines
9.7 KiB
Rust
// Copyright (c) 2004-present, Facebook, Inc.
|
|
// All Rights Reserved.
|
|
//
|
|
// This software may be used and distributed according to the terms of the
|
|
// GNU General Public License version 2 or any later version.
|
|
|
|
use clap::{App, Arg, ArgMatches, SubCommand};
|
|
use cloned::cloned;
|
|
use context::CoreContext;
|
|
use failure_ext::Error;
|
|
use futures::{future, Future, Stream};
|
|
use futures_ext::{try_boxfuture, BoxFuture, FutureExt};
|
|
use mercurial_types::HgChangesetId;
|
|
use mononoke_types::Timestamp;
|
|
use serde_json::{json, to_string_pretty};
|
|
use slog::Logger;
|
|
|
|
use blobrepo::BlobRepo;
|
|
use bookmarks::{Bookmark, BookmarkUpdateReason};
|
|
|
|
use crate::common::{fetch_bonsai_changeset, format_bookmark_log_entry};
|
|
|
|
const SET_CMD: &'static str = "set";
|
|
const GET_CMD: &'static str = "get";
|
|
const LOG_CMD: &'static str = "log";
|
|
|
|
pub fn prepare_command<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
|
|
let set = SubCommand::with_name(SET_CMD)
|
|
.about(
|
|
"sets a bookmark to a specific hg changeset, if the bookmark does not exist it will
|
|
be created",
|
|
)
|
|
.args_from_usage(
|
|
"<BOOKMARK_NAME> 'bookmark to target'
|
|
<HG_CHANGESET_ID> 'revision to which the bookmark should point to'",
|
|
);
|
|
|
|
let get = SubCommand::with_name(GET_CMD)
|
|
.about("gets the changeset of a specific bookmark")
|
|
.args_from_usage(
|
|
r#"
|
|
<BOOKMARK_NAME> 'bookmark to target'
|
|
--json 'if provided json will be returned'
|
|
"#,
|
|
)
|
|
.arg(
|
|
Arg::with_name("changeset-type")
|
|
.long("changeset-type")
|
|
.short("cs")
|
|
.takes_value(true)
|
|
.possible_values(&["bonsai", "hg"])
|
|
.required(false)
|
|
.help("What changeset type to return, either bonsai or hg. Defaults to hg."),
|
|
);
|
|
|
|
let log = SubCommand::with_name(LOG_CMD)
|
|
.about("gets the log of changesets for a specific bookmark")
|
|
.args_from_usage(
|
|
r#"
|
|
<BOOKMARK_NAME> 'bookmark to target'
|
|
--json 'if provided json will be returned'
|
|
"#,
|
|
)
|
|
.arg(
|
|
Arg::with_name("changeset-type")
|
|
.long("changeset-type")
|
|
.short("cs")
|
|
.takes_value(true)
|
|
.possible_values(&["bonsai", "hg"])
|
|
.required(false)
|
|
.help("What changeset type to return, either bonsai or hg. Defaults to hg."),
|
|
)
|
|
.arg(
|
|
Arg::with_name("limit")
|
|
.long("limit")
|
|
.short("l")
|
|
.takes_value(true)
|
|
.required(false)
|
|
.help("Imposes the limit on number of log records in output."),
|
|
);
|
|
|
|
app.about("set of commands to manipulate bookmarks")
|
|
.subcommand(set)
|
|
.subcommand(get)
|
|
.subcommand(log)
|
|
}
|
|
|
|
pub fn handle_command<'a>(
|
|
ctx: CoreContext,
|
|
repo: BoxFuture<BlobRepo, Error>,
|
|
matches: &ArgMatches<'a>,
|
|
_logger: Logger,
|
|
) -> BoxFuture<(), Error> {
|
|
match matches.subcommand() {
|
|
(GET_CMD, Some(sub_m)) => handle_get(sub_m, ctx, repo),
|
|
(SET_CMD, Some(sub_m)) => handle_set(sub_m, ctx, repo),
|
|
(LOG_CMD, Some(sub_m)) => handle_log(sub_m, ctx, repo),
|
|
_ => {
|
|
println!("{}", matches.usage());
|
|
::std::process::exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn format_output(json_flag: bool, changeset_id: String, changeset_type: &str) -> String {
|
|
if json_flag {
|
|
let answer = json!({
|
|
"changeset_type": changeset_type,
|
|
"changeset_id": changeset_id
|
|
});
|
|
to_string_pretty(&answer).unwrap()
|
|
} else {
|
|
format!("({}) {}", changeset_type.to_uppercase(), changeset_id)
|
|
}
|
|
}
|
|
|
|
fn handle_get<'a>(
|
|
args: &ArgMatches<'a>,
|
|
ctx: CoreContext,
|
|
repo: BoxFuture<BlobRepo, Error>,
|
|
) -> BoxFuture<(), Error> {
|
|
let bookmark_name = args.value_of("BOOKMARK_NAME").unwrap().to_string();
|
|
let bookmark = Bookmark::new(bookmark_name).unwrap();
|
|
let changeset_type = args.value_of("changeset-type").unwrap_or("hg");
|
|
let json_flag: bool = args.is_present("json");
|
|
|
|
match changeset_type {
|
|
"hg" => repo
|
|
.and_then(move |repo| repo.get_bookmark(ctx, &bookmark))
|
|
.and_then(move |cs| {
|
|
let changeset_id_str = cs.expect("bookmark could not be found").to_string();
|
|
let output = format_output(json_flag, changeset_id_str, "hg");
|
|
println!("{}", output);
|
|
future::ok(())
|
|
})
|
|
.boxify(),
|
|
|
|
"bonsai" => repo
|
|
.and_then(move |repo| {
|
|
fetch_bonsai_changeset(ctx, bookmark.to_string().as_str(), &repo).and_then(
|
|
move |bonsai_cs| {
|
|
let changeset_id_str = bonsai_cs.get_changeset_id().to_string();
|
|
let output = format_output(json_flag, changeset_id_str, "bonsai");
|
|
println!("{}", output);
|
|
future::ok(())
|
|
},
|
|
)
|
|
})
|
|
.boxify(),
|
|
|
|
_ => panic!("Unknown changeset-type supplied"),
|
|
}
|
|
}
|
|
|
|
fn list_hg_bookmark_log_entries(
|
|
repo: BlobRepo,
|
|
ctx: CoreContext,
|
|
name: Bookmark,
|
|
max_rec: u32,
|
|
) -> impl Stream<
|
|
Item = BoxFuture<(Option<HgChangesetId>, BookmarkUpdateReason, Timestamp), Error>,
|
|
Error = Error,
|
|
> {
|
|
repo.list_bookmark_log_entries(ctx.clone(), name, max_rec)
|
|
.map({
|
|
cloned!(ctx, repo);
|
|
move |(cs_id, rs, ts)| match cs_id {
|
|
Some(x) => repo
|
|
.get_hg_from_bonsai_changeset(ctx.clone(), x)
|
|
.map(move |cs| (Some(cs), rs, ts))
|
|
.boxify(),
|
|
None => future::ok((None, rs, ts)).boxify(),
|
|
}
|
|
})
|
|
}
|
|
|
|
fn handle_log<'a>(
|
|
args: &ArgMatches<'a>,
|
|
ctx: CoreContext,
|
|
repo: BoxFuture<BlobRepo, Error>,
|
|
) -> BoxFuture<(), Error> {
|
|
let bookmark_name = args.value_of("BOOKMARK_NAME").unwrap().to_string();
|
|
let bookmark = Bookmark::new(bookmark_name).unwrap();
|
|
let changeset_type = args.value_of("changeset-type").unwrap_or("hg");
|
|
let json_flag = args.is_present("json");
|
|
let output_limit_as_string = args.value_of("limit").unwrap_or("25");
|
|
let max_rec = match output_limit_as_string.parse::<u32>() {
|
|
Ok(n) => n,
|
|
Err(e) => panic!(
|
|
"Bad limit value supplied: \"{}\" - {}",
|
|
output_limit_as_string, e
|
|
),
|
|
};
|
|
match changeset_type {
|
|
"hg" => repo
|
|
.and_then(move |repo| {
|
|
list_hg_bookmark_log_entries(repo, ctx, bookmark.clone(), max_rec)
|
|
.buffered(100)
|
|
.map(move |rows| {
|
|
let (cs_id, reason, timestamp) = rows;
|
|
let cs_id_str = match cs_id {
|
|
None => String::new(),
|
|
Some(x) => x.to_string(),
|
|
};
|
|
let output = format_bookmark_log_entry(
|
|
json_flag,
|
|
cs_id_str,
|
|
reason,
|
|
timestamp,
|
|
"hg",
|
|
bookmark.clone(),
|
|
None,
|
|
);
|
|
println!("{}", output);
|
|
})
|
|
.for_each(|_x| Ok(()))
|
|
})
|
|
.boxify(),
|
|
|
|
"bonsai" => repo
|
|
.and_then(move |repo| {
|
|
repo.list_bookmark_log_entries(ctx, bookmark.clone(), max_rec)
|
|
.map(move |rows| {
|
|
let (cs_id, reason, timestamp) = rows;
|
|
let cs_id_str = match cs_id {
|
|
None => String::new(),
|
|
Some(x) => x.to_string(),
|
|
};
|
|
let output = format_bookmark_log_entry(
|
|
json_flag,
|
|
cs_id_str,
|
|
reason,
|
|
timestamp,
|
|
"bonsai",
|
|
bookmark.clone(),
|
|
None,
|
|
);
|
|
println!("{}", output);
|
|
})
|
|
.for_each(|_| Ok(()))
|
|
})
|
|
.boxify(),
|
|
|
|
_ => panic!("Unknown changeset-type supplied"),
|
|
}
|
|
}
|
|
|
|
fn handle_set<'a>(
|
|
args: &ArgMatches<'a>,
|
|
ctx: CoreContext,
|
|
repo: BoxFuture<BlobRepo, Error>,
|
|
) -> BoxFuture<(), Error> {
|
|
let bookmark_name = args.value_of("BOOKMARK_NAME").unwrap().to_string();
|
|
let rev = args.value_of("HG_CHANGESET_ID").unwrap().to_string();
|
|
let bookmark = Bookmark::new(bookmark_name).unwrap();
|
|
|
|
repo.and_then(move |repo| {
|
|
fetch_bonsai_changeset(ctx.clone(), &rev, &repo).and_then(move |bonsai_cs| {
|
|
let mut transaction = repo.update_bookmark_transaction(ctx);
|
|
try_boxfuture!(transaction.force_set(
|
|
&bookmark,
|
|
bonsai_cs.get_changeset_id(),
|
|
BookmarkUpdateReason::ManualMove
|
|
));
|
|
transaction.commit().map(|_| ()).from_err().boxify()
|
|
})
|
|
})
|
|
.boxify()
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn json_output_format() {
|
|
let expected_answer = json!({
|
|
"changeset_type": "hg",
|
|
"changeset_id": "123"
|
|
});
|
|
assert_eq!(
|
|
format_output(true, "123".to_string(), "hg"),
|
|
to_string_pretty(&expected_answer).unwrap()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn plain_output_format() {
|
|
assert_eq!(format_output(false, "123".to_string(), "hg"), "(HG) 123");
|
|
}
|
|
}
|