mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-11-22 11:02:11 +03:00
Merge pull request #5357 from gitbutlerapp/persist-forge-ids-on-series
Persist ForgeIdentifier on series (branches)
This commit is contained in:
commit
ad0a0a20d0
@ -12,7 +12,7 @@
|
||||
import Modal from '@gitbutler/ui/Modal.svelte';
|
||||
import Tooltip from '@gitbutler/ui/Tooltip.svelte';
|
||||
import type { PullRequest } from '$lib/gitHost/interface/types';
|
||||
import type { Branch } from '$lib/vbranches/types';
|
||||
import type { Branch, ForgeIdentifier } from '$lib/vbranches/types';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
export let localBranch: Branch | undefined;
|
||||
@ -111,7 +111,14 @@
|
||||
remoteBranch?.name
|
||||
);
|
||||
} else {
|
||||
await branchController.createvBranchFromBranch(remoteBranch!.name);
|
||||
let forgeId: ForgeIdentifier | undefined = pr
|
||||
? { type: 'GitHub', subject: { prNumber: pr.number } }
|
||||
: undefined;
|
||||
await branchController.createvBranchFromBranch(
|
||||
remoteBranch!.name,
|
||||
undefined,
|
||||
forgeId
|
||||
);
|
||||
}
|
||||
goto(`/${project.id}/board`);
|
||||
} catch (e) {
|
||||
|
@ -67,7 +67,9 @@
|
||||
await remotesService.addRemote(project.id, remoteName, remoteUrl);
|
||||
await baseBranchService.fetchFromRemotes();
|
||||
await branchController.createvBranchFromBranch(
|
||||
`refs/remotes/${remoteName}/${pullrequest.sourceBranch}`
|
||||
`refs/remotes/${remoteName}/${pullrequest.sourceBranch}`,
|
||||
undefined,
|
||||
{ type: 'GitHub', subject: { prNumber: pullrequest.number } }
|
||||
);
|
||||
await virtualBranchService.refresh();
|
||||
|
||||
|
@ -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 { BranchPushResult, Hunk, LocalFile } from './types';
|
||||
import type { BranchPushResult, ForgeIdentifier, Hunk, LocalFile } from './types';
|
||||
import type { VirtualBranchService } from './virtualBranch';
|
||||
|
||||
export type CommitIdOrChangeId = { CommitId: string } | { ChangeId: string };
|
||||
@ -154,6 +154,30 @@ export class BranchController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the forge identifier for a branch/series.
|
||||
* This is useful for storing for example the Pull Request Number for a branch.
|
||||
* @param stackId The stack ID to update.
|
||||
* @param headName The branch name to update.
|
||||
* @param forgeId New forge id to be set for the branch (overrides current state). Setting to undefined will remove the forge id.
|
||||
*/
|
||||
async updateSeriesForgeId(
|
||||
stackId: string,
|
||||
headName: string,
|
||||
forgeId: ForgeIdentifier | undefined
|
||||
) {
|
||||
try {
|
||||
await invoke<void>('update_series_forge_ids', {
|
||||
projectId: this.projectId,
|
||||
stackId,
|
||||
headName,
|
||||
forgeId
|
||||
});
|
||||
} catch (err) {
|
||||
showError('Failed to update branch forge ids', err);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a new GitButler change reference associated with a branch.
|
||||
* @param branchId
|
||||
@ -389,12 +413,17 @@ export class BranchController {
|
||||
* have a local branch, this should be the branch.
|
||||
* @param remote Optionally sets another branch as the upstream.
|
||||
*/
|
||||
async createvBranchFromBranch(branch: string, remote: string | undefined = undefined) {
|
||||
async createvBranchFromBranch(
|
||||
branch: string,
|
||||
remote: string | undefined = undefined,
|
||||
forgeId: ForgeIdentifier | undefined = undefined
|
||||
) {
|
||||
try {
|
||||
await invoke<string>('create_virtual_branch_from_branch', {
|
||||
projectId: this.projectId,
|
||||
branch,
|
||||
remote
|
||||
remote,
|
||||
forgeId
|
||||
});
|
||||
} catch (err) {
|
||||
showError('Failed to create virtual branch', err);
|
||||
|
@ -445,6 +445,12 @@ export class PatchSeries {
|
||||
@Type(() => DetailedCommit)
|
||||
upstreamPatches!: DetailedCommit[];
|
||||
|
||||
/**
|
||||
* A list of identifiers for the review unit at possible forges (eg. Pull Request).
|
||||
* The list is empty if there is no review units, eg. no Pull Request has been created.
|
||||
*/
|
||||
forgeId?: ForgeIdentifier | undefined;
|
||||
|
||||
get localCommits() {
|
||||
return this.patches.filter((c) => c.status === 'local');
|
||||
}
|
||||
@ -465,3 +471,14 @@ export class PatchSeries {
|
||||
return this.name?.replace('refs/remotes/origin/', '');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a GitHub Pull Request identifier.
|
||||
*/
|
||||
export interface GitHubIdentifier {
|
||||
prNumber: number;
|
||||
}
|
||||
/**
|
||||
* Represents identifiers for the series at possible forges, eg. GitHub PR numbers.
|
||||
*/
|
||||
export type ForgeIdentifier = { type: 'GitHub'; subject: GitHubIdentifier };
|
||||
|
@ -25,6 +25,7 @@ use gitbutler_oplog::{
|
||||
entry::{OperationKind, SnapshotDetails},
|
||||
OplogExt, SnapshotExt,
|
||||
};
|
||||
use gitbutler_patch_reference::ForgeIdentifier;
|
||||
use gitbutler_project::{FetchResult, Project};
|
||||
use gitbutler_reference::{ReferenceName, Refname, RemoteRefname};
|
||||
use gitbutler_repo::RepositoryExt;
|
||||
@ -533,6 +534,7 @@ pub fn create_virtual_branch_from_branch(
|
||||
project: &Project,
|
||||
branch: &Refname,
|
||||
remote: Option<RemoteRefname>,
|
||||
forge_id: Option<ForgeIdentifier>,
|
||||
) -> Result<StackId> {
|
||||
let ctx = open_with_verify(project)?;
|
||||
assure_open_workspace_mode(&ctx)
|
||||
@ -540,7 +542,7 @@ pub fn create_virtual_branch_from_branch(
|
||||
let branch_manager = ctx.branch_manager();
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
branch_manager
|
||||
.create_virtual_branch_from_branch(branch, remote, guard.write_permission())
|
||||
.create_virtual_branch_from_branch(branch, remote, forge_id, guard.write_permission())
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ use gitbutler_cherry_pick::RepositoryExt as _;
|
||||
use gitbutler_commit::{commit_ext::CommitExt, commit_headers::HasCommitHeaders};
|
||||
use gitbutler_error::error::Marker;
|
||||
use gitbutler_oplog::SnapshotExt;
|
||||
use gitbutler_patch_reference::ForgeIdentifier;
|
||||
use gitbutler_project::access::WorktreeWritePermission;
|
||||
use gitbutler_reference::{Refname, RemoteRefname};
|
||||
use gitbutler_repo::{
|
||||
@ -122,6 +123,7 @@ impl BranchManager<'_> {
|
||||
&self,
|
||||
target: &Refname,
|
||||
upstream_branch: Option<RemoteRefname>,
|
||||
forge_id: Option<ForgeIdentifier>,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> Result<StackId> {
|
||||
// only set upstream if it's not the default target
|
||||
@ -247,6 +249,9 @@ impl BranchManager<'_> {
|
||||
)
|
||||
};
|
||||
|
||||
if let (Some(forge_id), Some(head)) = (forge_id, branch.heads().last()) {
|
||||
branch.set_forge_id(self.ctx, head, Some(forge_id))?;
|
||||
}
|
||||
branch.set_stack_head(self.ctx, head_commit.id(), Some(head_commit_tree.id()))?;
|
||||
self.ctx.add_branch_reference(&branch)?;
|
||||
|
||||
|
@ -3,7 +3,7 @@ use std::collections::HashMap;
|
||||
use anyhow::{Context, Result};
|
||||
use gitbutler_command_context::CommandContext;
|
||||
use gitbutler_commit::commit_ext::CommitExt;
|
||||
use gitbutler_patch_reference::{CommitOrChangeId, PatchReference};
|
||||
use gitbutler_patch_reference::{CommitOrChangeId, ForgeIdentifier, PatchReference};
|
||||
use gitbutler_project::Project;
|
||||
use gitbutler_repo_actions::RepoActionsExt;
|
||||
use gitbutler_stack::{PatchReferenceUpdate, Series};
|
||||
@ -45,6 +45,7 @@ pub fn create_series(
|
||||
target: target_patch,
|
||||
name: req.name,
|
||||
description: req.description,
|
||||
forge_id: Default::default(),
|
||||
},
|
||||
req.preceding_head,
|
||||
)
|
||||
@ -121,6 +122,27 @@ pub fn update_series_description(
|
||||
)
|
||||
}
|
||||
|
||||
/// Sets the forge identifiers for a given series/branch. Existing values are overwritten.
|
||||
///
|
||||
/// # Errors
|
||||
/// This method will return an error if:
|
||||
/// - The series does not exist
|
||||
/// - The stack cant be found
|
||||
/// - The stack has not been initialized
|
||||
/// - The project is not in workspace mode
|
||||
/// - Persisting the changes failed
|
||||
pub fn update_series_forge_ids(
|
||||
project: &Project,
|
||||
stack_id: StackId,
|
||||
head_name: String,
|
||||
forge_id: Option<ForgeIdentifier>,
|
||||
) -> Result<()> {
|
||||
let ctx = &open_with_verify(project)?;
|
||||
assure_open_workspace_mode(ctx).context("Requires an open workspace mode")?;
|
||||
let mut stack = ctx.project().virtual_branches().get_branch(stack_id)?;
|
||||
stack.set_forge_id(ctx, &head_name, forge_id)
|
||||
}
|
||||
|
||||
/// Pushes all series in the stack to the remote.
|
||||
/// This operation will error out if the target has no push remote configured.
|
||||
pub fn push_stack(project: &Project, branch_id: StackId, with_force: bool) -> Result<()> {
|
||||
@ -260,6 +282,7 @@ pub(crate) fn stack_series(
|
||||
upstream_reference,
|
||||
patches,
|
||||
upstream_patches,
|
||||
forge_id: series.head.forge_id,
|
||||
});
|
||||
}
|
||||
api_series.reverse();
|
||||
|
@ -21,6 +21,7 @@ use gitbutler_diff::{trees, GitHunk, Hunk};
|
||||
use gitbutler_error::error::Code;
|
||||
use gitbutler_operating_modes::assure_open_workspace_mode;
|
||||
use gitbutler_oxidize::git2_signature_to_gix_signature;
|
||||
use gitbutler_patch_reference::ForgeIdentifier;
|
||||
use gitbutler_project::access::WorktreeWritePermission;
|
||||
use gitbutler_reference::{normalize_branch_name, Refname, RemoteRefname};
|
||||
use gitbutler_repo::{
|
||||
@ -96,6 +97,9 @@ pub struct PatchSeries {
|
||||
pub patches: Vec<VirtualBranchCommit>,
|
||||
/// List of patches that only exist on the upstream branch
|
||||
pub upstream_patches: Vec<VirtualBranchCommit>,
|
||||
/// A list of identifiers for the review unit at possible forges (eg. Pull Request).
|
||||
/// The list is empty if there is no review units, eg. no Pull Request has been created.
|
||||
pub forge_id: Option<ForgeIdentifier>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Serialize)]
|
||||
|
@ -1137,6 +1137,7 @@ fn unapply_branch() -> Result<()> {
|
||||
let branch1_id = branch_manager.create_virtual_branch_from_branch(
|
||||
&Refname::from_str(&real_branch)?,
|
||||
None,
|
||||
None,
|
||||
guard.write_permission(),
|
||||
)?;
|
||||
let contents = std::fs::read(Path::new(&project.path).join(file_path))?;
|
||||
@ -1219,6 +1220,7 @@ fn apply_unapply_added_deleted_files() -> Result<()> {
|
||||
.create_virtual_branch_from_branch(
|
||||
&Refname::from_str(&real_branch_2).unwrap(),
|
||||
None,
|
||||
None,
|
||||
guard.write_permission(),
|
||||
)
|
||||
.unwrap();
|
||||
@ -1230,6 +1232,7 @@ fn apply_unapply_added_deleted_files() -> Result<()> {
|
||||
.create_virtual_branch_from_branch(
|
||||
&Refname::from_str(&real_branch_3).unwrap(),
|
||||
None,
|
||||
None,
|
||||
guard.write_permission(),
|
||||
)
|
||||
.unwrap();
|
||||
|
@ -94,6 +94,7 @@ fn rebase_commit() {
|
||||
project,
|
||||
&unapplied_branch,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@ -192,6 +193,7 @@ fn rebase_work() {
|
||||
project,
|
||||
&unapplied_branch,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
use gitbutler_branch::BranchCreateRequest;
|
||||
use gitbutler_patch_reference::{ForgeIdentifier, GitHubIdentifier};
|
||||
use gitbutler_reference::LocalRefname;
|
||||
|
||||
use super::*;
|
||||
@ -46,9 +47,13 @@ fn integration() {
|
||||
};
|
||||
|
||||
// checkout a existing remote branch
|
||||
let branch_id =
|
||||
gitbutler_branch_actions::create_virtual_branch_from_branch(project, &branch_name, None)
|
||||
.unwrap();
|
||||
let branch_id = gitbutler_branch_actions::create_virtual_branch_from_branch(
|
||||
project,
|
||||
&branch_name,
|
||||
None,
|
||||
Some(ForgeIdentifier::GitHub(GitHubIdentifier { pr_number: 123 })),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
// add a commit
|
||||
@ -96,6 +101,11 @@ fn integration() {
|
||||
.find(|branch| branch.id == branch_id)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
branch.series.first().unwrap().forge_id,
|
||||
Some(ForgeIdentifier::GitHub(GitHubIdentifier { pr_number: 123 }))
|
||||
);
|
||||
|
||||
assert!(branch.commits[0].is_remote);
|
||||
assert!(branch.commits[0].is_integrated);
|
||||
assert!(branch.commits[1].is_remote);
|
||||
@ -134,6 +144,7 @@ fn no_conflicts() {
|
||||
project,
|
||||
&"refs/remotes/origin/branch".parse().unwrap(),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@ -182,6 +193,7 @@ fn conflicts_with_uncommited() {
|
||||
project,
|
||||
&"refs/remotes/origin/branch".parse().unwrap(),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
let new_branch = gitbutler_branch_actions::list_virtual_branches(project)
|
||||
@ -236,6 +248,7 @@ fn conflicts_with_commited() {
|
||||
project,
|
||||
&"refs/remotes/origin/branch".parse().unwrap(),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
let new_branch = gitbutler_branch_actions::list_virtual_branches(project)
|
||||
@ -265,7 +278,8 @@ fn from_default_target() {
|
||||
gitbutler_branch_actions::create_virtual_branch_from_branch(
|
||||
project,
|
||||
&"refs/remotes/origin/master".parse().unwrap(),
|
||||
None
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap_err()
|
||||
.to_string(),
|
||||
@ -289,7 +303,8 @@ fn from_non_existent_branch() {
|
||||
gitbutler_branch_actions::create_virtual_branch_from_branch(
|
||||
project,
|
||||
&"refs/remotes/origin/branch".parse().unwrap(),
|
||||
None
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap_err()
|
||||
.to_string(),
|
||||
@ -330,6 +345,7 @@ fn from_state_remote_branch() {
|
||||
project,
|
||||
&"refs/remotes/origin/branch".parse().unwrap(),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@ -420,6 +436,7 @@ mod conflict_cases {
|
||||
project,
|
||||
&Refname::from_str(&branch_refname).unwrap(),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
@ -316,8 +316,13 @@ fn applying_first_branch() {
|
||||
let unapplied_branch =
|
||||
gitbutler_branch_actions::save_and_unapply_virutal_branch(project, branches[0].id).unwrap();
|
||||
let unapplied_branch = Refname::from_str(&unapplied_branch).unwrap();
|
||||
gitbutler_branch_actions::create_virtual_branch_from_branch(project, &unapplied_branch, None)
|
||||
.unwrap();
|
||||
gitbutler_branch_actions::create_virtual_branch_from_branch(
|
||||
project,
|
||||
&unapplied_branch,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let (branches, _) = gitbutler_branch_actions::list_virtual_branches(project).unwrap();
|
||||
assert_eq!(branches.len(), 1);
|
||||
|
@ -17,6 +17,25 @@ pub struct PatchReference {
|
||||
pub name: String,
|
||||
/// Optional description of the series. This could be markdown or anything our hearts desire.
|
||||
pub description: Option<String>,
|
||||
/// An identifier for a review unit at a forge (eg. GitHub Pull Request number).
|
||||
/// None if is no review unit, eg. no Pull Request has been created.
|
||||
#[serde(default)]
|
||||
pub forge_id: Option<ForgeIdentifier>,
|
||||
}
|
||||
|
||||
/// Represents identifiers for the series at possible forges, eg. GitHub PR numbers.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[serde(tag = "type", content = "subject")]
|
||||
pub enum ForgeIdentifier {
|
||||
GitHub(GitHubIdentifier),
|
||||
}
|
||||
|
||||
/// Represents a GitHub Pull Request identifier.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GitHubIdentifier {
|
||||
/// Pull Request number.
|
||||
pub pr_number: usize,
|
||||
}
|
||||
|
||||
/// A patch identifier which is either `CommitId` or a `ChangeId`.
|
||||
|
@ -11,6 +11,7 @@ use gitbutler_command_context::CommandContext;
|
||||
use gitbutler_commit::commit_ext::CommitExt;
|
||||
use gitbutler_commit::commit_ext::CommitVecExt;
|
||||
use gitbutler_id::id::Id;
|
||||
use gitbutler_patch_reference::ForgeIdentifier;
|
||||
use gitbutler_patch_reference::{CommitOrChangeId, PatchReference};
|
||||
use gitbutler_reference::{normalize_branch_name, Refname, RemoteRefname, VirtualRefname};
|
||||
use gitbutler_repo::{LogUntil, RepositoryExt};
|
||||
@ -229,6 +230,7 @@ impl Stack {
|
||||
generate_branch_name(author)?
|
||||
},
|
||||
description: None,
|
||||
forge_id: Default::default(),
|
||||
};
|
||||
let state = branch_state(ctx);
|
||||
|
||||
@ -302,6 +304,7 @@ impl Stack {
|
||||
target: current_top_head.target.clone(),
|
||||
name,
|
||||
description,
|
||||
forge_id: Default::default(),
|
||||
};
|
||||
self.add_series(ctx, new_head, Some(current_top_head.name.clone()))
|
||||
}
|
||||
@ -655,6 +658,34 @@ impl Stack {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sets the forge identifier for a given series/branch.
|
||||
/// Existing value is overwritten - passing `None` sets the forge identifier to `None`.
|
||||
///
|
||||
/// # Errors
|
||||
/// If the series does not exist, this method will return an error.
|
||||
/// If the stack has not been initialized, this method will return an error.
|
||||
pub fn set_forge_id(
|
||||
&mut self,
|
||||
ctx: &CommandContext,
|
||||
series_name: &str,
|
||||
new_forge_id: Option<ForgeIdentifier>,
|
||||
) -> Result<()> {
|
||||
if !self.initialized() {
|
||||
return Err(anyhow!("Stack has not been initialized"));
|
||||
}
|
||||
match self.heads.iter_mut().find(|r| r.name == series_name) {
|
||||
Some(head) => {
|
||||
head.forge_id = new_forge_id;
|
||||
branch_state(ctx).set_branch(self.clone())
|
||||
}
|
||||
None => bail!(
|
||||
"Series {} does not exist on stack {}",
|
||||
series_name,
|
||||
self.name
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_legacy_compatible_stack_reference(&mut self, ctx: &CommandContext) -> Result<()> {
|
||||
// self.upstream is only set if this is a branch that was created & manipulated by the legacy flow
|
||||
let legacy_refname = match self.upstream.clone().map(|r| r.branch().to_owned()) {
|
||||
@ -687,6 +718,10 @@ impl Stack {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn heads(&self) -> Vec<String> {
|
||||
self.heads.iter().map(|h| h.name.clone()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Request to update a PatchReference.
|
||||
|
@ -4,7 +4,9 @@ pub mod ownership;
|
||||
use anyhow::Result;
|
||||
use gitbutler_command_context::CommandContext;
|
||||
use gitbutler_commit::commit_ext::CommitExt;
|
||||
use gitbutler_patch_reference::{CommitOrChangeId, PatchReference};
|
||||
use gitbutler_patch_reference::{
|
||||
CommitOrChangeId, ForgeIdentifier, GitHubIdentifier, PatchReference,
|
||||
};
|
||||
use gitbutler_reference::RemoteRefname;
|
||||
use gitbutler_repo::{LogUntil, RepositoryExt as _};
|
||||
use gitbutler_repo_actions::RepoActionsExt;
|
||||
@ -58,6 +60,7 @@ fn add_series_success() -> Result<()> {
|
||||
name: "asdf".into(),
|
||||
target: CommitOrChangeId::ChangeId(test_ctx.commits[1].change_id().unwrap()),
|
||||
description: Some("my description".into()),
|
||||
forge_id: Default::default(),
|
||||
};
|
||||
let result = test_ctx.branch.add_series(&ctx, reference, None);
|
||||
assert!(result.is_ok());
|
||||
@ -112,6 +115,7 @@ fn add_series_top_base() -> Result<()> {
|
||||
name: "asdf".into(),
|
||||
target: CommitOrChangeId::CommitId(merge_base.id().to_string()),
|
||||
description: Some("my description".into()),
|
||||
forge_id: Default::default(),
|
||||
};
|
||||
let result = test_ctx.branch.add_series(&ctx, reference, None);
|
||||
println!("{:?}", result);
|
||||
@ -137,6 +141,7 @@ fn add_multiple_series() -> Result<()> {
|
||||
name: "head_4".into(),
|
||||
target: CommitOrChangeId::ChangeId(test_ctx.commits.last().unwrap().change_id().unwrap()),
|
||||
description: None,
|
||||
forge_id: Default::default(),
|
||||
};
|
||||
let result = test_ctx
|
||||
.branch
|
||||
@ -148,6 +153,7 @@ fn add_multiple_series() -> Result<()> {
|
||||
name: "head_2".into(),
|
||||
target: CommitOrChangeId::ChangeId(test_ctx.commits.last().unwrap().change_id().unwrap()),
|
||||
description: None,
|
||||
forge_id: Default::default(),
|
||||
};
|
||||
let result = test_ctx.branch.add_series(&ctx, head_2, None);
|
||||
assert!(result.is_ok());
|
||||
@ -160,6 +166,7 @@ fn add_multiple_series() -> Result<()> {
|
||||
name: "head_1".into(),
|
||||
target: CommitOrChangeId::ChangeId(test_ctx.commits.first().unwrap().change_id().unwrap()),
|
||||
description: None,
|
||||
forge_id: Default::default(),
|
||||
};
|
||||
|
||||
let result = test_ctx.branch.add_series(&ctx, head_1, None);
|
||||
@ -185,6 +192,7 @@ fn add_series_commit_id_when_change_id_available() -> Result<()> {
|
||||
name: "asdf".into(),
|
||||
target: CommitOrChangeId::CommitId(test_ctx.commits[1].id().to_string()),
|
||||
description: None,
|
||||
forge_id: Default::default(),
|
||||
};
|
||||
let result = test_ctx.branch.add_series(&ctx, reference, None);
|
||||
assert_eq!(
|
||||
@ -206,6 +214,7 @@ fn add_series_invalid_name_fails() -> Result<()> {
|
||||
name: "name with spaces".into(),
|
||||
target: CommitOrChangeId::CommitId(test_ctx.commits[0].id().to_string()),
|
||||
description: None,
|
||||
forge_id: Default::default(),
|
||||
};
|
||||
let result = test_ctx.branch.add_series(&ctx, reference, None);
|
||||
assert_eq!(result.err().unwrap().to_string(), "Invalid branch name");
|
||||
@ -221,6 +230,7 @@ fn add_series_duplicate_name_fails() -> Result<()> {
|
||||
name: "asdf".into(),
|
||||
target: CommitOrChangeId::ChangeId(test_ctx.commits[1].change_id().unwrap()),
|
||||
description: None,
|
||||
forge_id: Default::default(),
|
||||
};
|
||||
let result = test_ctx.branch.add_series(&ctx, reference.clone(), None);
|
||||
assert!(result.is_ok());
|
||||
@ -241,6 +251,7 @@ fn add_series_matching_git_ref_is_ok() -> Result<()> {
|
||||
name: "existing-branch".into(),
|
||||
target: test_ctx.commits[0].clone().into(),
|
||||
description: None,
|
||||
forge_id: Default::default(),
|
||||
};
|
||||
let result = test_ctx.branch.add_series(&ctx, reference.clone(), None);
|
||||
assert!(result.is_ok()); // allow this
|
||||
@ -256,6 +267,7 @@ fn add_series_including_refs_head_fails() -> Result<()> {
|
||||
name: "refs/heads/my-branch".into(),
|
||||
target: CommitOrChangeId::CommitId(test_ctx.commits[0].id().to_string()),
|
||||
description: None,
|
||||
forge_id: Default::default(),
|
||||
};
|
||||
let result = test_ctx.branch.add_series(&ctx, reference.clone(), None);
|
||||
assert_eq!(
|
||||
@ -274,6 +286,7 @@ fn add_series_target_commit_doesnt_exist() -> Result<()> {
|
||||
name: "my-branch".into(),
|
||||
target: CommitOrChangeId::CommitId("30696678319e0fa3a20e54f22d47fc8cf1ceaade".into()), // does not exist
|
||||
description: None,
|
||||
forge_id: Default::default(),
|
||||
};
|
||||
let result = test_ctx.branch.add_series(&ctx, reference.clone(), None);
|
||||
assert!(result
|
||||
@ -293,6 +306,7 @@ fn add_series_target_change_id_doesnt_exist() -> Result<()> {
|
||||
name: "my-branch".into(),
|
||||
target: CommitOrChangeId::ChangeId("does-not-exist".into()), // does not exist
|
||||
description: None,
|
||||
forge_id: Default::default(),
|
||||
};
|
||||
let result = test_ctx.branch.add_series(&ctx, reference.clone(), None);
|
||||
assert_eq!(
|
||||
@ -312,6 +326,7 @@ fn add_series_target_commit_not_in_stack() -> Result<()> {
|
||||
name: "my-branch".into(),
|
||||
target: CommitOrChangeId::CommitId(other_commit_id.clone()), // does not exist
|
||||
description: None,
|
||||
forge_id: Default::default(),
|
||||
};
|
||||
let result = test_ctx.branch.add_series(&ctx, reference.clone(), None);
|
||||
assert_eq!(
|
||||
@ -368,6 +383,7 @@ fn remove_series_with_multiple_last_heads() -> Result<()> {
|
||||
name: "to_stay".into(),
|
||||
target: CommitOrChangeId::ChangeId(test_ctx.commits.last().unwrap().change_id().unwrap()),
|
||||
description: None,
|
||||
forge_id: Default::default(),
|
||||
};
|
||||
let result = test_ctx.branch.add_series(&ctx, to_stay.clone(), None);
|
||||
assert!(result.is_ok());
|
||||
@ -399,6 +415,7 @@ fn remove_series_no_orphan_commits() -> Result<()> {
|
||||
name: "to_stay".into(),
|
||||
target: CommitOrChangeId::ChangeId(test_ctx.commits.first().unwrap().change_id().unwrap()),
|
||||
description: None,
|
||||
forge_id: Default::default(),
|
||||
}; // references the oldest commit
|
||||
let result = test_ctx.branch.add_series(&ctx, to_stay.clone(), None);
|
||||
assert!(result.is_ok());
|
||||
@ -561,6 +578,7 @@ fn update_series_target_success() -> Result<()> {
|
||||
name: "series_1".into(),
|
||||
target: commit_0_change_id.clone(),
|
||||
description: None,
|
||||
forge_id: Default::default(),
|
||||
};
|
||||
let result = test_ctx.branch.add_series(&ctx, series_1, None);
|
||||
assert!(result.is_ok());
|
||||
@ -662,6 +680,7 @@ fn list_series_two_heads_same_commit() -> Result<()> {
|
||||
name: "head_before".into(),
|
||||
target: CommitOrChangeId::ChangeId(test_ctx.commits.last().unwrap().change_id().unwrap()),
|
||||
description: None,
|
||||
forge_id: Default::default(),
|
||||
};
|
||||
// add `head_before` before the initial head
|
||||
let result = test_ctx.branch.add_series(&ctx, head_before, None);
|
||||
@ -697,6 +716,7 @@ fn list_series_two_heads_different_commit() -> Result<()> {
|
||||
// point to the first commit
|
||||
target: CommitOrChangeId::ChangeId(test_ctx.commits.first().unwrap().change_id().unwrap()),
|
||||
description: None,
|
||||
forge_id: Default::default(),
|
||||
};
|
||||
// add `head_before` before the initial head
|
||||
let result = test_ctx.branch.add_series(&ctx, head_before, None);
|
||||
@ -761,6 +781,7 @@ fn replace_head_single() -> Result<()> {
|
||||
name: "from_head".into(),
|
||||
target: CommitOrChangeId::ChangeId(test_ctx.commits[1].change_id().unwrap()),
|
||||
description: None,
|
||||
forge_id: Default::default(),
|
||||
};
|
||||
test_ctx.branch.add_series(&ctx, from_head, None)?;
|
||||
// replace with previous head
|
||||
@ -792,6 +813,7 @@ fn replace_head_single_with_merge_base() -> Result<()> {
|
||||
name: "from_head".into(),
|
||||
target: CommitOrChangeId::ChangeId(test_ctx.commits[1].change_id().unwrap()),
|
||||
description: None,
|
||||
forge_id: Default::default(),
|
||||
};
|
||||
test_ctx.branch.add_series(&ctx, from_head, None)?;
|
||||
// replace with merge base
|
||||
@ -827,6 +849,7 @@ fn replace_head_with_invalid_commit_error() -> Result<()> {
|
||||
name: "from_head".into(),
|
||||
target: CommitOrChangeId::ChangeId(test_ctx.commits[1].change_id().unwrap()),
|
||||
description: None,
|
||||
forge_id: Default::default(),
|
||||
};
|
||||
test_ctx.branch.add_series(&ctx, from_head, None)?;
|
||||
let stack = test_ctx.branch.clone();
|
||||
@ -853,6 +876,7 @@ fn replace_head_with_same_noop() -> Result<()> {
|
||||
name: "from_head".into(),
|
||||
target: CommitOrChangeId::ChangeId(test_ctx.commits[1].change_id().unwrap()),
|
||||
description: None,
|
||||
forge_id: Default::default(),
|
||||
};
|
||||
test_ctx.branch.add_series(&ctx, from_head, None)?;
|
||||
let stack = test_ctx.branch.clone();
|
||||
@ -938,11 +962,13 @@ fn replace_head_multiple() -> Result<()> {
|
||||
name: "from_head_1".into(),
|
||||
target: CommitOrChangeId::ChangeId(test_ctx.commits[1].change_id().unwrap()),
|
||||
description: None,
|
||||
forge_id: Default::default(),
|
||||
};
|
||||
let from_head_2 = PatchReference {
|
||||
name: "from_head_2".into(),
|
||||
target: CommitOrChangeId::ChangeId(test_ctx.commits[1].change_id().unwrap()),
|
||||
description: None,
|
||||
forge_id: Default::default(),
|
||||
};
|
||||
// both references point to the same commit
|
||||
test_ctx.branch.add_series(&ctx, from_head_1, None)?;
|
||||
@ -982,6 +1008,7 @@ fn replace_head_top_of_stack_multiple() -> Result<()> {
|
||||
name: "extra_head".into(),
|
||||
target: CommitOrChangeId::ChangeId(test_ctx.commits[1].change_id().unwrap()),
|
||||
description: None,
|
||||
forge_id: Default::default(),
|
||||
};
|
||||
// an extra head just beneath the top of the stack
|
||||
test_ctx.branch.add_series(&ctx, extra_head, None)?;
|
||||
@ -1047,6 +1074,7 @@ fn set_legacy_refname_multiple_heads() -> Result<()> {
|
||||
name: "extra_head".into(),
|
||||
target: CommitOrChangeId::ChangeId(test_ctx.commits[1].change_id().unwrap()),
|
||||
description: None,
|
||||
forge_id: Default::default(),
|
||||
};
|
||||
// an extra head just beneath the top of the stack
|
||||
test_ctx.branch.add_series(&ctx, extra_head, None)?;
|
||||
@ -1117,6 +1145,7 @@ fn prune_heads_success() -> Result<()> {
|
||||
target: test_ctx.other_commits.first().cloned().unwrap().into(),
|
||||
name: "foo".to_string(),
|
||||
description: None,
|
||||
forge_id: Default::default(),
|
||||
},
|
||||
);
|
||||
assert_eq!(test_ctx.branch.heads.len(), 2);
|
||||
@ -1146,6 +1175,7 @@ fn does_not_prune_head_on_merge_base() -> Result<()> {
|
||||
target: merge_base.into(),
|
||||
name: "bottom".to_string(),
|
||||
description: None,
|
||||
forge_id: Default::default(),
|
||||
},
|
||||
None,
|
||||
)?;
|
||||
@ -1160,6 +1190,48 @@ fn does_not_prune_head_on_merge_base() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_forge_identifiers_success() -> Result<()> {
|
||||
let (ctx, _temp_dir) = command_ctx("multiple-commits")?;
|
||||
let mut test_ctx = test_ctx(&ctx)?;
|
||||
test_ctx.branch.initialize(&ctx)?;
|
||||
let result = test_ctx.branch.set_forge_id(
|
||||
&ctx,
|
||||
"a-branch-2",
|
||||
Some(ForgeIdentifier::GitHub(GitHubIdentifier { pr_number: 123 })),
|
||||
);
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(
|
||||
test_ctx.branch.heads[0].forge_id,
|
||||
Some(ForgeIdentifier::GitHub(GitHubIdentifier { pr_number: 123 }))
|
||||
);
|
||||
// Assert persisted
|
||||
assert_eq!(
|
||||
test_ctx.branch,
|
||||
test_ctx.handle.get_branch(test_ctx.branch.id)?
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_forge_identifiers_series_not_found_fails() -> Result<()> {
|
||||
let (ctx, _temp_dir) = command_ctx("multiple-commits")?;
|
||||
let mut test_ctx = test_ctx(&ctx)?;
|
||||
test_ctx.branch.initialize(&ctx)?;
|
||||
let result = test_ctx.branch.set_forge_id(
|
||||
&ctx,
|
||||
"does-not-exist",
|
||||
Some(ForgeIdentifier::GitHub(GitHubIdentifier { pr_number: 123 })),
|
||||
);
|
||||
assert_eq!(
|
||||
result.err().unwrap().to_string(),
|
||||
format!(
|
||||
"Series does-not-exist does not exist on stack {}",
|
||||
test_ctx.branch.name
|
||||
)
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
fn command_ctx(name: &str) -> Result<(CommandContext, TempDir)> {
|
||||
gitbutler_testsupport::writable::fixture("stacking.sh", name)
|
||||
}
|
||||
|
@ -195,6 +195,7 @@ fn main() {
|
||||
stack::remove_series,
|
||||
stack::update_series_name,
|
||||
stack::update_series_description,
|
||||
stack::update_series_forge_ids,
|
||||
stack::push_stack,
|
||||
secret::secret_get_global,
|
||||
secret::secret_set_global,
|
||||
|
@ -1,4 +1,5 @@
|
||||
use gitbutler_branch_actions::stack::CreateSeriesRequest;
|
||||
use gitbutler_patch_reference::ForgeIdentifier;
|
||||
use gitbutler_project as projects;
|
||||
use gitbutler_project::ProjectId;
|
||||
use gitbutler_stack::StackId;
|
||||
@ -80,6 +81,24 @@ pub fn update_series_description(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
#[instrument(skip(projects, windows), err(Debug))]
|
||||
pub fn update_series_forge_ids(
|
||||
windows: State<'_, WindowState>,
|
||||
projects: State<'_, projects::Controller>,
|
||||
project_id: ProjectId,
|
||||
stack_id: StackId,
|
||||
head_name: String,
|
||||
forge_id: Option<ForgeIdentifier>,
|
||||
) -> Result<(), Error> {
|
||||
let project = projects.get(project_id)?;
|
||||
gitbutler_branch_actions::stack::update_series_forge_ids(
|
||||
&project, stack_id, head_name, forge_id,
|
||||
)?;
|
||||
emit_vbranches(&windows, project_id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
#[instrument(skip(projects, windows), err(Debug))]
|
||||
pub fn push_stack(
|
||||
|
@ -10,6 +10,7 @@ pub mod commands {
|
||||
RemoteBranchData, RemoteBranchFile, RemoteCommit, StackOrder, VirtualBranches,
|
||||
};
|
||||
use gitbutler_command_context::CommandContext;
|
||||
use gitbutler_patch_reference::ForgeIdentifier;
|
||||
use gitbutler_project as projects;
|
||||
use gitbutler_project::{FetchResult, ProjectId};
|
||||
use gitbutler_reference::{normalize_branch_name as normalize_name, Refname, RemoteRefname};
|
||||
@ -101,10 +102,12 @@ pub mod commands {
|
||||
project_id: ProjectId,
|
||||
branch: Refname,
|
||||
remote: Option<RemoteRefname>,
|
||||
forge_id: Option<ForgeIdentifier>,
|
||||
) -> Result<StackId, Error> {
|
||||
let project = projects.get(project_id)?;
|
||||
let branch_id =
|
||||
gitbutler_branch_actions::create_virtual_branch_from_branch(&project, &branch, remote)?;
|
||||
let branch_id = gitbutler_branch_actions::create_virtual_branch_from_branch(
|
||||
&project, &branch, remote, forge_id,
|
||||
)?;
|
||||
emit_vbranches(&windows, project_id);
|
||||
Ok(branch_id)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user