Merge pull request #1416 from gitbutlerapp/make-vbranch-remote-a-branch

Make vbranch remote a branch
This commit is contained in:
Nikita Galaiko 2023-10-19 09:51:02 +02:00 committed by GitHub
commit d8a54e5c49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 83 additions and 81 deletions

View File

@ -23,13 +23,12 @@ use super::{branch, get_default_target, iterator::BranchIterator as Iterator, Au
// an array of them can be requested from the frontend to show in the sidebar
// Tray and should only contain branches that have not been converted into
// virtual branches yet (ie, we have no `Branch` struct persisted in our data.
#[derive(Debug, Clone, Serialize)]
#[derive(Debug, Clone, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct RemoteBranch {
pub sha: String,
pub name: String,
pub sha: git::Oid,
pub name: git::BranchName,
pub behind: u32,
pub upstream: Option<git::RemoteBranchName>,
pub commits: Vec<RemoteCommit>,
}
@ -166,42 +165,48 @@ pub fn list_remote_branches(
.collect();
let top_branches = sorted_branches.into_iter().take(20).collect::<Vec<_>>(); // Take the first 20 entries.
let mut branches: Vec<RemoteBranch> = Vec::new();
for branch in &top_branches {
if let Some(branch_oid) = branch.target() {
let branches = top_branches
.into_iter()
.map(|branch| branch_to_remote_branch(project_repository, &branch, main_oid))
.collect::<Result<Vec<_>>>()
.context("failed to convert branches")?
.into_iter()
.flatten()
.filter(|branch| !branch.commits.is_empty())
.collect::<Vec<_>>();
Ok(branches)
}
pub fn branch_to_remote_branch(
project_repository: &project_repository::Repository,
branch: &git::Branch,
base: git::Oid,
) -> Result<Option<RemoteBranch>> {
branch
.target()
.map(|sha| {
let ahead = project_repository
.log(branch_oid, LogUntil::Commit(main_oid))
.log(sha, LogUntil::Commit(base))
.context("failed to get ahead commits")?;
if ahead.is_empty() {
continue;
}
let branch_name = branch.refname().context("could not get branch name")?;
let name = git::BranchName::try_from(branch).context("could not get branch name")?;
let count_behind = project_repository
.distance(main_oid, branch_oid)
.distance(base, sha)
.context("failed to get behind count")?;
let upstream = branch
.upstream()
.ok()
.map(|upstream_branch| git::RemoteBranchName::try_from(&upstream_branch))
.transpose()?;
branches.push(RemoteBranch {
sha: branch_oid.to_string(),
name: branch_name.to_string(),
upstream,
Ok(RemoteBranch {
sha,
name,
behind: count_behind,
commits: ahead
.into_iter()
.map(|commit| commit_to_remote_commit(&commit))
.collect::<Result<Vec<_>>>()?,
});
}
}
Ok(branches)
})
})
.transpose()
}
pub fn commit_to_remote_commit(commit: &git::Commit) -> Result<RemoteCommit> {

View File

@ -1186,7 +1186,7 @@ fn test_merge_vbranch_upstream_clean() -> Result<()> {
let branch1 = &branches[0];
assert_eq!(branch1.files.len(), 1);
assert_eq!(branch1.commits.len(), 1);
assert_eq!(branch1.upstream_commits.len(), 1);
assert_eq!(branch1.upstream.as_ref().unwrap().commits.len(), 1);
merge_virtual_branch_upstream(
&gb_repository,
@ -1208,7 +1208,7 @@ fn test_merge_vbranch_upstream_clean() -> Result<()> {
assert_eq!("file2\n", String::from_utf8(contents)?);
assert_eq!(branch1.files.len(), 0);
assert_eq!(branch1.commits.len(), 3);
assert_eq!(branch1.upstream_commits.len(), 0);
assert_eq!(branch1.upstream.as_ref().unwrap().commits.len(), 0);
// make sure the last commit was signed
let last_id = &branch1.commits[0].id;
@ -1315,7 +1315,7 @@ fn test_merge_vbranch_upstream_conflict() -> Result<()> {
assert_eq!(branch1.files.len(), 1);
assert_eq!(branch1.commits.len(), 1);
assert_eq!(branch1.upstream_commits.len(), 1);
assert_eq!(branch1.upstream.as_ref().unwrap().commits.len(), 1);
merge_virtual_branch_upstream(&gb_repository, &project_repository, &branch1.id, None, None)?;
@ -2218,7 +2218,7 @@ fn test_detect_mergeable_branch() -> Result<()> {
list_remote_branches(&gb_repository, &project_repository).expect("failed to list remotes");
let remote1 = &remotes
.iter()
.find(|b| b.name == "refs/remotes/origin/remote_branch")
.find(|b| b.name.to_string() == "refs/remotes/origin/remote_branch")
.unwrap();
assert!(!is_remote_branch_mergeable(
&gb_repository,
@ -2230,7 +2230,7 @@ fn test_detect_mergeable_branch() -> Result<()> {
let remote2 = &remotes
.iter()
.find(|b| b.name == "refs/remotes/origin/remote_branch2")
.find(|b| b.name.to_string() == "refs/remotes/origin/remote_branch2")
.unwrap();
assert!(is_remote_branch_mergeable(
&gb_repository,

View File

@ -20,7 +20,7 @@ use crate::{
use super::{
branch::{self, Branch, BranchCreateRequest, BranchId, FileOwnership, Hunk, Ownership},
target, Iterator,
branch_to_remote_branch, target, Iterator, RemoteBranch,
};
type AppliedStatuses = Vec<(branch::Branch, Vec<VirtualBranchFile>)>;
@ -45,10 +45,9 @@ pub struct VirtualBranch {
pub requires_force: bool, // does this branch require a force push to the upstream?
pub conflicted: bool, // is this branch currently in a conflicted state (only for applied branches)
pub order: usize, // the order in which this branch should be displayed in the UI
pub upstream: Option<git::RemoteBranchName>, // the name of the upstream branch this branch this pushes to
pub upstream: Option<RemoteBranch>, // the upstream branch where this branch pushes to, if any
pub base_current: bool, // is this vbranch based on the current base branch? if false, this needs to be manually merged with conflicts
pub ownership: Ownership,
pub upstream_commits: Vec<VirtualBranchCommit>,
}
// this is the struct that maps to the view `Commit` type in Typescript
@ -655,20 +654,20 @@ pub fn list_virtual_branches(
let repo = &project_repository.git_repository;
// see if we can identify some upstream
let mut upstream_commit = None;
if let Some(branch_upstream) = &branch.upstream {
if let Ok(upstream_oid) = repo.refname_to_id(&branch_upstream.to_string()) {
if let Ok(upstream_commit_obj) = repo.find_commit(upstream_oid) {
upstream_commit = Some(upstream_commit_obj);
}
}
}
let upstream_branch = branch
.upstream
.as_ref()
.map(|name| repo.find_branch(&git::BranchName::from(name.clone())))
.transpose()?;
let upstram_branch_commit = upstream_branch
.as_ref()
.map(git::Branch::peel_to_commit)
.transpose()?;
// find upstream commits if we found an upstream reference
let mut upstream_commits = vec![];
let mut pushed_commits = HashMap::new();
if let Some(upstream) = &upstream_commit {
if let Some(upstream) = &upstram_branch_commit {
let merge_base =
repo.merge_base(upstream.id(), default_target.sha)
.context(format!(
@ -679,13 +678,6 @@ pub fn list_virtual_branches(
for oid in project_repository.l(upstream.id(), LogUntil::Commit(merge_base))? {
pushed_commits.insert(oid, true);
}
// find any commits on the upstream that aren't in our branch (someone else pushed to our branch)
for commit in project_repository.log(upstream.id(), LogUntil::Commit(branch.head))? {
let commit =
commit_to_vbranch_commit(project_repository, &default_target, &commit, None)?;
upstream_commits.push(commit);
}
}
// find all commits on head that are not on target.sha
@ -713,8 +705,14 @@ pub fn list_virtual_branches(
}
}
let requires_force = is_requires_force(project_repository, branch)?;
let upstream = upstream_branch
.map(|upstream_branch| {
branch_to_remote_branch(project_repository, &upstream_branch, branch.head)
})
.transpose()?
.flatten();
let requires_force = is_requires_force(project_repository, branch)?;
let branch = VirtualBranch {
id: branch.id,
name: branch.name.clone(),
@ -724,11 +722,10 @@ pub fn list_virtual_branches(
order: branch.order,
commits,
requires_force,
upstream: branch.upstream.clone(),
upstream,
conflicted: conflicts::is_resolving(project_repository),
base_current,
ownership: branch.ownership.clone(),
upstream_commits,
};
branches.push(branch);
}

View File

@ -353,8 +353,8 @@ mod references {
assert_eq!(branches[0].id, branch1_id);
assert_eq!(branches[0].name, "name");
assert_eq!(
branches[0].upstream,
Some("refs/remotes/origin/name".parse().unwrap())
branches[0].upstream.as_ref().unwrap().name.to_string(),
"refs/remotes/origin/name"
);
let refnames = repository
@ -362,7 +362,7 @@ mod references {
.into_iter()
.filter_map(|reference| reference.name().map(|name| name.to_string()))
.collect::<Vec<_>>();
assert!(refnames.contains(&branches[0].upstream.clone().unwrap().to_string()));
assert!(refnames.contains(&branches[0].upstream.clone().unwrap().name.to_string()));
}
#[tokio::test]
@ -448,15 +448,15 @@ mod references {
assert_eq!(branches[0].id, branch1_id);
assert_eq!(branches[0].name, "updated name");
assert_eq!(
branches[0].upstream,
Some("refs/remotes/origin/name".parse().unwrap())
branches[0].upstream.as_ref().unwrap().name,
"refs/remotes/origin/name".parse().unwrap()
);
// new branch is pushing to new ref remotely
assert_eq!(branches[1].id, branch2_id);
assert_eq!(branches[1].name, "name");
assert_eq!(
branches[1].upstream,
Some("refs/remotes/origin/name-1".parse().unwrap())
branches[1].upstream.as_ref().unwrap().name,
"refs/remotes/origin/name-1".parse().unwrap()
);
let refnames = repository
@ -464,8 +464,8 @@ mod references {
.into_iter()
.filter_map(|reference| reference.name().map(|name| name.to_string()))
.collect::<Vec<_>>();
assert!(refnames.contains(&branches[0].upstream.clone().unwrap().to_string()));
assert!(refnames.contains(&branches[1].upstream.clone().unwrap().to_string()));
assert!(refnames.contains(&branches[0].upstream.clone().unwrap().name.to_string()));
assert!(refnames.contains(&branches[1].upstream.clone().unwrap().name.to_string()));
}
}
}

View File

@ -40,9 +40,8 @@ export class Branch {
requiresForce!: boolean;
description!: string;
order!: number;
upstream?: string;
@Type(() => Commit)
upstreamCommits!: Commit[];
@Type(() => RemoteBranch)
upstream?: RemoteBranch;
conflicted!: boolean;
baseCurrent!: boolean;
ownership!: string;

View File

@ -90,11 +90,11 @@
$: pullRequestPromise =
githubContext && branch.upstream
? getPullRequestByBranch(githubContext, branch.upstream.split('/').slice(-1)[0])
? getPullRequestByBranch(githubContext, branch.upstream?.name.split('/').slice(-1)[0])
: undefined;
let shouldCreatePr = false;
$: branchName = branch.upstream?.split('/').slice(-1)[0];
$: branchName = branch.upstream?.name.split('/').slice(-1)[0];
$: if (shouldCreatePr && branchName && githubContext) {
createPR();
shouldCreatePr = false;
@ -184,7 +184,7 @@
let upstreamCommitsShown = false;
$: if (upstreamCommitsShown && branch.upstreamCommits.length === 0) {
$: if (upstreamCommitsShown && branch.upstream?.commits.length === 0) {
upstreamCommitsShown = false;
}
@ -405,12 +405,12 @@
/>
{/if}
{#if branch.upstreamCommits.length > 0 && !branch.conflicted}
{#if branch.upstream?.commits.length && branch.upstream?.commits.length > 0 && !branch.conflicted}
<div class="bg-zinc-300 p-2 dark:bg-zinc-800">
<div class="flex flex-row justify-between">
<div class="p-1 text-purple-700">
{branch.upstreamCommits.length}
upstream {branch.upstreamCommits.length > 1 ? 'commits' : 'commit'}
{branch.upstream.commits.length}
upstream {branch.upstream.commits.length > 1 ? 'commits' : 'commit'}
</div>
<Button
class="w-20"
@ -435,7 +435,7 @@
id="upstreamCommits"
>
<div class="bg-light-100">
{#each branch.upstreamCommits as commit}
{#each branch.upstream.commits as commit}
<CommitCard {commit} {projectId} />
{/each}
</div>
@ -655,10 +655,10 @@
<Link
target="_blank"
rel="noreferrer"
href={branchUrl(base, branch.upstream)}
href={branchUrl(base, branch.upstream?.name)}
class="inline-block max-w-full truncate text-sm font-bold"
>
{branch.upstream.split('refs/remotes/')[1]}
{branch.upstream.name.split('refs/remotes/')[1]}
</Link>
{#await pullRequestPromise then pr}
{#if githubContext && pr}

View File

@ -1,7 +1,8 @@
<script lang="ts">
import type { BranchController } from '$lib/vbranches/branchController';
import type { RemoteBranch } from '$lib/vbranches/types';
export let branch: string;
export let branch: RemoteBranch;
export let branchController: BranchController;
export let branchId: string;
@ -9,7 +10,7 @@
let remoteBranchName = '';
$: if (branch) {
let parts = branch.replace('refs/remotes/', '').split('/');
let parts = branch.name.replace('refs/remotes/', '').split('/');
remoteName = parts[0];
// remoteBranchName is the rest
let rbn = parts.slice(1).join('/');

View File

@ -29,7 +29,7 @@
<div>
<p class="text-lg font-bold" title="name of virtual branch">{branch.name}</p>
<p class="text-light-700 dark:text-dark-200" title="upstream target">
{branch.upstream?.replace('refs/remotes/', '') || ''}
{branch.upstream?.name.replace('refs/remotes/', '') || ''}
</p>
</div>
<div>