Merge pull request #1121 from gitbutlerapp/integrated-commits

mark individual commits as integrated
This commit is contained in:
Nikita Galaiko 2023-08-29 15:28:36 +02:00 committed by GitHub
commit af2f232772
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 72 additions and 63 deletions

View File

@ -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 {

View File

@ -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);

View File

@ -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")?;

View File

@ -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[];
}

View 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}

View File

@ -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>

View File

@ -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"

View File

@ -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;

View File

@ -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>