Skip to content

Commit

Permalink
eagerepo: add pushrebase_one() function
Browse files Browse the repository at this point in the history
Summary:
This diff adds the pushrebase_one() function for pushrebasing one source commit to the destination commit. It basically does two things (thanks muirdm for the idea):
1. Generate a new manifest tree root id.
2. Generate a new commit.

Currently, this function does not handle the following two cases, we extend the function later:
1. Does not support merge commits.
2. Need to update the time filed for the new commit raw text if eagerepo is not used for non-test cases.

Reviewed By: muirdm

Differential Revision: D64540085

fbshipit-source-id: 4db8c914897dcf7819143a20d2de1d2f2af73231
  • Loading branch information
zzl0 authored and facebook-github-bot committed Oct 18, 2024
1 parent f957495 commit 5fa11af
Showing 1 changed file with 81 additions and 0 deletions.
81 changes: 81 additions & 0 deletions eden/scm/lib/eagerepo/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ use futures::stream::TryStreamExt;
use futures::StreamExt;
use http::StatusCode;
use http::Version;
use manifest::DiffType;
use manifest::Manifest;
use manifest_tree::Flag;
use manifest_tree::TreeManifest;
Expand All @@ -98,6 +99,9 @@ use nonblocking::non_blocking_result;
use pathmatcher::AlwaysMatcher;
use repourl::RepoUrl;
use storemodel::types::AugmentedTreeWithDigest;
use storemodel::InsertOpts;
use storemodel::KeyStore;
use storemodel::Kind;
use storemodel::SerializationFormat;
use tracing::debug;
use tracing::error;
Expand Down Expand Up @@ -1275,6 +1279,83 @@ impl SaplingRemoteApi for EagerRepo {
panic!("not implemented");
}

#[allow(dead_code)]
async fn pushrebase_one(
repo: &EagerRepo,
base_commit: HgId,
source_commit: HgId,
dest_commit: HgId,
) -> anyhow::Result<HgId> {
let base_manifest = repo.commit_to_manifest(base_commit).await?;
let source_manifest = repo.commit_to_manifest(source_commit).await?;
let dest_manifest = repo.commit_to_manifest(dest_commit).await?;

let mut new_manifest = dest_manifest.clone();
let matcher = AlwaysMatcher::new();
// generate new manifest
for e in base_manifest.diff(&source_manifest, &matcher)? {
let e = e?;
match e.diff_type {
DiffType::LeftOnly(_) => {
new_manifest.remove(&e.path)?;
}
DiffType::Changed(_, right) => {
new_manifest.insert(e.path, right)?;
}
DiffType::RightOnly(right) => {
new_manifest.insert(e.path.clone(), right)?;
}
}
}

let new_tree_id = match repo.store.format {
SerializationFormat::Hg => {
let new_parents = vec![&dest_manifest];
let mut manifest_id: Option<HgId> = None;
for (path, hgid, raw, p1, p2) in new_manifest.finalize(new_parents)? {
let insert_opts = InsertOpts {
parents: vec![p1, p2],
kind: Kind::Tree,
..Default::default()
};
repo.store.insert_data(insert_opts, &path, &raw)?;
if path.is_empty() {
manifest_id = Some(hgid);
}
}
match manifest_id {
Some(manifest_id) => manifest_id,
None => {
return Err(anyhow!(
"empty commit is not supported: {}",
source_commit.to_hex()
));
}
}
}
SerializationFormat::Git => new_manifest.flush()?,
};

// generate new commit
let old_raw_text = match repo.store.get_content(source_commit)? {
None => {
return Err(anyhow!(
"commit content cannot be found: {}",
source_commit.to_hex()
));
}
Some(raw_text) => raw_text,
};
let mut new_raw_text: Vec<u8> = Vec::new();
writeln!(new_raw_text, "{}", new_tree_id)?;
new_raw_text.extend_from_slice(&old_raw_text[HgId::hex_len()..]);

let commit_parents = vec![dest_commit];
let new_commit = repo.add_commit(&commit_parents, &new_raw_text).await?;

Ok(new_commit)
}

/// `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(
Expand Down

0 comments on commit 5fa11af

Please sign in to comment.