Skip to content

Commit

Permalink
eagerepo: add pushrebase conflict detection
Browse files Browse the repository at this point in the history
Summary: This diff adds pushrebase conflict detection logic. Currently, it only check if the paths are equal. Later, we will add support for checking path component prefix, which will be added for the `RepoPathBuf` struct. Then we will support the prefix check in pushrebase conflict detection logic.

Reviewed By: quark-zju

Differential Revision: D64264847

fbshipit-source-id: ee7b4ce688ab1c488543fd04efd74d77d8d34ab1
  • Loading branch information
zzl0 authored and facebook-github-bot committed Oct 12, 2024
1 parent 1c9be44 commit 9fc6d41
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 0 deletions.
2 changes: 2 additions & 0 deletions eden/scm/lib/eagerepo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ format-util = { version = "0.1.0", path = "../util/format-util" }
futures = { version = "0.3.30", features = ["async-await", "compat"] }
http = "0.2"
identity = { version = "0.1.0", path = "../identity" }
manifest = { version = "0.1.0", path = "../manifest" }
manifest-tree = { version = "0.1.0", path = "../manifest-tree" }
metalog = { version = "0.1.0", path = "../metalog" }
minibytes = { version = "0.1.0", path = "../minibytes" }
mutationstore = { version = "0.1.0", path = "../mutationstore" }
nonblocking = { version = "0.1.0", path = "../nonblocking" }
parking_lot = { version = "0.12.1", features = ["send_guard"] }
pathmatcher = { version = "0.1.0", path = "../pathmatcher" }
repourl = { version = "0.1.0", path = "../repo/url" }
sha1 = "0.10.5"
storemodel = { version = "0.1.0", path = "../storemodel" }
Expand Down
2 changes: 2 additions & 0 deletions eden/scm/lib/eagerepo/TARGETS
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@ rust_library(
"//eden/scm/lib/dag:dag",
"//eden/scm/lib/edenapi/trait:edenapi_trait",
"//eden/scm/lib/identity:identity",
"//eden/scm/lib/manifest:manifest",
"//eden/scm/lib/manifest-tree:manifest-tree",
"//eden/scm/lib/metalog:metalog",
"//eden/scm/lib/minibytes:minibytes",
"//eden/scm/lib/mutationstore:mutationstore",
"//eden/scm/lib/nonblocking:nonblocking",
"//eden/scm/lib/pathmatcher:pathmatcher",
"//eden/scm/lib/repo/url:repourl",
"//eden/scm/lib/storemodel:storemodel",
"//eden/scm/lib/util/factory:factory",
Expand Down
67 changes: 67 additions & 0 deletions eden/scm/lib/eagerepo/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* GNU General Public License version 2.
*/

use std::cmp::Ordering;
use std::collections::HashMap;
use std::collections::HashSet;
use std::io::Write;
Expand Down Expand Up @@ -63,6 +64,7 @@ use edenapi::types::NodeInfo;
use edenapi::types::Parents;
use edenapi::types::RepoPathBuf;
use edenapi::types::SaplingRemoteApiServerError;
use edenapi::types::ServerError;
use edenapi::types::SetBookmarkResponse;
use edenapi::types::SuffixQueryResponse;
use edenapi::types::TreeAttributes;
Expand All @@ -87,10 +89,13 @@ use futures::stream::TryStreamExt;
use futures::StreamExt;
use http::StatusCode;
use http::Version;
use manifest::Manifest;
use manifest_tree::Flag;
use manifest_tree::TreeManifest;
use minibytes::Bytes;
use mutationstore::MutationEntry;
use nonblocking::non_blocking_result;
use pathmatcher::AlwaysMatcher;
use repourl::RepoUrl;
use storemodel::types::AugmentedTreeWithDigest;
use storemodel::SerializationFormat;
Expand Down Expand Up @@ -1251,8 +1256,70 @@ impl SaplingRemoteApi for EagerRepo {
self.flush_for_api("land_stack").await?;
return Ok(LandStackResponse { data: Ok(data) });
} else {
let head_manifest = self.commit_to_manifest(head).await.map_err(map_crate_err)?;
let base_manifest = self.commit_to_manifest(base).await.map_err(map_crate_err)?;
let bookmark_manifest = self
.commit_to_manifest(latest_bookmark_id)
.await
.map_err(map_crate_err)?;

let conflicts =
pushrebase_conflicts(&base_manifest, &bookmark_manifest, &head_manifest)?;
if !conflicts.is_empty() {
let e =
ServerError::generic(format!("Conflicts while pushrebasing: {:?}", conflicts));

return Ok(LandStackResponse { data: Err(e) });
}

panic!("not implemented");
}

/// `left` and `right` are considerered to be conflit free, if none of the element
/// from `left` is prefix of element from `right`, and vice versa.
fn pushrebase_conflicts(
mbase: &TreeManifest,
mleft: &TreeManifest,
mright: &TreeManifest,
) -> anyhow::Result<Vec<(RepoPathBuf, RepoPathBuf)>> {
let matcher = AlwaysMatcher::new();
let mut left = mbase
.diff(mleft, &matcher)?
.map(|e| e.map(|e| e.path))
.collect::<anyhow::Result<Vec<_>>>()?;
left.sort_unstable();
let mut left_iter = left.into_iter();

let mut right = mbase
.diff(mright, &matcher)?
.map(|e| e.map(|e| e.path))
.collect::<anyhow::Result<Vec<_>>>()?;
right.sort_unstable();
let mut right_iter = right.into_iter();

let mut conflicts = Vec::new();
let mut state = (left_iter.next(), right_iter.next());
loop {
state = match state {
(Some(l), Some(r)) => match l.cmp(&r) {
Ordering::Equal => {
conflicts.push((l.clone(), r.clone()));
(left_iter.next(), right_iter.next())
}
// todo: handle repo path prefix
Ordering::Less => {
panic!("not implemented");
}
Ordering::Greater => {
panic!("not implemented");
}
},
_ => break,
}
}

Ok(conflicts)
}
}
}

Expand Down
9 changes: 9 additions & 0 deletions eden/scm/tests/test-pushrebase-eagerepo.t
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,12 @@ the master bookmark should point to the latest commit
@ ea98a8f95390 changed message remote/master
o 2bb9d20e471c initial

test pushrebase conflicts
$ hg go -q 2bb9d20e471c --debug -v
$ echo y >> a && hg commit -qm "update a"
#if slapi
$ hg push --to master -q
abort: Server error: Conflicts while pushrebasing: [(RepoPathBuf("a"), RepoPathBuf("a"))]
[255]
#endif

0 comments on commit 9fc6d41

Please sign in to comment.