copytrace: implement trace_rename_backward API

Summary: This diff implements `trace_rename_backward` API

Reviewed By: quark-zju

Differential Revision: D44082134

fbshipit-source-id: e4df6256eba6993571284e5876efbf2ad6d7a26b
This commit is contained in:
Zhaolong Zhu 2023-03-28 13:28:54 -07:00 committed by Facebook GitHub Bot
parent e5613aefb8
commit 95e0240416
5 changed files with 116 additions and 6 deletions

View File

@ -7,10 +7,14 @@ edition = "2021"
[dependencies]
anyhow = "1.0.65"
async-trait = "0.1.58"
dag = { version = "0.1.0", path = "../dag" }
git2 = "0.14"
manifest = { version = "0.1.0", path = "../manifest" }
manifest-tree = { version = "0.1.0", path = "../manifest-tree" }
pathhistory = { version = "0.1.0", path = "../pathhistory" }
pathmatcher = { version = "0.1.0", path = "../pathmatcher" }
storemodel = { version = "0.1.0", path = "../storemodel" }
thiserror = "1.0.36"
tracing = "0.1.35"
types = { version = "0.1.0", path = "../types" }

View File

@ -8,11 +8,13 @@
use std::collections::HashMap;
use anyhow::Result;
use async_trait::async_trait;
use dag::Vertex;
use manifest_tree::TreeManifest;
use types::RepoPathBuf;
/// Tracing the rename history of a file for rename detection in rebase, amend etc
#[async_trait]
pub trait CopyTrace {
/// Trace the corresponding path of `src_path` in `dst` vertex across renames.
/// Depending on the relationship of `src` and `dst`, it will search backward,
@ -21,12 +23,12 @@ pub trait CopyTrace {
/// Trace the corresponding path of `dst_path` in `src` commit across renames.
/// It will search backward, i.e. from `dst` to `src` vertex.
fn trace_rename_backward(
async fn trace_rename_backward(
&self,
src: Vertex,
dst: Vertex,
dst_path: RepoPathBuf,
) -> Option<RepoPathBuf>;
) -> Result<Option<RepoPathBuf>>;
/// Trace the corresponding path of `src_path` in `dst` commit across renames.
/// It will search forward, i.e. from `src` to `dst` vertex.

View File

@ -9,17 +9,21 @@ use std::collections::HashMap;
use std::sync::Arc;
use anyhow::Result;
use async_trait::async_trait;
use dag::DagAlgorithm;
use manifest::DiffType;
use manifest_tree::Diff;
use manifest_tree::TreeManifest;
use manifest_tree::TreeStore;
use pathhistory::RenameTracer;
use pathmatcher::AlwaysMatcher;
use storemodel::ReadFileContents;
use storemodel::ReadRootTreeIds;
use types::HgId;
use types::Key;
use types::RepoPathBuf;
use crate::error::CopyTraceError;
use crate::CopyTrace;
#[allow(dead_code)]
@ -67,8 +71,40 @@ impl DagCopyTrace {
.collect();
Ok(map)
}
async fn vertex_to_tree_manifest(
&self,
old_commit: &dag::Vertex,
new_commit: &dag::Vertex,
) -> Result<(TreeManifest, TreeManifest)> {
let commit_hgids = vec![
HgId::from_slice(old_commit.as_ref())?,
HgId::from_slice(new_commit.as_ref())?,
];
let commit_to_tree_ids: HashMap<HgId, HgId> = self
.root_tree_reader
.read_root_tree_ids(commit_hgids.clone())
.await?
.into_iter()
.collect();
let tree_ids = commit_hgids
.iter()
.map(|i| {
commit_to_tree_ids
.get(i)
.ok_or(CopyTraceError::RootTreeIdNotFound(commit_hgids[0]))
})
.collect::<Result<Vec<&HgId>, _>>()?;
let old_manifest = TreeManifest::durable(self.tree_store.clone(), *tree_ids[0]);
let new_manifest = TreeManifest::durable(self.tree_store.clone(), *tree_ids[1]);
Ok((old_manifest, new_manifest))
}
}
#[async_trait]
impl CopyTrace for DagCopyTrace {
#[allow(unused_variables)]
fn trace_rename(
@ -80,14 +116,61 @@ impl CopyTrace for DagCopyTrace {
todo!()
}
#[allow(unused_variables)]
fn trace_rename_backward(
async fn trace_rename_backward(
&self,
src: dag::Vertex,
dst: dag::Vertex,
dst_path: types::RepoPathBuf,
) -> Option<types::RepoPathBuf> {
todo!()
) -> Result<Option<types::RepoPathBuf>> {
tracing::trace!(?src, ?dst, ?dst_path, "trace_rename_backward");
let mut dst = dst;
let mut dst_path = dst_path;
loop {
tracing::trace!(?dst, ?dst_path, " inside loop");
// setup RenameTracer
let set = self.dag.range(src.clone().into(), dst.into()).await?;
let mut rename_tracer = RenameTracer::new(
set,
dst_path.clone(),
self.root_tree_reader.clone(),
self.tree_store.clone(),
)
.await?;
// find rename commit
let rename_commit = rename_tracer.next().await?;
if let Some(rename_commit) = rename_commit {
if rename_commit == src {
return Ok(Some(dst_path));
}
// find renames by comparing rename_commit and its parent commit
let parents = self.dag.parent_names(rename_commit.clone()).await?;
if parents.is_empty() {
return Err(CopyTraceError::NoParents(rename_commit).into());
}
// For simplicity, we only check p1.
let p1 = &parents[0];
let (old_manifest, new_manifest) =
self.vertex_to_tree_manifest(p1, &rename_commit).await?;
let renames = self.find_renames(&old_manifest, &new_manifest)?;
if let Some(prev_path) = renames.get(&dst_path) {
dst = p1.clone();
dst_path = prev_path.clone();
} else {
// dst_path was new added path
return Ok(None);
}
} else {
// dst_path does not exist
return Ok(None);
}
}
}
#[allow(unused_variables)]

View File

@ -0,0 +1,20 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
use dag::Vertex;
use types::HgId;
#[derive(Debug, thiserror::Error)]
pub enum CopyTraceError {
/// Root tree id can not be found for the given commit
#[error("Root tree id can not be found for commit: {0:?}")]
RootTreeIdNotFound(HgId),
/// Parents can not be found for the given commit
#[error("Parents can not be found for commit: {0:?}")]
NoParents(Vertex),
}

View File

@ -7,6 +7,7 @@
mod copy_trace;
mod dag_copy_trace;
mod error;
mod git_copy_trace;
pub use crate::copy_trace::CopyTrace;