mirror of
https://github.com/facebook/sapling.git
synced 2024-10-11 09:17:30 +03:00
f1e4f62e2d
Summary:
Fsnodes have a lot of data about files, but right now we can't access it
through a Fsnode lookup or a manifest walk, because the LeafId for a Fsnode is
just the content id and the file type.
This is a bit sad, because it means we e.g. cannot dump a manifest with file
sizes (D23471561 (179e4eb80e
)).
Just changing the LeafId is easy, but that brings a new problem with Fsnode
derivation.
Indeed, deriving manifests normally expects us to have the "derive leaf"
function produce a LeafId (so we'd want to produce a `FsnodeFile`), but in
Fsnodes, this currently happens in deriving trees instead.
Unfortunately, we cannot easily just move the code that produces `FsnodeFile`
from the tree derivation to the leaf derivation, that is, do:
```
fn check_fsnode_leaf(
leaf_info: LeafInfo<FsnodeFile, (ContentId, FileType)>,
) -> impl Future<Item = (Option<FsnodeSummary>, FsnodeFile), Error = Error>
```
Indeed, the performance of Fsnode derivation relies on all the leaves for a
given tree being derived together with the tree and its parents in context.
So, we'd need the ability for deriving a new leaf to return something different
from the actual leaf id. This means we want to return a `(ContentId,
FileType)`, even though our `LeafId` is a `FsnodeFile`.
To do this, this diff introduces a new `IntermediateLeafId` type in the
derivation. This represents the type of the leaf that is passed from deriving a
leaf to deriving a tree. We need to be able to turn a real `LeafId` into it,
because sometimes we don't re-derive leaves.
I think we could also refactor some of the code that passes a context here to
just do this through the `IntermediateLeafId`, but I didn't look into this too
much.
So, this diff does that, and uses it in Mononoke Admin so we can print file
sizes.
Reviewed By: StanislavGlebik
Differential Revision: D23497754
fbshipit-source-id: 2fc480be0b1e4d3d261da1d4d3dcd9c7b8501b9b
112 lines
3.2 KiB
Rust
112 lines
3.2 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 crate::error::SubcommandError;
|
|
|
|
use anyhow::Error;
|
|
use blobrepo::BlobRepo;
|
|
use clap::{App, Arg, ArgMatches, SubCommand};
|
|
use cmdlib::{args, helpers};
|
|
use context::CoreContext;
|
|
use derived_data::BonsaiDerived;
|
|
use fbinit::FacebookInit;
|
|
use futures::{
|
|
compat::{Future01CompatExt, Stream01CompatExt},
|
|
stream::StreamExt,
|
|
};
|
|
use manifest::{Entry, ManifestOps, PathOrPrefix};
|
|
|
|
use fsnodes::RootFsnodeId;
|
|
use mononoke_types::{ChangesetId, MPath};
|
|
use slog::{info, Logger};
|
|
|
|
pub const FSNODES: &str = "fsnodes";
|
|
const COMMAND_TREE: &str = "tree";
|
|
const ARG_CSID: &str = "csid";
|
|
const ARG_PATH: &str = "path";
|
|
|
|
pub fn build_subcommand<'a, 'b>() -> App<'a, 'b> {
|
|
SubCommand::with_name(FSNODES)
|
|
.about("inspect fsnodes")
|
|
.subcommand(
|
|
SubCommand::with_name(COMMAND_TREE)
|
|
.about("recursively list all fsnode entries starting with prefix")
|
|
.arg(
|
|
Arg::with_name(ARG_CSID)
|
|
.help("{hg|bonsai} changeset id or bookmark name")
|
|
.required(true),
|
|
)
|
|
.arg(Arg::with_name(ARG_PATH).help("path")),
|
|
)
|
|
}
|
|
|
|
pub async fn subcommand_fsnodes<'a>(
|
|
fb: FacebookInit,
|
|
logger: Logger,
|
|
matches: &'a ArgMatches<'_>,
|
|
sub_matches: &'a ArgMatches<'_>,
|
|
) -> Result<(), SubcommandError> {
|
|
args::init_cachelib(fb, &matches, None);
|
|
|
|
let repo = args::open_repo(fb, &logger, &matches).compat().await?;
|
|
let ctx = CoreContext::new_with_logger(fb, logger.clone());
|
|
|
|
match sub_matches.subcommand() {
|
|
(COMMAND_TREE, Some(matches)) => {
|
|
let hash_or_bookmark = String::from(matches.value_of(ARG_CSID).unwrap());
|
|
let path = matches.value_of(ARG_PATH).map(MPath::new).transpose()?;
|
|
|
|
let csid = helpers::csid_resolve(ctx.clone(), repo.clone(), hash_or_bookmark)
|
|
.compat()
|
|
.await?;
|
|
subcommand_tree(&ctx, &repo, csid, path).await?;
|
|
Ok(())
|
|
}
|
|
_ => Err(SubcommandError::InvalidArgs),
|
|
}
|
|
}
|
|
|
|
async fn subcommand_tree(
|
|
ctx: &CoreContext,
|
|
repo: &BlobRepo,
|
|
csid: ChangesetId,
|
|
path: Option<MPath>,
|
|
) -> Result<(), Error> {
|
|
let root = RootFsnodeId::derive(ctx.clone(), repo.clone(), csid)
|
|
.compat()
|
|
.await?;
|
|
|
|
info!(ctx.logger(), "ROOT: {:?}", root);
|
|
info!(ctx.logger(), "PATH: {:?}", path);
|
|
|
|
let mut stream = root
|
|
.fsnode_id()
|
|
.find_entries(
|
|
ctx.clone(),
|
|
repo.get_blobstore(),
|
|
vec![PathOrPrefix::Prefix(path)],
|
|
)
|
|
.compat();
|
|
|
|
while let Some((path, entry)) = stream.next().await.transpose()? {
|
|
match entry {
|
|
Entry::Tree(..) => {}
|
|
Entry::Leaf(file) => {
|
|
println!(
|
|
"{}\t{}\t{}\t{}",
|
|
MPath::display_opt(path.as_ref()),
|
|
file.content_id(),
|
|
file.file_type(),
|
|
file.size(),
|
|
);
|
|
}
|
|
};
|
|
}
|
|
|
|
Ok(())
|
|
}
|