mirror of
https://github.com/facebook/sapling.git
synced 2024-10-15 19:29:13 +03:00
mononoke: new get_file_history
method in API
Summary: Simple implementation of the `file_history` using batched Unodes. It only contains 2 additional parameters: `skip` and `limit`. Current implementation is very simple: it queries batched history only once and returns result based on skip, limit and first history batch. Reviewed By: StanislavGlebik Differential Revision: D17262719 fbshipit-source-id: 054944ec6d1ea0c75d879d33798e20720b35ae1a
This commit is contained in:
parent
16f0f7a6e4
commit
d23e2b2348
@ -11,10 +11,11 @@ use futures_ext::BoxFuture;
|
|||||||
use apiserver_thrift::client::{make_MononokeAPIService, MononokeAPIService};
|
use apiserver_thrift::client::{make_MononokeAPIService, MononokeAPIService};
|
||||||
use apiserver_thrift::types::{
|
use apiserver_thrift::types::{
|
||||||
MononokeBlob, MononokeBranches, MononokeChangeset, MononokeDirectory, MononokeDirectoryUnodes,
|
MononokeBlob, MononokeBranches, MononokeChangeset, MononokeDirectory, MononokeDirectoryUnodes,
|
||||||
MononokeGetBlobParams, MononokeGetBranchesParams, MononokeGetChangesetParams,
|
MononokeFileHistory, MononokeGetBlobParams, MononokeGetBranchesParams,
|
||||||
MononokeGetLastCommitOnPathParams, MononokeGetRawParams, MononokeGetTreeParams,
|
MononokeGetChangesetParams, MononokeGetFileHistoryParams, MononokeGetLastCommitOnPathParams,
|
||||||
MononokeIsAncestorParams, MononokeListDirectoryParams, MononokeListDirectoryUnodesParams,
|
MononokeGetRawParams, MononokeGetTreeParams, MononokeIsAncestorParams,
|
||||||
MononokeNodeHash, MononokeRevision, MononokeTreeHash,
|
MononokeListDirectoryParams, MononokeListDirectoryUnodesParams, MononokeNodeHash,
|
||||||
|
MononokeRevision, MononokeTreeHash,
|
||||||
};
|
};
|
||||||
use srclient::SRChannelBuilder;
|
use srclient::SRChannelBuilder;
|
||||||
|
|
||||||
@ -69,6 +70,22 @@ impl MononokeAPIClient {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_file_history(
|
||||||
|
&self,
|
||||||
|
revision: String,
|
||||||
|
path: String,
|
||||||
|
limit: i32,
|
||||||
|
skip: i32,
|
||||||
|
) -> BoxFuture<MononokeFileHistory, failure_ext::Error> {
|
||||||
|
self.inner.get_file_history(&MononokeGetFileHistoryParams {
|
||||||
|
repo: self.repo.clone(),
|
||||||
|
revision: MononokeRevision::commit_hash(revision),
|
||||||
|
path: path.into_bytes(),
|
||||||
|
limit,
|
||||||
|
skip,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_last_commit_on_path(
|
pub fn get_last_commit_on_path(
|
||||||
&self,
|
&self,
|
||||||
revision: String,
|
revision: String,
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
use std::string::String;
|
use std::string::String;
|
||||||
|
|
||||||
use clap::{App, Arg, ArgMatches, SubCommand};
|
use clap::{App, Arg, ArgMatches, SubCommand};
|
||||||
|
use cmdlib::args;
|
||||||
use futures::{Future, IntoFuture};
|
use futures::{Future, IntoFuture};
|
||||||
use futures_ext::{BoxFuture, FutureExt};
|
use futures_ext::{BoxFuture, FutureExt};
|
||||||
|
|
||||||
@ -53,6 +54,28 @@ fn get_branches(client: MononokeAPIClient) -> BoxFuture<(), ()> {
|
|||||||
.boxify()
|
.boxify()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_file_history(client: MononokeAPIClient, matches: &ArgMatches<'_>) -> BoxFuture<(), ()> {
|
||||||
|
let path = matches.value_of("path").expect("must provide path");
|
||||||
|
let revision = matches.value_of("revision").expect("must provide revision");
|
||||||
|
|
||||||
|
let limit = args::get_i32(&matches, "limit", 100);
|
||||||
|
let skip = args::get_i32(&matches, "skip", 0);
|
||||||
|
|
||||||
|
client
|
||||||
|
.get_file_history(revision.to_string(), path.to_string(), limit, skip)
|
||||||
|
.and_then(|r| {
|
||||||
|
Ok(
|
||||||
|
serde_json::to_string(&r)
|
||||||
|
.unwrap_or("Error converting response to json".to_string()),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.map_err(|e| eprintln!("error: {}", e))
|
||||||
|
.map(|res| {
|
||||||
|
println!("{}", res);
|
||||||
|
})
|
||||||
|
.boxify()
|
||||||
|
}
|
||||||
|
|
||||||
fn get_last_commit_on_path(
|
fn get_last_commit_on_path(
|
||||||
client: MononokeAPIClient,
|
client: MononokeAPIClient,
|
||||||
matches: &ArgMatches<'_>,
|
matches: &ArgMatches<'_>,
|
||||||
@ -212,6 +235,40 @@ fn main() -> Result<(), ()> {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
.subcommand(SubCommand::with_name("get_branches").about("get all branches"))
|
.subcommand(SubCommand::with_name("get_branches").about("get all branches"))
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("get_file_history")
|
||||||
|
.about("get history of changes for the given file")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("revision")
|
||||||
|
.short("c")
|
||||||
|
.long("revision")
|
||||||
|
.value_name("HASH")
|
||||||
|
.help("hash/bookmark of the revision you want to query")
|
||||||
|
.required(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("path")
|
||||||
|
.short("p")
|
||||||
|
.long("path")
|
||||||
|
.value_name("PATH")
|
||||||
|
.help("path to the file or directory")
|
||||||
|
.required(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("limit")
|
||||||
|
.short("l")
|
||||||
|
.value_name("NUM")
|
||||||
|
.help("number of history commits to query")
|
||||||
|
.required(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("skip")
|
||||||
|
.short("s")
|
||||||
|
.value_name("NUM")
|
||||||
|
.help("number of the latest commits in history to skip")
|
||||||
|
.default_value("0"),
|
||||||
|
),
|
||||||
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("get_last_commit_on_path")
|
SubCommand::with_name("get_last_commit_on_path")
|
||||||
.about("get the last commit cantaining changes on the path")
|
.about("get the last commit cantaining changes on the path")
|
||||||
@ -330,6 +387,8 @@ fn main() -> Result<(), ()> {
|
|||||||
get_changeset(client, matches)
|
get_changeset(client, matches)
|
||||||
} else if let Some(_) = matches.subcommand_matches("get_branches") {
|
} else if let Some(_) = matches.subcommand_matches("get_branches") {
|
||||||
get_branches(client)
|
get_branches(client)
|
||||||
|
} else if let Some(matches) = matches.subcommand_matches("get_file_history") {
|
||||||
|
get_file_history(client, matches)
|
||||||
} else if let Some(matches) = matches.subcommand_matches("get_last_commit_on_path") {
|
} else if let Some(matches) = matches.subcommand_matches("get_last_commit_on_path") {
|
||||||
get_last_commit_on_path(client, matches)
|
get_last_commit_on_path(client, matches)
|
||||||
} else if let Some(matches) = matches.subcommand_matches("list_directory") {
|
} else if let Some(matches) = matches.subcommand_matches("list_directory") {
|
||||||
|
@ -54,6 +54,14 @@ struct MononokeGetBranchesParams{
|
|||||||
1: string repo,
|
1: string repo,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct MononokeGetFileHistoryParams{
|
||||||
|
1: string repo,
|
||||||
|
2: MononokeRevision revision,
|
||||||
|
3: binary path,
|
||||||
|
4: i32 limit,
|
||||||
|
5: i32 skip,
|
||||||
|
}
|
||||||
|
|
||||||
struct MononokeGetLastCommitOnPathParams{
|
struct MononokeGetLastCommitOnPathParams{
|
||||||
1: string repo,
|
1: string repo,
|
||||||
2: MononokeRevision revision,
|
2: MononokeRevision revision,
|
||||||
@ -114,6 +122,10 @@ struct MononokeFile {
|
|||||||
5: optional string content_sha1,
|
5: optional string content_sha1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct MononokeFileHistory {
|
||||||
|
1: list<MononokeChangeset> history,
|
||||||
|
}
|
||||||
|
|
||||||
struct MononokeDirectoryUnodes {
|
struct MononokeDirectoryUnodes {
|
||||||
1: list<MononokeEntryUnodes> entries,
|
1: list<MononokeEntryUnodes> entries,
|
||||||
}
|
}
|
||||||
@ -144,6 +156,9 @@ service MononokeAPIService extends source_control.SourceControlService {
|
|||||||
MononokeBranches get_branches(1: MononokeGetBranchesParams params)
|
MononokeBranches get_branches(1: MononokeGetBranchesParams params)
|
||||||
throws (1: MononokeAPIException e),
|
throws (1: MononokeAPIException e),
|
||||||
|
|
||||||
|
MononokeFileHistory get_file_history(1: MononokeGetFileHistoryParams params)
|
||||||
|
throws (1: MononokeAPIException e),
|
||||||
|
|
||||||
MononokeChangeset get_last_commit_on_path(1: MononokeGetLastCommitOnPathParams params)
|
MononokeChangeset get_last_commit_on_path(1: MononokeGetLastCommitOnPathParams params)
|
||||||
throws (1: MononokeAPIException e),
|
throws (1: MononokeAPIException e),
|
||||||
|
|
||||||
|
@ -13,9 +13,9 @@ use serde_derive::Serialize;
|
|||||||
|
|
||||||
use apiserver_thrift::types::{
|
use apiserver_thrift::types::{
|
||||||
MononokeGetBlobParams, MononokeGetBranchesParams, MononokeGetChangesetParams,
|
MononokeGetBlobParams, MononokeGetBranchesParams, MononokeGetChangesetParams,
|
||||||
MononokeGetLastCommitOnPathParams, MononokeGetRawParams, MononokeGetTreeParams,
|
MononokeGetFileHistoryParams, MononokeGetLastCommitOnPathParams, MononokeGetRawParams,
|
||||||
MononokeIsAncestorParams, MononokeListDirectoryParams, MononokeListDirectoryUnodesParams,
|
MononokeGetTreeParams, MononokeIsAncestorParams, MononokeListDirectoryParams,
|
||||||
MononokeRevision,
|
MononokeListDirectoryUnodesParams, MononokeRevision,
|
||||||
};
|
};
|
||||||
use types::api::{DataRequest, HistoryRequest, TreeRequest};
|
use types::api::{DataRequest, HistoryRequest, TreeRequest};
|
||||||
|
|
||||||
@ -51,6 +51,12 @@ pub enum MononokeRepoQuery {
|
|||||||
revision: Revision,
|
revision: Revision,
|
||||||
},
|
},
|
||||||
GetBranches,
|
GetBranches,
|
||||||
|
GetFileHistory {
|
||||||
|
path: String,
|
||||||
|
revision: Revision,
|
||||||
|
limit: i32,
|
||||||
|
skip: i32,
|
||||||
|
},
|
||||||
GetLastCommitOnPath {
|
GetLastCommitOnPath {
|
||||||
path: String,
|
path: String,
|
||||||
revision: Revision,
|
revision: Revision,
|
||||||
@ -121,6 +127,26 @@ impl TryFrom<MononokeGetBranchesParams> for MononokeQuery {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<MononokeGetFileHistoryParams> for MononokeQuery {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(params: MononokeGetFileHistoryParams) -> Result<MononokeQuery, Self::Error> {
|
||||||
|
let repo = params.repo;
|
||||||
|
let path = String::from_utf8(params.path)?;
|
||||||
|
let limit = params.limit;
|
||||||
|
let skip = params.skip;
|
||||||
|
params.revision.try_into().map(|rev| MononokeQuery {
|
||||||
|
repo,
|
||||||
|
kind: MononokeRepoQuery::GetFileHistory {
|
||||||
|
path,
|
||||||
|
revision: rev,
|
||||||
|
limit,
|
||||||
|
skip,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TryFrom<MononokeGetLastCommitOnPathParams> for MononokeQuery {
|
impl TryFrom<MononokeGetLastCommitOnPathParams> for MononokeQuery {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use std::{
|
use std::{
|
||||||
|
cmp,
|
||||||
collections::{BTreeMap, HashMap},
|
collections::{BTreeMap, HashMap},
|
||||||
convert::{TryFrom, TryInto},
|
convert::{TryFrom, TryInto},
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock},
|
||||||
@ -20,10 +21,11 @@ use context::CoreContext;
|
|||||||
use derive_unode_manifest::derived_data_unodes::{RootUnodeManifestId, RootUnodeManifestMapping};
|
use derive_unode_manifest::derived_data_unodes::{RootUnodeManifestId, RootUnodeManifestMapping};
|
||||||
use derived_data::BonsaiDerived;
|
use derived_data::BonsaiDerived;
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
|
use fastlog::{prefetch_history, RootFastlog, RootFastlogMapping};
|
||||||
use futures::{
|
use futures::{
|
||||||
future::{self, err, join_all, ok},
|
future::{self, err, join_all, ok},
|
||||||
lazy,
|
lazy,
|
||||||
stream::{iter_ok, repeat, FuturesUnordered},
|
stream::{futures_ordered, iter_ok, repeat, FuturesUnordered},
|
||||||
Future, IntoFuture, Stream,
|
Future, IntoFuture, Stream,
|
||||||
};
|
};
|
||||||
use futures_ext::{try_boxfuture, BoxFuture, FutureExt, StreamExt};
|
use futures_ext::{try_boxfuture, BoxFuture, FutureExt, StreamExt};
|
||||||
@ -34,7 +36,9 @@ use repo_client::gettreepack_entries;
|
|||||||
use slog::{debug, Logger};
|
use slog::{debug, Logger};
|
||||||
use time_ext::DurationExt;
|
use time_ext::DurationExt;
|
||||||
|
|
||||||
use mercurial_types::{manifest::Content, HgChangesetId, HgEntry, HgFileNodeId, HgManifestId};
|
use mercurial_types::{
|
||||||
|
blobs::HgBlobChangeset, manifest::Content, HgChangesetId, HgEntry, HgFileNodeId, HgManifestId,
|
||||||
|
};
|
||||||
use metaconfig_types::{CommonConfig, RepoConfig};
|
use metaconfig_types::{CommonConfig, RepoConfig};
|
||||||
use scuba_ext::{ScubaSampleBuilder, ScubaSampleBuilderExt};
|
use scuba_ext::{ScubaSampleBuilder, ScubaSampleBuilderExt};
|
||||||
use stats::{define_stats, Timeseries};
|
use stats::{define_stats, Timeseries};
|
||||||
@ -43,7 +47,10 @@ use types::{
|
|||||||
DataEntry, Key, RepoPathBuf, WireHistoryEntry,
|
DataEntry, Key, RepoPathBuf, WireHistoryEntry,
|
||||||
};
|
};
|
||||||
|
|
||||||
use mononoke_types::{ChangesetId, FileUnodeId, MPath, ManifestUnodeId, RepositoryId};
|
use mononoke_types::{
|
||||||
|
fastlog_batch::max_entries_in_fastlog_batch, ChangesetId, FileUnodeId, MPath, ManifestUnodeId,
|
||||||
|
RepositoryId,
|
||||||
|
};
|
||||||
use reachabilityindex::ReachabilityIndex;
|
use reachabilityindex::ReachabilityIndex;
|
||||||
use skiplist::{deserialize_skiplist_index, SkiplistIndex};
|
use skiplist::{deserialize_skiplist_index, SkiplistIndex};
|
||||||
|
|
||||||
@ -64,6 +71,7 @@ define_stats! {
|
|||||||
get_tree: timeseries(RATE, SUM),
|
get_tree: timeseries(RATE, SUM),
|
||||||
get_changeset: timeseries(RATE, SUM),
|
get_changeset: timeseries(RATE, SUM),
|
||||||
get_branches: timeseries(RATE, SUM),
|
get_branches: timeseries(RATE, SUM),
|
||||||
|
get_file_history: timeseries(RATE, SUM),
|
||||||
get_last_commit_on_path: timeseries(RATE, SUM),
|
get_last_commit_on_path: timeseries(RATE, SUM),
|
||||||
is_ancestor: timeseries(RATE, SUM),
|
is_ancestor: timeseries(RATE, SUM),
|
||||||
eden_get_data: timeseries(RATE, SUM),
|
eden_get_data: timeseries(RATE, SUM),
|
||||||
@ -233,7 +241,7 @@ impl MononokeRepo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_root_manifest_unode_entry(
|
fn get_unode_entry(
|
||||||
&self,
|
&self,
|
||||||
ctx: CoreContext,
|
ctx: CoreContext,
|
||||||
revision: Revision,
|
revision: Revision,
|
||||||
@ -274,6 +282,37 @@ impl MononokeRepo {
|
|||||||
.boxify()
|
.boxify()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn do_get_last_commit_on_path(
|
||||||
|
&self,
|
||||||
|
ctx: CoreContext,
|
||||||
|
revision: Revision,
|
||||||
|
path: String,
|
||||||
|
) -> BoxFuture<HgBlobChangeset, ErrorKind> {
|
||||||
|
cloned!(ctx, self.repo);
|
||||||
|
let blobstore = repo.get_blobstore();
|
||||||
|
self.get_unode_entry(ctx.clone(), revision, path)
|
||||||
|
.and_then({
|
||||||
|
cloned!(blobstore, ctx);
|
||||||
|
move |entry| entry.load(ctx, &blobstore).map_err(Error::from).from_err()
|
||||||
|
})
|
||||||
|
.and_then({
|
||||||
|
cloned!(ctx, repo);
|
||||||
|
move |unode| {
|
||||||
|
let changeset_id = match unode {
|
||||||
|
ManifestEntry::Tree(mf_unode) => mf_unode.linknode().clone(),
|
||||||
|
ManifestEntry::Leaf(file_unode) => file_unode.linknode().clone(),
|
||||||
|
};
|
||||||
|
repo.get_hg_from_bonsai_changeset(ctx, changeset_id)
|
||||||
|
.from_err()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.and_then(move |hg_changeset_id| {
|
||||||
|
repo.get_changeset_by_changesetid(ctx.clone(), hg_changeset_id)
|
||||||
|
.from_err()
|
||||||
|
})
|
||||||
|
.boxify()
|
||||||
|
}
|
||||||
|
|
||||||
fn get_raw_file(
|
fn get_raw_file(
|
||||||
&self,
|
&self,
|
||||||
ctx: CoreContext,
|
ctx: CoreContext,
|
||||||
@ -346,6 +385,148 @@ impl MononokeRepo {
|
|||||||
.boxify()
|
.boxify()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_file_history(
|
||||||
|
&self,
|
||||||
|
ctx: CoreContext,
|
||||||
|
revision: Revision,
|
||||||
|
path: String,
|
||||||
|
limit: i32,
|
||||||
|
skip: i32,
|
||||||
|
) -> BoxFuture<MononokeRepoResponse, ErrorKind> {
|
||||||
|
STATS::get_file_history.add_value(1);
|
||||||
|
let limit = limit as usize;
|
||||||
|
let skip = skip as usize;
|
||||||
|
|
||||||
|
// for now we fetch only one history batch
|
||||||
|
let max_entries = max_entries_in_fastlog_batch();
|
||||||
|
if skip >= max_entries {
|
||||||
|
return future::err(ErrorKind::InvalidInput(
|
||||||
|
format!("cannot skip {}, batch size is {}", skip, max_entries),
|
||||||
|
None,
|
||||||
|
))
|
||||||
|
.boxify();
|
||||||
|
}
|
||||||
|
if limit + skip > max_entries {
|
||||||
|
return future::err(ErrorKind::InvalidInput(
|
||||||
|
format!("cannot fetch {}, batch size is {}", limit, max_entries),
|
||||||
|
None,
|
||||||
|
))
|
||||||
|
.boxify();
|
||||||
|
}
|
||||||
|
if limit == 0 {
|
||||||
|
return future::err(ErrorKind::InvalidInput(
|
||||||
|
"0 commits requested".to_string(),
|
||||||
|
None,
|
||||||
|
))
|
||||||
|
.boxify();
|
||||||
|
}
|
||||||
|
|
||||||
|
// it's not necessary to fetch history in this case, we need just the most recent commit
|
||||||
|
if skip == 0 && limit == 1 {
|
||||||
|
return self
|
||||||
|
.do_get_last_commit_on_path(ctx.clone(), revision, path)
|
||||||
|
.and_then(move |changeset| {
|
||||||
|
changeset
|
||||||
|
.try_into()
|
||||||
|
.map_err(Error::from)
|
||||||
|
.map_err(ErrorKind::from)
|
||||||
|
})
|
||||||
|
.map(move |changeset| MononokeRepoResponse::GetFileHistory {
|
||||||
|
history: vec![changeset],
|
||||||
|
})
|
||||||
|
.boxify();
|
||||||
|
}
|
||||||
|
|
||||||
|
cloned!(ctx, self.repo);
|
||||||
|
let bcs_id_fut = self.get_bonsai_id_from_revision(ctx.clone(), revision.clone());
|
||||||
|
self.get_unode_entry(ctx.clone(), revision.clone(), path.clone())
|
||||||
|
.join(bcs_id_fut)
|
||||||
|
.and_then({
|
||||||
|
cloned!(ctx, repo);
|
||||||
|
move |(entry, bcs_id)| {
|
||||||
|
// optimistically try to fetch history for a unode
|
||||||
|
prefetch_history(ctx.clone(), repo.clone(), entry)
|
||||||
|
.map_err(Error::from)
|
||||||
|
.from_err()
|
||||||
|
.and_then({
|
||||||
|
move |maybe_history| match maybe_history {
|
||||||
|
Some(history) => ok(history).left_future(),
|
||||||
|
// if there is no history, let's try to derive batched fastlog data
|
||||||
|
// and fetch history again
|
||||||
|
None => {
|
||||||
|
let fastlog_derived_mapping = Arc::new(
|
||||||
|
RootFastlogMapping::new(Arc::new(repo.get_blobstore())),
|
||||||
|
);
|
||||||
|
RootFastlog::derive(
|
||||||
|
ctx.clone(),
|
||||||
|
repo.clone(),
|
||||||
|
fastlog_derived_mapping,
|
||||||
|
bcs_id,
|
||||||
|
)
|
||||||
|
.map_err(ErrorKind::InternalError)
|
||||||
|
.and_then({
|
||||||
|
cloned!(ctx, repo);
|
||||||
|
move |_| {
|
||||||
|
prefetch_history(ctx.clone(), repo.clone(), entry)
|
||||||
|
.map_err(Error::from)
|
||||||
|
.from_err()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.and_then(move |maybe_history| {
|
||||||
|
maybe_history.ok_or(ErrorKind::NotFound(
|
||||||
|
format!("{:?} {:?}", revision, path),
|
||||||
|
None,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.right_future()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.and_then({
|
||||||
|
cloned!(ctx, repo);
|
||||||
|
move |history| {
|
||||||
|
let number = cmp::min(history.len(), skip + limit);
|
||||||
|
if number < skip {
|
||||||
|
// we skip more commits than the history has
|
||||||
|
ok(vec![]).left_future()
|
||||||
|
} else {
|
||||||
|
let changeset_ids: Vec<_> = history[skip..number]
|
||||||
|
.into_iter()
|
||||||
|
.map(|(cs_id, _)| *cs_id)
|
||||||
|
.collect();
|
||||||
|
repo.get_hg_bonsai_mapping(ctx.clone(), changeset_ids)
|
||||||
|
.from_err()
|
||||||
|
.right_future()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.and_then({
|
||||||
|
move |hg_bcs_id_mapping| {
|
||||||
|
let mut history_chunk_fut = vec![];
|
||||||
|
for (hg_changeset_id, _) in hg_bcs_id_mapping {
|
||||||
|
cloned!(ctx, repo);
|
||||||
|
history_chunk_fut.push(
|
||||||
|
repo.get_changeset_by_changesetid(ctx.clone(), hg_changeset_id)
|
||||||
|
.from_err()
|
||||||
|
.and_then(move |changeset| {
|
||||||
|
changeset
|
||||||
|
.try_into()
|
||||||
|
.map_err(Error::from)
|
||||||
|
.map_err(ErrorKind::from)
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
futures_ordered(history_chunk_fut).collect()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(move |history_chunk| MononokeRepoResponse::GetFileHistory {
|
||||||
|
history: history_chunk,
|
||||||
|
})
|
||||||
|
.boxify()
|
||||||
|
}
|
||||||
|
|
||||||
fn get_last_commit_on_path(
|
fn get_last_commit_on_path(
|
||||||
&self,
|
&self,
|
||||||
ctx: CoreContext,
|
ctx: CoreContext,
|
||||||
@ -354,28 +535,7 @@ impl MononokeRepo {
|
|||||||
) -> BoxFuture<MononokeRepoResponse, ErrorKind> {
|
) -> BoxFuture<MononokeRepoResponse, ErrorKind> {
|
||||||
STATS::get_last_commit_on_path.add_value(1);
|
STATS::get_last_commit_on_path.add_value(1);
|
||||||
|
|
||||||
cloned!(ctx, self.repo);
|
self.do_get_last_commit_on_path(ctx.clone(), revision, path)
|
||||||
let blobstore = repo.get_blobstore();
|
|
||||||
self.get_root_manifest_unode_entry(ctx.clone(), revision, path)
|
|
||||||
.and_then({
|
|
||||||
cloned!(blobstore, ctx);
|
|
||||||
move |entry| entry.load(ctx, &blobstore).map_err(Error::from).from_err()
|
|
||||||
})
|
|
||||||
.and_then({
|
|
||||||
cloned!(ctx, repo);
|
|
||||||
move |unode| {
|
|
||||||
let changeset_id = match unode {
|
|
||||||
ManifestEntry::Tree(mf_unode) => mf_unode.linknode().clone(),
|
|
||||||
ManifestEntry::Leaf(file_unode) => file_unode.linknode().clone(),
|
|
||||||
};
|
|
||||||
repo.get_hg_from_bonsai_changeset(ctx, changeset_id)
|
|
||||||
.from_err()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.and_then(move |hg_changeset_id| {
|
|
||||||
repo.get_changeset_by_changesetid(ctx.clone(), hg_changeset_id)
|
|
||||||
.from_err()
|
|
||||||
})
|
|
||||||
.and_then(move |changeset| {
|
.and_then(move |changeset| {
|
||||||
changeset
|
changeset
|
||||||
.try_into()
|
.try_into()
|
||||||
@ -429,7 +589,7 @@ impl MononokeRepo {
|
|||||||
|
|
||||||
cloned!(ctx, self.repo);
|
cloned!(ctx, self.repo);
|
||||||
let blobstore = repo.get_blobstore();
|
let blobstore = repo.get_blobstore();
|
||||||
self.get_root_manifest_unode_entry(ctx.clone(), revision, path.clone())
|
self.get_unode_entry(ctx.clone(), revision, path.clone())
|
||||||
.and_then({
|
.and_then({
|
||||||
cloned!(blobstore, ctx);
|
cloned!(blobstore, ctx);
|
||||||
move |entry| match entry {
|
move |entry| match entry {
|
||||||
@ -725,6 +885,12 @@ impl MononokeRepo {
|
|||||||
GetTree { hash } => self.get_tree(ctx, hash),
|
GetTree { hash } => self.get_tree(ctx, hash),
|
||||||
GetChangeset { revision } => self.get_changeset(ctx, revision),
|
GetChangeset { revision } => self.get_changeset(ctx, revision),
|
||||||
GetBranches => self.get_branches(ctx),
|
GetBranches => self.get_branches(ctx),
|
||||||
|
GetFileHistory {
|
||||||
|
revision,
|
||||||
|
path,
|
||||||
|
limit,
|
||||||
|
skip,
|
||||||
|
} => self.get_file_history(ctx, revision, path, limit, skip),
|
||||||
GetLastCommitOnPath { revision, path } => {
|
GetLastCommitOnPath { revision, path } => {
|
||||||
self.get_last_commit_on_path(ctx, revision, path)
|
self.get_last_commit_on_path(ctx, revision, path)
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,9 @@ pub enum MononokeRepoResponse {
|
|||||||
GetBranches {
|
GetBranches {
|
||||||
branches: BTreeMap<String, String>,
|
branches: BTreeMap<String, String>,
|
||||||
},
|
},
|
||||||
|
GetFileHistory {
|
||||||
|
history: Vec<Changeset>,
|
||||||
|
},
|
||||||
GetLastCommitOnPath {
|
GetLastCommitOnPath {
|
||||||
commit: Changeset,
|
commit: Changeset,
|
||||||
},
|
},
|
||||||
@ -125,6 +128,7 @@ impl Responder for MononokeRepoResponse {
|
|||||||
GetTree { files } => Json(files).respond_to(req),
|
GetTree { files } => Json(files).respond_to(req),
|
||||||
GetChangeset { changeset } => Json(changeset).respond_to(req),
|
GetChangeset { changeset } => Json(changeset).respond_to(req),
|
||||||
GetBranches { branches } => Json(branches).respond_to(req),
|
GetBranches { branches } => Json(branches).respond_to(req),
|
||||||
|
GetFileHistory { history } => Json(history).respond_to(req),
|
||||||
GetLastCommitOnPath { commit } => Json(commit).respond_to(req),
|
GetLastCommitOnPath { commit } => Json(commit).respond_to(req),
|
||||||
IsAncestor { answer } => Ok(binary_response({
|
IsAncestor { answer } => Ok(binary_response({
|
||||||
if answer {
|
if answer {
|
||||||
|
@ -9,15 +9,15 @@ use std::{convert::TryFrom, convert::TryInto, mem::size_of, sync::Arc};
|
|||||||
use crate::errors::ErrorKind;
|
use crate::errors::ErrorKind;
|
||||||
use apiserver_thrift::server::MononokeApiservice;
|
use apiserver_thrift::server::MononokeApiservice;
|
||||||
use apiserver_thrift::services::mononoke_apiservice::{
|
use apiserver_thrift::services::mononoke_apiservice::{
|
||||||
GetBlobExn, GetBranchesExn, GetChangesetExn, GetLastCommitOnPathExn, GetRawExn, GetTreeExn,
|
GetBlobExn, GetBranchesExn, GetChangesetExn, GetFileHistoryExn, GetLastCommitOnPathExn,
|
||||||
IsAncestorExn, ListDirectoryExn, ListDirectoryUnodesExn,
|
GetRawExn, GetTreeExn, IsAncestorExn, ListDirectoryExn, ListDirectoryUnodesExn,
|
||||||
};
|
};
|
||||||
use apiserver_thrift::types::{
|
use apiserver_thrift::types::{
|
||||||
MononokeAPIException, MononokeBlob, MononokeBranches, MononokeChangeset, MononokeDirectory,
|
MononokeAPIException, MononokeBlob, MononokeBranches, MononokeChangeset, MononokeDirectory,
|
||||||
MononokeDirectoryUnodes, MononokeGetBlobParams, MononokeGetBranchesParams,
|
MononokeDirectoryUnodes, MononokeFileHistory, MononokeGetBlobParams, MononokeGetBranchesParams,
|
||||||
MononokeGetChangesetParams, MononokeGetLastCommitOnPathParams, MononokeGetRawParams,
|
MononokeGetChangesetParams, MononokeGetFileHistoryParams, MononokeGetLastCommitOnPathParams,
|
||||||
MononokeGetTreeParams, MononokeIsAncestorParams, MononokeListDirectoryParams,
|
MononokeGetRawParams, MononokeGetTreeParams, MononokeIsAncestorParams,
|
||||||
MononokeListDirectoryUnodesParams, MononokeRevision,
|
MononokeListDirectoryParams, MononokeListDirectoryUnodesParams, MononokeRevision,
|
||||||
};
|
};
|
||||||
use apiserver_thrift::MononokeRevision::UnknownField;
|
use apiserver_thrift::MononokeRevision::UnknownField;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
@ -243,6 +243,54 @@ impl MononokeApiservice for MononokeAPIServiceImpl {
|
|||||||
resp
|
resp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_file_history(
|
||||||
|
&self,
|
||||||
|
params: MononokeGetFileHistoryParams,
|
||||||
|
) -> Result<MononokeFileHistory, GetFileHistoryExn> {
|
||||||
|
let scuba = self.create_scuba_logger(
|
||||||
|
Some(params.path.clone()),
|
||||||
|
Some(params.revision.clone()),
|
||||||
|
params.repo.clone(),
|
||||||
|
);
|
||||||
|
let ctx = self.create_ctx(scuba);
|
||||||
|
|
||||||
|
let resp = self
|
||||||
|
.convert_and_call(
|
||||||
|
ctx.clone(),
|
||||||
|
params,
|
||||||
|
|resp: MononokeRepoResponse| match resp {
|
||||||
|
MononokeRepoResponse::GetFileHistory { history } => Ok(MononokeFileHistory {
|
||||||
|
history: history
|
||||||
|
.into_iter()
|
||||||
|
.map(|commit| MononokeChangeset::from(commit))
|
||||||
|
.collect(),
|
||||||
|
}),
|
||||||
|
_ => Err(ErrorKind::InternalError(err_msg(
|
||||||
|
"Actor returned wrong response type to query".to_string(),
|
||||||
|
))),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
log_response_size(
|
||||||
|
ctx.scuba().clone(),
|
||||||
|
resp.as_ref()
|
||||||
|
.map(|resp| {
|
||||||
|
resp.history
|
||||||
|
.iter()
|
||||||
|
.map(|commit| {
|
||||||
|
commit.commit_hash.as_bytes().len()
|
||||||
|
+ commit.message.len()
|
||||||
|
+ commit.author.as_bytes().len()
|
||||||
|
+ size_of::<i64>()
|
||||||
|
})
|
||||||
|
.sum()
|
||||||
|
})
|
||||||
|
.unwrap_or(0),
|
||||||
|
);
|
||||||
|
resp
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_last_commit_on_path(
|
async fn get_last_commit_on_path(
|
||||||
&self,
|
&self,
|
||||||
params: MononokeGetLastCommitOnPathParams,
|
params: MononokeGetLastCommitOnPathParams,
|
||||||
|
@ -801,6 +801,17 @@ pub fn get_u64_opt<'a>(matches: &ArgMatches<'a>, key: &str) -> Option<u64> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_i32<'a>(matches: &ArgMatches<'a>, key: &str, default: i32) -> i32 {
|
||||||
|
matches
|
||||||
|
.value_of(key)
|
||||||
|
.map(|val| {
|
||||||
|
val.parse::<i32>()
|
||||||
|
.expect(&format!("{} must be integer", key))
|
||||||
|
})
|
||||||
|
.unwrap_or(default)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_i64_opt<'a>(matches: &ArgMatches<'a>, key: &str) -> Option<i64> {
|
pub fn get_i64_opt<'a>(matches: &ArgMatches<'a>, key: &str) -> Option<i64> {
|
||||||
matches.value_of(key).map(|val| {
|
matches.value_of(key).map(|val| {
|
||||||
|
Loading…
Reference in New Issue
Block a user