Assure we can use different merge bases when showing branch details.

Alternatively, correct the assumption that there is a difference.
This commit is contained in:
Sebastian Thiel 2024-08-11 20:40:41 +02:00
parent 07dc9efe02
commit 7362bab7aa
No known key found for this signature in database
GPG Key ID: 9CB5EE7895E8268B
8 changed files with 45 additions and 20 deletions

View File

@ -314,11 +314,12 @@ fn _print_tree(repo: &git2::Repository, tree: &git2::Tree) -> Result<()> {
Ok(()) Ok(())
} }
// try to update the target branch /// try to update the target branch
// this means that we need to: /// this means that we need to:
// determine if what the target branch is now pointing to is mergeable with our current working directory /// - determine if what the target branch is now pointing to is mergeable with our current working directory,
// merge the target branch into our current working directory /// - merge the target branch into our current working directory
// update the target sha /// - update the target sha
/// - return all conflicting references that were unapplied to avoid the conflict
pub(crate) fn update_base_branch( pub(crate) fn update_base_branch(
ctx: &CommandContext, ctx: &CommandContext,
perm: &mut WorktreeWritePermission, perm: &mut WorktreeWritePermission,

View File

@ -428,7 +428,7 @@ pub fn get_branch_listing_details(
let repo = ctx.repository(); let repo = ctx.repository();
let branches = list_branches(ctx, None, Some(branch_names))?; let branches = list_branches(ctx, None, Some(branch_names))?;
let (default_target_upstream_commit_id, default_target_merge_base) = { let (default_target_current_upstream_commit_id, default_target_seen_at_last_update) = {
let target = ctx let target = ctx
.project() .project()
.virtual_branches() .virtual_branches()
@ -446,12 +446,12 @@ pub fn get_branch_listing_details(
for branch in branches { for branch in branches {
let other_branch_commit_id = if let Some(virtual_branch) = branch.virtual_branch { let other_branch_commit_id = if let Some(virtual_branch) = branch.virtual_branch {
if virtual_branch.in_workspace { if virtual_branch.in_workspace {
default_target_merge_base default_target_seen_at_last_update
} else { } else {
default_target_upstream_commit_id default_target_current_upstream_commit_id
} }
} else { } else {
default_target_upstream_commit_id default_target_current_upstream_commit_id
}; };
let Ok(base) = repo.merge_base(other_branch_commit_id, branch.head) else { let Ok(base) = repo.merge_base(other_branch_commit_id, branch.head) else {
continue; continue;

View File

@ -49,4 +49,7 @@ git clone remote complex-repo
echo non-virtual-feature >> file echo non-virtual-feature >> file
git commit -am "non-virtual-feat-$round" git commit -am "non-virtual-feat-$round"
done done
# pretend the remote is at the same state as our local `main`
git update-ref refs/remotes/origin/main main
) )

View File

@ -52,12 +52,13 @@ fn many_commits_in_all_branch_types() -> anyhow::Result<()> {
list[0], list[0],
BranchListingDetails { BranchListingDetails {
name: "feature".into(), name: "feature".into(),
lines_added: 100 + 5, lines_added: 100,
lines_removed: 0, lines_removed: 0,
number_of_files: 1, number_of_files: 1,
number_of_commits: 100 + 5, /* local tracking branch is merge base */ number_of_commits: 100,
authors: vec![default_author()], authors: vec![default_author()],
} },
"local branches use the *current* local tracking branch…"
); );
assert_eq!( assert_eq!(
list[1], list[1],
@ -66,21 +67,24 @@ fn many_commits_in_all_branch_types() -> anyhow::Result<()> {
lines_added: 15, lines_added: 15,
lines_removed: 0, lines_removed: 0,
number_of_files: 1, number_of_files: 1,
// TODO(ST): why is it also going against the local tracking branch instead of the local `main`?
number_of_commits: 10 + 5, number_of_commits: 10 + 5,
authors: vec![default_author()], authors: vec![default_author()],
} },
"…while virtual branches use the 'target' stored when the workspace was last updated.\
That way the 'update' of the workspace performs the calculation to put it back on top of \
the local tracking branch."
); );
assert_eq!( assert_eq!(
list[2], list[2],
BranchListingDetails { BranchListingDetails {
name: "non-virtual-feature".into(), name: "non-virtual-feature".into(),
lines_added: 55, lines_added: 50,
lines_removed: 0, lines_removed: 0,
number_of_files: 1, number_of_files: 1,
number_of_commits: 50 + 5, number_of_commits: 50,
authors: vec![default_author()], authors: vec![default_author()],
} },
"This is a non-virtual brnach, so it sees the local tracking branch as well"
); );
Ok(()) Ok(())
} }

View File

@ -5,12 +5,19 @@ use serde::{ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializ
pub struct Target { pub struct Target {
/// The combination of remote name and branch name, i.e. `origin` and `main`. /// The combination of remote name and branch name, i.e. `origin` and `main`.
/// The remote name is the one used to fetch from. /// The remote name is the one used to fetch from.
/// It's equivalent to e.g. `refs/remotes/origin/main` , and the type `RemoteRefName`
/// stores it as `<remote>` and `<suffix>` so that finding references named `<remote>/<suffix>`
/// will typically find the local tracking branch unambiguously.
pub branch: RemoteRefname, pub branch: RemoteRefname,
/// The URL of the remote behind the symbolic name. /// The URL of the remote behind the symbolic name.
pub remote_url: String, pub remote_url: String,
/// The merge-base between `branch` and the current worktree `HEAD`. /// The merge-base between `branch` and the current worktree `HEAD` upon first creation,
// TODO(ST): is it safe/correct to rename this to `merge_base_commit_id`? /// but then it's the set to the new destination of e.g. `refs/remotes/origin/main` after
// It seems like it, but why was it named just `sha` in the first place? /// the remote was fetched. This value is used to determine if there was a change,
/// and if the *workspace* needs to be recalculated/rebased against the new commit.
// TODO(ST): is it safe/correct to rename this to `branch_target_id`? Should be!
// It's just a bit strange it starts life as merge-base, but maybe it ends
// up the same anyway? Definitely could use a test then.
pub sha: git2::Oid, pub sha: git2::Oid,
/// The name of the remote to push to. /// The name of the remote to push to.
pub push_remote_name: Option<String>, pub push_remote_name: Option<String>,

View File

@ -71,6 +71,8 @@ pub mod vbranch {
}, },
/// List all branches that can be relevant. /// List all branches that can be relevant.
ListAll, ListAll,
/// After fetching the target (i.e. local tracking branch), recompute our workspace against it.
UpdateTarget,
} }
} }

View File

@ -8,6 +8,11 @@ use gitbutler_project::Project;
use crate::command::debug_print; use crate::command::debug_print;
pub fn update_target(project: Project) -> Result<()> {
let unapplied = VirtualBranchActions.update_base_branch(&project)?;
debug_print(unapplied)
}
pub fn list_all(project: Project) -> Result<()> { pub fn list_all(project: Project) -> Result<()> {
let ctx = CommandContext::open(&project)?; let ctx = CommandContext::open(&project)?;
debug_print(list_branches(&ctx, None, None)?) debug_print(list_branches(&ctx, None, None)?)

View File

@ -36,6 +36,9 @@ fn main() -> Result<()> {
command::vbranch::details(project, names) command::vbranch::details(project, names)
} }
Some(vbranch::SubCommands::ListAll) => command::vbranch::list_all(project), Some(vbranch::SubCommands::ListAll) => command::vbranch::list_all(project),
Some(vbranch::SubCommands::UpdateTarget) => {
command::vbranch::update_target(project)
}
None => command::vbranch::list(project), None => command::vbranch::list(project),
} }
} }