mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-24 18:12:48 +03:00
Merged origin/master into Collapsable lane
This commit is contained in:
commit
3427a3e8c5
@ -214,6 +214,7 @@ fn main() {
|
||||
virtual_branches::commands::cherry_pick_onto_virtual_branch,
|
||||
virtual_branches::commands::amend_virtual_branch,
|
||||
virtual_branches::commands::list_remote_branches,
|
||||
virtual_branches::commands::get_remote_branch_data,
|
||||
virtual_branches::commands::squash_branch_commit,
|
||||
virtual_branches::commands::fetch_from_target,
|
||||
menu::menu_item_set_enabled,
|
||||
|
@ -1,3 +1,5 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::watcher;
|
||||
use anyhow::Context;
|
||||
use tauri::{AppHandle, Manager};
|
||||
@ -519,6 +521,28 @@ pub async fn list_remote_branches(
|
||||
Ok(branches)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
#[instrument(skip(handle))]
|
||||
pub async fn get_remote_branch_data(
|
||||
handle: tauri::AppHandle,
|
||||
project_id: &str,
|
||||
refname: &str,
|
||||
) -> Result<super::RemoteBranchData, Error> {
|
||||
let project_id = project_id.parse().map_err(|_| Error::UserError {
|
||||
code: Code::Validation,
|
||||
message: "Malformed project id".to_string(),
|
||||
})?;
|
||||
let refname = git::Refname::from_str(refname).map_err(|_| Error::UserError {
|
||||
code: Code::Validation,
|
||||
message: "Malformed refname".to_string(),
|
||||
})?;
|
||||
let branch_data = handle
|
||||
.state::<Controller>()
|
||||
.get_remote_branch_data(&project_id, &refname)
|
||||
.await?;
|
||||
Ok(branch_data)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
#[instrument(skip(handle))]
|
||||
pub async fn squash_branch_commit(
|
||||
|
@ -14,8 +14,8 @@ use crate::{
|
||||
use super::{
|
||||
branch::{BranchId, Ownership},
|
||||
errors::{
|
||||
self, FetchFromTargetError, GetBaseBranchDataError, IsRemoteBranchMergableError,
|
||||
ListRemoteBranchesError,
|
||||
self, FetchFromTargetError, GetBaseBranchDataError, GetRemoteBranchDataError,
|
||||
IsRemoteBranchMergableError, ListRemoteBranchesError,
|
||||
},
|
||||
target_to_base_branch, BaseBranch, RemoteBranchFile,
|
||||
};
|
||||
@ -313,6 +313,16 @@ impl Controller {
|
||||
.list_remote_branches(project_id)
|
||||
}
|
||||
|
||||
pub async fn get_remote_branch_data(
|
||||
&self,
|
||||
project_id: &ProjectId,
|
||||
refname: &git::Refname,
|
||||
) -> Result<super::RemoteBranchData, ControllerError<GetRemoteBranchDataError>> {
|
||||
self.inner(project_id)
|
||||
.await
|
||||
.get_remote_branch_data(project_id, refname)
|
||||
}
|
||||
|
||||
pub async fn squash(
|
||||
&self,
|
||||
project_id: &ProjectId,
|
||||
@ -808,6 +818,25 @@ impl ControllerInner {
|
||||
.map_err(ControllerError::Action)
|
||||
}
|
||||
|
||||
pub fn get_remote_branch_data(
|
||||
&self,
|
||||
project_id: &ProjectId,
|
||||
refname: &git::Refname,
|
||||
) -> Result<super::RemoteBranchData, ControllerError<GetRemoteBranchDataError>> {
|
||||
let project = self.projects.get(project_id).map_err(Error::from)?;
|
||||
let project_repository =
|
||||
project_repository::Repository::open(&project).map_err(Error::from)?;
|
||||
let user = self.users.get_user().map_err(Error::from)?;
|
||||
let gb_repository = gb_repository::Repository::open(
|
||||
&self.local_data_dir,
|
||||
&project_repository,
|
||||
user.as_ref(),
|
||||
)
|
||||
.context("failed to open gitbutler repository")?;
|
||||
super::get_branch_data(&gb_repository, &project_repository, refname)
|
||||
.map_err(ControllerError::Action)
|
||||
}
|
||||
|
||||
pub async fn squash(
|
||||
&self,
|
||||
project_id: &ProjectId,
|
||||
|
@ -751,6 +751,26 @@ pub enum ListRemoteBranchesError {
|
||||
Other(#[from] anyhow::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum GetRemoteBranchDataError {
|
||||
#[error("default target not set")]
|
||||
DefaultTargetNotSet(DefaultTargetNotSetError),
|
||||
#[error(transparent)]
|
||||
Other(#[from] anyhow::Error),
|
||||
}
|
||||
|
||||
impl From<GetRemoteBranchDataError> for Error {
|
||||
fn from(value: GetRemoteBranchDataError) -> Self {
|
||||
match value {
|
||||
GetRemoteBranchDataError::DefaultTargetNotSet(error) => error.into(),
|
||||
GetRemoteBranchDataError::Other(error) => {
|
||||
tracing::error!(?error, "get remote branch data error");
|
||||
Error::Unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ListRemoteBranchesError> for Error {
|
||||
fn from(value: ListRemoteBranchesError) -> Self {
|
||||
match value {
|
||||
|
@ -27,6 +27,16 @@ pub struct RemoteBranch {
|
||||
pub commits: Vec<RemoteCommit>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RemoteBranchData {
|
||||
pub sha: git::Oid,
|
||||
pub name: git::Refname,
|
||||
pub upstream: Option<git::RemoteRefname>,
|
||||
pub behind: u32,
|
||||
pub commits: Vec<RemoteCommit>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RemoteCommit {
|
||||
@ -66,6 +76,43 @@ pub fn list_remote_branches(
|
||||
Ok(remote_branches)
|
||||
}
|
||||
|
||||
pub fn get_branch_data(
|
||||
gb_repository: &gb_repository::Repository,
|
||||
project_repository: &project_repository::Repository,
|
||||
refname: &git::Refname,
|
||||
) -> Result<super::RemoteBranchData, errors::GetRemoteBranchDataError> {
|
||||
let default_target = gb_repository
|
||||
.default_target()
|
||||
.context("failed to get default target")?
|
||||
.ok_or_else(|| {
|
||||
errors::GetRemoteBranchDataError::DefaultTargetNotSet(
|
||||
errors::DefaultTargetNotSetError {
|
||||
project_id: project_repository.project().id,
|
||||
},
|
||||
)
|
||||
})?;
|
||||
|
||||
let branch = project_repository
|
||||
.git_repository
|
||||
.find_branch(refname)
|
||||
.context(format!("failed to find branch with refname {refname}"))?;
|
||||
|
||||
let branch_data = branch_to_remote_branch(project_repository, &branch, default_target.sha)
|
||||
.context("failed to get branch data")?;
|
||||
|
||||
branch_data
|
||||
.ok_or_else(|| {
|
||||
errors::GetRemoteBranchDataError::Other(anyhow::anyhow!("no data found for branch"))
|
||||
})
|
||||
.map(|branch_data| RemoteBranchData {
|
||||
sha: branch_data.sha,
|
||||
name: branch_data.name,
|
||||
upstream: branch_data.upstream,
|
||||
behind: branch_data.behind,
|
||||
commits: branch_data.commits,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn branch_to_remote_branch(
|
||||
project_repository: &project_repository::Repository,
|
||||
branch: &git::Branch,
|
||||
|
@ -5,6 +5,7 @@
|
||||
import ScrollableContainer from './ScrollableContainer.svelte';
|
||||
import CommitCard from '$lib/components/CommitCard.svelte';
|
||||
import { type SettingsStore, SETTINGS_CONTEXT } from '$lib/settings/userSettings';
|
||||
import { getRemoteBranchData } from '$lib/stores/remoteBranches';
|
||||
import { Ownership } from '$lib/vbranches/ownership';
|
||||
import lscache from 'lscache';
|
||||
import { marked } from 'marked';
|
||||
@ -59,18 +60,20 @@
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if branch.commits && branch.commits.length > 0}
|
||||
<div class="flex w-full flex-col gap-y-2">
|
||||
{#each branch.commits as commit (commit.id)}
|
||||
<CommitCard
|
||||
{commit}
|
||||
{projectId}
|
||||
{selectedFiles}
|
||||
commitUrl={base?.commitUrl(commit.id)}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{#await getRemoteBranchData({ projectId, refname: branch.name }) then branchData}
|
||||
{#if branchData.commits && branchData.commits.length > 0}
|
||||
<div class="flex w-full flex-col gap-y-2">
|
||||
{#each branchData.commits as commit (commit.id)}
|
||||
<CommitCard
|
||||
{commit}
|
||||
{projectId}
|
||||
{selectedFiles}
|
||||
commitUrl={base?.commitUrl(commit.id)}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{/await}
|
||||
</div>
|
||||
</ScrollableContainer>
|
||||
<Resizer
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { invoke } from '$lib/backend/ipc';
|
||||
import * as toasts from '$lib/utils/toasts';
|
||||
import { RemoteBranch } from '$lib/vbranches/types';
|
||||
import { RemoteBranch, RemoteBranchData } from '$lib/vbranches/types';
|
||||
import { plainToInstance } from 'class-transformer';
|
||||
import {
|
||||
BehaviorSubject,
|
||||
@ -25,7 +25,7 @@ export class RemoteBranchService {
|
||||
baseBranch$: Observable<any>
|
||||
) {
|
||||
this.branches$ = combineLatest([baseBranch$, this.reload$, head$, fetches$]).pipe(
|
||||
switchMap(() => getRemoteBranchesData({ projectId })),
|
||||
switchMap(() => listRemoteBranches({ projectId })),
|
||||
map((branches) => branches.filter((b) => b.ahead != 0)),
|
||||
shareReplay(1),
|
||||
catchError((e) => {
|
||||
@ -42,9 +42,7 @@ export class RemoteBranchService {
|
||||
}
|
||||
}
|
||||
|
||||
export async function getRemoteBranchesData(params: {
|
||||
projectId: string;
|
||||
}): Promise<RemoteBranch[]> {
|
||||
async function listRemoteBranches(params: { projectId: string }): Promise<RemoteBranch[]> {
|
||||
const branches = plainToInstance(
|
||||
RemoteBranch,
|
||||
await invoke<any[]>('list_remote_branches', params)
|
||||
@ -52,3 +50,15 @@ export async function getRemoteBranchesData(params: {
|
||||
|
||||
return branches;
|
||||
}
|
||||
|
||||
export async function getRemoteBranchData(params: {
|
||||
projectId: string;
|
||||
refname: string;
|
||||
}): Promise<RemoteBranchData> {
|
||||
const branchData = plainToInstance(
|
||||
RemoteBranchData,
|
||||
await invoke<any>('get_remote_branch_data', params)
|
||||
);
|
||||
|
||||
return branchData;
|
||||
}
|
||||
|
@ -216,6 +216,40 @@ export class RemoteBranch {
|
||||
}
|
||||
}
|
||||
|
||||
export class RemoteBranchData {
|
||||
sha!: string;
|
||||
name!: string;
|
||||
upstream?: string;
|
||||
behind!: number;
|
||||
@Type(() => RemoteCommit)
|
||||
commits!: RemoteCommit[];
|
||||
isMergeable!: boolean | undefined;
|
||||
|
||||
get ahead(): number {
|
||||
return this.commits.length;
|
||||
}
|
||||
|
||||
get lastCommitTs(): Date | undefined {
|
||||
return this.commits[0]?.createdAt;
|
||||
}
|
||||
|
||||
get firstCommitAt(): Date {
|
||||
return this.commits[this.commits.length - 1].createdAt;
|
||||
}
|
||||
|
||||
get authors(): Author[] {
|
||||
const allAuthors = this.commits.map((commit) => commit.author);
|
||||
const uniqueAuthors = allAuthors.filter(
|
||||
(author, index) => allAuthors.findIndex((a) => a.email == author.email) == index
|
||||
);
|
||||
return uniqueAuthors;
|
||||
}
|
||||
|
||||
get displayName(): string {
|
||||
return this.name.replace('refs/remotes/', '').replace('origin/', '').replace('refs/heads/', '');
|
||||
}
|
||||
}
|
||||
|
||||
export class BaseBranch {
|
||||
branchName!: string;
|
||||
remoteName!: string;
|
||||
|
Loading…
Reference in New Issue
Block a user