mirror of
https://github.com/facebook/sapling.git
synced 2024-10-10 08:47:12 +03:00
c2c904f933
Summary: Basically every single Mononoke binary starts with the same preamble: - Init mononoke - Init caching - Init logging - Init tunables Some of them forget to do it, some don't, etc. This is a mess. To make things messier, our initialization consists of a bunch of lazy statics interacting with each other (init logging & init configerator are kinda intertwined due to the fact that configerator wants a logger but dynamic observability wants a logger), and methods you must only call once. This diff attempts to clean this up by moving all this initialization into the construction of MononokeMatches. I didn't change all the accessor methods (though I did update those that would otherwise return things instantiated at startup). I'm planning to do a bit more on top of this, as my actual goal here is to make it easier to thread arguments from MononokeMatches to RepoFactory, and to do so I'd like to just pass my MononokeEnvironment as an input to RepoFactory. Reviewed By: HarveyHunt Differential Revision: D27767698 fbshipit-source-id: 00d66b07b8c69f072b92d3d3919393300dd7a392
134 lines
3.9 KiB
Rust
134 lines
3.9 KiB
Rust
/*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This software may be used and distributed according to the terms of the
|
|
* GNU General Public License version 2.
|
|
*/
|
|
|
|
use anyhow::{anyhow, Error};
|
|
use args::MononokeClapApp;
|
|
use ascii::AsciiStr;
|
|
use blobrepo::BlobRepo;
|
|
use blobrepo_hg::BlobRepoHg;
|
|
use blobstore::Loadable;
|
|
use clap::{Arg, ArgGroup};
|
|
use cloned::cloned;
|
|
use cmdlib::args;
|
|
use context::CoreContext;
|
|
use fbinit::FacebookInit;
|
|
use futures::stream;
|
|
use futures_util::future::TryFutureExt;
|
|
use futures_util::stream::{StreamExt, TryStreamExt};
|
|
use mercurial_types::HgChangesetId;
|
|
use std::fs;
|
|
use std::io::{self, BufRead};
|
|
use std::path::Path;
|
|
|
|
fn setup_app<'a, 'b>() -> MononokeClapApp<'a, 'b> {
|
|
args::MononokeAppBuilder::new("Tool to backfill git mappings for given commits")
|
|
.build()
|
|
.arg(Arg::with_name("git").long("git"))
|
|
.arg(Arg::with_name("svnrev").long("svnrev"))
|
|
.group(
|
|
ArgGroup::with_name("mode")
|
|
.args(&["git", "svnrev"])
|
|
.required(true),
|
|
)
|
|
.arg(Arg::from_usage(
|
|
"<IN_FILENAME> 'file with hg changeset ids (separated by newlines)'",
|
|
))
|
|
}
|
|
|
|
fn parse_input<P: AsRef<Path>>(
|
|
file: P,
|
|
) -> Result<impl Iterator<Item = Result<HgChangesetId, Error>>, Error> {
|
|
let file = fs::File::open(file)?;
|
|
let iter = io::BufReader::new(file)
|
|
.lines()
|
|
.map(|line| HgChangesetId::from_ascii_str(AsciiStr::from_ascii(&line?)?));
|
|
Ok(iter)
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
pub enum BackfillMode {
|
|
Git,
|
|
Svnrev,
|
|
}
|
|
|
|
pub async fn backfill<P: AsRef<Path>>(
|
|
ctx: CoreContext,
|
|
repo: BlobRepo,
|
|
in_path: P,
|
|
mode: BackfillMode,
|
|
) -> Result<(), Error> {
|
|
let chunk_size = 1000;
|
|
let ids = parse_input(in_path)?;
|
|
stream::iter(ids)
|
|
.and_then(|hg_cs_id| {
|
|
cloned!(ctx, repo);
|
|
async move {
|
|
let id = repo
|
|
.get_bonsai_from_hg(ctx.clone(), hg_cs_id)
|
|
.await?
|
|
.ok_or(anyhow!("hg commit {} is missing", hg_cs_id))?;
|
|
Ok(id)
|
|
}
|
|
})
|
|
.map_ok({
|
|
cloned!(ctx, repo);
|
|
move |id| {
|
|
cloned!(ctx, repo);
|
|
async move { id.load(&ctx, repo.blobstore()).await }.map_err(anyhow::Error::from)
|
|
}
|
|
})
|
|
.try_buffer_unordered(chunk_size)
|
|
// TryStreamExt doesn't have the try_chunks method yet so we have to do it by folding.
|
|
.chunks(chunk_size)
|
|
.map(|chunk| chunk.into_iter().collect::<Result<Vec<_>, _>>())
|
|
.try_for_each(|chunk| {
|
|
cloned!(ctx, repo);
|
|
async move {
|
|
match mode {
|
|
BackfillMode::Git => {
|
|
repo.bonsai_git_mapping()
|
|
.bulk_import_from_bonsai(&ctx, &chunk)
|
|
.await
|
|
}
|
|
BackfillMode::Svnrev => {
|
|
repo.bonsai_svnrev_mapping()
|
|
.bulk_import_from_bonsai(&ctx, &chunk)
|
|
.await
|
|
}
|
|
}
|
|
}
|
|
})
|
|
.await
|
|
}
|
|
|
|
#[fbinit::main]
|
|
fn main(fb: FacebookInit) -> Result<(), Error> {
|
|
let matches = setup_app().get_matches(fb)?;
|
|
|
|
let logger = matches.logger();
|
|
let runtime = matches.runtime();
|
|
|
|
let ctx = CoreContext::new_with_logger(fb, logger.clone());
|
|
|
|
let mode = if matches.is_present("git") {
|
|
BackfillMode::Git
|
|
} else if matches.is_present("svnrev") {
|
|
BackfillMode::Svnrev
|
|
} else {
|
|
panic!("backfill mode not specified");
|
|
};
|
|
|
|
let run = async {
|
|
let repo = args::open_repo(fb, &logger, &matches).await?;
|
|
let in_filename = matches.value_of("IN_FILENAME").unwrap();
|
|
backfill(ctx, repo, in_filename, mode).await
|
|
};
|
|
|
|
runtime.block_on(run)?;
|
|
Ok(())
|
|
}
|