mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-25 10:33:21 +03:00
Merge pull request #4197 from gitbutlerapp/refactor-rebasing-functions
refactor: move rebase functions out of virtual_branches module
This commit is contained in:
commit
1137111430
@ -27,6 +27,7 @@ pub mod ops;
|
||||
pub mod path;
|
||||
pub mod project_repository;
|
||||
pub mod projects;
|
||||
pub mod rebase;
|
||||
pub mod remotes;
|
||||
pub mod ssh;
|
||||
pub mod storage;
|
||||
|
101
crates/gitbutler-core/src/rebase/mod.rs
Normal file
101
crates/gitbutler-core/src/rebase/mod.rs
Normal file
@ -0,0 +1,101 @@
|
||||
use crate::{
|
||||
git::CommitExt, git::RepositoryExt, project_repository, virtual_branches::errors::Marker,
|
||||
};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use bstr::ByteSlice;
|
||||
|
||||
/// cherry-pick based rebase, which handles empty commits
|
||||
/// this function takes a commit range and generates a Vector of commit oids
|
||||
/// and then passes them to `cherry_rebase_group` to rebase them onto the target commit
|
||||
pub fn cherry_rebase(
|
||||
project_repository: &project_repository::Repository,
|
||||
target_commit_oid: git2::Oid,
|
||||
start_commit_oid: git2::Oid,
|
||||
end_commit_oid: git2::Oid,
|
||||
) -> Result<Option<git2::Oid>> {
|
||||
// get a list of the commits to rebase
|
||||
let mut ids_to_rebase = project_repository.l(
|
||||
end_commit_oid,
|
||||
project_repository::LogUntil::Commit(start_commit_oid),
|
||||
)?;
|
||||
|
||||
if ids_to_rebase.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let new_head_id =
|
||||
cherry_rebase_group(project_repository, target_commit_oid, &mut ids_to_rebase)?;
|
||||
|
||||
Ok(Some(new_head_id))
|
||||
}
|
||||
|
||||
/// takes a vector of commit oids and rebases them onto a target commit and returns the
|
||||
/// new head commit oid if it's successful
|
||||
/// the difference between this and a libgit2 based rebase is that this will successfully
|
||||
/// rebase empty commits (two commits with identical trees)
|
||||
pub fn cherry_rebase_group(
|
||||
project_repository: &project_repository::Repository,
|
||||
target_commit_oid: git2::Oid,
|
||||
ids_to_rebase: &mut [git2::Oid],
|
||||
) -> Result<git2::Oid> {
|
||||
ids_to_rebase.reverse();
|
||||
// now, rebase unchanged commits onto the new commit
|
||||
let commits_to_rebase = ids_to_rebase
|
||||
.iter()
|
||||
.map(|oid| project_repository.repo().find_commit(oid.to_owned()))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.context("failed to read commits to rebase")?;
|
||||
|
||||
let new_head_id = commits_to_rebase
|
||||
.into_iter()
|
||||
.fold(
|
||||
project_repository
|
||||
.repo()
|
||||
.find_commit(target_commit_oid)
|
||||
.context("failed to find new commit"),
|
||||
|head, to_rebase| {
|
||||
let head = head?;
|
||||
|
||||
let mut cherrypick_index = project_repository
|
||||
.repo()
|
||||
.cherrypick_commit(&to_rebase, &head, 0, None)
|
||||
.context("failed to cherry pick")?;
|
||||
|
||||
if cherrypick_index.has_conflicts() {
|
||||
return Err(anyhow!("failed to rebase")).context(Marker::BranchConflict);
|
||||
}
|
||||
|
||||
let merge_tree_oid = cherrypick_index
|
||||
.write_tree_to(project_repository.repo())
|
||||
.context("failed to write merge tree")?;
|
||||
|
||||
let merge_tree = project_repository
|
||||
.repo()
|
||||
.find_tree(merge_tree_oid)
|
||||
.context("failed to find merge tree")?;
|
||||
|
||||
let change_id = to_rebase.change_id();
|
||||
|
||||
let commit_oid = project_repository
|
||||
.repo()
|
||||
.commit_with_signature(
|
||||
None,
|
||||
&to_rebase.author(),
|
||||
&to_rebase.committer(),
|
||||
&to_rebase.message_bstr().to_str_lossy(),
|
||||
&merge_tree,
|
||||
&[&head],
|
||||
change_id.as_deref(),
|
||||
)
|
||||
.context("failed to create commit")?;
|
||||
|
||||
project_repository
|
||||
.repo()
|
||||
.find_commit(commit_oid)
|
||||
.context("failed to find commit")
|
||||
},
|
||||
)?
|
||||
.id();
|
||||
|
||||
Ok(new_head_id)
|
||||
}
|
@ -11,13 +11,13 @@ use super::{
|
||||
},
|
||||
target, BranchId, RemoteCommit, VirtualBranchHunk, VirtualBranchesHandle,
|
||||
};
|
||||
use crate::{git::RepositoryExt, virtual_branches::errors::Marker};
|
||||
use crate::{git::RepositoryExt, rebase::cherry_rebase, virtual_branches::errors::Marker};
|
||||
use crate::{
|
||||
git::{self, diff},
|
||||
project_repository::{self, LogUntil},
|
||||
projects::FetchResult,
|
||||
users,
|
||||
virtual_branches::{branch::BranchOwnershipClaims, cherry_rebase},
|
||||
virtual_branches::branch::BranchOwnershipClaims,
|
||||
};
|
||||
|
||||
#[derive(Debug, Serialize, PartialEq, Clone)]
|
||||
|
@ -29,6 +29,7 @@ use crate::error::Code;
|
||||
use crate::git::diff::GitHunk;
|
||||
use crate::git::diff::{diff_files_into_hunks, trees, FileDiff};
|
||||
use crate::git::{CommitExt, RepositoryExt};
|
||||
use crate::rebase::{cherry_rebase, cherry_rebase_group};
|
||||
use crate::time::now_since_unix_epoch_ms;
|
||||
use crate::virtual_branches::branch::HunkHash;
|
||||
use crate::virtual_branches::errors::Marker;
|
||||
@ -3201,102 +3202,6 @@ pub fn undo_commit(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// cherry-pick based rebase, which handles empty commits
|
||||
// this function takes a commit range and generates a Vector of commit oids
|
||||
// and then passes them to `cherry_rebase_group` to rebase them onto the target commit
|
||||
pub fn cherry_rebase(
|
||||
project_repository: &project_repository::Repository,
|
||||
target_commit_oid: git2::Oid,
|
||||
start_commit_oid: git2::Oid,
|
||||
end_commit_oid: git2::Oid,
|
||||
) -> Result<Option<git2::Oid>> {
|
||||
// get a list of the commits to rebase
|
||||
let mut ids_to_rebase = project_repository.l(
|
||||
end_commit_oid,
|
||||
project_repository::LogUntil::Commit(start_commit_oid),
|
||||
)?;
|
||||
|
||||
if ids_to_rebase.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let new_head_id =
|
||||
cherry_rebase_group(project_repository, target_commit_oid, &mut ids_to_rebase)?;
|
||||
|
||||
Ok(Some(new_head_id))
|
||||
}
|
||||
|
||||
// takes a vector of commit oids and rebases them onto a target commit and returns the
|
||||
// new head commit oid if it's successful
|
||||
// the difference between this and a libgit2 based rebase is that this will successfully
|
||||
// rebase empty commits (two commits with identical trees)
|
||||
fn cherry_rebase_group(
|
||||
project_repository: &project_repository::Repository,
|
||||
target_commit_oid: git2::Oid,
|
||||
ids_to_rebase: &mut [git2::Oid],
|
||||
) -> Result<git2::Oid> {
|
||||
ids_to_rebase.reverse();
|
||||
// now, rebase unchanged commits onto the new commit
|
||||
let commits_to_rebase = ids_to_rebase
|
||||
.iter()
|
||||
.map(|oid| project_repository.repo().find_commit(oid.to_owned()))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.context("failed to read commits to rebase")?;
|
||||
|
||||
let new_head_id = commits_to_rebase
|
||||
.into_iter()
|
||||
.fold(
|
||||
project_repository
|
||||
.repo()
|
||||
.find_commit(target_commit_oid)
|
||||
.context("failed to find new commit"),
|
||||
|head, to_rebase| {
|
||||
let head = head?;
|
||||
|
||||
let mut cherrypick_index = project_repository
|
||||
.repo()
|
||||
.cherrypick_commit(&to_rebase, &head, 0, None)
|
||||
.context("failed to cherry pick")?;
|
||||
|
||||
if cherrypick_index.has_conflicts() {
|
||||
return Err(anyhow!("failed to rebase")).context(Marker::BranchConflict);
|
||||
}
|
||||
|
||||
let merge_tree_oid = cherrypick_index
|
||||
.write_tree_to(project_repository.repo())
|
||||
.context("failed to write merge tree")?;
|
||||
|
||||
let merge_tree = project_repository
|
||||
.repo()
|
||||
.find_tree(merge_tree_oid)
|
||||
.context("failed to find merge tree")?;
|
||||
|
||||
let change_id = to_rebase.change_id();
|
||||
|
||||
let commit_oid = project_repository
|
||||
.repo()
|
||||
.commit_with_signature(
|
||||
None,
|
||||
&to_rebase.author(),
|
||||
&to_rebase.committer(),
|
||||
&to_rebase.message_bstr().to_str_lossy(),
|
||||
&merge_tree,
|
||||
&[&head],
|
||||
change_id.as_deref(),
|
||||
)
|
||||
.context("failed to create commit")?;
|
||||
|
||||
project_repository
|
||||
.repo()
|
||||
.find_commit(commit_oid)
|
||||
.context("failed to find commit")
|
||||
},
|
||||
)?
|
||||
.id();
|
||||
|
||||
Ok(new_head_id)
|
||||
}
|
||||
|
||||
pub fn cherry_pick(
|
||||
project_repository: &project_repository::Repository,
|
||||
branch_id: BranchId,
|
||||
|
Loading…
Reference in New Issue
Block a user