mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-01 20:45:57 +03:00
Merge pull request #1121 from gitbutlerapp/integrated-commits
mark individual commits as integrated
This commit is contained in:
commit
af2f232772
@ -472,7 +472,7 @@ pub fn target_to_base_branch(
|
||||
.log(oid, project_repository::LogUntil::Commit(target.sha))
|
||||
.context("failed to get upstream commits")?
|
||||
.iter()
|
||||
.map(|c| super::commit_to_vbranch_commit(project_repository, c, None))
|
||||
.map(|c| super::commit_to_vbranch_commit(project_repository, target, c, None))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
// get some recent commits
|
||||
@ -480,7 +480,7 @@ pub fn target_to_base_branch(
|
||||
.log(target.sha, LogUntil::Take(20))
|
||||
.context("failed to get recent commits")?
|
||||
.iter()
|
||||
.map(|c| super::commit_to_vbranch_commit(project_repository, c, None))
|
||||
.map(|c| super::commit_to_vbranch_commit(project_repository, target, c, None))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
let base = super::BaseBranch {
|
||||
|
@ -2450,17 +2450,17 @@ fn test_upstream_integrated_vbranch() -> Result<()> {
|
||||
let branches = list_virtual_branches(&gb_repo, &project_repository)?;
|
||||
|
||||
let branch1 = &branches.iter().find(|b| b.id == branch1_id).unwrap();
|
||||
assert!(branch1.integrated);
|
||||
assert!(branch1.commits.iter().any(|c| c.is_integrated));
|
||||
assert_eq!(branch1.files.len(), 0);
|
||||
assert_eq!(branch1.commits.len(), 1);
|
||||
|
||||
let branch2 = &branches.iter().find(|b| b.id == branch2_id).unwrap();
|
||||
assert!(!branch2.integrated);
|
||||
assert!(!branch2.commits.iter().any(|c| c.is_integrated));
|
||||
assert_eq!(branch2.files.len(), 0);
|
||||
assert_eq!(branch2.commits.len(), 1);
|
||||
|
||||
let branch3 = &branches.iter().find(|b| b.id == branch3_id).unwrap();
|
||||
assert!(!branch3.integrated);
|
||||
assert!(!branch3.commits.iter().any(|c| c.is_integrated));
|
||||
assert_eq!(branch3.files.len(), 1);
|
||||
assert_eq!(branch3.commits.len(), 0);
|
||||
|
||||
|
@ -45,7 +45,6 @@ pub struct VirtualBranch {
|
||||
pub order: usize, // the order in which this branch should be displayed in the UI
|
||||
pub upstream: Option<project_repository::branch::RemoteName>, // the name of the upstream branch this branch this pushes to
|
||||
pub base_current: bool, // is this vbranch based on the current base branch? if false, this needs to be manually merged with conflicts
|
||||
pub integrated: bool, // this branch is already integrated into upstream base branch work that is not yet merged
|
||||
}
|
||||
|
||||
// this is the struct that maps to the view `Commit` type in Typescript
|
||||
@ -64,8 +63,9 @@ pub struct VirtualBranchCommit {
|
||||
pub created_at: u128,
|
||||
pub author: Author,
|
||||
pub is_remote: bool,
|
||||
// only present if is_remove is false
|
||||
// only present if is_remote is false
|
||||
pub files: Vec<VirtualBranchFile>,
|
||||
pub is_integrated: bool,
|
||||
}
|
||||
|
||||
// this struct is a mapping to the view `File` type in Typescript
|
||||
@ -660,10 +660,14 @@ pub fn list_remote_branches(
|
||||
commits: ahead
|
||||
.into_iter()
|
||||
.map(|commit| {
|
||||
commit_to_vbranch_commit(project_repository, &commit, None)
|
||||
.unwrap()
|
||||
commit_to_vbranch_commit(
|
||||
project_repository,
|
||||
&default_target,
|
||||
&commit,
|
||||
None,
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
.collect::<Result<Vec<_>>>()?,
|
||||
});
|
||||
};
|
||||
}
|
||||
@ -921,8 +925,12 @@ pub fn list_virtual_branches(
|
||||
.log(branch.head, LogUntil::Commit(default_target.sha))
|
||||
.context(format!("failed to get log for branch {}", branch.name))?
|
||||
{
|
||||
let commit =
|
||||
commit_to_vbranch_commit(project_repository, &commit, Some(&upstream_commits))?;
|
||||
let commit = commit_to_vbranch_commit(
|
||||
project_repository,
|
||||
&default_target,
|
||||
&commit,
|
||||
Some(&upstream_commits),
|
||||
)?;
|
||||
commits.push(commit);
|
||||
}
|
||||
|
||||
@ -955,7 +963,6 @@ pub fn list_virtual_branches(
|
||||
}
|
||||
}
|
||||
|
||||
let integrated = is_branch_integrated(project_repository, &default_target, branch)?;
|
||||
let branch = VirtualBranch {
|
||||
id: branch.id.to_string(),
|
||||
name: branch.name.to_string(),
|
||||
@ -969,7 +976,6 @@ pub fn list_virtual_branches(
|
||||
upstream: branch.upstream.clone(),
|
||||
conflicted: conflicts::is_resolving(project_repository),
|
||||
base_current,
|
||||
integrated,
|
||||
};
|
||||
branches.push(branch);
|
||||
}
|
||||
@ -1001,6 +1007,7 @@ fn list_commit_files(
|
||||
|
||||
pub fn commit_to_vbranch_commit(
|
||||
repository: &project_repository::Repository,
|
||||
target: &target::Target,
|
||||
commit: &git2::Commit,
|
||||
upstream_commits: Option<&HashMap<git2::Oid, bool>>,
|
||||
) -> Result<VirtualBranchCommit> {
|
||||
@ -1020,6 +1027,8 @@ pub fn commit_to_vbranch_commit(
|
||||
list_commit_files(repository, commit).context("failed to list commit files")?
|
||||
};
|
||||
|
||||
let is_integrated = is_commit_integrated(repository, target, commit)?;
|
||||
|
||||
let commit = VirtualBranchCommit {
|
||||
id: sha,
|
||||
created_at: timestamp * 1000,
|
||||
@ -1027,6 +1036,7 @@ pub fn commit_to_vbranch_commit(
|
||||
description: message,
|
||||
is_remote,
|
||||
files,
|
||||
is_integrated,
|
||||
};
|
||||
|
||||
Ok(commit)
|
||||
@ -1977,10 +1987,10 @@ pub fn mark_all_unapplied(gb_repository: &gb_repository::Repository) -> Result<(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_branch_integrated(
|
||||
fn is_commit_integrated(
|
||||
project_repository: &project_repository::Repository,
|
||||
target: &target::Target,
|
||||
branch: &branch::Branch,
|
||||
commit: &git2::Commit,
|
||||
) -> Result<bool> {
|
||||
let remote_branch = project_repository
|
||||
.git_repository
|
||||
@ -1991,7 +2001,7 @@ fn is_branch_integrated(
|
||||
project_repository::LogUntil::Commit(target.sha),
|
||||
)?;
|
||||
|
||||
if target.sha.eq(&branch.head) {
|
||||
if target.sha.eq(&commit.id()) {
|
||||
// could not be integrated if heads are the same.
|
||||
return Ok(false);
|
||||
}
|
||||
@ -2003,14 +2013,13 @@ fn is_branch_integrated(
|
||||
|
||||
let merge_base = project_repository
|
||||
.git_repository
|
||||
.merge_base(target.sha, branch.head)?;
|
||||
if merge_base.eq(&branch.head) {
|
||||
.merge_base(target.sha, commit.id())?;
|
||||
if merge_base.eq(&commit.id()) {
|
||||
// if merge branch is the same as branch head and there are upstream commits
|
||||
// then it's integrated
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
let head_tree = project_repository.git_repository.find_tree(branch.tree)?;
|
||||
let merge_commit = project_repository.git_repository.find_commit(merge_base)?;
|
||||
let merge_tree = merge_commit.tree()?;
|
||||
let upstream = project_repository
|
||||
@ -2025,7 +2034,7 @@ fn is_branch_integrated(
|
||||
.merge_trees(
|
||||
&merge_tree,
|
||||
&upstream_tree,
|
||||
&head_tree,
|
||||
&commit.tree()?,
|
||||
Some(&git2::MergeOptions::new()),
|
||||
)
|
||||
.context("failed to merge trees")?;
|
||||
|
@ -58,7 +58,6 @@ export class Branch {
|
||||
upstream?: string;
|
||||
conflicted!: boolean;
|
||||
baseCurrent!: boolean;
|
||||
integrated!: boolean;
|
||||
}
|
||||
|
||||
export class Commit {
|
||||
@ -68,6 +67,7 @@ export class Commit {
|
||||
@Transform((obj) => new Date(obj.value))
|
||||
createdAt!: Date;
|
||||
isRemote!: boolean;
|
||||
isIntegrated!: boolean;
|
||||
@Type(() => File)
|
||||
files!: File[];
|
||||
}
|
||||
|
@ -82,7 +82,7 @@
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#each branches.filter((c) => c.active) as { id, name, files, commits, order, conflicted, integrated, upstream } (id)}
|
||||
{#each branches.filter((c) => c.active) as { id, name, files, commits, order, conflicted, upstream } (id)}
|
||||
<Lane
|
||||
on:dragstart={(e) => {
|
||||
if (!e.dataTransfer) return;
|
||||
@ -94,7 +94,6 @@
|
||||
{files}
|
||||
{commits}
|
||||
{conflicted}
|
||||
{integrated}
|
||||
on:empty={handleEmpty}
|
||||
{order}
|
||||
{projectId}
|
||||
|
@ -55,7 +55,6 @@
|
||||
export let projectId: string;
|
||||
export let order: number;
|
||||
export let conflicted: boolean;
|
||||
export let integrated: boolean;
|
||||
export let base: BaseBranch | undefined;
|
||||
export let cloudEnabled: boolean;
|
||||
export let cloud: ReturnType<typeof getCloudApiClient>;
|
||||
@ -243,14 +242,6 @@
|
||||
bind:this={rsViewport}
|
||||
class="flex flex-grow cursor-default flex-col overflow-x-hidden border-l border-r border-light-400 bg-light-150 dark:border-dark-600 dark:bg-dark-1000 dark:text-dark-100"
|
||||
>
|
||||
{#if integrated}
|
||||
<div class="mb-2 flex flex-col">
|
||||
<div class="bg-light-800 text-center text-light-200">Branch is Integrated</div>
|
||||
<div class="text-center text-sm text-light-600">
|
||||
It will be removed when you merge upstream
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<div
|
||||
class="flex bg-light-200 text-light-900 dark:bg-dark-800 dark:font-normal dark:text-dark-100"
|
||||
>
|
||||
@ -421,7 +412,7 @@
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
<div class="relative flex flex-grow overflow-y-hidden" class:opacity-40={integrated}>
|
||||
<div class="relative flex flex-grow overflow-y-hidden">
|
||||
<div
|
||||
bind:this={viewport}
|
||||
class="hide-native-scrollbar flex max-h-full flex-grow flex-col overflow-y-scroll pb-8"
|
||||
@ -496,9 +487,7 @@
|
||||
>
|
||||
<div
|
||||
class="dark:form-dark-600 absolute top-4 ml-[0.75rem] w-px bg-gradient-to-b from-light-400 via-light-500 via-90% dark:from-dark-600 dark:via-dark-600"
|
||||
style={remoteCommits.length == 0
|
||||
? 'height: calc(100% - 1rem);'
|
||||
: 'height: 100%;'}
|
||||
style={remoteCommits.length == 0 ? 'height: calc();' : 'height: 100%;'}
|
||||
/>
|
||||
|
||||
<div class="relative flex flex-col gap-2">
|
||||
@ -509,11 +498,7 @@
|
||||
<div
|
||||
class="ml-2 flex-grow font-mono text-sm font-bold text-dark-300 dark:text-light-300"
|
||||
>
|
||||
{#if integrated}
|
||||
integrated
|
||||
{:else}
|
||||
local
|
||||
{/if}
|
||||
local
|
||||
</div>
|
||||
<Button
|
||||
class="w-20"
|
||||
@ -540,7 +525,7 @@
|
||||
class="h-3 w-3 rounded-full border-2 border-light-500 bg-light-200 dark:border-dark-600 dark:bg-dark-1000"
|
||||
/>
|
||||
</div>
|
||||
<CommitCard {commit} />
|
||||
<CommitCard {commit} isIntegrated={commit.isRemote} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
@ -570,9 +555,6 @@
|
||||
class="inline-block max-w-full truncate text-sm font-bold"
|
||||
>
|
||||
{upstream.split('refs/remotes/')[1]}
|
||||
{#if integrated}
|
||||
(integrated)
|
||||
{/if}
|
||||
</Link>
|
||||
{/if}
|
||||
</div>
|
||||
@ -591,7 +573,11 @@
|
||||
class:dark:bg-dark-500={commit.isRemote}
|
||||
/>
|
||||
</div>
|
||||
<CommitCard {commit} url={base?.commitUrl(commit.id)} />
|
||||
<CommitCard
|
||||
{commit}
|
||||
url={base?.commitUrl(commit.id)}
|
||||
isIntegrated={commit.isIntegrated}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
@ -2,31 +2,43 @@
|
||||
import { open } from '@tauri-apps/api/shell';
|
||||
import type { Commit } from '$lib/vbranches/types';
|
||||
import TimeAgo from '$lib/components/TimeAgo/TimeAgo.svelte';
|
||||
import Tooltip from '$lib/components/Tooltip/Tooltip.svelte';
|
||||
|
||||
export let commit: Commit;
|
||||
export let isIntegrated = false;
|
||||
export let url: string | undefined = undefined;
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="w-full truncate rounded border border-light-400 bg-light-50 p-2 text-left dark:border-dark-600 dark:bg-dark-900"
|
||||
>
|
||||
<div class="mb-1 truncate">
|
||||
{#if url}
|
||||
<!-- on:click required when there is a stopPropagation on a parent -->
|
||||
<a
|
||||
href={url}
|
||||
on:click={() => {
|
||||
if (url) open(url);
|
||||
}}
|
||||
target="_blank"
|
||||
title="Open in browser"
|
||||
>
|
||||
<div class="mb-1 flex justify-between">
|
||||
<div class="truncate">
|
||||
{#if url}
|
||||
<!-- on:click required when there is a stopPropagation on a parent -->
|
||||
<a
|
||||
href={url}
|
||||
on:click={() => {
|
||||
if (url) open(url);
|
||||
}}
|
||||
target="_blank"
|
||||
title="Open in browser"
|
||||
>
|
||||
{commit.description}
|
||||
</a>
|
||||
{:else}
|
||||
{commit.description}
|
||||
</a>
|
||||
{:else}
|
||||
{commit.description}
|
||||
{/if}
|
||||
</div>
|
||||
{#if isIntegrated}
|
||||
<div>
|
||||
<Tooltip label="This commit is integrated into Trunk and will dissapear once you merge it.">
|
||||
<i>integrated</i>
|
||||
</Tooltip>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="flex space-x-1 text-sm text-light-700">
|
||||
<img
|
||||
class="relative z-20 inline-block h-4 w-4 rounded-full ring-1 ring-white dark:ring-black"
|
||||
|
@ -9,7 +9,6 @@
|
||||
import BaseBranchPeek from './BaseBranchPeek.svelte';
|
||||
import RemoteBranchPeek from './RemoteBranchPeek.svelte';
|
||||
import Resizer from '$lib/components/Resizer.svelte';
|
||||
import { Tooltip } from '$lib/components';
|
||||
|
||||
export let item: Readable<BranchData | Branch | BaseBranch | undefined> | undefined;
|
||||
export let base: BaseBranch | undefined;
|
||||
|
@ -98,7 +98,11 @@
|
||||
<p class="mb-2 w-full overflow-hidden font-semibold">Commits</p>
|
||||
<div class="flex w-full flex-col gap-y-2">
|
||||
{#each branch.commits as commit}
|
||||
<CommitCard {commit} url={base?.commitUrl(commit.id)} />
|
||||
<CommitCard
|
||||
{commit}
|
||||
url={base?.commitUrl(commit.id)}
|
||||
isIntegrated={commit.isIntegrated}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user