Always overwrite local references

This commit is contained in:
Caleb Owens 2024-07-25 15:47:10 +02:00
parent 3f37df4d84
commit 7a6c7472a4
No known key found for this signature in database
13 changed files with 36 additions and 255 deletions

View File

@ -6,8 +6,6 @@
import ContextMenuItem from '$lib/components/contextmenu/ContextMenuItem.svelte';
import ContextMenuSection from '$lib/components/contextmenu/ContextMenuSection.svelte';
import { projectAiGenEnabled } from '$lib/config/config';
import Select from '$lib/select/Select.svelte';
import SelectItem from '$lib/select/SelectItem.svelte';
import Button from '$lib/shared/Button.svelte';
import Modal from '$lib/shared/Modal.svelte';
import TextBox from '$lib/shared/TextBox.svelte';
@ -15,7 +13,7 @@
import { User } from '$lib/stores/user';
import { getContext, getContextStore } from '$lib/utils/context';
import { BranchController } from '$lib/vbranches/branchController';
import { VirtualBranch, type NameConflictResolution } from '$lib/vbranches/types';
import { VirtualBranch } from '$lib/vbranches/types';
export let contextMenuEl: ContextMenu;
export let target: HTMLElement;
@ -49,68 +47,9 @@
aiConfigurationValid = await aiService.validateConfiguration(user?.access_token);
}
let unapplyBranchModal: Modal;
type ResolutionVariants = NameConflictResolution['type'];
const resolutions: { value: ResolutionVariants; label: string }[] = [
{
value: 'overwrite',
label: 'Overwrite the existing branch'
},
{
value: 'suffix',
label: 'Suffix the branch name'
},
{
value: 'rename',
label: 'Use a new name'
}
];
let selectedResolution: ResolutionVariants = resolutions[0].value;
let newBranchName = '';
function unapplyBranchWithSelectedResolution() {
let resolution: NameConflictResolution | undefined;
if (selectedResolution === 'rename') {
resolution = {
type: selectedResolution,
value: newBranchName
};
} else {
resolution = {
type: selectedResolution,
value: undefined
};
}
branchController.convertToRealBranch(branch.id, resolution);
unapplyBranchModal.close();
}
const remoteBranches = branchController.remoteBranchService.branches;
function tryUnapplyBranch() {
if ($remoteBranches.find((b) => b.name.endsWith(normalizedBranchName))) {
unapplyBranchModal.show();
} else {
// No resolution required
function unapplyBranch() {
branchController.convertToRealBranch(branch.id);
}
}
function setButtonCoppy() {
switch (selectedResolution) {
case 'overwrite':
return 'Overwrite and unapply';
case 'suffix':
return 'Suffix and unapply';
case 'rename':
return 'Rename and unapply';
}
}
let normalizedBranchName: string;
@ -126,56 +65,6 @@
}
</script>
<Modal width="small" bind:this={unapplyBranchModal}>
<div class="flow">
<div class="modal-copy">
<p class="text-base-14 text-semibold">
"{normalizedBranchName}" branch already exists
</p>
<p class="text-base-body-13 modal-copy-caption">
A branch with the same name already exists.
<br />
Please select a resolution:
</p>
</div>
<Select
value={selectedResolution}
options={resolutions}
onselect={(value) => {
selectedResolution = value as ResolutionVariants;
}}
>
{#snippet itemSnippet({ item, highlighted })}
<SelectItem selected={item.value === selectedResolution} {highlighted}>
{item.label}
</SelectItem>
{/snippet}
</Select>
{#if selectedResolution === 'rename'}
<TextBox
label="New branch name"
id="newBranchName"
bind:value={newBranchName}
placeholder="Enter new branch name"
/>
{/if}
</div>
{#snippet controls()}
<Button style="ghost" outline on:click={() => unapplyBranchModal.close()}>Cancel</Button>
<Button
style="pop"
kind="solid"
on:click={unapplyBranchWithSelectedResolution}
disabled={!newBranchName && selectedResolution === 'rename'}
>
{setButtonCoppy()}
</Button>
{/snippet}
</Modal>
<ContextMenu bind:this={contextMenuEl} {target}>
<ContextMenuSection>
<ContextMenuItem
@ -190,7 +79,7 @@
<ContextMenuItem
label="Unapply"
on:click={() => {
tryUnapplyBranch();
unapplyBranch();
contextMenuEl.close();
}}
/>
@ -301,21 +190,3 @@
</Button>
{/snippet}
</Modal>
<style lang="postcss">
.flow {
display: flex;
flex-direction: column;
gap: 16px;
}
.modal-copy {
display: flex;
flex-direction: column;
gap: 8px;
}
.modal-copy-caption {
color: var(--clr-text-2);
}
</style>

View File

@ -4,7 +4,7 @@ import * as toasts from '$lib/utils/toasts';
import posthog from 'posthog-js';
import type { BaseBranchService } from '$lib/baseBranch/baseBranchService';
import type { RemoteBranchService } from '$lib/stores/remoteBranches';
import type { VirtualBranch, Hunk, LocalFile, NameConflictResolution } from './types';
import type { VirtualBranch, Hunk, LocalFile } from './types';
import type { VirtualBranchService } from './virtualBranch';
export class BranchController {
@ -180,15 +180,11 @@ export class BranchController {
}
}
async convertToRealBranch(
branchId: string,
nameConflictResolution: NameConflictResolution = { type: 'suffix', value: undefined }
) {
async convertToRealBranch(branchId: string) {
try {
await invoke<void>('convert_to_real_branch', {
projectId: this.projectId,
branch: branchId,
nameConflictResolution
branch: branchId
});
this.remoteBranchService.refresh();
} catch (err) {

View File

@ -356,17 +356,3 @@ export class BranchData {
return this.name.replace('refs/remotes/', '').replace('origin/', '').replace('refs/heads/', '');
}
}
export type NameConflictResolution =
| {
type: 'suffix';
value: undefined;
}
| {
type: 'overwrite';
value: undefined;
}
| {
type: 'rename';
value: string;
};

View File

@ -336,7 +336,6 @@ impl VirtualBranchActions {
&self,
project: &Project,
branch_id: BranchId,
name_conflict_resolution: branch::NameConflictResolution,
) -> Result<ReferenceName> {
let project_repository = open_with_verify(project)?;
let mut guard = project.exclusive_worktree_access();
@ -344,11 +343,7 @@ impl VirtualBranchActions {
.project()
.prepare_snapshot(guard.read_permission());
let branch_manager = project_repository.branch_manager();
let result = branch_manager.convert_to_real_branch(
branch_id,
name_conflict_resolution,
guard.write_permission(),
);
let result = branch_manager.convert_to_real_branch(branch_id, guard.write_permission());
let _ = snapshot_tree.and_then(|snapshot_tree| {
project_repository.project().snapshot_branch_unapplied(

View File

@ -423,7 +423,7 @@ pub(crate) fn update_base_branch(
// branch tree conflicts with new target, unapply branch for now. we'll handle it later, when user applies it back.
let branch_manager = project_repository.branch_manager();
let unapplied_real_branch =
branch_manager.convert_to_real_branch(branch.id, Default::default(), perm)?;
branch_manager.convert_to_real_branch(branch.id, perm)?;
unapplied_branch_names.push(unapplied_real_branch);
@ -457,7 +457,7 @@ pub(crate) fn update_base_branch(
// unapplied. conflicts witll be dealt with when applying it back.
let branch_manager = project_repository.branch_manager();
let unapplied_real_branch =
branch_manager.convert_to_real_branch(branch.id, Default::default(), perm)?;
branch_manager.convert_to_real_branch(branch.id, perm)?;
unapplied_branch_names.push(unapplied_real_branch);
return Ok(None);

View File

@ -333,7 +333,7 @@ impl BranchManager<'_> {
.iter()
.filter(|branch| branch.id != branch_id)
{
self.convert_to_real_branch(branch.id, Default::default(), perm)?;
self.convert_to_real_branch(branch.id, perm)?;
}
// apply the branch

View File

@ -5,9 +5,9 @@ use crate::{
ensure_selected_for_changes, get_applied_status,
hunk::VirtualBranchHunk,
integration::get_integration_commiter,
NameConflictResolution, VirtualBranchesExt,
VirtualBranchesExt,
};
use anyhow::{anyhow, Context, Result};
use anyhow::{Context, Result};
use gitbutler_branch::{Branch, BranchExt, BranchId};
use gitbutler_commit::commit_headers::CommitHeadersV2;
use gitbutler_oplog::SnapshotExt;
@ -23,7 +23,6 @@ impl BranchManager<'_> {
pub fn convert_to_real_branch(
&self,
branch_id: BranchId,
name_conflict_resolution: NameConflictResolution,
perm: &mut WorktreeWritePermission,
) -> Result<ReferenceName> {
let vb_state = self.project_repository.project().virtual_branches();
@ -31,7 +30,7 @@ impl BranchManager<'_> {
let mut target_branch = vb_state.get_branch(branch_id)?;
// Convert the vbranch to a real branch
let real_branch = self.build_real_branch(&mut target_branch, name_conflict_resolution)?;
let real_branch = self.build_real_branch(&mut target_branch)?;
self.delete_branch(branch_id, perm)?;
@ -129,51 +128,12 @@ impl BranchManager<'_> {
}
impl BranchManager<'_> {
fn build_real_branch(
&self,
vbranch: &mut Branch,
name_conflict_resolution: NameConflictResolution,
) -> Result<git2::Branch<'_>> {
fn build_real_branch(&self, vbranch: &mut Branch) -> Result<git2::Branch<'_>> {
let repo = self.project_repository.repo();
let target_commit = repo.find_commit(vbranch.head)?;
let branch_name = vbranch.name.clone();
let branch_name = normalize_branch_name(&branch_name);
// Is there a name conflict?
let branch_name = if repo
.find_branch(branch_name.as_str(), git2::BranchType::Local)
.is_ok()
{
match name_conflict_resolution {
NameConflictResolution::Suffix => {
let mut suffix = 1;
loop {
let new_branch_name = format!("{}-{}", branch_name, suffix);
if repo
.find_branch(new_branch_name.as_str(), git2::BranchType::Local)
.is_err()
{
break new_branch_name;
}
suffix += 1;
}
}
NameConflictResolution::Rename(new_name) => {
if repo
.find_branch(new_name.as_str(), git2::BranchType::Local)
.is_ok()
{
Err(anyhow!("Branch with name {} already exists", new_name))?
} else {
new_name
}
}
NameConflictResolution::Overwrite => branch_name,
}
} else {
branch_name
};
let vb_state = self.project_repository.project().virtual_branches();
let branch = repo.branch(&branch_name, &target_commit, true)?;
vbranch.source_refname = Some(Refname::try_from(&branch)?);

View File

@ -20,7 +20,7 @@ use std::{
use anyhow::{anyhow, bail, Context, Result};
use bstr::ByteSlice;
use git2_hooks::HookResult;
use serde::{Deserialize, Serialize};
use serde::Serialize;
use crate::branch_manager::BranchManagerExt;
use crate::commit::{commit_to_vbranch_commit, VirtualBranchCommit};
@ -82,15 +82,6 @@ pub struct VirtualBranches {
pub skipped_files: Vec<gitbutler_diff::FileDiff>,
}
#[derive(Default, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", tag = "type", content = "value")]
pub enum NameConflictResolution {
#[default]
Suffix,
Rename(String),
Overwrite,
}
pub fn unapply_ownership(
project_repository: &ProjectRepository,
ownership: &BranchOwnershipClaims,
@ -256,7 +247,7 @@ fn resolve_old_applied_state(
for mut branch in branches {
if branch.is_old_unapplied() {
branch_manager.convert_to_real_branch(branch.id, Default::default(), perm)?;
branch_manager.convert_to_real_branch(branch.id, perm)?;
} else {
branch.applied = branch.in_workspace;
vb_state.set_branch(branch)?;

View File

@ -1160,11 +1160,8 @@ fn unapply_branch() -> Result<()> {
assert!(branch.active);
let branch_manager = project_repository.branch_manager();
let real_branch = branch_manager.convert_to_real_branch(
branch1_id,
Default::default(),
guard.write_permission(),
)?;
let real_branch =
branch_manager.convert_to_real_branch(branch1_id, guard.write_permission())?;
let contents = std::fs::read(Path::new(&project.path).join(file_path))?;
assert_eq!("line1\nline2\nline3\nline4\n", String::from_utf8(contents)?);
@ -1251,21 +1248,15 @@ fn apply_unapply_added_deleted_files() -> Result<()> {
list_virtual_branches(project_repository, guard.write_permission()).unwrap();
let branch_manager = project_repository.branch_manager();
let real_branch_2 = branch_manager.convert_to_real_branch(
branch2_id,
Default::default(),
guard.write_permission(),
)?;
let real_branch_2 =
branch_manager.convert_to_real_branch(branch2_id, guard.write_permission())?;
// check that file2 is back
let contents = std::fs::read(Path::new(&project.path).join(file_path2))?;
assert_eq!("file2\n", String::from_utf8(contents)?);
let real_branch_3 = branch_manager.convert_to_real_branch(
branch3_id,
Default::default(),
guard.write_permission(),
)?;
let real_branch_3 =
branch_manager.convert_to_real_branch(branch3_id, guard.write_permission())?;
// check that file3 is gone
assert!(!Path::new(&project.path).join(file_path3).exists());
@ -1345,16 +1336,8 @@ fn detect_mergeable_branch() -> Result<()> {
// unapply both branches and create some conflicting ones
let branch_manager = project_repository.branch_manager();
branch_manager.convert_to_real_branch(
branch1_id,
Default::default(),
guard.write_permission(),
)?;
branch_manager.convert_to_real_branch(
branch2_id,
Default::default(),
guard.write_permission(),
)?;
branch_manager.convert_to_real_branch(branch1_id, guard.write_permission())?;
branch_manager.convert_to_real_branch(branch2_id, guard.write_permission())?;
project_repository.repo().set_head("refs/heads/master")?;
project_repository

View File

@ -54,7 +54,7 @@ async fn rebase_commit() {
let unapplied_branch = {
// unapply first vbranch
let unapplied_branch = controller
.convert_to_real_branch(project, branch1_id, Default::default())
.convert_to_real_branch(project, branch1_id)
.await
.unwrap();
@ -163,7 +163,7 @@ async fn rebase_work() {
let unapplied_branch = {
// unapply first vbranch
let unapplied_branch = controller
.convert_to_real_branch(project, branch1_id, Default::default())
.convert_to_real_branch(project, branch1_id)
.await
.unwrap();

View File

@ -23,7 +23,7 @@ async fn unapply_with_data() {
assert_eq!(branches.len(), 1);
controller
.convert_to_real_branch(project, branches[0].id, Default::default())
.convert_to_real_branch(project, branches[0].id)
.await
.unwrap();
@ -72,7 +72,7 @@ async fn conflicting() {
);
let unapplied_branch = controller
.convert_to_real_branch(project, branches[0].id, Default::default())
.convert_to_real_branch(project, branches[0].id)
.await
.unwrap();
@ -118,7 +118,7 @@ async fn conflicting() {
{
// Converting the branch to a real branch should put us back in an unconflicted state
controller
.convert_to_real_branch(project, branch_id, Default::default())
.convert_to_real_branch(project, branch_id)
.await
.unwrap();
@ -151,7 +151,7 @@ async fn delete_if_empty() {
assert_eq!(branches.len(), 1);
controller
.convert_to_real_branch(project, branches[0].id, Default::default())
.convert_to_real_branch(project, branches[0].id)
.await
.unwrap();

View File

@ -39,7 +39,7 @@ async fn unapplying_selected_branch_selects_anther() {
assert!(!b2.selected_for_changes);
controller
.convert_to_real_branch(project, b_id, Default::default())
.convert_to_real_branch(project, b_id)
.await
.unwrap();
@ -303,7 +303,7 @@ async fn unapply_virtual_branch_should_reset_selected_for_changes() {
assert!(!b2.selected_for_changes);
controller
.convert_to_real_branch(project, b1_id, Default::default())
.convert_to_real_branch(project, b1_id)
.await
.unwrap();
@ -371,7 +371,7 @@ async fn applying_first_branch() {
assert_eq!(branches.len(), 1);
let unapplied_branch = controller
.convert_to_real_branch(project, branches[0].id, Default::default())
.convert_to_real_branch(project, branches[0].id)
.await
.unwrap();
let unapplied_branch = Refname::from_str(&unapplied_branch).unwrap();

View File

@ -5,8 +5,8 @@ pub mod commands {
use gitbutler_branch::{BranchCreateRequest, BranchId, BranchUpdateRequest};
use gitbutler_branch_actions::BaseBranch;
use gitbutler_branch_actions::RemoteBranchFile;
use gitbutler_branch_actions::{NameConflictResolution, VirtualBranchActions, VirtualBranches};
use gitbutler_branch_actions::{RemoteBranch, RemoteBranchData};
use gitbutler_branch_actions::{VirtualBranchActions, VirtualBranches};
use gitbutler_error::error::Code;
use gitbutler_project as projects;
use gitbutler_project::{FetchResult, ProjectId};
@ -203,11 +203,10 @@ pub mod commands {
projects: State<'_, projects::Controller>,
project_id: ProjectId,
branch: BranchId,
name_conflict_resolution: NameConflictResolution,
) -> Result<(), Error> {
let project = projects.get(project_id)?;
VirtualBranchActions
.convert_to_real_branch(&project, branch, name_conflict_resolution)
.convert_to_real_branch(&project, branch)
.await?;
emit_vbranches(&windows, project_id).await;
Ok(())