Skip to content

Commit

Permalink
Merge pull request #4718 from gitbutlerapp/branch-stacking-first-stab
Browse files Browse the repository at this point in the history
branch stacking first stab
  • Loading branch information
krlvi authored Aug 27, 2024
2 parents a9b12f4 + 06f50b4 commit 75805d3
Show file tree
Hide file tree
Showing 23 changed files with 809 additions and 35 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

69 changes: 69 additions & 0 deletions apps/desktop/src/lib/commit/CommitCard.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
import { BaseBranch } from '$lib/baseBranch/baseBranch';
import CommitMessageInput from '$lib/commit/CommitMessageInput.svelte';
import { persistedCommitMessage } from '$lib/config/config';
import { featureBranchStacking } from '$lib/config/uiFeatureFlags';
import { draggableCommit } from '$lib/dragging/draggable';
import { DraggableCommit, nonDraggable } from '$lib/dragging/draggables';
import BranchFilesList from '$lib/file/BranchFilesList.svelte';
import { ModeService } from '$lib/modes/service';
import TextBox from '$lib/shared/TextBox.svelte';
import { copyToClipboard } from '$lib/utils/clipboard';
import { getContext, getContextStore, maybeGetContext } from '$lib/utils/context';
import { openExternalUrl } from '$lib/utils/url';
Expand Down Expand Up @@ -48,6 +50,8 @@
const currentCommitMessage = persistedCommitMessage(project.id, branch?.id || '');
const branchStacking = featureBranchStacking();
let draggableCommitElement: HTMLElement | null = null;
let files: RemoteFile[] = [];
let showDetails = false;
Expand Down Expand Up @@ -82,6 +86,20 @@
let commitMessageValid = false;
let description = '';
let createRefModal: Modal;
let createRefName = $baseBranch.remoteName + '/';
function openCreateRefModal(e: Event, commit: DetailedCommit | Commit) {
e.stopPropagation();
createRefModal.show(commit);
}
function pushCommitRef(commit: DetailedCommit) {
if (branch && commit.remoteRef) {
branchController.pushChangeReference(branch.id, commit.remoteRef);
}
}
function openCommitMessageModal(e: Event) {
e.stopPropagation();
Expand Down Expand Up @@ -156,6 +174,29 @@
{/snippet}
</Modal>

<Modal bind:this={createRefModal} width="small">
{#snippet children(commit)}
<TextBox label="Remote branch name" id="newRemoteName" bind:value={createRefName} focus />
<Button
style="pop"
kind="solid"
onclick={() => {
branchController.createChangeReference(
branch?.id || '',
'refs/remotes/' + createRefName,
commit.changeId
);
createRefModal.close();
}}
>
Ok
</Button>
{/snippet}
{#snippet controls(close)}
<Button style="ghost" outline type="reset" onclick={close}>Cancel</Button>
{/snippet}
</Modal>

<div
class="commit-row"
class:is-commit-open={showDetails}
Expand Down Expand Up @@ -315,6 +356,14 @@
<span class="commit__subtitle-divider">•</span>

<span>{getTimeAndAuthor()}</span>

{#if $branchStacking && commit instanceof DetailedCommit}
<div
style="background-color:var(--clr-core-pop-80); border-radius: 3px; padding: 2px;"
>
{commit?.remoteRef}
</div>
{/if}
</div>
{/if}
</div>
Expand Down Expand Up @@ -352,6 +401,26 @@
icon="edit-small"
onclick={openCommitMessageModal}>Edit message</Button
>
{#if $branchStacking && commit instanceof DetailedCommit && !commit.remoteRef}
<Button
size="tag"
style="ghost"
outline
icon="branch"
onclick={(e: Event) => {openCreateRefModal(e, commit)}}>Create ref</Button
>
{/if}
{#if $branchStacking && commit instanceof DetailedCommit && commit.remoteRef}
<Button
size="tag"
style="ghost"
outline
icon="remote"
onclick={() => {
pushCommitRef(commit);
}}>Push ref</Button
>
{/if}
{/if}
{#if canEdit() && project.succeedingRebases}
<Button size="tag" style="ghost" outline onclick={editPatch}>
Expand Down
5 changes: 5 additions & 0 deletions apps/desktop/src/lib/config/uiFeatureFlags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,8 @@ export function featureInlineUnifiedDiffs(): Persisted<boolean> {
const key = 'inlineUnifiedDiffs';
return persisted(false, key);
}

export function featureBranchStacking(): Persisted<boolean> {
const key = 'branchStacking';
return persisted(false, key);
}
38 changes: 38 additions & 0 deletions apps/desktop/src/lib/vbranches/branchController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,44 @@ export class BranchController {
}
}

/*
* Creates a new GitButler change reference associated with a branch.
* @param branchId
* @param reference in the format refs/remotes/origin/my-branch (must be remote)
* @param changeId The change id to point the reference to
*/
async createChangeReference(branchId: string, referenceName: string, changeId: string) {
try {
await invoke<void>('create_change_reference', {
projectId: this.projectId,
branchId: branchId,
name: referenceName,
changeId: changeId
});
} catch (err) {
showError('Failed to create branch reference', err);
}
}

/*
* Pushes a change reference to (converted to a git reference to a commit) to the remote
* @param branchId
* @param reference in the format refs/remotes/origin/my-branch (must be remote)
* @param changeId The change id that is being pushed
*/
async pushChangeReference(branchId: string, referenceName: string, withForce: boolean = false) {
try {
await invoke<void>('push_change_reference', {
projectId: this.projectId,
branchId: branchId,
name: referenceName,
withForce: withForce
});
} catch (err) {
showError('Failed to push change reference', err);
}
}

async updateBranchRemoteName(branchId: string, upstream: string) {
try {
await invoke<void>('update_virtual_branch', {
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop/src/lib/vbranches/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ export class DetailedCommit {
isSigned!: boolean;
relatedTo?: Commit;
conflicted!: boolean;
// Set if a GitButler branch reference pointing to this commit exists. In the format of "refs/remotes/origin/my-branch"
remoteRef?: string | undefined;

prev?: DetailedCommit;
next?: DetailedCommit;
Expand Down
17 changes: 16 additions & 1 deletion apps/desktop/src/routes/settings/experimental/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
import SectionCard from '$lib/components/SectionCard.svelte';
import {
featureBaseBranchSwitching,
featureInlineUnifiedDiffs
featureInlineUnifiedDiffs,
featureBranchStacking
} from '$lib/config/uiFeatureFlags';
import ContentWrapper from '$lib/settings/ContentWrapper.svelte';
import Toggle from '$lib/shared/Toggle.svelte';
const baseBranchSwitching = featureBaseBranchSwitching();
const inlineUnifiedDiffs = featureInlineUnifiedDiffs();
const branchStacking = featureBranchStacking();
</script>

<ContentWrapper title="Experimental features">
Expand Down Expand Up @@ -45,6 +47,19 @@
/>
</svelte:fragment>
</SectionCard>
<SectionCard labelFor="branchStacking" orientation="row">
<svelte:fragment slot="title">Branch stacking</svelte:fragment>
<svelte:fragment slot="caption">
Allows for branch / pull request stacking. The user interface for this is still very crude.
</svelte:fragment>
<svelte:fragment slot="actions">
<Toggle
id="branchStacking"
checked={$branchStacking}
on:click={() => ($branchStacking = !$branchStacking)}
/>
</svelte:fragment>
</SectionCard>
</ContentWrapper>

<style>
Expand Down
28 changes: 27 additions & 1 deletion crates/gitbutler-branch-actions/src/actions.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use anyhow::{Context, Result};
use gitbutler_branch::{BranchCreateRequest, BranchId, BranchOwnershipClaims, BranchUpdateRequest};
use gitbutler_branch::{
BranchCreateRequest, BranchId, BranchOwnershipClaims, BranchUpdateRequest, ChangeReference,
};
use gitbutler_command_context::CommandContext;
use gitbutler_operating_modes::assure_open_workspace_mode;
use gitbutler_oplog::{
Expand Down Expand Up @@ -356,6 +358,30 @@ impl VirtualBranchActions {
branch::insert_blank_commit(&ctx, branch_id, commit_oid, offset).map_err(Into::into)
}

pub fn create_change_reference(
&self,
project: &Project,
branch_id: BranchId,
name: ReferenceName,
change_id: String,
) -> Result<ChangeReference> {
let ctx = open_with_verify(project)?;
assure_open_workspace_mode(&ctx).context("Requires an open workspace mode")?;
gitbutler_repo::create_change_reference(&ctx, branch_id, name, change_id)
}

pub fn push_change_reference(
&self,
project: &Project,
branch_id: BranchId,
name: ReferenceName,
with_force: bool,
) -> Result<()> {
let helper = Helper::default();
let ctx = open_with_verify(project)?;
gitbutler_repo::push_change_reference(&ctx, branch_id, name, with_force, &helper)
}

pub fn reorder_commit(
&self,
project: &Project,
Expand Down
1 change: 1 addition & 0 deletions crates/gitbutler-branch-actions/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ pub(crate) fn set_base_branch(
applied: true,
in_workspace: true,
not_in_workspace_wip_change_id: None,
references: vec![],
};

vb_state.set_branch(branch)?;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ impl BranchManager<'_> {
in_workspace: true,
not_in_workspace_wip_change_id: None,
source_refname: None,
references: vec![],
};

if let Some(ownership) = &create.ownership {
Expand Down Expand Up @@ -256,6 +257,7 @@ impl BranchManager<'_> {
applied: true,
in_workspace: true,
not_in_workspace_wip_change_id: None,
references: vec![],
}
};

Expand Down
18 changes: 15 additions & 3 deletions crates/gitbutler-branch-actions/src/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use anyhow::{Context, Result};
use gitbutler_branch::{Branch, BranchId};
use gitbutler_command_context::CommandContext;
use gitbutler_commit::commit_ext::CommitExt;
use gitbutler_reference::ReferenceName;
use gitbutler_repo::list_branch_references;
use gitbutler_serde::BStringForFrontend;
use serde::Serialize;

Expand Down Expand Up @@ -34,10 +36,11 @@ pub struct VirtualBranchCommit {
pub change_id: Option<String>,
pub is_signed: bool,
pub conflicted: bool,
pub remote_ref: Option<ReferenceName>,
}

pub(crate) fn commit_to_vbranch_commit(
repository: &CommandContext,
ctx: &CommandContext,
branch: &Branch,
commit: &git2::Commit,
is_integrated: bool,
Expand All @@ -46,8 +49,7 @@ pub(crate) fn commit_to_vbranch_commit(
let timestamp = u128::try_from(commit.time().seconds())?;
let message = commit.message_bstr().to_owned();

let files =
list_virtual_commit_files(repository, commit).context("failed to list commit files")?;
let files = list_virtual_commit_files(ctx, commit).context("failed to list commit files")?;

let parent_ids: Vec<git2::Oid> = commit
.parents()
Expand All @@ -56,6 +58,15 @@ pub(crate) fn commit_to_vbranch_commit(
c
})
.collect::<Vec<_>>();
let remote_ref = list_branch_references(ctx, branch.id)
.map(|references| {
references
.into_iter()
.find(|r| Some(r.change_id.clone()) == commit.change_id())
})
.ok()
.flatten()
.map(|r| r.name);

let commit = VirtualBranchCommit {
id: commit.id(),
Expand All @@ -70,6 +81,7 @@ pub(crate) fn commit_to_vbranch_commit(
change_id: commit.change_id(),
is_signed: commit.is_signed(),
conflicted: commit.is_conflicted(),
remote_ref,
};

Ok(commit)
Expand Down
4 changes: 3 additions & 1 deletion crates/gitbutler-branch/src/branch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use gitbutler_reference::{normalize_branch_name, Refname, RemoteRefname, Virtual
use serde::{Deserialize, Serialize, Serializer};
use std::ops::Deref;

use crate::ownership::BranchOwnershipClaims;
use crate::{ownership::BranchOwnershipClaims, reference::ChangeReference};

pub type BranchId = Id<Branch>;

Expand Down Expand Up @@ -67,6 +67,8 @@ pub struct Branch {
pub in_workspace: bool,
#[serde(default)]
pub not_in_workspace_wip_change_id: Option<String>,
#[serde(default)]
pub references: Vec<ChangeReference>,
}

fn default_true() -> bool {
Expand Down
2 changes: 2 additions & 0 deletions crates/gitbutler-branch/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ pub use ownership::{reconcile_claims, BranchOwnershipClaims, ClaimOutcome};
pub mod serde;
mod target;
pub use target::Target;
mod reference;
pub use reference::ChangeReference;

mod state;
use lazy_static::lazy_static;
Expand Down
20 changes: 20 additions & 0 deletions crates/gitbutler-branch/src/reference.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use gitbutler_reference::ReferenceName;
use serde::{Deserialize, Serialize};

use crate::BranchId;

/// GitButler reference associated with a change (commit) on a virtual branch.
/// These are not the same as regular Git references, but rather app-managed refs.
/// Represent a deployable / reviewable part of a virtual branch that can be pushed to a remote
/// and have a "Pull Request" created for it.
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct ChangeReference {
/// Branch id of the virtual branch this reference belongs to
/// Multiple references may belong to the same virtual branch, representing separate deployable / reviewable parts of the vbranch.
pub branch_id: BranchId,
/// Fully qualified reference name.
/// The reference must be a remote reference.
pub name: ReferenceName,
/// The change id this reference points to.
pub change_id: String,
}
Loading

0 comments on commit 75805d3

Please sign in to comment.