mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-11-23 11:45:06 +03:00
Merge pull request #5057 from gitbutlerapp/Refactor-Branch-to-abstract-setting-and-getting-the-head
-property
Refactor Branch to abstract setting and getting the `head` property
This commit is contained in:
commit
0658920abc
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2151,6 +2151,7 @@ dependencies = [
|
||||
"gitbutler-patch-reference",
|
||||
"gitbutler-reference",
|
||||
"gitbutler-serde",
|
||||
"gitbutler-time",
|
||||
"gix",
|
||||
"hex",
|
||||
"itertools 0.13.0",
|
||||
|
@ -3,7 +3,7 @@ use std::{path::Path, time};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use git2::Index;
|
||||
use gitbutler_branch::{
|
||||
self, Branch, BranchId, BranchOwnershipClaims, Target, VirtualBranchesHandle,
|
||||
self, Branch, BranchOwnershipClaims, Target, VirtualBranchesHandle,
|
||||
GITBUTLER_WORKSPACE_REFERENCE,
|
||||
};
|
||||
use gitbutler_command_context::CommandContext;
|
||||
@ -86,7 +86,7 @@ fn go_back_to_integration(ctx: &CommandContext, default_target: &Target) -> Resu
|
||||
// merge this branches tree with our tree
|
||||
let branch_head = ctx
|
||||
.repository()
|
||||
.find_commit(branch.head)
|
||||
.find_commit(branch.head())
|
||||
.context("failed to find branch head")?;
|
||||
let branch_tree = branch_head
|
||||
.tree()
|
||||
@ -208,8 +208,6 @@ pub(crate) fn set_base_branch(
|
||||
},
|
||||
);
|
||||
|
||||
let now_ms = gitbutler_time::time::now_ms();
|
||||
|
||||
let (upstream, upstream_head) = if let Refname::Local(head_name) = &head_name {
|
||||
let upstream_name = target_branch_ref.with_branch(head_name.branch());
|
||||
if upstream_name.eq(target_branch_ref) {
|
||||
@ -235,29 +233,22 @@ pub(crate) fn set_base_branch(
|
||||
(None, None)
|
||||
};
|
||||
|
||||
let branch = Branch {
|
||||
id: BranchId::generate(),
|
||||
name: head_name.to_string().replace("refs/heads/", ""),
|
||||
notes: String::new(),
|
||||
source_refname: Some(head_name),
|
||||
let mut branch = Branch::new(
|
||||
head_name.to_string().replace("refs/heads/", ""),
|
||||
Some(head_name),
|
||||
upstream,
|
||||
upstream_head,
|
||||
created_timestamp_ms: now_ms,
|
||||
updated_timestamp_ms: now_ms,
|
||||
head: current_head_commit.id(),
|
||||
tree: gitbutler_diff::write::hunks_onto_commit(
|
||||
gitbutler_diff::write::hunks_onto_commit(
|
||||
ctx,
|
||||
current_head_commit.id(),
|
||||
gitbutler_diff::diff_files_into_hunks(wd_diff),
|
||||
)?,
|
||||
ownership,
|
||||
order: 0,
|
||||
selected_for_changes: None,
|
||||
allow_rebasing: ctx.project().ok_with_force_push.into(),
|
||||
in_workspace: true,
|
||||
not_in_workspace_wip_change_id: None,
|
||||
heads: Default::default(),
|
||||
};
|
||||
current_head_commit.id(),
|
||||
0,
|
||||
None,
|
||||
ctx.project().ok_with_force_push.into(),
|
||||
);
|
||||
branch.ownership = ownership;
|
||||
|
||||
vb_state.set_branch(branch)?;
|
||||
}
|
||||
@ -369,20 +360,22 @@ pub(crate) fn update_base_branch(
|
||||
.map(|(mut branch, _)| -> Result<Option<Branch>> {
|
||||
let branch_tree = repo.find_tree(branch.tree)?;
|
||||
|
||||
let branch_head_commit = repo.find_commit(branch.head).context(format!(
|
||||
let branch_head_commit = repo.find_commit(branch.head()).context(format!(
|
||||
"failed to find commit {} for branch {}",
|
||||
branch.head, branch.id
|
||||
branch.head(),
|
||||
branch.id
|
||||
))?;
|
||||
let branch_head_tree = branch_head_commit.tree().context(format!(
|
||||
"failed to find tree for commit {} for branch {}",
|
||||
branch.head, branch.id
|
||||
branch.head(),
|
||||
branch.id
|
||||
))?;
|
||||
|
||||
let result_integrated_detected = |mut branch: Branch| -> Result<Option<Branch>> {
|
||||
// branch head tree is the same as the new target tree.
|
||||
// meaning we can safely use the new target commit as the branch head.
|
||||
|
||||
branch.head = new_target_commit.id();
|
||||
branch.set_head(new_target_commit.id());
|
||||
|
||||
// it also means that the branch is fully integrated into the target.
|
||||
// disconnect it from the upstream
|
||||
@ -429,9 +422,9 @@ pub(crate) fn update_base_branch(
|
||||
return result_integrated_detected(branch);
|
||||
}
|
||||
|
||||
if branch.head == target.sha {
|
||||
if branch.head() == target.sha {
|
||||
// there are no commits on the branch, so we can just update the head to the new target and calculate the new tree
|
||||
branch.head = new_target_commit.id();
|
||||
branch.set_head(new_target_commit.id());
|
||||
branch.tree = branch_merge_index_tree_oid;
|
||||
vb_state.set_branch(branch.clone())?;
|
||||
return Ok(Some(branch));
|
||||
@ -486,7 +479,7 @@ pub(crate) fn update_base_branch(
|
||||
)
|
||||
.context("failed to commit merge")?;
|
||||
|
||||
branch.head = new_target_head;
|
||||
branch.set_head(new_target_head);
|
||||
branch.tree = branch_merge_index_tree_oid;
|
||||
vb_state.set_branch(branch.clone())?;
|
||||
Ok(Some(branch))
|
||||
@ -501,7 +494,7 @@ pub(crate) fn update_base_branch(
|
||||
ctx,
|
||||
new_target_commit.id(),
|
||||
new_target_commit.id(),
|
||||
branch.head,
|
||||
branch.head(),
|
||||
);
|
||||
|
||||
// rebase failed, just do the merge
|
||||
@ -511,7 +504,7 @@ pub(crate) fn update_base_branch(
|
||||
|
||||
if let Some(rebased_head_oid) = rebased_head_oid? {
|
||||
// rebase worked out, rewrite the branch head
|
||||
branch.head = rebased_head_oid;
|
||||
branch.set_head(rebased_head_oid);
|
||||
branch.tree = branch_merge_index_tree_oid;
|
||||
vb_state.set_branch(branch.clone())?;
|
||||
return Ok(Some(branch));
|
||||
|
@ -261,7 +261,7 @@ fn branch_group_to_branch(
|
||||
// If there is a virtual branch let's get it's head. Alternatively, pick the first local branch and use it's head.
|
||||
// If there are no local branches, pick the first remote branch.
|
||||
let head_commit = if let Some(vbranch) = virtual_branch {
|
||||
Some(git2_to_gix_object_id(vbranch.head).attach(repo))
|
||||
Some(git2_to_gix_object_id(vbranch.head()).attach(repo))
|
||||
} else if let Some(mut branch) = local_branches.into_iter().next() {
|
||||
branch.peel_to_id_in_place_packed(packed).ok()
|
||||
} else if let Some(mut branch) = remote_branches.into_iter().next() {
|
||||
|
@ -98,27 +98,17 @@ impl BranchManager<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
let now = gitbutler_time::time::now_ms();
|
||||
|
||||
let mut branch = Branch {
|
||||
id: BranchId::generate(),
|
||||
name: name.clone(),
|
||||
notes: String::new(),
|
||||
upstream: None,
|
||||
upstream_head: None,
|
||||
tree: tree.id(),
|
||||
head: default_target.sha,
|
||||
created_timestamp_ms: now,
|
||||
updated_timestamp_ms: now,
|
||||
ownership: BranchOwnershipClaims::default(),
|
||||
let mut branch = Branch::new(
|
||||
name.clone(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
tree.id(),
|
||||
default_target.sha,
|
||||
order,
|
||||
selected_for_changes,
|
||||
allow_rebasing: self.ctx.project().ok_with_force_push.into(),
|
||||
in_workspace: true,
|
||||
not_in_workspace_wip_change_id: None,
|
||||
source_refname: None,
|
||||
heads: Default::default(),
|
||||
};
|
||||
self.ctx.project().ok_with_force_push.into(),
|
||||
);
|
||||
|
||||
if let Some(ownership) = &create.ownership {
|
||||
vbranch::set_ownership(&vb_state, &mut branch, ownership)
|
||||
@ -199,8 +189,6 @@ impl BranchManager<'_> {
|
||||
.any(|b| b.selected_for_changes.is_some()))
|
||||
.then_some(now_since_unix_epoch_ms());
|
||||
|
||||
let now = gitbutler_time::time::now_ms();
|
||||
|
||||
// add file ownership based off the diff
|
||||
let target_commit = repo.find_commit(default_target.sha)?;
|
||||
let merge_base_oid = repo.merge_base(target_commit.id(), head_commit.id())?;
|
||||
@ -235,7 +223,7 @@ impl BranchManager<'_> {
|
||||
branch.upstream_head = upstream_branch.is_some().then_some(head_commit.id());
|
||||
branch.upstream = upstream_branch;
|
||||
branch.tree = head_commit_tree.id();
|
||||
branch.head = head_commit.id();
|
||||
branch.set_head(head_commit.id());
|
||||
branch.ownership = ownership;
|
||||
branch.order = order;
|
||||
branch.selected_for_changes = selected_for_changes;
|
||||
@ -244,25 +232,18 @@ impl BranchManager<'_> {
|
||||
|
||||
branch
|
||||
} else {
|
||||
Branch {
|
||||
id: BranchId::generate(),
|
||||
name: branch_name.clone(),
|
||||
notes: String::new(),
|
||||
source_refname: Some(target.clone()),
|
||||
upstream_head: upstream_branch.is_some().then_some(head_commit.id()),
|
||||
upstream: upstream_branch,
|
||||
tree: head_commit_tree.id(),
|
||||
head: head_commit.id(),
|
||||
created_timestamp_ms: now,
|
||||
updated_timestamp_ms: now,
|
||||
ownership,
|
||||
let upstream_head = upstream_branch.is_some().then_some(head_commit.id());
|
||||
Branch::new(
|
||||
branch_name.clone(),
|
||||
Some(target.clone()),
|
||||
upstream_branch,
|
||||
upstream_head,
|
||||
head_commit_tree.id(),
|
||||
head_commit.id(),
|
||||
order,
|
||||
selected_for_changes,
|
||||
allow_rebasing: self.ctx.project().ok_with_force_push.into(),
|
||||
in_workspace: true,
|
||||
not_in_workspace_wip_change_id: None,
|
||||
heads: Default::default(),
|
||||
}
|
||||
self.ctx.project().ok_with_force_push.into(),
|
||||
)
|
||||
};
|
||||
|
||||
vb_state.set_branch(branch.clone())?;
|
||||
@ -309,10 +290,11 @@ impl BranchManager<'_> {
|
||||
// if not, we need to merge or rebase the branch to get it up to date
|
||||
|
||||
let merge_base = repo
|
||||
.merge_base(default_target.sha, branch.head)
|
||||
.merge_base(default_target.sha, branch.head())
|
||||
.context(format!(
|
||||
"failed to find merge base between {} and {}",
|
||||
default_target.sha, branch.head
|
||||
default_target.sha,
|
||||
branch.head()
|
||||
))?;
|
||||
|
||||
// Branch is out of date, merge or rebase it
|
||||
@ -397,7 +379,7 @@ impl BranchManager<'_> {
|
||||
}
|
||||
|
||||
let new_head = if branch.allow_rebasing {
|
||||
let commits_to_rebase = repo.l(branch.head, LogUntil::Commit(merge_base))?;
|
||||
let commits_to_rebase = repo.l(branch.head(), LogUntil::Commit(merge_base))?;
|
||||
|
||||
let head_oid =
|
||||
cherry_rebase_group(repo, default_target.sha, &commits_to_rebase, true)?;
|
||||
@ -414,7 +396,7 @@ impl BranchManager<'_> {
|
||||
{
|
||||
gitbutler_merge_commits(
|
||||
repo,
|
||||
repo.find_commit(branch.head)?,
|
||||
repo.find_commit(branch.head())?,
|
||||
repo.find_commit(default_target.sha)?,
|
||||
&branch.name,
|
||||
default_target.branch.branch(),
|
||||
@ -425,14 +407,14 @@ impl BranchManager<'_> {
|
||||
} else {
|
||||
gitbutler_merge_commits(
|
||||
repo,
|
||||
repo.find_commit(branch.head)?,
|
||||
repo.find_commit(branch.head())?,
|
||||
repo.find_commit(default_target.sha)?,
|
||||
&branch.name,
|
||||
default_target.branch.branch(),
|
||||
)?
|
||||
};
|
||||
|
||||
branch.head = new_head.id();
|
||||
branch.set_head(new_head.id());
|
||||
branch.tree = repo.find_real_tree(&new_head, Default::default())?.id();
|
||||
|
||||
vb_state.set_branch(branch.clone())?;
|
||||
@ -445,15 +427,18 @@ impl BranchManager<'_> {
|
||||
.context("failed to ensure selected for changes")?;
|
||||
|
||||
{
|
||||
if let Some(wip_commit_to_unapply) = branch.not_in_workspace_wip_change_id {
|
||||
let potential_wip_commit = repo.find_commit(branch.head)?;
|
||||
if let Some(wip_commit_to_unapply) = &branch.not_in_workspace_wip_change_id {
|
||||
let potential_wip_commit = repo.find_commit(branch.head())?;
|
||||
|
||||
// Don't try to undo commit if its conflicted
|
||||
if !potential_wip_commit.is_conflicted() {
|
||||
if let Some(headers) = potential_wip_commit.gitbutler_headers() {
|
||||
if headers.change_id == wip_commit_to_unapply {
|
||||
branch =
|
||||
crate::undo_commit::undo_commit(self.ctx, branch.id, branch.head)?;
|
||||
if headers.change_id == wip_commit_to_unapply.clone() {
|
||||
branch = crate::undo_commit::undo_commit(
|
||||
self.ctx,
|
||||
branch.id,
|
||||
branch.head(),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,7 +108,7 @@ impl BranchManager<'_> {
|
||||
.map(|file| (file.path, file.hunks))
|
||||
.collect::<Vec<(PathBuf, Vec<VirtualBranchHunk>)>>();
|
||||
let tree_oid =
|
||||
gitbutler_diff::write::hunks_onto_oid(self.ctx, branch.head, files)?;
|
||||
gitbutler_diff::write::hunks_onto_oid(self.ctx, branch.head(), files)?;
|
||||
let branch_tree = repo.find_tree(tree_oid)?;
|
||||
let mut result =
|
||||
repo.merge_trees(&base_tree, &final_tree, &branch_tree, None)?;
|
||||
@ -147,7 +147,7 @@ impl BranchManager<'_> {
|
||||
#[instrument(level = tracing::Level::DEBUG, skip(self, vbranch), err(Debug))]
|
||||
fn build_real_branch(&self, vbranch: &mut Branch) -> Result<git2::Branch<'_>> {
|
||||
let repo = self.ctx.repository();
|
||||
let target_commit = repo.find_commit(vbranch.head)?;
|
||||
let target_commit = repo.find_commit(vbranch.head())?;
|
||||
let branch_name = vbranch.name.clone();
|
||||
let branch_name = normalize_branch_name(&branch_name)?;
|
||||
|
||||
@ -170,7 +170,7 @@ impl BranchManager<'_> {
|
||||
|
||||
// Build wip tree as either any uncommitted changes or an empty tree
|
||||
let vbranch_wip_tree = repo.find_tree(vbranch.tree)?;
|
||||
let vbranch_head_tree = repo.find_commit(vbranch.head)?.tree()?;
|
||||
let vbranch_head_tree = repo.find_commit(vbranch.head())?.tree()?;
|
||||
|
||||
let tree = if vbranch_head_tree.id() != vbranch_wip_tree.id() {
|
||||
vbranch_wip_tree
|
||||
|
@ -36,7 +36,7 @@ pub fn checkout_branch_trees<'a>(
|
||||
Ok(tree)
|
||||
} else {
|
||||
let merge_base = repository
|
||||
.merge_base_octopussy(&branches.iter().map(|b| b.head).collect::<Vec<_>>())?;
|
||||
.merge_base_octopussy(&branches.iter().map(|b| b.head()).collect::<Vec<_>>())?;
|
||||
|
||||
let merge_base_tree = repository.find_commit(merge_base)?.tree()?;
|
||||
|
||||
@ -92,7 +92,7 @@ pub fn compute_updated_branch_head(
|
||||
) -> Result<BranchHeadAndTree> {
|
||||
compute_updated_branch_head_for_commits(
|
||||
repository,
|
||||
branch.head,
|
||||
branch.head(),
|
||||
branch.tree,
|
||||
new_head,
|
||||
fearless_rebasing,
|
||||
|
@ -46,12 +46,12 @@ pub(crate) fn get_workspace_head(ctx: &CommandContext) -> Result<git2::Oid> {
|
||||
let merge_parent = conflicts::merge_parent(ctx)?.ok_or(anyhow!("No merge parent"))?;
|
||||
let first_branch = virtual_branches.first().ok_or(anyhow!("No branches"))?;
|
||||
|
||||
let merge_base = repo.merge_base(first_branch.head, merge_parent)?;
|
||||
let merge_base = repo.merge_base(first_branch.head(), merge_parent)?;
|
||||
workspace_tree = repo.find_commit(merge_base)?.tree()?;
|
||||
} else {
|
||||
for branch in virtual_branches.iter_mut() {
|
||||
let merge_tree = repo.find_commit(target.sha)?.tree()?;
|
||||
let branch_tree = repo.find_commit(branch.head)?;
|
||||
let branch_tree = repo.find_commit(branch.head())?;
|
||||
let branch_tree = repo.find_real_tree(&branch_tree, Default::default())?;
|
||||
|
||||
let mut index = repo.merge_trees(&merge_tree, &workspace_tree, &branch_tree, None)?;
|
||||
@ -71,8 +71,8 @@ pub(crate) fn get_workspace_head(ctx: &CommandContext) -> Result<git2::Oid> {
|
||||
let author = gitbutler_branch::signature(SignaturePurpose::Author)?;
|
||||
let mut heads: Vec<git2::Commit<'_>> = virtual_branches
|
||||
.iter()
|
||||
.filter(|b| b.head != target.sha)
|
||||
.map(|b| repo.find_commit(b.head))
|
||||
.filter(|b| b.head() != target.sha)
|
||||
.map(|b| repo.find_commit(b.head()))
|
||||
.filter_map(Result::ok)
|
||||
.collect();
|
||||
|
||||
@ -183,9 +183,9 @@ pub fn update_workspace_commit(
|
||||
message.push_str(format!(" ({})", &branch.refname()?).as_str());
|
||||
message.push('\n');
|
||||
|
||||
if branch.head != target.sha {
|
||||
if branch.head() != target.sha {
|
||||
message.push_str(" branch head: ");
|
||||
message.push_str(&branch.head.to_string());
|
||||
message.push_str(&branch.head().to_string());
|
||||
message.push('\n');
|
||||
}
|
||||
for file in &branch.ownership.claims {
|
||||
@ -240,7 +240,7 @@ pub fn update_workspace_commit(
|
||||
// finally, update the refs/gitbutler/ heads to the states of the current virtual branches
|
||||
for branch in &virtual_branches {
|
||||
let wip_tree = repo.find_tree(branch.tree)?;
|
||||
let mut branch_head = repo.find_commit(branch.head)?;
|
||||
let mut branch_head = repo.find_commit(branch.head())?;
|
||||
let head_tree = branch_head.tree()?;
|
||||
|
||||
// create a wip commit if there is wip
|
||||
@ -371,7 +371,7 @@ fn verify_head_is_clean(ctx: &CommandContext, perm: &mut WorktreeWritePermission
|
||||
// rebasing the extra commits onto the new branch
|
||||
let vb_state = ctx.project().virtual_branches();
|
||||
// let mut head = new_branch.head;
|
||||
let mut head = new_branch.head;
|
||||
let mut head = new_branch.head();
|
||||
for commit in extra_commits {
|
||||
let new_branch_head = ctx
|
||||
.repository()
|
||||
@ -402,7 +402,7 @@ fn verify_head_is_clean(ctx: &CommandContext, perm: &mut WorktreeWritePermission
|
||||
rebased_commit_oid
|
||||
))?;
|
||||
|
||||
new_branch.head = rebased_commit.id();
|
||||
new_branch.set_head(rebased_commit.id());
|
||||
new_branch.tree = rebased_commit.tree_id();
|
||||
vb_state
|
||||
.set_branch(new_branch.clone())
|
||||
|
@ -32,7 +32,7 @@ pub(crate) fn move_commit(
|
||||
|
||||
let (ref mut source_branch, source_status) = applied_statuses
|
||||
.iter_mut()
|
||||
.find(|(b, _)| b.head == commit_id)
|
||||
.find(|(b, _)| b.head() == commit_id)
|
||||
.ok_or_else(|| anyhow!("commit {commit_id} to be moved could not be found"))?;
|
||||
|
||||
let source_branch_non_comitted_files = source_status;
|
||||
@ -116,17 +116,17 @@ pub(crate) fn move_commit(
|
||||
|
||||
let new_destination_head_oid = cherry_rebase_group(
|
||||
ctx.repository(),
|
||||
destination_branch.head,
|
||||
destination_branch.head(),
|
||||
&[source_commit.id()],
|
||||
true,
|
||||
)?;
|
||||
|
||||
// reset the source branch to the parent commit
|
||||
// and update the destination branch head
|
||||
source_branch.head = source_branch_head_parent.id();
|
||||
source_branch.set_head(source_branch_head_parent.id());
|
||||
vb_state.set_branch(source_branch.clone())?;
|
||||
|
||||
destination_branch.head = new_destination_head_oid;
|
||||
destination_branch.set_head(new_destination_head_oid);
|
||||
vb_state.set_branch(destination_branch.clone())?;
|
||||
|
||||
checkout_branch_trees(ctx, perm)?;
|
||||
|
@ -58,13 +58,13 @@ pub(crate) fn reorder_commit(
|
||||
merge_base,
|
||||
subject_commit_oid,
|
||||
offset,
|
||||
&repository.l(branch.head, LogUntil::Commit(merge_base))?,
|
||||
&repository.l(branch.head(), LogUntil::Commit(merge_base))?,
|
||||
&repository.find_tree(branch.tree)?,
|
||||
ctx.project().succeeding_rebases,
|
||||
)?;
|
||||
|
||||
branch.tree = tree;
|
||||
branch.head = head;
|
||||
branch.set_head(head);
|
||||
|
||||
branch.updated_timestamp_ms = gitbutler_time::time::now_ms();
|
||||
vb_state.set_branch(branch.clone())?;
|
||||
|
@ -122,7 +122,7 @@ pub fn push_stack(project: &Project, branch_id: BranchId, with_force: bool) -> R
|
||||
|
||||
let repo = ctx.repository();
|
||||
let merge_base =
|
||||
repo.find_commit(repo.merge_base(stack.head, state.get_default_target()?.sha)?)?;
|
||||
repo.find_commit(repo.merge_base(stack.head(), state.get_default_target()?.sha)?)?;
|
||||
let merge_base = if let Some(change_id) = merge_base.change_id() {
|
||||
CommitOrChangeId::ChangeId(change_id)
|
||||
} else {
|
||||
|
@ -218,7 +218,7 @@ pub fn get_applied_status_cached(
|
||||
// write updated state if not resolving
|
||||
if !ctx.is_resolving() {
|
||||
for (vbranch, files) in &mut hunks_by_branch {
|
||||
vbranch.tree = gitbutler_diff::write::hunks_onto_oid(ctx, vbranch.head, files)?;
|
||||
vbranch.tree = gitbutler_diff::write::hunks_onto_oid(ctx, vbranch.head(), files)?;
|
||||
vb_state
|
||||
.set_branch(vbranch.clone())
|
||||
.context(format!("failed to write virtual branch {}", vbranch.name))?;
|
||||
@ -261,7 +261,7 @@ fn compute_locks(
|
||||
let branch_path_diffs = virtual_branches
|
||||
.iter()
|
||||
.filter_map(|branch| {
|
||||
let commit = repository.find_commit(branch.head).ok()?;
|
||||
let commit = repository.find_commit(branch.head()).ok()?;
|
||||
let tree = repository
|
||||
.find_real_tree(&commit, Default::default())
|
||||
.ok()?;
|
||||
@ -319,7 +319,7 @@ fn compute_locks(
|
||||
.iter()
|
||||
.map(|b| HunkLock {
|
||||
branch_id: b.id,
|
||||
commit_id: b.head,
|
||||
commit_id: b.head(),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
|
@ -29,12 +29,12 @@ pub(crate) fn undo_commit(
|
||||
|
||||
let new_head_commit = inner_undo_commit(
|
||||
ctx.repository(),
|
||||
branch.head,
|
||||
branch.head(),
|
||||
commit_oid,
|
||||
succeeding_rebases,
|
||||
)?;
|
||||
|
||||
branch.head = new_head_commit;
|
||||
branch.set_head(new_head_commit);
|
||||
branch.updated_timestamp_ms = gitbutler_time::time::now_ms();
|
||||
vb_state.set_branch(branch.clone())?;
|
||||
|
||||
|
@ -150,14 +150,14 @@ pub fn upstream_integration_statuses(
|
||||
.iter()
|
||||
.map(|virtual_branch| {
|
||||
let tree = repository.find_tree(virtual_branch.tree)?;
|
||||
let head = repository.find_commit(virtual_branch.head)?;
|
||||
let head = repository.find_commit(virtual_branch.head())?;
|
||||
let head_tree = repository.find_real_tree(&head, Default::default())?;
|
||||
|
||||
// Try cherry pick the branch's head commit onto the target to
|
||||
// see if it conflics. This is equivalent to doing a merge
|
||||
// but accounts for the commit being conflicted.
|
||||
|
||||
let has_commits = virtual_branch.head != old_target.id();
|
||||
let has_commits = virtual_branch.head() != old_target.id();
|
||||
let has_uncommited_changes = head_tree.id() != tree.id();
|
||||
|
||||
// Is the branch completly empty?
|
||||
@ -320,7 +320,7 @@ pub(crate) fn integrate_upstream(
|
||||
continue;
|
||||
};
|
||||
|
||||
branch.head = *head;
|
||||
branch.set_head(*head);
|
||||
branch.tree = *tree;
|
||||
|
||||
virtual_branches_state.set_branch(branch.clone())?;
|
||||
@ -422,7 +422,7 @@ fn compute_resolutions(
|
||||
// Make a merge commit on top of the branch commits,
|
||||
// then rebase the tree ontop of that. If the tree ends
|
||||
// up conflicted, commit the tree.
|
||||
let target_commit = repository.find_commit(virtual_branch.head)?;
|
||||
let target_commit = repository.find_commit(virtual_branch.head())?;
|
||||
|
||||
let new_head = gitbutler_merge_commits(
|
||||
repository,
|
||||
@ -465,7 +465,7 @@ fn compute_resolutions(
|
||||
|
||||
// Rebase virtual branches' commits
|
||||
let virtual_branch_commits =
|
||||
repository.l(virtual_branch.head, LogUntil::Commit(lower_bound))?;
|
||||
repository.l(virtual_branch.head(), LogUntil::Commit(lower_bound))?;
|
||||
|
||||
let new_head = cherry_rebase_group(
|
||||
repository,
|
||||
@ -497,33 +497,27 @@ fn compute_resolutions(
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use gitbutler_branch::BranchOwnershipClaims;
|
||||
use gitbutler_commit::commit_ext::CommitExt as _;
|
||||
use gitbutler_testsupport::testing_repository::TestingRepository;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::*;
|
||||
|
||||
fn make_branch(head: git2::Oid, tree: git2::Oid) -> Branch {
|
||||
Branch {
|
||||
id: Uuid::new_v4().into(),
|
||||
name: "branchy branch".into(),
|
||||
notes: "bla bla bla".into(),
|
||||
source_refname: None,
|
||||
upstream: None,
|
||||
upstream_head: None,
|
||||
created_timestamp_ms: 69420,
|
||||
updated_timestamp_ms: 69420,
|
||||
let mut branch = Branch::new(
|
||||
"branchy branch".into(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
tree,
|
||||
head,
|
||||
ownership: BranchOwnershipClaims::default(),
|
||||
order: 0,
|
||||
selected_for_changes: None,
|
||||
allow_rebasing: true,
|
||||
in_workspace: true,
|
||||
not_in_workspace_wip_change_id: None,
|
||||
heads: Default::default(),
|
||||
}
|
||||
0,
|
||||
None,
|
||||
true,
|
||||
);
|
||||
branch.created_timestamp_ms = 69420;
|
||||
branch.updated_timestamp_ms = 69420;
|
||||
branch.notes = "bla bla bla".into();
|
||||
branch
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -343,7 +343,7 @@ pub fn list_virtual_branches_cached(
|
||||
let mut is_remote = false;
|
||||
|
||||
// find all commits on head that are not on target.sha
|
||||
let commits = repo.log(branch.head, LogUntil::Commit(default_target.sha))?;
|
||||
let commits = repo.log(branch.head(), LogUntil::Commit(default_target.sha))?;
|
||||
let check_commit = IsCommitIntegrated::new(ctx, &default_target)?;
|
||||
let vbranch_commits = {
|
||||
let _span = tracing::debug_span!(
|
||||
@ -385,7 +385,7 @@ pub fn list_virtual_branches_cached(
|
||||
};
|
||||
|
||||
let merge_base = repo
|
||||
.merge_base(default_target.sha, branch.head)
|
||||
.merge_base(default_target.sha, branch.head())
|
||||
.context("failed to find merge base")?;
|
||||
let base_current = true;
|
||||
|
||||
@ -426,6 +426,8 @@ pub fn list_virtual_branches_cached(
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
|
||||
let head = branch.head();
|
||||
let branch = VirtualBranch {
|
||||
id: branch.id,
|
||||
name: branch.name,
|
||||
@ -445,7 +447,7 @@ pub fn list_virtual_branches_cached(
|
||||
updated_at: branch.updated_timestamp_ms,
|
||||
selected_for_changes: branch.selected_for_changes == Some(max_selected_for_changes),
|
||||
allow_rebasing: branch.allow_rebasing,
|
||||
head: branch.head,
|
||||
head,
|
||||
merge_base,
|
||||
fork_point,
|
||||
refname,
|
||||
@ -482,7 +484,7 @@ fn stack_series(
|
||||
});
|
||||
let mut patches: Vec<VirtualBranchCommit> = vec![];
|
||||
for patch in series.clone().local_commits {
|
||||
let commit = commit_by_oid_or_change_id(&patch, ctx, branch.head, default_target)?;
|
||||
let commit = commit_by_oid_or_change_id(&patch, ctx, branch.head(), default_target)?;
|
||||
let is_integrated = check_commit.is_integrated(&commit)?;
|
||||
let vcommit = commit_to_vbranch_commit(
|
||||
ctx,
|
||||
@ -497,7 +499,7 @@ fn stack_series(
|
||||
patches.reverse();
|
||||
let mut upstream_patches = vec![];
|
||||
for patch in series.upstream_only(&stack_series) {
|
||||
let commit = commit_by_oid_or_change_id(&patch, ctx, branch.head, default_target)?;
|
||||
let commit = commit_by_oid_or_change_id(&patch, ctx, branch.head(), default_target)?;
|
||||
let is_integrated = check_commit.is_integrated(&commit)?;
|
||||
let vcommit = commit_to_vbranch_commit(
|
||||
ctx,
|
||||
@ -580,7 +582,7 @@ fn is_requires_force(ctx: &CommandContext, branch: &Branch) -> Result<bool> {
|
||||
|
||||
let merge_base = ctx
|
||||
.repository()
|
||||
.merge_base(upstream_commit.id(), branch.head)?;
|
||||
.merge_base(upstream_commit.id(), branch.head())?;
|
||||
|
||||
Ok(merge_base != upstream_commit.id())
|
||||
}
|
||||
@ -624,12 +626,12 @@ pub fn integrate_upstream_commits(ctx: &CommandContext, branch_id: BranchId) ->
|
||||
let upstream_oid = repo.refname_to_id(&upstream_branch.to_string())?;
|
||||
let upstream_commit = repo.find_commit(upstream_oid)?;
|
||||
|
||||
if upstream_commit.id() == branch.head {
|
||||
if upstream_commit.id() == branch.head() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let upstream_commits = repo.list_commits(upstream_commit.id(), default_target.sha)?;
|
||||
let branch_commits = repo.list_commits(branch.head, default_target.sha)?;
|
||||
let branch_commits = repo.list_commits(branch.head(), default_target.sha)?;
|
||||
|
||||
let branch_commit_ids = branch_commits.iter().map(|c| c.id()).collect::<Vec<_>>();
|
||||
|
||||
@ -716,7 +718,7 @@ pub fn integrate_upstream_commits(ctx: &CommandContext, branch_id: BranchId) ->
|
||||
.force()
|
||||
.checkout()?;
|
||||
} else {
|
||||
branch.head = new_head;
|
||||
branch.set_head(new_head);
|
||||
branch.tree = head_commit.tree()?.id();
|
||||
vb_state.set_branch(branch.clone())?;
|
||||
repo.checkout_index_builder(&mut merge_index)
|
||||
@ -735,7 +737,7 @@ pub(crate) fn integrate_with_rebase(
|
||||
) -> Result<git2::Oid> {
|
||||
cherry_rebase_group(
|
||||
ctx.repository(),
|
||||
branch.head,
|
||||
branch.head(),
|
||||
unknown_commits.as_mut_slice(),
|
||||
ctx.project().succeeding_rebases,
|
||||
)
|
||||
@ -775,7 +777,7 @@ pub(crate) fn integrate_with_merge(
|
||||
|
||||
let merge_tree_oid = merge_index.write_tree_to(ctx.repository())?;
|
||||
let merge_tree = repo.find_tree(merge_tree_oid)?;
|
||||
let head_commit = repo.find_commit(branch.head)?;
|
||||
let head_commit = repo.find_commit(branch.head())?;
|
||||
|
||||
ctx.commit(
|
||||
format!(
|
||||
@ -937,7 +939,7 @@ pub(crate) fn reset_branch(
|
||||
let default_target = vb_state.get_default_target()?;
|
||||
|
||||
let mut branch = vb_state.get_branch_in_workspace(branch_id)?;
|
||||
if branch.head == target_commit_id {
|
||||
if branch.head() == target_commit_id {
|
||||
// nothing to do
|
||||
return Ok(());
|
||||
}
|
||||
@ -945,7 +947,7 @@ pub(crate) fn reset_branch(
|
||||
if default_target.sha != target_commit_id
|
||||
&& !ctx
|
||||
.repository()
|
||||
.l(branch.head, LogUntil::Commit(default_target.sha))?
|
||||
.l(branch.head(), LogUntil::Commit(default_target.sha))?
|
||||
.contains(&target_commit_id)
|
||||
{
|
||||
bail!("commit {target_commit_id} not in the branch");
|
||||
@ -955,7 +957,7 @@ pub(crate) fn reset_branch(
|
||||
// what hunks were released by this reset, and assign them to this branch.
|
||||
let old_head = get_workspace_head(ctx)?;
|
||||
|
||||
branch.head = target_commit_id;
|
||||
branch.set_head(target_commit_id);
|
||||
branch.updated_timestamp_ms = gitbutler_time::time::now_ms();
|
||||
vb_state.set_branch(branch.clone())?;
|
||||
|
||||
@ -1076,19 +1078,19 @@ pub fn commit(
|
||||
Some((file.path, hunks))
|
||||
}
|
||||
});
|
||||
gitbutler_diff::write::hunks_onto_commit(ctx, branch.head, files)?
|
||||
gitbutler_diff::write::hunks_onto_commit(ctx, branch.head(), files)?
|
||||
} else {
|
||||
let files = files
|
||||
.into_iter()
|
||||
.map(|file| (file.path, file.hunks))
|
||||
.collect::<Vec<(PathBuf, Vec<VirtualBranchHunk>)>>();
|
||||
gitbutler_diff::write::hunks_onto_commit(ctx, branch.head, files)?
|
||||
gitbutler_diff::write::hunks_onto_commit(ctx, branch.head(), files)?
|
||||
};
|
||||
|
||||
let git_repository = ctx.repository();
|
||||
let parent_commit = git_repository
|
||||
.find_commit(branch.head)
|
||||
.context(format!("failed to find commit {:?}", branch.head))?;
|
||||
.find_commit(branch.head())
|
||||
.context(format!("failed to find commit {:?}", branch.head()))?;
|
||||
let tree = git_repository
|
||||
.find_tree(tree_oid)
|
||||
.context(format!("failed to find tree {:?}", tree_oid))?;
|
||||
@ -1120,7 +1122,7 @@ pub fn commit(
|
||||
|
||||
let vb_state = ctx.project().virtual_branches();
|
||||
branch.tree = tree_oid;
|
||||
branch.head = commit_oid;
|
||||
branch.set_head(commit_oid);
|
||||
branch.updated_timestamp_ms = gitbutler_time::time::now_ms();
|
||||
vb_state.set_branch(branch.clone())?;
|
||||
if let Err(e) = branch.set_stack_head(ctx, commit_oid) {
|
||||
@ -1177,10 +1179,10 @@ pub(crate) fn push(
|
||||
))
|
||||
};
|
||||
|
||||
ctx.push(vbranch.head, &remote_branch, with_force, None, askpass)?;
|
||||
ctx.push(vbranch.head(), &remote_branch, with_force, None, askpass)?;
|
||||
|
||||
vbranch.upstream = Some(remote_branch.clone());
|
||||
vbranch.upstream_head = Some(vbranch.head);
|
||||
vbranch.upstream_head = Some(vbranch.head());
|
||||
vb_state
|
||||
.set_branch(vbranch.clone())
|
||||
.context("failed to write target branch after push")?;
|
||||
@ -1343,7 +1345,7 @@ pub(crate) fn move_commit_file(
|
||||
// find all the commits upstream from the target "to" commit
|
||||
let mut upstream_commits = ctx
|
||||
.repository()
|
||||
.l(target_branch.head, LogUntil::Commit(amend_commit.id()))?;
|
||||
.l(target_branch.head(), LogUntil::Commit(amend_commit.id()))?;
|
||||
|
||||
// get a list of all the diffs across all the virtual branches
|
||||
let base_file_diffs = gitbutler_diff::workdir(ctx.repository(), default_target.sha)
|
||||
@ -1457,19 +1459,23 @@ pub(crate) fn move_commit_file(
|
||||
.context("commit failed")?;
|
||||
|
||||
// rebase everything above the new "from" commit that has the moved changes removed
|
||||
let new_head =
|
||||
match cherry_rebase(ctx, new_from_commit_oid, from_commit_id, target_branch.head) {
|
||||
Ok(Some(new_head)) => new_head,
|
||||
Ok(None) => bail!("no rebase was performed"),
|
||||
Err(err) => return Err(err).context("rebase failed"),
|
||||
};
|
||||
let new_head = match cherry_rebase(
|
||||
ctx,
|
||||
new_from_commit_oid,
|
||||
from_commit_id,
|
||||
target_branch.head(),
|
||||
) {
|
||||
Ok(Some(new_head)) => new_head,
|
||||
Ok(None) => bail!("no rebase was performed"),
|
||||
Err(err) => return Err(err).context("rebase failed"),
|
||||
};
|
||||
|
||||
// ok, now we need to identify which the new "to" commit is in the rebased history
|
||||
// so we'll take a list of the upstream oids and find it simply based on location
|
||||
// (since the order should not have changed in our simple rebase)
|
||||
let old_upstream_commit_oids = ctx
|
||||
.repository()
|
||||
.l(target_branch.head, LogUntil::Commit(default_target.sha))?;
|
||||
.l(target_branch.head(), LogUntil::Commit(default_target.sha))?;
|
||||
|
||||
let new_upstream_commit_oids = ctx
|
||||
.repository()
|
||||
@ -1529,7 +1535,7 @@ pub(crate) fn move_commit_file(
|
||||
|
||||
// if there are no upstream commits (the "to" commit was the branch head), then we're done
|
||||
if upstream_commits.is_empty() {
|
||||
target_branch.head = commit_oid;
|
||||
target_branch.set_head(commit_oid);
|
||||
vb_state.set_branch(target_branch.clone())?;
|
||||
crate::integration::update_workspace_commit(&vb_state, ctx)?;
|
||||
return Ok(commit_oid);
|
||||
@ -1541,7 +1547,7 @@ pub(crate) fn move_commit_file(
|
||||
|
||||
// if that rebase worked, update the branch head and the gitbutler workspace
|
||||
if let Some(new_head) = new_head {
|
||||
target_branch.head = new_head;
|
||||
target_branch.set_head(new_head);
|
||||
vb_state.set_branch(target_branch.clone())?;
|
||||
crate::integration::update_workspace_commit(&vb_state, ctx)?;
|
||||
Ok(commit_oid)
|
||||
@ -1586,7 +1592,7 @@ pub(crate) fn amend(
|
||||
|
||||
if ctx
|
||||
.repository()
|
||||
.l(target_branch.head, LogUntil::Commit(default_target.sha))?
|
||||
.l(target_branch.head(), LogUntil::Commit(default_target.sha))?
|
||||
.is_empty()
|
||||
{
|
||||
bail!("branch has no commits - there is nothing to amend to");
|
||||
@ -1654,10 +1660,10 @@ pub(crate) fn amend(
|
||||
// now rebase upstream commits, if needed
|
||||
let upstream_commits = ctx
|
||||
.repository()
|
||||
.l(target_branch.head, LogUntil::Commit(amend_commit.id()))?;
|
||||
.l(target_branch.head(), LogUntil::Commit(amend_commit.id()))?;
|
||||
// if there are no upstream commits, we're done
|
||||
if upstream_commits.is_empty() {
|
||||
target_branch.head = commit_oid;
|
||||
target_branch.set_head(commit_oid);
|
||||
vb_state.set_branch(target_branch.clone())?;
|
||||
crate::integration::update_workspace_commit(&vb_state, ctx)?;
|
||||
return Ok(commit_oid);
|
||||
@ -1668,7 +1674,7 @@ pub(crate) fn amend(
|
||||
let new_head = cherry_rebase(ctx, commit_oid, amend_commit.id(), last_commit)?;
|
||||
|
||||
if let Some(new_head) = new_head {
|
||||
target_branch.head = new_head;
|
||||
target_branch.set_head(new_head);
|
||||
vb_state.set_branch(target_branch.clone())?;
|
||||
crate::integration::update_workspace_commit(&vb_state, ctx)?;
|
||||
Ok(commit_oid)
|
||||
@ -1706,16 +1712,16 @@ pub(crate) fn insert_blank_commit(
|
||||
.unwrap();
|
||||
let blank_commit_oid = ctx.commit("", &commit_tree, &[&commit], None)?;
|
||||
|
||||
if commit.id() == branch.head && offset < 0 {
|
||||
if commit.id() == branch.head() && offset < 0 {
|
||||
// inserting before the first commit
|
||||
branch.head = blank_commit_oid;
|
||||
branch.set_head(blank_commit_oid);
|
||||
crate::integration::update_workspace_commit(&vb_state, ctx)
|
||||
.context("failed to update gitbutler workspace")?;
|
||||
} else {
|
||||
// rebase all commits above it onto the new commit
|
||||
match cherry_rebase(ctx, blank_commit_oid, commit.id(), branch.head) {
|
||||
match cherry_rebase(ctx, blank_commit_oid, commit.id(), branch.head()) {
|
||||
Ok(Some(new_head)) => {
|
||||
branch.head = new_head;
|
||||
branch.set_head(new_head);
|
||||
crate::integration::update_workspace_commit(&vb_state, ctx)
|
||||
.context("failed to update gitbutler workspace")?;
|
||||
}
|
||||
@ -1744,7 +1750,7 @@ pub(crate) fn squash(
|
||||
let default_target = vb_state.get_default_target()?;
|
||||
let branch_commit_oids = ctx
|
||||
.repository()
|
||||
.l(branch.head, LogUntil::Commit(default_target.sha))?;
|
||||
.l(branch.head(), LogUntil::Commit(default_target.sha))?;
|
||||
|
||||
if !branch_commit_oids.contains(&commit_id) {
|
||||
bail!("commit {commit_id} not in the branch")
|
||||
@ -1821,7 +1827,7 @@ pub(crate) fn squash(
|
||||
) {
|
||||
Ok(new_head_id) => {
|
||||
// save new branch head
|
||||
branch.head = new_head_id;
|
||||
branch.set_head(new_head_id);
|
||||
branch.updated_timestamp_ms = gitbutler_time::time::now_ms();
|
||||
vb_state.set_branch(branch.clone())?;
|
||||
|
||||
@ -1851,7 +1857,7 @@ pub(crate) fn update_commit_message(
|
||||
let mut branch = vb_state.get_branch_in_workspace(branch_id)?;
|
||||
let branch_commit_oids = ctx
|
||||
.repository()
|
||||
.l(branch.head, LogUntil::Commit(default_target.sha))?;
|
||||
.l(branch.head(), LogUntil::Commit(default_target.sha))?;
|
||||
|
||||
if !branch_commit_oids.contains(&commit_id) {
|
||||
bail!("commit {commit_id} not in the branch");
|
||||
@ -1907,7 +1913,7 @@ pub(crate) fn update_commit_message(
|
||||
)
|
||||
.map_err(|err| err.context("rebase error"))?;
|
||||
// save new branch head
|
||||
branch.head = new_head_id;
|
||||
branch.set_head(new_head_id);
|
||||
branch.updated_timestamp_ms = gitbutler_time::time::now_ms();
|
||||
vb_state.set_branch(branch.clone())?;
|
||||
|
||||
|
@ -845,7 +845,7 @@ fn merge_vbranch_upstream_clean_rebase() -> Result<()> {
|
||||
.expect("failed to create virtual branch");
|
||||
|
||||
branch.upstream = Some(remote_branch.clone());
|
||||
branch.head = last_push;
|
||||
branch.set_head(last_push);
|
||||
vb_state.set_branch(branch.clone())?;
|
||||
|
||||
// create the branch
|
||||
@ -970,7 +970,7 @@ fn merge_vbranch_upstream_conflict() -> Result<()> {
|
||||
.create_virtual_branch(&BranchCreateRequest::default(), guard.write_permission())
|
||||
.expect("failed to create virtual branch");
|
||||
branch.upstream = Some(remote_branch.clone());
|
||||
branch.head = last_push;
|
||||
branch.set_head(last_push);
|
||||
vb_state.set_branch(branch.clone())?;
|
||||
|
||||
internal::update_branch(
|
||||
|
@ -1,30 +1,25 @@
|
||||
use gitbutler_branch::{Branch, BranchOwnershipClaims};
|
||||
use uuid::Uuid;
|
||||
use gitbutler_branch::Branch;
|
||||
|
||||
/// Makes a Branch struct with a bunch of default values.
|
||||
///
|
||||
/// This assumes that the only relevant properties for your test are the head
|
||||
/// and tree Oids.
|
||||
fn make_branch(head: git2::Oid, tree: git2::Oid) -> Branch {
|
||||
Branch {
|
||||
id: Uuid::new_v4().into(),
|
||||
name: "branchy branch".into(),
|
||||
notes: "bla bla bla".into(),
|
||||
source_refname: None,
|
||||
upstream: None,
|
||||
upstream_head: None,
|
||||
created_timestamp_ms: 69420,
|
||||
updated_timestamp_ms: 69420,
|
||||
let mut branch = Branch::new(
|
||||
"branchy branch".into(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
tree,
|
||||
head,
|
||||
ownership: BranchOwnershipClaims::default(),
|
||||
order: 0,
|
||||
selected_for_changes: None,
|
||||
allow_rebasing: true,
|
||||
in_workspace: true,
|
||||
not_in_workspace_wip_change_id: None,
|
||||
heads: Default::default(),
|
||||
}
|
||||
0,
|
||||
None,
|
||||
true,
|
||||
);
|
||||
branch.created_timestamp_ms = 69420;
|
||||
branch.updated_timestamp_ms = 69420;
|
||||
branch.notes = "bla bla bla".into();
|
||||
branch
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -52,7 +47,7 @@ mod compute_updated_branch_head {
|
||||
compute_updated_branch_head(&test_repository.repository, &branch, head.id(), true)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(head, branch.head);
|
||||
assert_eq!(head, branch.head());
|
||||
assert_eq!(tree, branch.tree);
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ gitbutler-fs.workspace = true
|
||||
gitbutler-diff.workspace = true
|
||||
gitbutler-oxidize.workspace = true
|
||||
gitbutler-patch-reference.workspace = true
|
||||
gitbutler-time.workspace = true
|
||||
itertools = "0.13"
|
||||
toml.workspace = true
|
||||
serde = { workspace = true, features = ["std"] }
|
||||
|
@ -44,7 +44,7 @@ pub struct Branch {
|
||||
pub tree: git2::Oid,
|
||||
/// head is id of the last "virtual" commit in this branch
|
||||
#[serde(with = "gitbutler_serde::oid")]
|
||||
pub head: git2::Oid,
|
||||
head: git2::Oid,
|
||||
pub ownership: BranchOwnershipClaims,
|
||||
// order is the number by which UI should sort branches
|
||||
pub order: usize,
|
||||
@ -86,9 +86,52 @@ where
|
||||
}
|
||||
|
||||
impl Branch {
|
||||
/// Creates a new `Branch` with the given name. The `in_workspace` flag is set to `true`.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
name: String,
|
||||
source_refname: Option<Refname>,
|
||||
upstream: Option<RemoteRefname>,
|
||||
upstream_head: Option<git2::Oid>,
|
||||
tree: git2::Oid,
|
||||
head: git2::Oid,
|
||||
order: usize,
|
||||
selected_for_changes: Option<i64>,
|
||||
allow_rebasing: bool,
|
||||
) -> Self {
|
||||
let now = gitbutler_time::time::now_ms();
|
||||
Self {
|
||||
id: BranchId::generate(),
|
||||
name,
|
||||
notes: String::new(),
|
||||
source_refname,
|
||||
upstream,
|
||||
upstream_head,
|
||||
created_timestamp_ms: now,
|
||||
updated_timestamp_ms: now,
|
||||
tree,
|
||||
head,
|
||||
ownership: BranchOwnershipClaims::default(),
|
||||
order,
|
||||
selected_for_changes,
|
||||
allow_rebasing,
|
||||
in_workspace: true,
|
||||
not_in_workspace_wip_change_id: None,
|
||||
heads: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn refname(&self) -> anyhow::Result<VirtualRefname> {
|
||||
self.try_into()
|
||||
}
|
||||
|
||||
pub fn head(&self) -> git2::Oid {
|
||||
self.head
|
||||
}
|
||||
|
||||
pub fn set_head(&mut self, head: git2::Oid) {
|
||||
self.head = head;
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Branch> for VirtualRefname {
|
||||
|
@ -119,14 +119,12 @@ pub fn reconcile_claims(
|
||||
});
|
||||
}
|
||||
|
||||
let mut updated_branch = claiming_branch.clone();
|
||||
updated_branch.ownership.claims = new_claims.to_owned();
|
||||
|
||||
// Add the claiming branch to the list of outcomes
|
||||
claim_outcomes.push(ClaimOutcome {
|
||||
updated_branch: Branch {
|
||||
ownership: BranchOwnershipClaims {
|
||||
claims: new_claims.to_owned(),
|
||||
},
|
||||
..claiming_branch.clone()
|
||||
},
|
||||
updated_branch,
|
||||
removed_claims: Vec::new(),
|
||||
});
|
||||
|
||||
|
@ -1,73 +1,65 @@
|
||||
use std::{path::PathBuf, vec};
|
||||
|
||||
use gitbutler_branch::{reconcile_claims, Branch, BranchId, BranchOwnershipClaims, OwnershipClaim};
|
||||
use gitbutler_branch::{reconcile_claims, Branch, BranchOwnershipClaims, OwnershipClaim};
|
||||
use gitbutler_diff::Hunk;
|
||||
|
||||
#[test]
|
||||
fn reconcile_ownership_simple() {
|
||||
let branch_a = Branch {
|
||||
name: "a".to_string(),
|
||||
ownership: BranchOwnershipClaims {
|
||||
claims: vec![OwnershipClaim {
|
||||
file_path: PathBuf::from("foo"),
|
||||
hunks: vec![
|
||||
Hunk {
|
||||
start: 1,
|
||||
end: 3,
|
||||
hash: Some(Hunk::hash("1,3")),
|
||||
},
|
||||
Hunk {
|
||||
start: 4,
|
||||
end: 6,
|
||||
hash: Some(Hunk::hash("4,6")),
|
||||
},
|
||||
],
|
||||
}],
|
||||
},
|
||||
tree: git2::Oid::zero(),
|
||||
head: git2::Oid::zero(),
|
||||
id: BranchId::default(),
|
||||
notes: String::default(),
|
||||
upstream: None,
|
||||
upstream_head: None,
|
||||
created_timestamp_ms: u128::default(),
|
||||
updated_timestamp_ms: u128::default(),
|
||||
order: usize::default(),
|
||||
selected_for_changes: None,
|
||||
allow_rebasing: true,
|
||||
in_workspace: true,
|
||||
not_in_workspace_wip_change_id: None,
|
||||
source_refname: None,
|
||||
heads: Default::default(),
|
||||
let mut branch_a = Branch::new(
|
||||
"a".to_string(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
git2::Oid::zero(),
|
||||
git2::Oid::zero(),
|
||||
usize::default(),
|
||||
None,
|
||||
true,
|
||||
);
|
||||
branch_a.ownership = BranchOwnershipClaims {
|
||||
claims: vec![OwnershipClaim {
|
||||
file_path: PathBuf::from("foo"),
|
||||
hunks: vec![
|
||||
Hunk {
|
||||
start: 1,
|
||||
end: 3,
|
||||
hash: Some(Hunk::hash("1,3")),
|
||||
},
|
||||
Hunk {
|
||||
start: 4,
|
||||
end: 6,
|
||||
hash: Some(Hunk::hash("4,6")),
|
||||
},
|
||||
],
|
||||
}],
|
||||
};
|
||||
let branch_b = Branch {
|
||||
name: "b".to_string(),
|
||||
ownership: BranchOwnershipClaims {
|
||||
claims: vec![OwnershipClaim {
|
||||
file_path: PathBuf::from("foo"),
|
||||
hunks: vec![Hunk {
|
||||
start: 7,
|
||||
end: 9,
|
||||
hash: Some(Hunk::hash("7,9")),
|
||||
}],
|
||||
branch_a.created_timestamp_ms = u128::default();
|
||||
branch_a.updated_timestamp_ms = u128::default();
|
||||
|
||||
let mut branch_b = Branch::new(
|
||||
"b".to_string(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
git2::Oid::zero(),
|
||||
git2::Oid::zero(),
|
||||
usize::default(),
|
||||
None,
|
||||
true,
|
||||
);
|
||||
branch_b.ownership = BranchOwnershipClaims {
|
||||
claims: vec![OwnershipClaim {
|
||||
file_path: PathBuf::from("foo"),
|
||||
hunks: vec![Hunk {
|
||||
start: 7,
|
||||
end: 9,
|
||||
hash: Some(Hunk::hash("7,9")),
|
||||
}],
|
||||
},
|
||||
tree: git2::Oid::zero(),
|
||||
head: git2::Oid::zero(),
|
||||
id: BranchId::default(),
|
||||
notes: String::default(),
|
||||
upstream: None,
|
||||
upstream_head: None,
|
||||
created_timestamp_ms: u128::default(),
|
||||
updated_timestamp_ms: u128::default(),
|
||||
order: usize::default(),
|
||||
selected_for_changes: None,
|
||||
allow_rebasing: true,
|
||||
in_workspace: true,
|
||||
not_in_workspace_wip_change_id: None,
|
||||
source_refname: None,
|
||||
heads: Default::default(),
|
||||
}],
|
||||
};
|
||||
branch_b.created_timestamp_ms = u128::default();
|
||||
branch_b.updated_timestamp_ms = u128::default();
|
||||
|
||||
let all_branches: Vec<Branch> = vec![branch_a.clone(), branch_b.clone()];
|
||||
let claim: Vec<OwnershipClaim> = vec![OwnershipClaim {
|
||||
file_path: PathBuf::from("foo"),
|
||||
|
@ -216,7 +216,7 @@ pub(crate) fn save_and_return_to_workspace(
|
||||
.context("Failed to commit new commit")?;
|
||||
|
||||
// Rebase all all commits on top of the new commit and update reference
|
||||
let new_branch_head = cherry_rebase(ctx, new_commit_oid, commit.id(), virtual_branch.head)
|
||||
let new_branch_head = cherry_rebase(ctx, new_commit_oid, commit.id(), virtual_branch.head())
|
||||
.context("Failed to rebase commits onto new commit")?
|
||||
.unwrap_or(new_commit_oid);
|
||||
|
||||
@ -232,7 +232,7 @@ pub(crate) fn save_and_return_to_workspace(
|
||||
fearless_rebasing,
|
||||
)?;
|
||||
|
||||
virtual_branch.head = new_branch_head;
|
||||
virtual_branch.set_head(new_branch_head);
|
||||
virtual_branch.tree = new_branch_tree;
|
||||
virtual_branch.updated_timestamp_ms = gitbutler_time::time::now_ms();
|
||||
vb_state
|
||||
|
@ -372,7 +372,7 @@ fn prepare_snapshot(ctx: &Project, _shared_access: &WorktreeReadPermission) -> R
|
||||
|
||||
// let's get all the commits between the branch head and the target
|
||||
let mut revwalk = repo.revwalk()?;
|
||||
revwalk.push(branch.head)?;
|
||||
revwalk.push(branch.head())?;
|
||||
revwalk.hide(default_target_commit.id())?;
|
||||
|
||||
let mut commits_tree_builder = repo.treebuilder(None)?;
|
||||
|
@ -79,7 +79,7 @@ impl RepoActionsExt for CommandContext {
|
||||
.find_reference(&branch.refname()?.to_string())
|
||||
{
|
||||
Ok(reference) => match reference.target() {
|
||||
Some(head_oid) => Ok((head_oid != branch.head, true)),
|
||||
Some(head_oid) => Ok((head_oid != branch.head(), true)),
|
||||
None => Ok((true, true)),
|
||||
},
|
||||
Err(err) => match err.code() {
|
||||
@ -93,7 +93,7 @@ impl RepoActionsExt for CommandContext {
|
||||
self.repository()
|
||||
.reference(
|
||||
&branch.refname()?.to_string(),
|
||||
branch.head,
|
||||
branch.head(),
|
||||
with_force,
|
||||
"new vbranch",
|
||||
)
|
||||
|
@ -147,7 +147,7 @@ impl Stack for Branch {
|
||||
if self.initialized() {
|
||||
return Ok(());
|
||||
}
|
||||
let commit = ctx.repository().find_commit(self.head)?;
|
||||
let commit = ctx.repository().find_commit(self.head())?;
|
||||
let mut reference = PatchReference {
|
||||
target: if let Some(change_id) = commit.change_id() {
|
||||
CommitOrChangeId::ChangeId(change_id.to_string())
|
||||
@ -199,9 +199,9 @@ impl Stack for Branch {
|
||||
None
|
||||
};
|
||||
let state = branch_state(ctx);
|
||||
let patches = stack_patches(ctx, &state, self.head, true)?;
|
||||
let patches = stack_patches(ctx, &state, self.head(), true)?;
|
||||
validate_name(&new_head, ctx, &state)?;
|
||||
validate_target(&new_head, ctx, self.head, &state)?;
|
||||
validate_target(&new_head, ctx, self.head(), &state)?;
|
||||
let updated_heads = add_head(self.heads.clone(), new_head, preceding_head, patches)?;
|
||||
self.heads = updated_heads;
|
||||
state.set_branch(self.clone())
|
||||
@ -240,7 +240,7 @@ impl Stack for Branch {
|
||||
if !self.initialized() {
|
||||
return Err(anyhow!("Stack has not been initialized"));
|
||||
}
|
||||
if self.head != commit_id {
|
||||
if self.head() != commit_id {
|
||||
bail!("The commit {} is not the head of the stack", commit_id);
|
||||
}
|
||||
let commit = ctx.repository().find_commit(commit_id)?;
|
||||
@ -251,12 +251,13 @@ impl Stack for Branch {
|
||||
};
|
||||
|
||||
let state = branch_state(ctx);
|
||||
let stack_head = self.head();
|
||||
let head = self
|
||||
.heads
|
||||
.last_mut()
|
||||
.ok_or_else(|| anyhow!("Invalid state: no heads found"))?;
|
||||
head.target = patch.clone();
|
||||
validate_target(head, ctx, self.head, &state)?;
|
||||
validate_target(head, ctx, stack_head, &state)?;
|
||||
state.set_branch(self.clone())
|
||||
}
|
||||
|
||||
@ -274,7 +275,7 @@ impl Stack for Branch {
|
||||
}
|
||||
|
||||
let state = branch_state(ctx);
|
||||
let patches = stack_patches(ctx, &state, self.head, true)?;
|
||||
let patches = stack_patches(ctx, &state, self.head(), true)?;
|
||||
let mut updated_heads = self.heads.clone();
|
||||
|
||||
// Handle target updates
|
||||
@ -285,7 +286,7 @@ impl Stack for Branch {
|
||||
.find(|h| h.name == branch_name)
|
||||
.ok_or_else(|| anyhow!("Series with name {} not found", branch_name))?;
|
||||
new_head.target = target_update.target.clone();
|
||||
validate_target(&new_head, ctx, self.head, &state)?;
|
||||
validate_target(&new_head, ctx, self.head(), &state)?;
|
||||
let preceding_head = update
|
||||
.target_update
|
||||
.clone()
|
||||
@ -345,7 +346,7 @@ impl Stack for Branch {
|
||||
let (_, reference) = get_head(&self.heads, &branch_name)?;
|
||||
let default_target = branch_state(ctx).get_default_target()?;
|
||||
let commit =
|
||||
commit_by_oid_or_change_id(&reference.target, ctx, self.head, &default_target)?;
|
||||
commit_by_oid_or_change_id(&reference.target, ctx, self.head(), &default_target)?;
|
||||
let remote_name = branch_state(ctx)
|
||||
.get_default_target()?
|
||||
.push_remote_name
|
||||
@ -373,10 +374,10 @@ impl Stack for Branch {
|
||||
let mut all_series: Vec<Series> = vec![];
|
||||
let repo = ctx.repository();
|
||||
let default_target = state.get_default_target()?;
|
||||
let mut previous_head = repo.merge_base(self.head, default_target.sha)?;
|
||||
let mut previous_head = repo.merge_base(self.head(), default_target.sha)?;
|
||||
for head in self.heads.clone() {
|
||||
let head_commit =
|
||||
commit_by_oid_or_change_id(&head.target, ctx, self.head, &default_target)?.id();
|
||||
commit_by_oid_or_change_id(&head.target, ctx, self.head(), &default_target)?.id();
|
||||
let local_patches = repo
|
||||
.log(head_commit, LogUntil::Commit(previous_head))?
|
||||
.iter()
|
||||
|
@ -22,7 +22,7 @@ fn init_success() -> Result<()> {
|
||||
branch.heads[0].target,
|
||||
CommitOrChangeId::ChangeId(
|
||||
ctx.repository()
|
||||
.find_commit(branch.head)?
|
||||
.find_commit(branch.head())?
|
||||
.change_id()
|
||||
.unwrap()
|
||||
)
|
||||
@ -101,7 +101,7 @@ fn add_series_top_base() -> Result<()> {
|
||||
test_ctx.branch.initialize(&ctx)?;
|
||||
let merge_base = ctx.repository().find_commit(
|
||||
ctx.repository()
|
||||
.merge_base(test_ctx.branch.head, test_ctx.default_target.sha)?,
|
||||
.merge_base(test_ctx.branch.head(), test_ctx.default_target.sha)?,
|
||||
)?;
|
||||
let reference = PatchReference {
|
||||
name: "asdf".into(),
|
||||
@ -772,11 +772,11 @@ fn test_ctx(ctx: &CommandContext) -> Result<TestContext> {
|
||||
let target = handle.get_default_target()?;
|
||||
let mut branch_commits = ctx
|
||||
.repository()
|
||||
.log(branch.head, LogUntil::Commit(target.sha))?;
|
||||
.log(branch.head(), LogUntil::Commit(target.sha))?;
|
||||
branch_commits.reverse();
|
||||
let mut other_commits = ctx
|
||||
.repository()
|
||||
.log(other_branch.head, LogUntil::Commit(target.sha))?;
|
||||
.log(other_branch.head(), LogUntil::Commit(target.sha))?;
|
||||
other_commits.reverse();
|
||||
Ok(TestContext {
|
||||
branch: branch.clone(),
|
||||
|
Loading…
Reference in New Issue
Block a user