admin: add blame-v2 to blame compute

Summary:
Add `--blame-v2` to `mononoke_admin blame compute`.  This can be used to compute
blames in the new format and validate that they are correct.

Reviewed By: mitrandir77

Differential Revision: D28183160

fbshipit-source-id: f698a77c109bfce05aeb66cd405c6f20bf158801
This commit is contained in:
Mark Juggurnauth-Thomas 2021-05-06 05:44:48 -07:00 committed by Facebook GitHub Bot
parent 5fe6d54116
commit 9d6a583988

View File

@ -30,6 +30,7 @@ use futures::{
use manifest::ManifestOps;
use mononoke_types::{
blame::{Blame, BlameMaybeRejected, BlameRejected},
blame_v2::BlameV2,
BlameId, ChangesetId, FileUnodeId, MPath,
};
use slog::Logger;
@ -47,6 +48,13 @@ const ARG_CSID: &str = "csid";
const ARG_PATH: &str = "path";
const ARG_PRINT_ERRORS: &str = "print-errors";
const ARG_LINE: &str = "line";
const ARG_BLAME_V2: &str = "blame-v2";
#[derive(Clone, Debug)]
enum EitherBlame {
V1(Blame),
V2(BlameV2),
}
pub fn build_subcommand<'a, 'b>() -> App<'a, 'b> {
let csid_arg = Arg::with_name(ARG_CSID)
@ -63,6 +71,11 @@ pub fn build_subcommand<'a, 'b>() -> App<'a, 'b> {
.long("line-number")
.takes_value(false)
.required(false);
let blame_v2_arg = Arg::with_name(ARG_BLAME_V2)
.help("use blame-v2")
.long("blame-v2")
.takes_value(false)
.required(false);
SubCommand::with_name(BLAME)
.about("fetch/derive blame for specified changeset and path")
@ -81,7 +94,8 @@ pub fn build_subcommand<'a, 'b>() -> App<'a, 'b> {
SubCommand::with_name(COMMAND_COMPUTE)
.arg(line_number_arg.clone())
.arg(csid_arg.clone())
.arg(path_arg.clone()),
.arg(path_arg.clone())
.arg(blame_v2_arg.clone()),
)
.subcommand(
SubCommand::with_name(COMMAND_FIND_REJECTED)
@ -120,8 +134,9 @@ pub async fn subcommand_blame<'a>(
(COMMAND_COMPUTE, Some(matches)) => {
let repo = args::open_repo(fb, &logger, toplevel_matches).await?;
let line_number = matches.is_present(ARG_LINE);
let blame_v2 = matches.is_present(ARG_BLAME_V2);
with_changeset_and_path(ctx, repo, matches, move |ctx, repo, csid, path| {
subcommand_compute_blame(ctx, repo, csid, path, line_number)
subcommand_compute_blame(ctx, repo, csid, path, line_number, blame_v2)
})
.await
}
@ -196,7 +211,8 @@ async fn subcommand_show_blame(
line_number: bool,
) -> Result<(), Error> {
let (content, blame) = fetch_blame(&ctx, &repo, csid, path).await?;
let annotate = blame_hg_annotate(ctx, repo, content, blame, line_number).await?;
let annotate =
blame_hg_annotate(ctx, repo, content, EitherBlame::V1(blame), line_number).await?;
println!("{}", annotate);
Ok(())
}
@ -275,13 +291,14 @@ async fn diff(
Ok(String::from_utf8_lossy(&diff).into_owned())
}
/// Recalculate balme by going through whole history of a file
/// Recalculate blame by going through whole history of a file
async fn subcommand_compute_blame(
ctx: CoreContext,
repo: BlobRepo,
csid: ChangesetId,
path: MPath,
line_number: bool,
blame_v2: bool,
) -> Result<(), Error> {
let blobstore = repo.get_blobstore().boxed();
let blame_mapping = BlameRoot::default_mapping(&ctx, &repo)?;
@ -318,7 +335,7 @@ async fn subcommand_compute_blame(
.map(|unode_id| (*unode_id, path.clone()))
.chain(copy_parent)
.collect();
Ok(((csid, path, file_unode_id), parents))
Ok::<_, Error>(((csid, path, file_unode_id), parents))
}
.boxed()
}
@ -326,7 +343,7 @@ async fn subcommand_compute_blame(
{
|
(csid, path, file_unode_id),
parents: Iter<Result<(bytes_05::Bytes, Blame), BlameRejected>>,
parents: Iter<Result<(bytes_05::Bytes, EitherBlame), BlameRejected>>,
| {
cloned!(ctx, repo);
async move {
@ -334,14 +351,36 @@ async fn subcommand_compute_blame(
fetch_file_full_content(&ctx, &repo, file_unode_id, blame_options).await?;
match content {
Err(rejected) => Ok(Err(rejected)),
Ok(content) => {
Ok(content) => if blame_v2 {
let parents = parents
.into_iter()
.filter_map(|result| result.ok())
.filter_map(|parent| match parent {
Ok((content, EitherBlame::V2(blame))) => Some((content, blame)),
_ => None,
})
.collect();
Blame::from_parents(csid, content.clone(), path.clone(), parents)
.map(move |blame| Ok((content, blame)))
Ok(EitherBlame::V2(BlameV2::new(
csid,
path.clone(),
content.clone(),
parents,
)?))
} else {
let parents = parents
.into_iter()
.filter_map(|parent| match parent {
Ok((content, EitherBlame::V1(blame))) => Some((content, blame)),
_ => None,
})
.collect();
Ok(EitherBlame::V1(Blame::from_parents(
csid,
content.clone(),
path.clone(),
parents,
)?))
}
.map(move |blame| Ok((content, blame))),
}
}
.boxed()
@ -360,31 +399,59 @@ async fn blame_hg_annotate<C: AsRef<[u8]> + 'static + Send>(
ctx: CoreContext,
repo: BlobRepo,
content: C,
blame: Blame,
blame: EitherBlame,
show_line_number: bool,
) -> Result<String, Error> {
if content.as_ref().is_empty() {
return Ok(String::new());
}
let csids: Vec<_> = blame.ranges().iter().map(|range| range.csid).collect();
let mapping = repo.get_hg_bonsai_mapping(ctx, csids).await?;
let mapping: HashMap<_, _> = mapping.into_iter().map(|(k, v)| (v, k)).collect();
let content = String::from_utf8_lossy(content.as_ref());
let mut result = String::new();
for (line, (csid, _path, line_number)) in content.lines().zip(blame.lines()) {
let hg_csid = mapping
.get(&csid)
.ok_or_else(|| format_err!("unresolved bonsai csid: {}", csid))?;
result.push_str(&hg_csid.to_string()[..12]);
result.push(':');
if show_line_number {
write!(&mut result, "{:>4}:", line_number + 1)?;
match blame {
EitherBlame::V1(blame) => {
let csids: Vec<_> = blame.ranges().iter().map(|range| range.csid).collect();
let mapping = repo.get_hg_bonsai_mapping(ctx, csids).await?;
let mapping: HashMap<_, _> = mapping.into_iter().map(|(k, v)| (v, k)).collect();
for (line, (csid, _path, line_number)) in content.lines().zip(blame.lines()) {
let hg_csid = mapping
.get(&csid)
.ok_or_else(|| format_err!("unresolved bonsai csid: {}", csid))?;
result.push_str(&hg_csid.to_string()[..12]);
result.push(':');
if show_line_number {
write!(&mut result, "{:>4}:", line_number + 1)?;
}
result.push(' ');
result.push_str(line);
result.push('\n');
}
}
EitherBlame::V2(blame) => {
let csids: Vec<_> = blame.changeset_ids()?.collect();
let mapping = repo.get_hg_bonsai_mapping(ctx, csids).await?;
let mapping: HashMap<_, _> = mapping.into_iter().map(|(k, v)| (v, k)).collect();
for (line, blame_line) in content.lines().zip(blame.lines()?) {
let hg_csid = mapping.get(blame_line.changeset_id).ok_or_else(|| {
format_err!("unresolved bonsai csid: {}", blame_line.changeset_id)
})?;
write!(
result,
"{:>5} ",
format!("#{}", blame_line.changeset_index + 1)
)?;
result.push_str(&hg_csid.to_string()[..12]);
result.push(':');
if show_line_number {
write!(&mut result, "{:>4}:", blame_line.origin_offset + 1)?;
}
result.push(' ');
result.push_str(line);
result.push('\n');
}
}
result.push(' ');
result.push_str(line);
result.push('\n');
}
Ok(result)