mirror of
https://github.com/facebook/sapling.git
synced 2024-10-10 16:57:49 +03:00
source control service blame integration
Summary: Expose Blame via source control service and integrate with scsc Reviewed By: mitrandir77 Differential Revision: D18615736 fbshipit-source-id: 7534a3027eb3886fa153219e550ffa444521a93a
This commit is contained in:
parent
3fd44a39f9
commit
b57d8bcce5
@ -10,16 +10,18 @@ use std::fmt;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
|
||||
use blame::fetch_blame;
|
||||
use bytes::Bytes;
|
||||
use cloned::cloned;
|
||||
use failure_ext::err_msg;
|
||||
use failure_ext::{err_msg, Error};
|
||||
use filestore::FetchKey;
|
||||
use futures::Future as FutureLegacy;
|
||||
use futures_preview::compat::{Future01CompatExt, Stream01CompatExt};
|
||||
use futures_preview::future::{FutureExt, Shared};
|
||||
use futures_util::{try_join, try_stream::TryStreamExt};
|
||||
use manifest::{Entry, ManifestOps};
|
||||
use mononoke_types::{
|
||||
ChangesetId, ContentId, FileType, FileUnodeId, FsnodeId, MPath, ManifestUnodeId,
|
||||
Blame, ChangesetId, ContentId, FileType, FileUnodeId, FsnodeId, MPath, ManifestUnodeId,
|
||||
};
|
||||
use xdiff;
|
||||
|
||||
@ -197,6 +199,20 @@ impl ChangesetPathContext {
|
||||
};
|
||||
Ok(entry)
|
||||
}
|
||||
|
||||
pub async fn blame(&self) -> Result<(Bytes, Blame), MononokeError> {
|
||||
let ctx = self.changeset.ctx().clone();
|
||||
let repo = self.changeset.repo().blob_repo().clone();
|
||||
let csid = self.changeset.id();
|
||||
let path = self.mpath.as_ref().ok_or_else(|| {
|
||||
MononokeError::InvalidRequest(format!("Blame is not available for directory: `/`"))
|
||||
})?;
|
||||
|
||||
fetch_blame(ctx, repo, csid, path.clone())
|
||||
.map_err(|error| MononokeError::from(Error::from(error)))
|
||||
.compat()
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
/// Renders the diff (in the git diff format) against some other path.
|
||||
|
@ -88,6 +88,7 @@ impl_into_thrift_error!(service::CommitInfoExn);
|
||||
impl_into_thrift_error!(service::CommitCompareExn);
|
||||
impl_into_thrift_error!(service::CommitIsAncestorOfExn);
|
||||
impl_into_thrift_error!(service::CommitPathInfoExn);
|
||||
impl_into_thrift_error!(service::CommitPathBlameExn);
|
||||
impl_into_thrift_error!(service::TreeListExn);
|
||||
impl_into_thrift_error!(service::FileExistsExn);
|
||||
impl_into_thrift_error!(service::FileInfoExn);
|
||||
|
@ -414,7 +414,7 @@ impl Blame {
|
||||
Blame::new(ranges)
|
||||
}
|
||||
|
||||
fn lines<'a>(&'a self) -> BlameLines<'a> {
|
||||
pub fn lines<'a>(&'a self) -> BlameLines<'a> {
|
||||
BlameLines::new(&self.ranges)
|
||||
}
|
||||
|
||||
@ -509,7 +509,7 @@ fn blame_merge(csid: ChangesetId, blames: Vec<Blame>) -> Result<Blame, Error> {
|
||||
|
||||
/// Iterator over balme object as if it was just a list of lines with associated
|
||||
/// changeset id and path. Implementation is not cloning anyting.
|
||||
struct BlameLines<'a> {
|
||||
pub struct BlameLines<'a> {
|
||||
ranges: &'a Vec<BlameRange>,
|
||||
ranges_index: usize,
|
||||
index: u32,
|
||||
|
@ -7,9 +7,10 @@
|
||||
*/
|
||||
|
||||
use std::cmp::min;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap};
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::iter::FromIterator;
|
||||
use std::ops::RangeBounds;
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -803,6 +804,7 @@ mod errors {
|
||||
impl_into_thrift_error!(service::CommitCompareExn);
|
||||
impl_into_thrift_error!(service::CommitIsAncestorOfExn);
|
||||
impl_into_thrift_error!(service::CommitPathInfoExn);
|
||||
impl_into_thrift_error!(service::CommitPathBlameExn);
|
||||
impl_into_thrift_error!(service::TreeListExn);
|
||||
impl_into_thrift_error!(service::FileExistsExn);
|
||||
impl_into_thrift_error!(service::FileInfoExn);
|
||||
@ -1246,6 +1248,80 @@ impl SourceControlService for SourceControlServiceImpl {
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
async fn commit_path_blame(
|
||||
&self,
|
||||
commit_path: thrift::CommitPathSpecifier,
|
||||
params: thrift::CommitPathBlameParams,
|
||||
) -> Result<thrift::CommitPathBlameResponse, service::CommitPathBlameExn> {
|
||||
let ctx = self.create_ctx(Some(&commit_path));
|
||||
let (repo, changeset) = self.repo_changeset(ctx, &commit_path.commit).await?;
|
||||
let path = changeset.path(&commit_path.path)?;
|
||||
|
||||
let (content, blame) = path.blame().await?;
|
||||
let csids: Vec<_> = blame.ranges().iter().map(|range| range.csid).collect();
|
||||
let identities = map_commit_identities(
|
||||
&repo,
|
||||
csids.clone(),
|
||||
&BTreeSet::from_iter(Some(params.identity_scheme)),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// author and date fields
|
||||
let info: HashMap<_, _> = try_join_all(csids.into_iter().map(move |csid| {
|
||||
let repo = repo.clone();
|
||||
async move {
|
||||
let changeset = repo
|
||||
.changeset(ChangesetSpecifier::Bonsai(csid))
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
MononokeError::InvalidRequest(format!("failed to resolve commit: {}", csid))
|
||||
})?;
|
||||
let date = changeset.author_date().await?;
|
||||
let date = thrift::DateTime {
|
||||
timestamp: date.timestamp(),
|
||||
tz: date.offset().local_minus_utc(),
|
||||
};
|
||||
let author = changeset.author().await?;
|
||||
Ok::<_, MononokeError>((csid, (author, date)))
|
||||
}
|
||||
}))
|
||||
.await?
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
let lines = String::from_utf8_lossy(content.as_ref())
|
||||
.lines()
|
||||
.zip(blame.lines())
|
||||
.enumerate()
|
||||
.map(
|
||||
|(line, (contents, (csid, path)))| -> Result<_, thrift::RequestError> {
|
||||
let commit = identities
|
||||
.get(&csid)
|
||||
.and_then(|ids| ids.get(¶ms.identity_scheme))
|
||||
.ok_or_else(|| {
|
||||
errors::commit_not_found(format!("failed to resolve commit: {}", csid))
|
||||
})?;
|
||||
let (author, date) = info.get(&csid).ok_or_else(|| {
|
||||
errors::commit_not_found(format!("failed to resolve commit: {}", csid))
|
||||
})?;
|
||||
Ok(thrift::BlameVerboseLine {
|
||||
line: (line + 1) as i32,
|
||||
contents: contents.to_string(),
|
||||
commit: commit.clone(),
|
||||
path: path.to_string(),
|
||||
author: author.clone(),
|
||||
date: date.clone(),
|
||||
})
|
||||
},
|
||||
)
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let blame = thrift::BlameVerbose { lines };
|
||||
|
||||
Ok(thrift::CommitPathBlameResponse {
|
||||
blame: thrift::Blame::blame_verbose(blame),
|
||||
})
|
||||
}
|
||||
|
||||
/// List the contents of a directory.
|
||||
async fn tree_list(
|
||||
&self,
|
||||
|
@ -140,6 +140,58 @@ diff
|
||||
+e
|
||||
+x
|
||||
|
||||
blame
|
||||
$ scsc blame --repo repo -i "$COMMIT_C" --path b
|
||||
323afe77a1b1e632e54e8d5a683ba2cc8511f299: b
|
||||
c29e0e474e30ae40ed639fa6292797a7502bc590: c
|
||||
323afe77a1b1e632e54e8d5a683ba2cc8511f299: d
|
||||
323afe77a1b1e632e54e8d5a683ba2cc8511f299: e
|
||||
323afe77a1b1e632e54e8d5a683ba2cc8511f299: f
|
||||
|
||||
$ scsc --json blame --repo repo -i "$COMMIT_C" --path b | jq -S .
|
||||
[
|
||||
{
|
||||
"author": "test",
|
||||
"commit": "323afe77a1b1e632e54e8d5a683ba2cc8511f299",
|
||||
"contents": "b",
|
||||
"datetime": "1970-01-01T00:00:00+00:00",
|
||||
"line": 1,
|
||||
"path": "b"
|
||||
},
|
||||
{
|
||||
"author": "test",
|
||||
"commit": "c29e0e474e30ae40ed639fa6292797a7502bc590",
|
||||
"contents": "c",
|
||||
"datetime": "1970-01-01T00:00:00+00:00",
|
||||
"line": 2,
|
||||
"path": "b"
|
||||
},
|
||||
{
|
||||
"author": "test",
|
||||
"commit": "323afe77a1b1e632e54e8d5a683ba2cc8511f299",
|
||||
"contents": "d",
|
||||
"datetime": "1970-01-01T00:00:00+00:00",
|
||||
"line": 3,
|
||||
"path": "b"
|
||||
},
|
||||
{
|
||||
"author": "test",
|
||||
"commit": "323afe77a1b1e632e54e8d5a683ba2cc8511f299",
|
||||
"contents": "e",
|
||||
"datetime": "1970-01-01T00:00:00+00:00",
|
||||
"line": 4,
|
||||
"path": "b"
|
||||
},
|
||||
{
|
||||
"author": "test",
|
||||
"commit": "323afe77a1b1e632e54e8d5a683ba2cc8511f299",
|
||||
"contents": "f",
|
||||
"datetime": "1970-01-01T00:00:00+00:00",
|
||||
"line": 5,
|
||||
"path": "b"
|
||||
}
|
||||
]
|
||||
|
||||
lookup using bookmarks
|
||||
$ scsc lookup --repo repo -B BOOKMARK_B
|
||||
323afe77a1b1e632e54e8d5a683ba2cc8511f299
|
||||
|
Loading…
Reference in New Issue
Block a user