Skip to content

Commit 1137111

Browse files
authored
Merge pull request #4197 from gitbutlerapp/refactor-rebasing-functions
refactor: move rebase functions out of virtual_branches module
2 parents f16afa6 + 251d566 commit 1137111

File tree

4 files changed

+105
-98
lines changed

4 files changed

+105
-98
lines changed

crates/gitbutler-core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ pub mod ops;
2727
pub mod path;
2828
pub mod project_repository;
2929
pub mod projects;
30+
pub mod rebase;
3031
pub mod remotes;
3132
pub mod ssh;
3233
pub mod storage;
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
use crate::{
2+
git::CommitExt, git::RepositoryExt, project_repository, virtual_branches::errors::Marker,
3+
};
4+
use anyhow::{anyhow, Context, Result};
5+
use bstr::ByteSlice;
6+
7+
/// cherry-pick based rebase, which handles empty commits
8+
/// this function takes a commit range and generates a Vector of commit oids
9+
/// and then passes them to `cherry_rebase_group` to rebase them onto the target commit
10+
pub fn cherry_rebase(
11+
project_repository: &project_repository::Repository,
12+
target_commit_oid: git2::Oid,
13+
start_commit_oid: git2::Oid,
14+
end_commit_oid: git2::Oid,
15+
) -> Result<Option<git2::Oid>> {
16+
// get a list of the commits to rebase
17+
let mut ids_to_rebase = project_repository.l(
18+
end_commit_oid,
19+
project_repository::LogUntil::Commit(start_commit_oid),
20+
)?;
21+
22+
if ids_to_rebase.is_empty() {
23+
return Ok(None);
24+
}
25+
26+
let new_head_id =
27+
cherry_rebase_group(project_repository, target_commit_oid, &mut ids_to_rebase)?;
28+
29+
Ok(Some(new_head_id))
30+
}
31+
32+
/// takes a vector of commit oids and rebases them onto a target commit and returns the
33+
/// new head commit oid if it's successful
34+
/// the difference between this and a libgit2 based rebase is that this will successfully
35+
/// rebase empty commits (two commits with identical trees)
36+
pub fn cherry_rebase_group(
37+
project_repository: &project_repository::Repository,
38+
target_commit_oid: git2::Oid,
39+
ids_to_rebase: &mut [git2::Oid],
40+
) -> Result<git2::Oid> {
41+
ids_to_rebase.reverse();
42+
// now, rebase unchanged commits onto the new commit
43+
let commits_to_rebase = ids_to_rebase
44+
.iter()
45+
.map(|oid| project_repository.repo().find_commit(oid.to_owned()))
46+
.collect::<Result<Vec<_>, _>>()
47+
.context("failed to read commits to rebase")?;
48+
49+
let new_head_id = commits_to_rebase
50+
.into_iter()
51+
.fold(
52+
project_repository
53+
.repo()
54+
.find_commit(target_commit_oid)
55+
.context("failed to find new commit"),
56+
|head, to_rebase| {
57+
let head = head?;
58+
59+
let mut cherrypick_index = project_repository
60+
.repo()
61+
.cherrypick_commit(&to_rebase, &head, 0, None)
62+
.context("failed to cherry pick")?;
63+
64+
if cherrypick_index.has_conflicts() {
65+
return Err(anyhow!("failed to rebase")).context(Marker::BranchConflict);
66+
}
67+
68+
let merge_tree_oid = cherrypick_index
69+
.write_tree_to(project_repository.repo())
70+
.context("failed to write merge tree")?;
71+
72+
let merge_tree = project_repository
73+
.repo()
74+
.find_tree(merge_tree_oid)
75+
.context("failed to find merge tree")?;
76+
77+
let change_id = to_rebase.change_id();
78+
79+
let commit_oid = project_repository
80+
.repo()
81+
.commit_with_signature(
82+
None,
83+
&to_rebase.author(),
84+
&to_rebase.committer(),
85+
&to_rebase.message_bstr().to_str_lossy(),
86+
&merge_tree,
87+
&[&head],
88+
change_id.as_deref(),
89+
)
90+
.context("failed to create commit")?;
91+
92+
project_repository
93+
.repo()
94+
.find_commit(commit_oid)
95+
.context("failed to find commit")
96+
},
97+
)?
98+
.id();
99+
100+
Ok(new_head_id)
101+
}

crates/gitbutler-core/src/virtual_branches/base.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ use super::{
1111
},
1212
target, BranchId, RemoteCommit, VirtualBranchHunk, VirtualBranchesHandle,
1313
};
14-
use crate::{git::RepositoryExt, virtual_branches::errors::Marker};
14+
use crate::{git::RepositoryExt, rebase::cherry_rebase, virtual_branches::errors::Marker};
1515
use crate::{
1616
git::{self, diff},
1717
project_repository::{self, LogUntil},
1818
projects::FetchResult,
1919
users,
20-
virtual_branches::{branch::BranchOwnershipClaims, cherry_rebase},
20+
virtual_branches::branch::BranchOwnershipClaims,
2121
};
2222

2323
#[derive(Debug, Serialize, PartialEq, Clone)]

crates/gitbutler-core/src/virtual_branches/virtual.rs

Lines changed: 1 addition & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use crate::error::Code;
2929
use crate::git::diff::GitHunk;
3030
use crate::git::diff::{diff_files_into_hunks, trees, FileDiff};
3131
use crate::git::{CommitExt, RepositoryExt};
32+
use crate::rebase::{cherry_rebase, cherry_rebase_group};
3233
use crate::time::now_since_unix_epoch_ms;
3334
use crate::virtual_branches::branch::HunkHash;
3435
use crate::virtual_branches::errors::Marker;
@@ -3201,102 +3202,6 @@ pub fn undo_commit(
32013202
Ok(())
32023203
}
32033204

3204-
// cherry-pick based rebase, which handles empty commits
3205-
// this function takes a commit range and generates a Vector of commit oids
3206-
// and then passes them to `cherry_rebase_group` to rebase them onto the target commit
3207-
pub fn cherry_rebase(
3208-
project_repository: &project_repository::Repository,
3209-
target_commit_oid: git2::Oid,
3210-
start_commit_oid: git2::Oid,
3211-
end_commit_oid: git2::Oid,
3212-
) -> Result<Option<git2::Oid>> {
3213-
// get a list of the commits to rebase
3214-
let mut ids_to_rebase = project_repository.l(
3215-
end_commit_oid,
3216-
project_repository::LogUntil::Commit(start_commit_oid),
3217-
)?;
3218-
3219-
if ids_to_rebase.is_empty() {
3220-
return Ok(None);
3221-
}
3222-
3223-
let new_head_id =
3224-
cherry_rebase_group(project_repository, target_commit_oid, &mut ids_to_rebase)?;
3225-
3226-
Ok(Some(new_head_id))
3227-
}
3228-
3229-
// takes a vector of commit oids and rebases them onto a target commit and returns the
3230-
// new head commit oid if it's successful
3231-
// the difference between this and a libgit2 based rebase is that this will successfully
3232-
// rebase empty commits (two commits with identical trees)
3233-
fn cherry_rebase_group(
3234-
project_repository: &project_repository::Repository,
3235-
target_commit_oid: git2::Oid,
3236-
ids_to_rebase: &mut [git2::Oid],
3237-
) -> Result<git2::Oid> {
3238-
ids_to_rebase.reverse();
3239-
// now, rebase unchanged commits onto the new commit
3240-
let commits_to_rebase = ids_to_rebase
3241-
.iter()
3242-
.map(|oid| project_repository.repo().find_commit(oid.to_owned()))
3243-
.collect::<Result<Vec<_>, _>>()
3244-
.context("failed to read commits to rebase")?;
3245-
3246-
let new_head_id = commits_to_rebase
3247-
.into_iter()
3248-
.fold(
3249-
project_repository
3250-
.repo()
3251-
.find_commit(target_commit_oid)
3252-
.context("failed to find new commit"),
3253-
|head, to_rebase| {
3254-
let head = head?;
3255-
3256-
let mut cherrypick_index = project_repository
3257-
.repo()
3258-
.cherrypick_commit(&to_rebase, &head, 0, None)
3259-
.context("failed to cherry pick")?;
3260-
3261-
if cherrypick_index.has_conflicts() {
3262-
return Err(anyhow!("failed to rebase")).context(Marker::BranchConflict);
3263-
}
3264-
3265-
let merge_tree_oid = cherrypick_index
3266-
.write_tree_to(project_repository.repo())
3267-
.context("failed to write merge tree")?;
3268-
3269-
let merge_tree = project_repository
3270-
.repo()
3271-
.find_tree(merge_tree_oid)
3272-
.context("failed to find merge tree")?;
3273-
3274-
let change_id = to_rebase.change_id();
3275-
3276-
let commit_oid = project_repository
3277-
.repo()
3278-
.commit_with_signature(
3279-
None,
3280-
&to_rebase.author(),
3281-
&to_rebase.committer(),
3282-
&to_rebase.message_bstr().to_str_lossy(),
3283-
&merge_tree,
3284-
&[&head],
3285-
change_id.as_deref(),
3286-
)
3287-
.context("failed to create commit")?;
3288-
3289-
project_repository
3290-
.repo()
3291-
.find_commit(commit_oid)
3292-
.context("failed to find commit")
3293-
},
3294-
)?
3295-
.id();
3296-
3297-
Ok(new_head_id)
3298-
}
3299-
33003205
pub fn cherry_pick(
33013206
project_repository: &project_repository::Repository,
33023207
branch_id: BranchId,

0 commit comments

Comments
 (0)