mononoke: add the filenode subcommand to the admin tool

Summary:
This is useful to inspect the Mercurial filenodes in Mononoke, like in S183272.

For example, I intend to use this subcommand to verify how well the future linknode healing works.

Reviewed By: krallin

Differential Revision: D16621516

fbshipit-source-id: 4266f85bce29b59072bf9c4f3e63777dae09a4f1
This commit is contained in:
Kostia Balytskyi 2019-08-02 12:42:02 -07:00 committed by Facebook Github Bot
parent 1420897ff8
commit bc985480e9
3 changed files with 131 additions and 1 deletions

View File

@ -23,3 +23,4 @@ pub const SKIPLIST_BUILD: &'static str = "build";
pub const SKIPLIST_READ: &'static str = "read";
pub const ADD_PUBLIC_PHASES: &'static str = "add-public-phases";
pub const BLACKLIST: &'static str = "blacklist";
pub const FILENODES: &'static str = "filenodes";

109
cmds/admin/filenodes.rs Normal file
View File

@ -0,0 +1,109 @@
// 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::ArgMatches;
use cmdlib::args;
use crate::common::{get_file_nodes, resolve_hg_rev};
use cloned::cloned;
use context::CoreContext;
use failure_ext::{format_err, Error};
use filenodes::FilenodeInfo;
use futures::future::{join_all, Future};
use futures_ext::{try_boxfuture, BoxFuture, FutureExt};
use mercurial_types::MPath;
use mononoke_types::RepoPath;
use slog::{debug, info, Logger};
pub fn subcommand_filenodes(
logger: Logger,
matches: &ArgMatches<'_>,
sub_m: &ArgMatches<'_>,
) -> BoxFuture<(), Error> {
let rev = sub_m
.value_of("hg-changeset-or-bookmark")
.unwrap()
.to_string();
let paths: Result<Vec<_>, Error> = sub_m
.values_of("paths")
.expect("at least one file")
.map(|path| {
let mpath = MPath::new(path);
match mpath {
Ok(mpath) => Ok(mpath),
Err(_) => Err(format_err!(
"The following path could not be parsed {}",
path
)),
}
})
.collect();
let paths: Vec<_> = try_boxfuture!(paths);
let ctx = CoreContext::test_mock();
args::init_cachelib(&matches);
args::open_repo(&logger, &matches)
.and_then({
cloned!(ctx);
move |blobrepo| {
resolve_hg_rev(ctx.clone(), &blobrepo, &rev).map(|cs_id| (blobrepo, cs_id))
}
})
.and_then({
cloned!(ctx, logger);
move |(blobrepo, cs_id)| {
debug!(logger, "using commit: {:?}", cs_id);
get_file_nodes(ctx.clone(), logger.clone(), &blobrepo, cs_id, paths.clone())
.map(|filenode_ids| (blobrepo, paths.into_iter().zip(filenode_ids.into_iter())))
}
})
.and_then({
cloned!(ctx);
move |(blobrepo, path_filenode_ids)| {
join_all(
path_filenode_ids
.into_iter()
.map(move |(path, filenode_id)| {
blobrepo.get_filenode(
ctx.clone(),
&RepoPath::FilePath(path),
filenode_id,
)
}),
)
}
})
.map(move |filenodes| {
filenodes.into_iter().for_each(|filenode| {
let FilenodeInfo {
path,
filenode,
p1,
p2,
copyfrom,
linknode,
} = filenode;
info!(
logger,
"Filenode {:?}:\n \
-- path: {:?}\n \
-- p1: {:?}\n \
-- p2: {:?}\n \
-- copyfrom: {:?}\n \
-- linknode: {:?}",
filenode,
path,
p1,
p2,
copyfrom,
linknode
);
});
})
.boxify()
}

View File

@ -19,11 +19,12 @@ use crate::blobstore_fetch::subcommand_blobstore_fetch;
use crate::bonsai_fetch::subcommand_bonsai_fetch;
use crate::cmdargs::{
ADD_PUBLIC_PHASES, BLACKLIST, BLOBSTORE_FETCH, BONSAI_FETCH, BOOKMARKS, CONTENT_FETCH,
HASH_CONVERT, HG_CHANGESET, HG_CHANGESET_DIFF, HG_CHANGESET_RANGE, HG_SYNC_BUNDLE,
FILENODES, HASH_CONVERT, HG_CHANGESET, HG_CHANGESET_DIFF, HG_CHANGESET_RANGE, HG_SYNC_BUNDLE,
HG_SYNC_FETCH_BUNDLE, HG_SYNC_LAST_PROCESSED, HG_SYNC_REMAINS, HG_SYNC_SHOW, HG_SYNC_VERIFY,
SKIPLIST, SKIPLIST_BUILD, SKIPLIST_READ,
};
use crate::content_fetch::subcommand_content_fetch;
use crate::filenodes::subcommand_filenodes;
use crate::hash_convert::subcommand_hash_convert;
use crate::hg_changeset::subcommand_hg_changeset;
use crate::hg_sync::subcommand_process_hg_sync;
@ -37,6 +38,7 @@ mod bookmarks_manager;
mod cmdargs;
mod common;
mod content_fetch;
mod filenodes;
mod hash_convert;
mod hg_changeset;
mod hg_sync;
@ -266,6 +268,22 @@ fn setup_app<'a, 'b>() -> App<'a, 'b> {
"#,
);
let filenodes = SubCommand::with_name(FILENODES)
.about("fetches hg filenodes information for a commit and one or more paths")
.arg(
Arg::with_name("hg-changeset-or-bookmark")
.required(true)
.takes_value(true)
.help("hg chageset to lookup filenodes for"),
)
.arg(
Arg::with_name("paths")
.required(true)
.multiple(true)
.takes_value(true)
.help("a list of file paths to lookup filenodes for"),
);
let app = args::MononokeApp {
safe_writes: false,
hide_advanced_args: true,
@ -287,6 +305,7 @@ fn setup_app<'a, 'b>() -> App<'a, 'b> {
.subcommand(hg_sync)
.subcommand(add_public_phases)
.subcommand(blacklist)
.subcommand(filenodes)
}
fn main() -> Result<()> {
@ -314,6 +333,7 @@ fn main() -> Result<()> {
(HASH_CONVERT, Some(sub_m)) => subcommand_hash_convert(logger, &matches, sub_m),
(ADD_PUBLIC_PHASES, Some(sub_m)) => subcommand_add_public_phases(logger, &matches, sub_m),
(BLACKLIST, Some(sub_m)) => subcommand_blacklist(logger, &matches, sub_m),
(FILENODES, Some(sub_m)) => subcommand_filenodes(logger, &matches, sub_m),
_ => {
eprintln!("{}", matches.usage());
::std::process::exit(1);