use crate::cherry_pick::{CherryPickError, cherry_pick};
use crate::commit::CommitsTable;
use crate::history::log;
use crate::object_store::GitObjectStore;
#[derive(Debug, thiserror::Error)]
pub enum RebaseError {
#[error("Cherry-pick failed: {0}")]
CherryPick(#[from] CherryPickError),
#[error("Commit not found: {0}")]
CommitNotFound(String),
#[error("Nothing to rebase (start equals onto)")]
NothingToRebase,
}
pub struct RebaseResult {
pub new_head: String,
pub replayed: usize,
}
pub fn rebase(
obj_store: &mut GitObjectStore,
commits_table: &mut CommitsTable,
start_commit_id: &str, end_commit_id: &str, onto_commit_id: &str, author: &str,
) -> Result<RebaseResult, RebaseError> {
if start_commit_id == onto_commit_id {
return Err(RebaseError::NothingToRebase);
}
let all = log(commits_table, end_commit_id, 0);
let mut to_replay: Vec<String> = Vec::new();
for commit in &all {
if commit.commit_id == start_commit_id {
break;
}
to_replay.push(commit.commit_id.clone());
}
to_replay.reverse();
if to_replay.is_empty() {
return Err(RebaseError::NothingToRebase);
}
let mut current_head = onto_commit_id.to_string();
let mut replayed = 0;
for commit_id in &to_replay {
let new_id = cherry_pick(obj_store, commits_table, commit_id, ¤t_head, author)?;
current_head = new_id;
replayed += 1;
}
Ok(RebaseResult {
new_head: current_head,
replayed,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{CommitsTable, GitObjectStore, checkout, create_commit};
use nusy_arrow_core::{Namespace, Triple, YLayer};
fn make_triple(s: &str, p: &str, o: &str) -> Triple {
Triple {
subject: s.to_string(),
predicate: p.to_string(),
object: o.to_string(),
graph: None,
confidence: Some(1.0),
source_document: None,
source_chunk_id: None,
extracted_by: None,
caused_by: None,
derived_from: None,
consolidated_at: None,
certifiability_class: None,
}
}
#[test]
fn test_rebase_linear_chain() {
let tmp = tempfile::tempdir().unwrap();
let mut obj = GitObjectStore::with_snapshot_dir(tmp.path());
let mut commits = CommitsTable::new();
obj.store
.add_triple(
&make_triple("a", "r", "1"),
Namespace::World,
YLayer::Semantic,
)
.unwrap();
let base = create_commit(&obj, &mut commits, vec![], "base", "test").unwrap();
obj.store
.add_triple(
&make_triple("b", "r", "2"),
Namespace::World,
YLayer::Semantic,
)
.unwrap();
let c1 = create_commit(
&obj,
&mut commits,
vec![base.commit_id.clone()],
"c1",
"test",
)
.unwrap();
obj.store
.add_triple(
&make_triple("c", "r", "3"),
Namespace::World,
YLayer::Semantic,
)
.unwrap();
let c2 =
create_commit(&obj, &mut commits, vec![c1.commit_id.clone()], "c2", "test").unwrap();
checkout(&mut obj, &commits, &base.commit_id).unwrap();
obj.store
.add_triple(
&make_triple("d", "r", "4"),
Namespace::World,
YLayer::Semantic,
)
.unwrap();
let new_base = create_commit(
&obj,
&mut commits,
vec![base.commit_id.clone()],
"new_base",
"test",
)
.unwrap();
let result = rebase(
&mut obj,
&mut commits,
&base.commit_id,
&c2.commit_id,
&new_base.commit_id,
"test",
)
.unwrap();
assert_eq!(result.replayed, 2);
assert_ne!(result.new_head, c2.commit_id);
}
#[test]
fn test_rebase_nothing_to_rebase() {
let tmp = tempfile::tempdir().unwrap();
let mut obj = GitObjectStore::with_snapshot_dir(tmp.path());
let mut commits = CommitsTable::new();
obj.store
.add_triple(
&make_triple("a", "r", "1"),
Namespace::World,
YLayer::Semantic,
)
.unwrap();
let base = create_commit(&obj, &mut commits, vec![], "base", "test").unwrap();
let result = rebase(
&mut obj,
&mut commits,
&base.commit_id,
&base.commit_id,
&base.commit_id,
"test",
);
assert!(result.is_err());
}
#[test]
fn test_rebase_single_commit() {
let tmp = tempfile::tempdir().unwrap();
let mut obj = GitObjectStore::with_snapshot_dir(tmp.path());
let mut commits = CommitsTable::new();
obj.store
.add_triple(
&make_triple("a", "r", "1"),
Namespace::World,
YLayer::Semantic,
)
.unwrap();
let base = create_commit(&obj, &mut commits, vec![], "base", "test").unwrap();
obj.store
.add_triple(
&make_triple("b", "r", "2"),
Namespace::World,
YLayer::Semantic,
)
.unwrap();
let c1 = create_commit(
&obj,
&mut commits,
vec![base.commit_id.clone()],
"c1",
"test",
)
.unwrap();
checkout(&mut obj, &commits, &base.commit_id).unwrap();
obj.store
.add_triple(
&make_triple("x", "r", "9"),
Namespace::World,
YLayer::Semantic,
)
.unwrap();
let new_base = create_commit(
&obj,
&mut commits,
vec![base.commit_id.clone()],
"new_base",
"test",
)
.unwrap();
let result = rebase(
&mut obj,
&mut commits,
&base.commit_id,
&c1.commit_id,
&new_base.commit_id,
"test",
)
.unwrap();
assert_eq!(result.replayed, 1);
}
}