mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-10-26 13:12:25 +03:00
Merge pull request #4489 from gitbutlerapp/Apply-from-local-branches
Apply from local branches
This commit is contained in:
commit
9e2f065109
@ -1,12 +1,19 @@
|
||||
<script lang="ts">
|
||||
import { tooltip } from '@gitbutler/ui/utils/tooltip';
|
||||
|
||||
export let name: 'remote-branch' | 'virtual-branch' | 'pr' | 'pr-draft' | 'pr-closed' | undefined;
|
||||
export let name:
|
||||
| 'remote-branch'
|
||||
| 'local-branch'
|
||||
| 'virtual-branch'
|
||||
| 'pr'
|
||||
| 'pr-draft'
|
||||
| 'pr-closed'
|
||||
| undefined;
|
||||
export let help: string | undefined;
|
||||
|
||||
function getIconColor(name: string | undefined) {
|
||||
if (name === 'remote-branch') return 'neutral';
|
||||
if (name === 'virtual-branch') return 'virtual';
|
||||
if (name === 'virtual-branch' || name === 'local-branch') return 'virtual';
|
||||
if (name === 'pr') return 'success';
|
||||
if (name === 'pr-draft') return 'purple';
|
||||
if (name === 'pr-closed') return 'neutral';
|
||||
@ -24,7 +31,7 @@
|
||||
/>
|
||||
</svg>
|
||||
{/if}
|
||||
{#if name === 'remote-branch'}
|
||||
{#if name === 'remote-branch' || name === 'local-branch'}
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M5.75 9.99973V4H4.25V16H5.75V13C5.75 11.7574 6.75736 10.75 8 10.75C10.0711 10.75 11.75 9.07107 11.75 7V4H10.25V7C10.25 8.24264 9.24264 9.25 8 9.25C7.1558 9.25 6.37675 9.52896 5.75 9.99973Z"
|
||||
|
@ -13,10 +13,13 @@
|
||||
import type { Branch } from '$lib/vbranches/types';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
export let branch: Branch;
|
||||
export let localBranch: Branch | undefined;
|
||||
export let remoteBranch: Branch | undefined;
|
||||
export let base: BaseBranch | undefined | null;
|
||||
export let pr: PullRequest | undefined;
|
||||
|
||||
$: branch = remoteBranch || localBranch!;
|
||||
|
||||
const branchController = getContext(BranchController);
|
||||
const project = getContext(Project);
|
||||
|
||||
@ -26,29 +29,36 @@
|
||||
<div class="header__wrapper">
|
||||
<div class="header card">
|
||||
<div class="header__info">
|
||||
<BranchLabel disabled bind:name={branch.name} />
|
||||
<BranchLabel disabled name={branch.name} />
|
||||
<div class="header__remote-branch">
|
||||
<div
|
||||
class="status-tag text-base-11 text-semibold remote"
|
||||
use:tooltip={'At least some of your changes have been pushed'}
|
||||
>
|
||||
<Icon name="remote-branch-small" /> remote
|
||||
</div>
|
||||
<Button
|
||||
size="tag"
|
||||
icon="open-link"
|
||||
style="ghost"
|
||||
outline
|
||||
shrinkable
|
||||
on:click={(e) => {
|
||||
const url = base?.branchUrl(branch.name);
|
||||
if (url) openExternalUrl(url);
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{branch.displayName}
|
||||
</Button>
|
||||
{#if remoteBranch}
|
||||
<div
|
||||
class="status-tag text-base-11 text-semibold remote"
|
||||
use:tooltip={'At least some of your changes have been pushed'}
|
||||
>
|
||||
<Icon name="remote-branch-small" />
|
||||
{localBranch ? 'local and remote' : 'remote'}
|
||||
</div>
|
||||
<Button
|
||||
size="tag"
|
||||
icon="open-link"
|
||||
style="ghost"
|
||||
outline
|
||||
shrinkable
|
||||
on:click={(e) => {
|
||||
const url = base?.branchUrl(branch.name);
|
||||
if (url) openExternalUrl(url);
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{branch.displayName}
|
||||
</Button>
|
||||
{:else}
|
||||
<div class="status-tag text-base-11 text-semibold remote">
|
||||
<Icon name="remote-branch-small" /> local
|
||||
</div>
|
||||
{/if}
|
||||
{#if pr?.htmlUrl}
|
||||
<Button
|
||||
size="tag"
|
||||
@ -79,7 +89,11 @@
|
||||
on:click={async () => {
|
||||
isApplying = true;
|
||||
try {
|
||||
await branchController.createvBranchFromBranch(branch.name);
|
||||
if (localBranch) {
|
||||
await branchController.createvBranchFromBranch(localBranch.name, remoteBranch?.name);
|
||||
} else {
|
||||
await branchController.createvBranchFromBranch(remoteBranch!.name);
|
||||
}
|
||||
goto(`/${project.id}/board`);
|
||||
} catch (e) {
|
||||
const err = 'Failed to apply branch';
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { CombinedBranch } from '$lib/branches/types';
|
||||
import { buildContextStore } from '$lib/utils/context';
|
||||
import { groupBy } from '$lib/utils/groupBy';
|
||||
import { derived, readable, writable, type Readable } from 'svelte/store';
|
||||
import type { NameNormalizationService } from '$lib/branches/nameNormalizationService';
|
||||
import type { GitHostListingService } from '$lib/gitHost/interface/gitHostListingService';
|
||||
import type { PullRequest } from '$lib/gitHost/interface/types';
|
||||
import type { RemoteBranchService } from '$lib/stores/remoteBranches';
|
||||
@ -18,7 +20,8 @@ export class BranchService {
|
||||
constructor(
|
||||
vbranchService: VirtualBranchService,
|
||||
remoteBranchService: RemoteBranchService,
|
||||
gitPrService: GitHostListingService | undefined
|
||||
gitPrService: GitHostListingService | undefined,
|
||||
nameNormalizationService: NameNormalizationService
|
||||
) {
|
||||
const vbranches = vbranchService.branches;
|
||||
const branches = remoteBranchService.branches;
|
||||
@ -26,44 +29,73 @@ export class BranchService {
|
||||
|
||||
this.branches = derived(
|
||||
[vbranches, branches, prs],
|
||||
([vbranches, remoteBranches, pullRequests]) => {
|
||||
return mergeBranchesAndPrs(vbranches, pullRequests, remoteBranches || []);
|
||||
}
|
||||
([vbranches, remoteBranches, pullRequests], set) => {
|
||||
// derived with a set does not allow you to return a promise
|
||||
mergeBranchesAndPrs(
|
||||
vbranches || [],
|
||||
pullRequests || [],
|
||||
remoteBranches || [],
|
||||
nameNormalizationService
|
||||
).then((combinedBranches) => {
|
||||
set(combinedBranches);
|
||||
});
|
||||
},
|
||||
[] as CombinedBranch[] // Use an empty array as the default, with sufficient typing
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mergeBranchesAndPrs(
|
||||
_vbranches: VirtualBranch[] | undefined,
|
||||
pullRequests: PullRequest[] | undefined,
|
||||
remoteBranches: Branch[] | undefined
|
||||
): CombinedBranch[] {
|
||||
async function mergeBranchesAndPrs(
|
||||
virtualBranches: VirtualBranch[],
|
||||
pullRequests: PullRequest[],
|
||||
branches: Branch[],
|
||||
nameNormalizationService: NameNormalizationService
|
||||
): Promise<CombinedBranch[]> {
|
||||
const contributions: CombinedBranch[] = [];
|
||||
|
||||
// Then remote branches that have no virtual branch, combined with pull requests if present
|
||||
if (remoteBranches) {
|
||||
contributions.push(
|
||||
...remoteBranches.map((rb) => {
|
||||
const pr = pullRequests?.find((pr) => pr.sha === rb.sha);
|
||||
return new CombinedBranch({ remoteBranch: rb, pr });
|
||||
})
|
||||
);
|
||||
const groupedBranches = groupBy(branches, (branch) => branch.givenName);
|
||||
|
||||
for (const [_, branches] of Object.entries(groupedBranches)) {
|
||||
// There should only ever be one local reference for a particular given name
|
||||
const localBranch = branches.find((branch) => !branch.isRemote);
|
||||
const remoteBranches = branches.filter((branch) => branch.isRemote);
|
||||
|
||||
// There must be a local branch if there are no remote branches
|
||||
if (remoteBranches.length === 0) {
|
||||
contributions.push(new CombinedBranch({ localBranch }));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
remoteBranches.forEach((remoteBranch) => {
|
||||
contributions.push(new CombinedBranch({ remoteBranch, localBranch }));
|
||||
});
|
||||
}
|
||||
|
||||
// And finally pull requests that lack any corresponding branch
|
||||
if (pullRequests) {
|
||||
contributions.push(
|
||||
...pullRequests
|
||||
.filter((pr) => !contributions.some((cb) => pr.sha === cb.upstreamSha))
|
||||
.map((pr) => {
|
||||
return new CombinedBranch({ pr });
|
||||
})
|
||||
contributions.forEach((contribution) => {
|
||||
const pullRequest = pullRequests.find(
|
||||
// This may be over-sensitive in rare cases, but is preferable to using the head sha
|
||||
(pullRequest) => contribution.remoteBranch?.givenName === pullRequest.sourceBranch
|
||||
);
|
||||
}
|
||||
|
||||
if (pullRequest) {
|
||||
contribution.pr = pullRequest;
|
||||
}
|
||||
});
|
||||
|
||||
const normalizedVirtualBranchNames = new Set(
|
||||
await Promise.all(
|
||||
virtualBranches.map(
|
||||
async (virtualBranch) => await nameNormalizationService.normalize(virtualBranch.name)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// This should be everything considered a branch in one list
|
||||
const filtered = contributions.sort((a, b) => {
|
||||
return (a.modifiedAt || new Date(0)) < (b.modifiedAt || new Date(0)) ? 1 : -1;
|
||||
});
|
||||
const filtered = contributions
|
||||
.filter((combinedBranch) => !normalizedVirtualBranchNames.has(combinedBranch.branch!.givenName))
|
||||
.sort((a, b) => {
|
||||
return (a.modifiedAt || new Date(0)) < (b.modifiedAt || new Date(0)) ? 1 : -1;
|
||||
});
|
||||
return filtered;
|
||||
}
|
||||
|
@ -4,19 +4,23 @@ import type { Author, VirtualBranch, Branch } from '$lib/vbranches/types';
|
||||
export class CombinedBranch {
|
||||
pr?: PullRequest;
|
||||
remoteBranch?: Branch;
|
||||
localBranch?: Branch;
|
||||
vbranch?: VirtualBranch;
|
||||
|
||||
constructor({
|
||||
vbranch,
|
||||
remoteBranch,
|
||||
localBranch,
|
||||
pr
|
||||
}: {
|
||||
vbranch?: VirtualBranch;
|
||||
remoteBranch?: Branch;
|
||||
localBranch?: Branch;
|
||||
pr?: PullRequest;
|
||||
}) {
|
||||
this.vbranch = vbranch;
|
||||
this.remoteBranch = remoteBranch;
|
||||
this.localBranch = localBranch;
|
||||
this.pr = pr;
|
||||
}
|
||||
|
||||
@ -24,6 +28,7 @@ export class CombinedBranch {
|
||||
return (
|
||||
this.pr?.sha ||
|
||||
this.remoteBranch?.sha ||
|
||||
this.localBranch?.sha ||
|
||||
this.vbranch?.upstream?.sha ||
|
||||
this.vbranch?.head ||
|
||||
'unknown'
|
||||
@ -32,7 +37,11 @@ export class CombinedBranch {
|
||||
|
||||
get displayName(): string {
|
||||
return (
|
||||
this.pr?.sourceBranch || this.remoteBranch?.displayName || this.vbranch?.name || 'unknown'
|
||||
this.pr?.sourceBranch ||
|
||||
this.remoteBranch?.displayName ||
|
||||
this.localBranch?.displayName ||
|
||||
this.vbranch?.name ||
|
||||
'unknown'
|
||||
);
|
||||
}
|
||||
|
||||
@ -41,9 +50,9 @@ export class CombinedBranch {
|
||||
if (this.pr?.author) {
|
||||
authors.push(this.pr.author);
|
||||
}
|
||||
if (this.remoteBranch) {
|
||||
if (this.remoteBranch.lastCommitAuthor) {
|
||||
authors.push({ name: this.remoteBranch.lastCommitAuthor });
|
||||
if (this.branch) {
|
||||
if (this.branch.lastCommitAuthor) {
|
||||
authors.push({ name: this.branch.lastCommitAuthor });
|
||||
}
|
||||
}
|
||||
if (this.vbranch) {
|
||||
@ -59,7 +68,14 @@ export class CombinedBranch {
|
||||
return this.authors[0];
|
||||
}
|
||||
|
||||
get icon(): 'remote-branch' | 'virtual-branch' | 'pr' | 'pr-draft' | 'pr-closed' | undefined {
|
||||
get icon():
|
||||
| 'remote-branch'
|
||||
| 'local-branch'
|
||||
| 'virtual-branch'
|
||||
| 'pr'
|
||||
| 'pr-draft'
|
||||
| 'pr-closed'
|
||||
| undefined {
|
||||
return this.currentState();
|
||||
}
|
||||
|
||||
@ -73,9 +89,9 @@ export class CombinedBranch {
|
||||
|
||||
get modifiedAt(): Date | undefined {
|
||||
if (this.vbranch) return this.vbranch.updatedAt;
|
||||
if (this.remoteBranch) {
|
||||
return this.remoteBranch.lastCommitTimestampMs
|
||||
? new Date(this.remoteBranch.lastCommitTimestampMs)
|
||||
if (this.branch) {
|
||||
return this.branch.lastCommitTimestampMs
|
||||
? new Date(this.branch.lastCommitTimestampMs)
|
||||
: undefined;
|
||||
}
|
||||
if (this.pr) {
|
||||
@ -90,6 +106,8 @@ export class CombinedBranch {
|
||||
return 'Virtual branch';
|
||||
case BranchState.RemoteBranch:
|
||||
return 'Remote branch';
|
||||
case BranchState.LocalBranch:
|
||||
return 'Local branch';
|
||||
case BranchState.PR:
|
||||
return 'Pull Request';
|
||||
case BranchState.PRClosed:
|
||||
@ -109,9 +127,9 @@ export class CombinedBranch {
|
||||
this.pr.author?.email && identifiers.push(this.pr.author.email);
|
||||
this.pr.author?.name && identifiers.push(this.pr.author.name);
|
||||
}
|
||||
if (this.remoteBranch) {
|
||||
identifiers.push(this.remoteBranch.displayName);
|
||||
this.remoteBranch.lastCommitAuthor && identifiers.push(this.remoteBranch.lastCommitAuthor);
|
||||
if (this.branch) {
|
||||
identifiers.push(this.branch.displayName);
|
||||
this.branch.lastCommitAuthor && identifiers.push(this.branch.lastCommitAuthor);
|
||||
}
|
||||
|
||||
return identifiers.map((identifier) => identifier.toLowerCase());
|
||||
@ -120,13 +138,21 @@ export class CombinedBranch {
|
||||
currentState(): BranchState | undefined {
|
||||
if (this.pr) return BranchState.PR;
|
||||
if (this.remoteBranch) return BranchState.RemoteBranch;
|
||||
if (this.localBranch) return BranchState.LocalBranch;
|
||||
if (this.vbranch) return BranchState.VirtualBranch;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
get branch() {
|
||||
// Prefer the local branch over the remote branch
|
||||
// We should always have at least one branch
|
||||
return this.localBranch || this.remoteBranch;
|
||||
}
|
||||
}
|
||||
|
||||
enum BranchState {
|
||||
RemoteBranch = 'remote-branch',
|
||||
LocalBranch = 'local-branch',
|
||||
VirtualBranch = 'virtual-branch',
|
||||
PR = 'pr',
|
||||
PRDraft = 'pr-draft',
|
||||
|
229
app/src/lib/components/BranchPreview.svelte
Normal file
229
app/src/lib/components/BranchPreview.svelte
Normal file
@ -0,0 +1,229 @@
|
||||
<script lang="ts">
|
||||
import BranchPreviewHeader from '../branch/BranchPreviewHeader.svelte';
|
||||
import Resizer from '../shared/Resizer.svelte';
|
||||
import ScrollableContainer from '../shared/ScrollableContainer.svelte';
|
||||
import { Project } from '$lib/backend/projects';
|
||||
import { BaseBranch } from '$lib/baseBranch/baseBranch';
|
||||
import CommitCard from '$lib/commit/CommitCard.svelte';
|
||||
import { transformAnyCommit } from '$lib/commitLines/transformers';
|
||||
import FileCard from '$lib/file/FileCard.svelte';
|
||||
import { SETTINGS, type Settings } from '$lib/settings/userSettings';
|
||||
import { RemoteBranchService } from '$lib/stores/remoteBranches';
|
||||
import { getContext, getContextStore, getContextStoreBySymbol } from '$lib/utils/context';
|
||||
import { getMarkdownRenderer } from '$lib/utils/markdown';
|
||||
import { FileIdSelection } from '$lib/vbranches/fileIdSelection';
|
||||
import { BranchData, type Branch } from '$lib/vbranches/types';
|
||||
import LineGroup from '@gitbutler/ui/CommitLines/LineGroup.svelte';
|
||||
import { LineManagerFactory } from '@gitbutler/ui/CommitLines/lineManager';
|
||||
import lscache from 'lscache';
|
||||
import { marked } from 'marked';
|
||||
import { onMount, setContext } from 'svelte';
|
||||
import { writable } from 'svelte/store';
|
||||
import type { PullRequest } from '$lib/gitHost/interface/types';
|
||||
|
||||
export let localBranch: Branch | undefined;
|
||||
export let remoteBranch: Branch | undefined;
|
||||
export let pr: PullRequest | undefined;
|
||||
|
||||
const project = getContext(Project);
|
||||
const remoteBranchService = getContext(RemoteBranchService);
|
||||
const baseBranch = getContextStore(BaseBranch);
|
||||
|
||||
const fileIdSelection = new FileIdSelection(project.id, writable([]));
|
||||
setContext(FileIdSelection, fileIdSelection);
|
||||
|
||||
$: selectedFile = fileIdSelection.selectedFile;
|
||||
|
||||
const defaultBranchWidthRem = 30;
|
||||
const laneWidthKey = 'branchPreviewLaneWidth';
|
||||
const userSettings = getContextStoreBySymbol<Settings>(SETTINGS);
|
||||
const lineManagerFactory = getContext(LineManagerFactory);
|
||||
|
||||
let localBranchData: BranchData | undefined;
|
||||
let remoteBranchData: BranchData | undefined;
|
||||
|
||||
// The remote branch service (which needs to be renamed) is responsible for
|
||||
// fetching local and remote branches.
|
||||
// We must manually set the branch data to undefined as the component
|
||||
// doesn't get completely re-rendered on a page change.
|
||||
$: if (localBranch) {
|
||||
remoteBranchService
|
||||
.getRemoteBranchData(localBranch.name)
|
||||
.then((branchData) => (localBranchData = branchData));
|
||||
} else {
|
||||
localBranchData = undefined;
|
||||
}
|
||||
|
||||
$: if (remoteBranch) {
|
||||
remoteBranchService
|
||||
.getRemoteBranchData(remoteBranch.name)
|
||||
.then((branchData) => (remoteBranchData = branchData));
|
||||
} else {
|
||||
remoteBranchData = undefined;
|
||||
}
|
||||
|
||||
$: remoteCommitShas = new Set(remoteBranchData?.commits.map((commit) => commit.id) || []);
|
||||
|
||||
// Find commits common in the local and remote
|
||||
$: localAndRemoteCommits =
|
||||
localBranchData?.commits.filter((commit) => remoteCommitShas.has(commit.id)) || [];
|
||||
|
||||
$: localAndRemoteCommitShas = new Set(localAndRemoteCommits.map((commit) => commit.id));
|
||||
|
||||
// Find the local and remote commits that are not shared
|
||||
$: localCommits =
|
||||
localBranchData?.commits.filter((commit) => !localAndRemoteCommitShas.has(commit.id)) || [];
|
||||
$: remoteCommits =
|
||||
remoteBranchData?.commits.filter((commit) => !localAndRemoteCommitShas.has(commit.id)) || [];
|
||||
|
||||
$: lineManager = lineManagerFactory.build(
|
||||
{
|
||||
remoteCommits: remoteCommits.map(transformAnyCommit),
|
||||
localCommits: localCommits.map(transformAnyCommit),
|
||||
localAndRemoteCommits: localAndRemoteCommits.map(transformAnyCommit),
|
||||
integratedCommits: []
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
let rsViewport: HTMLDivElement;
|
||||
let laneWidth: number;
|
||||
|
||||
onMount(() => {
|
||||
laneWidth = lscache.get(laneWidthKey);
|
||||
});
|
||||
|
||||
const renderer = getMarkdownRenderer();
|
||||
</script>
|
||||
|
||||
{#if remoteBranch || localBranch}
|
||||
<div class="base">
|
||||
<div
|
||||
class="base__left"
|
||||
bind:this={rsViewport}
|
||||
style:width={`${laneWidth || defaultBranchWidthRem}rem`}
|
||||
>
|
||||
<ScrollableContainer wide>
|
||||
<div class="branch-preview">
|
||||
<BranchPreviewHeader base={$baseBranch} {localBranch} {remoteBranch} {pr} />
|
||||
{#if pr}
|
||||
<div class="card">
|
||||
<div class="card__header text-base-body-14 text-semibold">{pr.title}</div>
|
||||
{#if pr.body}
|
||||
<div class="markdown card__content text-base-body-13">
|
||||
{@html marked.parse(pr.body, { renderer })}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
<div>
|
||||
{#if remoteCommits}
|
||||
{#each remoteCommits as commit, index (commit.id)}
|
||||
<CommitCard
|
||||
first={index === 0}
|
||||
last={index === remoteCommits.length - 1}
|
||||
{commit}
|
||||
commitUrl={$baseBranch?.commitUrl(commit.id)}
|
||||
type="remote"
|
||||
>
|
||||
{#snippet lines(topHeightPx)}
|
||||
<LineGroup lineGroup={lineManager.get(commit.id)} {topHeightPx} />
|
||||
{/snippet}
|
||||
</CommitCard>
|
||||
{/each}
|
||||
{/if}
|
||||
{#if localCommits}
|
||||
{#each localCommits as commit, index (commit.id)}
|
||||
<CommitCard
|
||||
first={index === 0}
|
||||
last={index === localCommits.length - 1}
|
||||
{commit}
|
||||
commitUrl={$baseBranch?.commitUrl(commit.id)}
|
||||
type="local"
|
||||
>
|
||||
{#snippet lines(topHeightPx)}
|
||||
<LineGroup lineGroup={lineManager.get(commit.id)} {topHeightPx} />
|
||||
{/snippet}
|
||||
</CommitCard>
|
||||
{/each}
|
||||
{/if}
|
||||
{#if localAndRemoteCommits}
|
||||
{#each localAndRemoteCommits as commit, index (commit.id)}
|
||||
<CommitCard
|
||||
first={index === 0}
|
||||
last={index === localAndRemoteCommits.length - 1}
|
||||
{commit}
|
||||
commitUrl={$baseBranch?.commitUrl(commit.id)}
|
||||
type="localAndRemote"
|
||||
>
|
||||
{#snippet lines(topHeightPx)}
|
||||
<LineGroup lineGroup={lineManager.get(commit.id)} {topHeightPx} />
|
||||
{/snippet}
|
||||
</CommitCard>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</ScrollableContainer>
|
||||
<Resizer
|
||||
viewport={rsViewport}
|
||||
direction="right"
|
||||
minWidth={320}
|
||||
on:width={(e) => {
|
||||
laneWidth = e.detail / (16 * $userSettings.zoom);
|
||||
lscache.set(laneWidthKey, laneWidth, 7 * 1440); // 7 day ttl
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="base__right">
|
||||
{#await $selectedFile then selected}
|
||||
{#if selected}
|
||||
<FileCard
|
||||
conflicted={selected.conflicted}
|
||||
file={selected}
|
||||
isUnapplied={false}
|
||||
readonly={true}
|
||||
on:close={() => {
|
||||
fileIdSelection.clear();
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{/await}
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<p>No local or remote branch found</p>
|
||||
{/if}
|
||||
|
||||
<style lang="postcss">
|
||||
.base {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.base__left {
|
||||
display: flex;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
overflow-x: hidden;
|
||||
position: relative;
|
||||
}
|
||||
.base__right {
|
||||
display: flex;
|
||||
overflow-x: auto;
|
||||
align-items: flex-start;
|
||||
padding: 12px 12px 12px 6px;
|
||||
width: 800px;
|
||||
}
|
||||
|
||||
.branch-preview {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
margin: 12px 6px 12px 12px;
|
||||
}
|
||||
|
||||
.card__content {
|
||||
color: var(--clr-scale-ntrl-30);
|
||||
}
|
||||
</style>
|
@ -1,141 +0,0 @@
|
||||
<script lang="ts">
|
||||
import BranchPreviewHeader from '../branch/BranchPreviewHeader.svelte';
|
||||
import Resizer from '../shared/Resizer.svelte';
|
||||
import ScrollableContainer from '../shared/ScrollableContainer.svelte';
|
||||
import { Project } from '$lib/backend/projects';
|
||||
import { BaseBranch } from '$lib/baseBranch/baseBranch';
|
||||
import CommitCard from '$lib/commit/CommitCard.svelte';
|
||||
import FileCard from '$lib/file/FileCard.svelte';
|
||||
import { SETTINGS, type Settings } from '$lib/settings/userSettings';
|
||||
import { RemoteBranchService } from '$lib/stores/remoteBranches';
|
||||
import { getContext, getContextStore, getContextStoreBySymbol } from '$lib/utils/context';
|
||||
import { getMarkdownRenderer } from '$lib/utils/markdown';
|
||||
import { FileIdSelection } from '$lib/vbranches/fileIdSelection';
|
||||
import { type Branch } from '$lib/vbranches/types';
|
||||
import lscache from 'lscache';
|
||||
import { marked } from 'marked';
|
||||
import { onMount, setContext } from 'svelte';
|
||||
import { writable } from 'svelte/store';
|
||||
import type { PullRequest } from '$lib/gitHost/interface/types';
|
||||
|
||||
export let branch: Branch;
|
||||
export let pr: PullRequest | undefined;
|
||||
|
||||
const project = getContext(Project);
|
||||
const remoteBranchService = getContext(RemoteBranchService);
|
||||
const baseBranch = getContextStore(BaseBranch);
|
||||
|
||||
const fileIdSelection = new FileIdSelection(project.id, writable([]));
|
||||
setContext(FileIdSelection, fileIdSelection);
|
||||
|
||||
$: selectedFile = fileIdSelection.selectedFile;
|
||||
|
||||
const defaultBranchWidthRem = 30;
|
||||
const laneWidthKey = 'branchPreviewLaneWidth';
|
||||
const userSettings = getContextStoreBySymbol<Settings>(SETTINGS);
|
||||
|
||||
let rsViewport: HTMLDivElement;
|
||||
let laneWidth: number;
|
||||
|
||||
onMount(() => {
|
||||
laneWidth = lscache.get(laneWidthKey);
|
||||
});
|
||||
|
||||
const renderer = getMarkdownRenderer();
|
||||
</script>
|
||||
|
||||
<div class="base">
|
||||
<div
|
||||
class="base__left"
|
||||
bind:this={rsViewport}
|
||||
style:width={`${laneWidth || defaultBranchWidthRem}rem`}
|
||||
>
|
||||
<ScrollableContainer wide>
|
||||
<div class="branch-preview">
|
||||
<BranchPreviewHeader base={$baseBranch} {branch} {pr} />
|
||||
{#if pr}
|
||||
<div class="card">
|
||||
<div class="card__header text-base-body-14 text-semibold">{pr.title}</div>
|
||||
{#if pr.body}
|
||||
<div class="markdown card__content text-base-body-13">
|
||||
{@html marked.parse(pr.body, { renderer })}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{#await remoteBranchService.getRemoteBranchData(branch.name) then branchData}
|
||||
{#if branchData.commits && branchData.commits.length > 0}
|
||||
<div>
|
||||
{#each branchData.commits as commit, index (commit.id)}
|
||||
<CommitCard
|
||||
first={index === 0}
|
||||
last={index === branchData.commits.length - 1}
|
||||
{commit}
|
||||
commitUrl={$baseBranch?.commitUrl(commit.id)}
|
||||
type="localAndRemote"
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{/await}
|
||||
</div>
|
||||
</ScrollableContainer>
|
||||
<Resizer
|
||||
viewport={rsViewport}
|
||||
direction="right"
|
||||
minWidth={320}
|
||||
on:width={(e) => {
|
||||
laneWidth = e.detail / (16 * $userSettings.zoom);
|
||||
lscache.set(laneWidthKey, laneWidth, 7 * 1440); // 7 day ttl
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="base__right">
|
||||
{#await $selectedFile then selected}
|
||||
{#if selected}
|
||||
<FileCard
|
||||
conflicted={selected.conflicted}
|
||||
file={selected}
|
||||
isUnapplied={false}
|
||||
readonly={true}
|
||||
on:close={() => {
|
||||
fileIdSelection.clear();
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{/await}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
.base {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.base__left {
|
||||
display: flex;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
overflow-x: hidden;
|
||||
position: relative;
|
||||
}
|
||||
.base__right {
|
||||
display: flex;
|
||||
overflow-x: auto;
|
||||
align-items: flex-start;
|
||||
padding: 12px 12px 12px 6px;
|
||||
width: 800px;
|
||||
}
|
||||
|
||||
.branch-preview {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
margin: 12px 6px 12px 12px;
|
||||
}
|
||||
|
||||
.card__content {
|
||||
color: var(--clr-scale-ntrl-30);
|
||||
}
|
||||
</style>
|
@ -10,7 +10,10 @@
|
||||
|
||||
function getBranchLink(b: CombinedBranch): string | undefined {
|
||||
if (b.vbranch) return `/${projectId}/board/`;
|
||||
if (b.remoteBranch) return `/${projectId}/remote/${branch?.remoteBranch?.displayName}`;
|
||||
// Here we specifically want to prefer looking at the remote branch as
|
||||
// the there may be multiple remotes that share the same local branch.
|
||||
if (b.branch)
|
||||
return `/${projectId}/branch/${branch?.remoteBranch?.name || branch?.localBranch?.name}`;
|
||||
if (b.pr) return `/${projectId}/pull/${b.pr.number}`;
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,7 @@
|
||||
if (b.pr) return false;
|
||||
}
|
||||
|
||||
if (params.includeRemote && b.remoteBranch) return true;
|
||||
if (params.includeRemote && b.branch) return true;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
@ -276,11 +276,18 @@ You can find them in the 'Branches' sidebar in order to resolve conflicts.`;
|
||||
}
|
||||
}
|
||||
|
||||
async createvBranchFromBranch(branch: string) {
|
||||
/**
|
||||
*
|
||||
* @param branch The branch you want to create a virtual branch for. If you
|
||||
* 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) {
|
||||
try {
|
||||
await invoke<string>('create_virtual_branch_from_branch', {
|
||||
projectId: this.projectId,
|
||||
branch
|
||||
branch,
|
||||
remote
|
||||
});
|
||||
} catch (err) {
|
||||
showError('Failed to create virtual branch', err);
|
||||
|
@ -315,14 +315,11 @@ export class Branch {
|
||||
lastCommitTimestampMs?: number | undefined;
|
||||
lastCommitAuthor?: string | undefined;
|
||||
givenName!: string;
|
||||
isRemote!: boolean;
|
||||
|
||||
get displayName(): string {
|
||||
return this.name.replace('refs/remotes/', '').replace('refs/heads/', '');
|
||||
}
|
||||
|
||||
get isRemote() {
|
||||
return !!this.upstream;
|
||||
}
|
||||
}
|
||||
|
||||
export class BranchData {
|
||||
|
@ -4,6 +4,7 @@
|
||||
import { BaseBranch, NoDefaultTarget } from '$lib/baseBranch/baseBranch';
|
||||
import { BaseBranchService } from '$lib/baseBranch/baseBranchService';
|
||||
import { BranchDragActionsFactory } from '$lib/branches/dragActions';
|
||||
import { getNameNormalizationServiceContext } from '$lib/branches/nameNormalizationService';
|
||||
import { BranchService, createBranchServiceStore } from '$lib/branches/service';
|
||||
import { CommitDragActionsFactory } from '$lib/commits/dragActions';
|
||||
import NoBaseBranch from '$lib/components/NoBaseBranch.svelte';
|
||||
@ -33,6 +34,8 @@
|
||||
|
||||
const { data, children }: { data: LayoutData; children: Snippet } = $props();
|
||||
|
||||
const nameNormalizationService = getNameNormalizationServiceContext();
|
||||
|
||||
const {
|
||||
vbranchService,
|
||||
project,
|
||||
@ -104,7 +107,14 @@
|
||||
|
||||
listServiceStore.set(ghListService);
|
||||
githubRepoServiceStore.set(gitHost);
|
||||
branchServiceStore.set(new BranchService(vbranchService, remoteBranchService, ghListService));
|
||||
branchServiceStore.set(
|
||||
new BranchService(
|
||||
vbranchService,
|
||||
remoteBranchService,
|
||||
ghListService,
|
||||
nameNormalizationService
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
// Once on load and every time the project id changes
|
||||
|
@ -4,15 +4,18 @@
|
||||
// - And it does NOT have a cooresponding vbranch
|
||||
// It may also display details about a cooresponding pr if they exist
|
||||
import { getBranchServiceStore } from '$lib/branches/service';
|
||||
import RemoteBranchPreview from '$lib/components/BranchPreview.svelte';
|
||||
import FullviewLoading from '$lib/components/FullviewLoading.svelte';
|
||||
import RemoteBranchPreview from '$lib/components/RemoteBranchPreview.svelte';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
const branchService = getBranchServiceStore();
|
||||
const branches = $derived($branchService?.branches);
|
||||
const error = $derived($branchService?.error);
|
||||
// Search for remote branch first as there may be multiple combined branches
|
||||
// which have the same local branch
|
||||
const branch = $derived(
|
||||
$branches?.find((cb) => cb.remoteBranch?.displayName === $page.params.name)
|
||||
$branches?.find((cb) => cb.remoteBranch?.name === $page.params.name) ||
|
||||
$branches?.find((cb) => cb.localBranch?.name === $page.params.name)
|
||||
);
|
||||
// $: branch = $branches?.find((b) => b.displayName === $page.params.name);
|
||||
</script>
|
||||
@ -21,8 +24,12 @@
|
||||
<p>Error...</p>
|
||||
{:else if !$branches}
|
||||
<FullviewLoading />
|
||||
{:else if branch?.remoteBranch}
|
||||
<RemoteBranchPreview branch={branch.remoteBranch} pr={branch.pr} />
|
||||
{:else if branch?.remoteBranch || branch?.localBranch}
|
||||
<RemoteBranchPreview
|
||||
localBranch={branch?.localBranch}
|
||||
remoteBranch={branch?.remoteBranch}
|
||||
pr={branch.pr}
|
||||
/>
|
||||
{:else}
|
||||
<p>Branch doesn't seem to exist</p>
|
||||
{/if}
|
@ -470,12 +470,13 @@ impl VirtualBranchActions {
|
||||
&self,
|
||||
project: &Project,
|
||||
branch: &Refname,
|
||||
remote: Option<RemoteRefname>,
|
||||
) -> Result<BranchId> {
|
||||
let project_repository = open_with_verify(project)?;
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let mut guard = project.exclusive_worktree_access();
|
||||
branch_manager
|
||||
.create_virtual_branch_from_branch(branch, guard.write_permission())
|
||||
.create_virtual_branch_from_branch(branch, remote, guard.write_permission())
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ use gitbutler_commit::commit_headers::HasCommitHeaders;
|
||||
use gitbutler_error::error::Marker;
|
||||
use gitbutler_oplog::SnapshotExt;
|
||||
use gitbutler_project::access::WorktreeWritePermission;
|
||||
use gitbutler_reference::Refname;
|
||||
use gitbutler_reference::{Refname, RemoteRefname};
|
||||
use gitbutler_repo::{rebase::cherry_rebase, RepoActionsExt, RepositoryExt};
|
||||
use gitbutler_time::time::now_since_unix_epoch_ms;
|
||||
use std::borrow::Cow;
|
||||
@ -36,7 +36,7 @@ impl BranchManager<'_> {
|
||||
|
||||
let tree = commit
|
||||
.tree()
|
||||
.context("failed to find defaut target commit tree")?;
|
||||
.context("failed to find default target commit tree")?;
|
||||
|
||||
let mut all_virtual_branches = vb_state
|
||||
.list_branches_in_workspace()
|
||||
@ -126,20 +126,26 @@ impl BranchManager<'_> {
|
||||
|
||||
pub fn create_virtual_branch_from_branch(
|
||||
&self,
|
||||
upstream: &Refname,
|
||||
target: &Refname,
|
||||
upstream_branch: Option<RemoteRefname>,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
) -> Result<BranchId> {
|
||||
// only set upstream if it's not the default target
|
||||
let upstream_branch = match upstream {
|
||||
Refname::Other(_) | Refname::Virtual(_) => {
|
||||
// we only support local or remote branches
|
||||
bail!("branch {upstream} must be a local or remote branch");
|
||||
let upstream_branch = match upstream_branch {
|
||||
Some(upstream_branch) => Some(upstream_branch),
|
||||
None => {
|
||||
match target {
|
||||
Refname::Other(_) | Refname::Virtual(_) => {
|
||||
// we only support local or remote branches
|
||||
bail!("branch {target} must be a local or remote branch");
|
||||
}
|
||||
Refname::Remote(remote) => Some(remote.clone()),
|
||||
Refname::Local(local) => local.remote().cloned(),
|
||||
}
|
||||
}
|
||||
Refname::Remote(remote) => Some(remote.clone()),
|
||||
Refname::Local(local) => local.remote().cloned(),
|
||||
};
|
||||
|
||||
let branch_name = upstream
|
||||
let branch_name = target
|
||||
.branch()
|
||||
.expect("always a branch reference")
|
||||
.to_string();
|
||||
@ -153,21 +159,21 @@ impl BranchManager<'_> {
|
||||
|
||||
let default_target = vb_state.get_default_target()?;
|
||||
|
||||
if let Refname::Remote(remote_upstream) = upstream {
|
||||
if let Refname::Remote(remote_upstream) = target {
|
||||
if default_target.branch == *remote_upstream {
|
||||
bail!("cannot create a branch from default target")
|
||||
}
|
||||
}
|
||||
|
||||
let repo = self.project_repository.repo();
|
||||
let head_reference =
|
||||
repo.find_reference(&upstream.to_string())
|
||||
.map_err(|err| match err {
|
||||
err if err.code() == git2::ErrorCode::NotFound => {
|
||||
anyhow!("branch {upstream} was not found")
|
||||
}
|
||||
err => err.into(),
|
||||
})?;
|
||||
let head_reference = repo
|
||||
.find_reference(&target.to_string())
|
||||
.map_err(|err| match err {
|
||||
err if err.code() == git2::ErrorCode::NotFound => {
|
||||
anyhow!("branch {target} was not found")
|
||||
}
|
||||
err => err.into(),
|
||||
})?;
|
||||
let head_commit = head_reference
|
||||
.peel_to_commit()
|
||||
.context("failed to peel to commit")?;
|
||||
@ -220,7 +226,7 @@ impl BranchManager<'_> {
|
||||
);
|
||||
|
||||
let branch = if let Ok(Some(mut branch)) =
|
||||
vb_state.find_by_source_refname_where_not_in_workspace(upstream)
|
||||
vb_state.find_by_source_refname_where_not_in_workspace(target)
|
||||
{
|
||||
branch.upstream_head = upstream_branch.is_some().then_some(head_commit.id());
|
||||
branch.upstream = upstream_branch;
|
||||
@ -239,7 +245,7 @@ impl BranchManager<'_> {
|
||||
id: BranchId::generate(),
|
||||
name: branch_name.clone(),
|
||||
notes: String::new(),
|
||||
source_refname: Some(upstream.clone()),
|
||||
source_refname: Some(target.clone()),
|
||||
upstream_head: upstream_branch.is_some().then_some(head_commit.id()),
|
||||
upstream: upstream_branch,
|
||||
tree: head_commit_tree.id(),
|
||||
|
@ -30,6 +30,7 @@ pub struct RemoteBranch {
|
||||
pub given_name: String,
|
||||
pub last_commit_timestamp_ms: Option<u128>,
|
||||
pub last_commit_author: Option<String>,
|
||||
pub is_remote: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
||||
@ -139,6 +140,7 @@ pub(crate) fn branch_to_remote_branch(
|
||||
.map(|t: u128| t * 1000)
|
||||
.ok(),
|
||||
last_commit_author: commit.author().name().map(std::string::ToString::to_string),
|
||||
is_remote: branch.get().is_remote(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1177,6 +1177,7 @@ fn unapply_branch() -> Result<()> {
|
||||
let branch_manager = project_repository.branch_manager();
|
||||
let branch1_id = branch_manager.create_virtual_branch_from_branch(
|
||||
&Refname::from_str(&real_branch)?,
|
||||
None,
|
||||
guard.write_permission(),
|
||||
)?;
|
||||
let contents = std::fs::read(Path::new(&project.path).join(file_path))?;
|
||||
@ -1271,6 +1272,7 @@ fn apply_unapply_added_deleted_files() -> Result<()> {
|
||||
branch_manager
|
||||
.create_virtual_branch_from_branch(
|
||||
&Refname::from_str(&real_branch_2).unwrap(),
|
||||
None,
|
||||
guard.write_permission(),
|
||||
)
|
||||
.unwrap();
|
||||
@ -1281,6 +1283,7 @@ fn apply_unapply_added_deleted_files() -> Result<()> {
|
||||
branch_manager
|
||||
.create_virtual_branch_from_branch(
|
||||
&Refname::from_str(&real_branch_3).unwrap(),
|
||||
None,
|
||||
guard.write_permission(),
|
||||
)
|
||||
.unwrap();
|
||||
|
@ -94,7 +94,7 @@ async fn rebase_commit() {
|
||||
{
|
||||
// apply first vbranch again
|
||||
branch1_id = controller
|
||||
.create_virtual_branch_from_branch(project, &unapplied_branch)
|
||||
.create_virtual_branch_from_branch(project, &unapplied_branch, None)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@ -192,7 +192,7 @@ async fn rebase_work() {
|
||||
{
|
||||
// apply first vbranch again
|
||||
branch1_id = controller
|
||||
.create_virtual_branch_from_branch(project, &unapplied_branch)
|
||||
.create_virtual_branch_from_branch(project, &unapplied_branch, None)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -92,7 +92,7 @@ async fn conflicting() {
|
||||
let branch_id = {
|
||||
// apply branch, it should conflict
|
||||
let branch_id = controller
|
||||
.create_virtual_branch_from_branch(project, &unapplied_branch)
|
||||
.create_virtual_branch_from_branch(project, &unapplied_branch, None)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -56,7 +56,7 @@ async fn integration() {
|
||||
|
||||
// checkout a existing remote branch
|
||||
let branch_id = controller
|
||||
.create_virtual_branch_from_branch(project, &branch_name)
|
||||
.create_virtual_branch_from_branch(project, &branch_name, None)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@ -151,7 +151,11 @@ async fn no_conflicts() {
|
||||
assert!(branches.is_empty());
|
||||
|
||||
let branch_id = controller
|
||||
.create_virtual_branch_from_branch(project, &"refs/remotes/origin/branch".parse().unwrap())
|
||||
.create_virtual_branch_from_branch(
|
||||
project,
|
||||
&"refs/remotes/origin/branch".parse().unwrap(),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@ -197,7 +201,11 @@ async fn conflicts_with_uncommited() {
|
||||
// branch should be created unapplied, because of the conflict
|
||||
|
||||
let new_branch_id = controller
|
||||
.create_virtual_branch_from_branch(project, &"refs/remotes/origin/branch".parse().unwrap())
|
||||
.create_virtual_branch_from_branch(
|
||||
project,
|
||||
&"refs/remotes/origin/branch".parse().unwrap(),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let new_branch = controller
|
||||
@ -253,7 +261,11 @@ async fn conflicts_with_commited() {
|
||||
// branch should be created unapplied, because of the conflict
|
||||
|
||||
let new_branch_id = controller
|
||||
.create_virtual_branch_from_branch(project, &"refs/remotes/origin/branch".parse().unwrap())
|
||||
.create_virtual_branch_from_branch(
|
||||
project,
|
||||
&"refs/remotes/origin/branch".parse().unwrap(),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let new_branch = controller
|
||||
@ -289,6 +301,7 @@ async fn from_default_target() {
|
||||
.create_virtual_branch_from_branch(
|
||||
project,
|
||||
&"refs/remotes/origin/master".parse().unwrap(),
|
||||
None
|
||||
)
|
||||
.await
|
||||
.unwrap_err()
|
||||
@ -317,6 +330,7 @@ async fn from_non_existent_branch() {
|
||||
.create_virtual_branch_from_branch(
|
||||
project,
|
||||
&"refs/remotes/origin/branch".parse().unwrap(),
|
||||
None
|
||||
)
|
||||
.await
|
||||
.unwrap_err()
|
||||
@ -355,7 +369,11 @@ async fn from_state_remote_branch() {
|
||||
.unwrap();
|
||||
|
||||
let branch_id = controller
|
||||
.create_virtual_branch_from_branch(project, &"refs/remotes/origin/branch".parse().unwrap())
|
||||
.create_virtual_branch_from_branch(
|
||||
project,
|
||||
&"refs/remotes/origin/branch".parse().unwrap(),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -136,7 +136,7 @@ async fn resolve_conflict_flow() {
|
||||
let branch1_id = {
|
||||
// when we apply conflicted branch, it has conflict
|
||||
let branch1_id = controller
|
||||
.create_virtual_branch_from_branch(project, &unapplied_branch)
|
||||
.create_virtual_branch_from_branch(project, &unapplied_branch, None)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -376,7 +376,7 @@ async fn applying_first_branch() {
|
||||
.unwrap();
|
||||
let unapplied_branch = Refname::from_str(&unapplied_branch).unwrap();
|
||||
controller
|
||||
.create_virtual_branch_from_branch(project, &unapplied_branch)
|
||||
.create_virtual_branch_from_branch(project, &unapplied_branch, None)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -54,7 +54,7 @@ mod applied_branch {
|
||||
{
|
||||
// applying the branch should produce conflict markers
|
||||
controller
|
||||
.create_virtual_branch_from_branch(project, &unapplied_branch)
|
||||
.create_virtual_branch_from_branch(project, &unapplied_branch, None)
|
||||
.await
|
||||
.unwrap();
|
||||
let (branches, _) = controller.list_virtual_branches(project).await.unwrap();
|
||||
@ -124,7 +124,7 @@ mod applied_branch {
|
||||
{
|
||||
// applying the branch should produce conflict markers
|
||||
controller
|
||||
.create_virtual_branch_from_branch(project, &unapplied_branch)
|
||||
.create_virtual_branch_from_branch(project, &unapplied_branch, None)
|
||||
.await
|
||||
.unwrap();
|
||||
let (branches, _) = controller.list_virtual_branches(project).await.unwrap();
|
||||
@ -200,7 +200,7 @@ mod applied_branch {
|
||||
{
|
||||
// applying the branch should produce conflict markers
|
||||
controller
|
||||
.create_virtual_branch_from_branch(project, &unapplied_branch)
|
||||
.create_virtual_branch_from_branch(project, &unapplied_branch, None)
|
||||
.await
|
||||
.unwrap();
|
||||
let (branches, _) = controller.list_virtual_branches(project).await.unwrap();
|
||||
@ -273,7 +273,7 @@ mod applied_branch {
|
||||
{
|
||||
// applying the branch should produce conflict markers
|
||||
controller
|
||||
.create_virtual_branch_from_branch(project, &unapplied_branch)
|
||||
.create_virtual_branch_from_branch(project, &unapplied_branch, None)
|
||||
.await
|
||||
.unwrap();
|
||||
let (branches, _) = controller.list_virtual_branches(project).await.unwrap();
|
||||
@ -346,7 +346,7 @@ mod applied_branch {
|
||||
{
|
||||
// applying the branch should produce conflict markers
|
||||
controller
|
||||
.create_virtual_branch_from_branch(project, &unapplied_branch)
|
||||
.create_virtual_branch_from_branch(project, &unapplied_branch, None)
|
||||
.await
|
||||
.unwrap();
|
||||
let (branches, _) = controller.list_virtual_branches(project).await.unwrap();
|
||||
@ -767,6 +767,7 @@ mod applied_branch {
|
||||
.create_virtual_branch_from_branch(
|
||||
project,
|
||||
&Refname::from_str(unapplied_refname.as_str()).unwrap(),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
@ -83,10 +83,11 @@ pub mod commands {
|
||||
projects: State<'_, projects::Controller>,
|
||||
project_id: ProjectId,
|
||||
branch: Refname,
|
||||
remote: Option<RemoteRefname>,
|
||||
) -> Result<BranchId, Error> {
|
||||
let project = projects.get(project_id)?;
|
||||
let branch_id = VirtualBranchActions
|
||||
.create_virtual_branch_from_branch(&project, &branch)
|
||||
.create_virtual_branch_from_branch(&project, &branch, remote)
|
||||
.await?;
|
||||
emit_vbranches(&windows, project_id).await;
|
||||
Ok(branch_id)
|
||||
|
Loading…
Reference in New Issue
Block a user