mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-01 20:45:57 +03:00
get rid of virtual / remote confusion
This commit is contained in:
parent
797942fedd
commit
edbbe1f0a8
@ -366,6 +366,7 @@ impl App {
|
|||||||
let diff = Self::diff_hunks_to_string(diff);
|
let diff = Self::diff_hunks_to_string(diff);
|
||||||
Ok(diff)
|
Ok(diff)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn git_commit_diff(
|
pub fn git_commit_diff(
|
||||||
&self,
|
&self,
|
||||||
project_id: &str,
|
project_id: &str,
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
use std::{collections::HashMap, path};
|
use std::{collections::HashMap, path};
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::git;
|
use crate::git;
|
||||||
|
|
||||||
use super::Repository;
|
use super::Repository;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone, Serialize)]
|
||||||
pub struct Hunk {
|
pub struct Hunk {
|
||||||
pub old_start: usize,
|
pub old_start: usize,
|
||||||
pub old_lines: usize,
|
pub old_lines: usize,
|
||||||
|
@ -8,7 +8,7 @@ use futures::future::join_all;
|
|||||||
use tauri::{generate_context, Manager};
|
use tauri::{generate_context, Manager};
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
use gitbutler::{virtual_branches::VirtualBranchCommit, *};
|
use gitbutler::*;
|
||||||
|
|
||||||
use crate::{error::Error, git, project_repository::activity};
|
use crate::{error::Error, git, project_repository::activity};
|
||||||
|
|
||||||
@ -366,7 +366,7 @@ async fn git_remote_branches_data(
|
|||||||
.map(|commit| {
|
.map(|commit| {
|
||||||
let proxy = proxy.clone();
|
let proxy = proxy.clone();
|
||||||
async move {
|
async move {
|
||||||
VirtualBranchCommit {
|
virtual_branches::RemoteCommit {
|
||||||
author: virtual_branches::Author {
|
author: virtual_branches::Author {
|
||||||
gravatar_url: proxy
|
gravatar_url: proxy
|
||||||
.proxy(&commit.author.gravatar_url)
|
.proxy(&commit.author.gravatar_url)
|
||||||
|
@ -451,7 +451,7 @@ pub fn target_to_base_branch(
|
|||||||
.log(oid, project_repository::LogUntil::Commit(target.sha))
|
.log(oid, project_repository::LogUntil::Commit(target.sha))
|
||||||
.context("failed to get upstream commits")?
|
.context("failed to get upstream commits")?
|
||||||
.iter()
|
.iter()
|
||||||
.map(|c| super::commit_to_vbranch_commit(project_repository, target, c, None))
|
.map(|commit| super::commit_to_remote_commit(&project_repository.git_repository, commit))
|
||||||
.collect::<Result<Vec<_>>>()?;
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
// get some recent commits
|
// get some recent commits
|
||||||
@ -459,7 +459,7 @@ pub fn target_to_base_branch(
|
|||||||
.log(target.sha, LogUntil::Take(20))
|
.log(target.sha, LogUntil::Take(20))
|
||||||
.context("failed to get recent commits")?
|
.context("failed to get recent commits")?
|
||||||
.iter()
|
.iter()
|
||||||
.map(|c| super::commit_to_vbranch_commit(project_repository, target, c, None))
|
.map(|commit| super::commit_to_remote_commit(&project_repository.git_repository, commit))
|
||||||
.collect::<Result<Vec<_>>>()?;
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
let base = super::BaseBranch {
|
let base = super::BaseBranch {
|
||||||
@ -576,7 +576,8 @@ pub fn create_virtual_branch_from_branch(
|
|||||||
// do a diff between the head of this branch and the target base
|
// do a diff between the head of this branch and the target base
|
||||||
let diff = diff::trees(&project_repository.git_repository, &merge_tree, &tree)
|
let diff = diff::trees(&project_repository.git_repository, &merge_tree, &tree)
|
||||||
.context("failed to diff trees")?;
|
.context("failed to diff trees")?;
|
||||||
let hunks_by_filepath = super::hunks_by_filepath(project_repository, &diff);
|
let hunks_by_filepath =
|
||||||
|
super::virtual_hunks_by_filepath(&project_repository.git_repository, &diff);
|
||||||
|
|
||||||
// assign ownership to the branch
|
// assign ownership to the branch
|
||||||
for hunk in hunks_by_filepath.values().flatten() {
|
for hunk in hunks_by_filepath.values().flatten() {
|
||||||
|
@ -349,7 +349,7 @@ impl Controller {
|
|||||||
.recent_commits
|
.recent_commits
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|commit| async move {
|
.map(|commit| async move {
|
||||||
super::VirtualBranchCommit {
|
super::RemoteCommit {
|
||||||
author: super::Author {
|
author: super::Author {
|
||||||
gravatar_url: self
|
gravatar_url: self
|
||||||
.assets_proxy
|
.assets_proxy
|
||||||
@ -373,7 +373,7 @@ impl Controller {
|
|||||||
.upstream_commits
|
.upstream_commits
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|commit| async move {
|
.map(|commit| async move {
|
||||||
super::VirtualBranchCommit {
|
super::RemoteCommit {
|
||||||
author: super::Author {
|
author: super::Author {
|
||||||
gravatar_url: self
|
gravatar_url: self
|
||||||
.assets_proxy
|
.assets_proxy
|
||||||
|
@ -64,7 +64,6 @@ pub struct VirtualBranchCommit {
|
|||||||
pub created_at: u128,
|
pub created_at: u128,
|
||||||
pub author: Author,
|
pub author: Author,
|
||||||
pub is_remote: bool,
|
pub is_remote: bool,
|
||||||
// only present if is_remote is false
|
|
||||||
pub files: Vec<VirtualBranchFile>,
|
pub files: Vec<VirtualBranchFile>,
|
||||||
pub is_integrated: bool,
|
pub is_integrated: bool,
|
||||||
}
|
}
|
||||||
@ -88,6 +87,14 @@ pub struct VirtualBranchFile {
|
|||||||
pub binary: bool,
|
pub binary: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct RemoteBranchFile {
|
||||||
|
pub path: path::PathBuf,
|
||||||
|
pub hunks: Vec<git::diff::Hunk>,
|
||||||
|
pub binary: bool,
|
||||||
|
}
|
||||||
|
|
||||||
// this struct is a mapping to the view `Hunk` type in Typescript
|
// this struct is a mapping to the view `Hunk` type in Typescript
|
||||||
// found in src-tauri/src/routes/repo/[project_id]/types.ts
|
// found in src-tauri/src/routes/repo/[project_id]/types.ts
|
||||||
// it holds a materialized view for presentation purposes of one entry of the
|
// it holds a materialized view for presentation purposes of one entry of the
|
||||||
@ -110,6 +117,19 @@ pub struct VirtualBranchHunk {
|
|||||||
pub locked: bool,
|
pub locked: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct RemoteBranchHunk {
|
||||||
|
pub id: String,
|
||||||
|
pub diff: String,
|
||||||
|
pub modified_at: u128,
|
||||||
|
pub file_path: path::PathBuf,
|
||||||
|
pub hash: String,
|
||||||
|
pub start: usize,
|
||||||
|
pub end: usize,
|
||||||
|
pub binary: bool,
|
||||||
|
}
|
||||||
|
|
||||||
// this struct is a mapping to the view `RemoteBranch` type in Typescript
|
// this struct is a mapping to the view `RemoteBranch` type in Typescript
|
||||||
// found in src-tauri/src/routes/repo/[project_id]/types.ts
|
// found in src-tauri/src/routes/repo/[project_id]/types.ts
|
||||||
//
|
//
|
||||||
@ -127,7 +147,17 @@ pub struct RemoteBranch {
|
|||||||
pub behind: u32,
|
pub behind: u32,
|
||||||
pub upstream: Option<git::RemoteBranchName>,
|
pub upstream: Option<git::RemoteBranchName>,
|
||||||
pub mergeable: bool,
|
pub mergeable: bool,
|
||||||
pub commits: Vec<VirtualBranchCommit>,
|
pub commits: Vec<RemoteCommit>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct RemoteCommit {
|
||||||
|
pub id: String,
|
||||||
|
pub description: String,
|
||||||
|
pub created_at: u128,
|
||||||
|
pub author: Author,
|
||||||
|
pub files: Vec<RemoteBranchFile>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, PartialEq, Clone)]
|
#[derive(Debug, Serialize, PartialEq, Clone)]
|
||||||
@ -139,8 +169,8 @@ pub struct BaseBranch {
|
|||||||
pub base_sha: String,
|
pub base_sha: String,
|
||||||
pub current_sha: String,
|
pub current_sha: String,
|
||||||
pub behind: u32,
|
pub behind: u32,
|
||||||
pub upstream_commits: Vec<VirtualBranchCommit>,
|
pub upstream_commits: Vec<RemoteCommit>,
|
||||||
pub recent_commits: Vec<VirtualBranchCommit>,
|
pub recent_commits: Vec<RemoteCommit>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Hash, Clone, PartialEq, Eq)]
|
#[derive(Debug, Serialize, Hash, Clone, PartialEq, Eq)]
|
||||||
@ -572,74 +602,91 @@ pub fn list_remote_branches(
|
|||||||
|
|
||||||
let mut branches: Vec<RemoteBranch> = Vec::new();
|
let mut branches: Vec<RemoteBranch> = Vec::new();
|
||||||
for branch in &top_branches {
|
for branch in &top_branches {
|
||||||
let branch_name = branch.refname().context("could not get branch name")?;
|
if let Some(branch_oid) = branch.target() {
|
||||||
match branch.target() {
|
let ahead = project_repository
|
||||||
Some(branch_oid) => {
|
.log(branch_oid, LogUntil::Commit(main_oid))
|
||||||
// get the branch ref
|
.context("failed to get ahead commits")?;
|
||||||
let branch_commit = repo
|
|
||||||
.find_commit(branch_oid)
|
|
||||||
.context("failed to find branch commit")?;
|
|
||||||
|
|
||||||
let count_behind = project_repository
|
if ahead.is_empty() {
|
||||||
.distance(main_oid, branch_oid)
|
continue;
|
||||||
.context("failed to get behind count")?;
|
|
||||||
|
|
||||||
let ahead = project_repository
|
|
||||||
.log(branch_oid, LogUntil::Commit(main_oid))
|
|
||||||
.context("failed to get ahead commits")?;
|
|
||||||
let count_ahead = ahead.len();
|
|
||||||
|
|
||||||
let upstream = branch
|
|
||||||
.upstream()
|
|
||||||
.ok()
|
|
||||||
.map(|upstream_branch| git::RemoteBranchName::try_from(&upstream_branch))
|
|
||||||
.transpose()?;
|
|
||||||
|
|
||||||
if count_ahead > 0 {
|
|
||||||
if let Ok(base_tree) = find_base_tree(repo, &branch_commit, &target_commit) {
|
|
||||||
// determine if this tree is mergeable
|
|
||||||
let branch_tree = branch_commit.tree()?;
|
|
||||||
let mergeable = !repo
|
|
||||||
.merge_trees(&base_tree, &branch_tree, &wd_tree)?
|
|
||||||
.has_conflicts();
|
|
||||||
|
|
||||||
branches.push(RemoteBranch {
|
|
||||||
sha: branch_oid.to_string(),
|
|
||||||
name: branch_name.to_string(),
|
|
||||||
upstream,
|
|
||||||
behind: count_behind,
|
|
||||||
mergeable,
|
|
||||||
commits: ahead
|
|
||||||
.into_iter()
|
|
||||||
.map(|commit| {
|
|
||||||
commit_to_vbranch_commit(
|
|
||||||
project_repository,
|
|
||||||
&default_target,
|
|
||||||
&commit,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<_>>>()?,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
// this is a detached head
|
|
||||||
branches.push(RemoteBranch {
|
|
||||||
sha: "".to_string(),
|
|
||||||
name: branch_name.to_string(),
|
|
||||||
behind: 0,
|
|
||||||
upstream: None,
|
|
||||||
mergeable: false,
|
|
||||||
commits: vec![],
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let branch_name = branch.refname().context("could not get branch name")?;
|
||||||
|
// get the branch ref
|
||||||
|
let branch_commit = repo
|
||||||
|
.find_commit(branch_oid)
|
||||||
|
.context("failed to find branch commit")?;
|
||||||
|
|
||||||
|
let count_behind = project_repository
|
||||||
|
.distance(main_oid, branch_oid)
|
||||||
|
.context("failed to get behind count")?;
|
||||||
|
|
||||||
|
let upstream = branch
|
||||||
|
.upstream()
|
||||||
|
.ok()
|
||||||
|
.map(|upstream_branch| git::RemoteBranchName::try_from(&upstream_branch))
|
||||||
|
.transpose()?;
|
||||||
|
|
||||||
|
let base_tree = find_base_tree(repo, &branch_commit, &target_commit)?;
|
||||||
|
// determine if this tree is mergeable
|
||||||
|
let branch_tree = branch_commit.tree()?;
|
||||||
|
let mergeable = !repo
|
||||||
|
.merge_trees(&base_tree, &branch_tree, &wd_tree)?
|
||||||
|
.has_conflicts();
|
||||||
|
|
||||||
|
branches.push(RemoteBranch {
|
||||||
|
sha: branch_oid.to_string(),
|
||||||
|
name: branch_name.to_string(),
|
||||||
|
upstream,
|
||||||
|
behind: count_behind,
|
||||||
|
mergeable,
|
||||||
|
commits: ahead
|
||||||
|
.into_iter()
|
||||||
|
.map(|commit| commit_to_remote_commit(repo, &commit))
|
||||||
|
.collect::<Result<Vec<_>>>()?,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(branches)
|
Ok(branches)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn commit_to_remote_commit(
|
||||||
|
repository: &git::Repository,
|
||||||
|
commit: &git::Commit,
|
||||||
|
) -> Result<RemoteCommit> {
|
||||||
|
Ok(RemoteCommit {
|
||||||
|
id: commit.id().to_string(),
|
||||||
|
description: commit.message().unwrap_or_default().to_string(),
|
||||||
|
created_at: commit.time().seconds().try_into().unwrap(),
|
||||||
|
author: commit.author().into(),
|
||||||
|
files: list_remote_commit_files(repository, commit)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list_remote_commit_files(
|
||||||
|
repository: &git::Repository,
|
||||||
|
commit: &git::Commit,
|
||||||
|
) -> Result<Vec<RemoteBranchFile>> {
|
||||||
|
if commit.parent_count() == 0 {
|
||||||
|
return Ok(vec![]);
|
||||||
|
}
|
||||||
|
let parent = commit.parent(0).context("failed to get parent commit")?;
|
||||||
|
let commit_tree = commit.tree().context("failed to get commit tree")?;
|
||||||
|
let parent_tree = parent.tree().context("failed to get parent tree")?;
|
||||||
|
let diff = diff::trees(repository, &parent_tree, &commit_tree)?;
|
||||||
|
|
||||||
|
let files = diff
|
||||||
|
.into_iter()
|
||||||
|
.map(|(file_path, hunks)| RemoteBranchFile {
|
||||||
|
path: file_path.clone(),
|
||||||
|
hunks: hunks.clone(),
|
||||||
|
binary: hunks.iter().any(|h| h.binary),
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
Ok(files)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_wd_tree(repo: &git::Repository) -> Result<git::Tree> {
|
pub fn get_wd_tree(repo: &git::Repository) -> Result<git::Tree> {
|
||||||
let mut index = repo.index()?;
|
let mut index = repo.index()?;
|
||||||
index.add_all(["*"], git2::IndexAddOption::DEFAULT, None)?;
|
index.add_all(["*"], git2::IndexAddOption::DEFAULT, None)?;
|
||||||
@ -819,7 +866,8 @@ fn calculate_non_commited_files(
|
|||||||
)
|
)
|
||||||
.context("failed to diff trees")?;
|
.context("failed to diff trees")?;
|
||||||
|
|
||||||
let non_commited_hunks_by_filepath = super::hunks_by_filepath(project_repository, &diff);
|
let non_commited_hunks_by_filepath =
|
||||||
|
super::virtual_hunks_by_filepath(&project_repository.git_repository, &diff);
|
||||||
|
|
||||||
let file_hunks = files
|
let file_hunks = files
|
||||||
.iter()
|
.iter()
|
||||||
@ -910,7 +958,7 @@ fn calculate_non_commited_files(
|
|||||||
Ok(vfiles)
|
Ok(vfiles)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_commit_files(
|
fn list_virtual_commit_files(
|
||||||
project_repository: &project_repository::Repository,
|
project_repository: &project_repository::Repository,
|
||||||
commit: &git::Commit,
|
commit: &git::Commit,
|
||||||
) -> Result<Vec<VirtualBranchFile>> {
|
) -> Result<Vec<VirtualBranchFile>> {
|
||||||
@ -925,8 +973,8 @@ fn list_commit_files(
|
|||||||
&parent_tree,
|
&parent_tree,
|
||||||
&commit_tree,
|
&commit_tree,
|
||||||
)?;
|
)?;
|
||||||
let hunks_by_filepath = hunks_by_filepath(project_repository, &diff);
|
let hunks_by_filepath = virtual_hunks_by_filepath(&project_repository.git_repository, &diff);
|
||||||
Ok(hunks_to_files(
|
Ok(virtual_hunks_to_virtual_files(
|
||||||
project_repository,
|
project_repository,
|
||||||
&hunks_by_filepath
|
&hunks_by_filepath
|
||||||
.values()
|
.values()
|
||||||
@ -952,11 +1000,8 @@ pub fn commit_to_vbranch_commit(
|
|||||||
None => true,
|
None => true,
|
||||||
};
|
};
|
||||||
|
|
||||||
let files = if is_remote {
|
let files =
|
||||||
vec![]
|
list_virtual_commit_files(repository, commit).context("failed to list commit files")?;
|
||||||
} else {
|
|
||||||
list_commit_files(repository, commit).context("failed to list commit files")?
|
|
||||||
};
|
|
||||||
|
|
||||||
let is_integrated = is_commit_integrated(repository, target, commit)?;
|
let is_integrated = is_commit_integrated(repository, target, commit)?;
|
||||||
|
|
||||||
@ -1242,8 +1287,8 @@ fn diff_hash(diff: &str) -> String {
|
|||||||
format!("{:x}", md5::compute(addition))
|
format!("{:x}", md5::compute(addition))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hunks_by_filepath(
|
pub fn virtual_hunks_by_filepath(
|
||||||
project_repository: &project_repository::Repository,
|
repository: &git::Repository,
|
||||||
diff: &HashMap<path::PathBuf, Vec<diff::Hunk>>,
|
diff: &HashMap<path::PathBuf, Vec<diff::Hunk>>,
|
||||||
) -> HashMap<path::PathBuf, Vec<VirtualBranchHunk>> {
|
) -> HashMap<path::PathBuf, Vec<VirtualBranchHunk>> {
|
||||||
let mut mtimes: HashMap<path::PathBuf, u128> = HashMap::new();
|
let mut mtimes: HashMap<path::PathBuf, u128> = HashMap::new();
|
||||||
@ -1253,7 +1298,7 @@ pub fn hunks_by_filepath(
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|hunk| VirtualBranchHunk {
|
.map(|hunk| VirtualBranchHunk {
|
||||||
id: format!("{}-{}", hunk.new_start, hunk.new_start + hunk.new_lines),
|
id: format!("{}-{}", hunk.new_start, hunk.new_start + hunk.new_lines),
|
||||||
modified_at: get_mtime(&mut mtimes, &project_repository.path().join(file_path)),
|
modified_at: get_mtime(&mut mtimes, &repository.path().join(file_path)),
|
||||||
file_path: file_path.clone(),
|
file_path: file_path.clone(),
|
||||||
diff: hunk.diff.clone(),
|
diff: hunk.diff.clone(),
|
||||||
start: hunk.new_start,
|
start: hunk.new_start,
|
||||||
@ -1369,7 +1414,8 @@ fn get_non_applied_status(
|
|||||||
.collect::<HashMap<_, _>>()
|
.collect::<HashMap<_, _>>()
|
||||||
})
|
})
|
||||||
.collect::<HashMap<_, _>>();
|
.collect::<HashMap<_, _>>();
|
||||||
let hunks_by_filepath = hunks_by_filepath(project_repository, &diff);
|
let hunks_by_filepath =
|
||||||
|
virtual_hunks_by_filepath(&project_repository.git_repository, &diff);
|
||||||
let hunks_by_filepath = hunks_by_filepath
|
let hunks_by_filepath = hunks_by_filepath
|
||||||
.values()
|
.values()
|
||||||
.flatten()
|
.flatten()
|
||||||
@ -1409,7 +1455,8 @@ fn get_applied_status(
|
|||||||
)
|
)
|
||||||
.context("failed to diff")?;
|
.context("failed to diff")?;
|
||||||
|
|
||||||
let mut hunks_by_filepath = hunks_by_filepath(project_repository, &diff);
|
let mut hunks_by_filepath =
|
||||||
|
virtual_hunks_by_filepath(&project_repository.git_repository, &diff);
|
||||||
|
|
||||||
// sort by order, so that the default branch is first (left in the ui)
|
// sort by order, so that the default branch is first (left in the ui)
|
||||||
virtual_branches.sort_by(|a, b| a.order.cmp(&b.order));
|
virtual_branches.sort_by(|a, b| a.order.cmp(&b.order));
|
||||||
@ -1584,7 +1631,7 @@ fn get_applied_status(
|
|||||||
Ok(files_by_branch)
|
Ok(files_by_branch)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hunks_to_files(
|
fn virtual_hunks_to_virtual_files(
|
||||||
project_repository: &project_repository::Repository,
|
project_repository: &project_repository::Repository,
|
||||||
hunks: &[VirtualBranchHunk],
|
hunks: &[VirtualBranchHunk],
|
||||||
) -> Vec<VirtualBranchFile> {
|
) -> Vec<VirtualBranchFile> {
|
||||||
@ -1619,7 +1666,7 @@ fn group_virtual_hunks(
|
|||||||
hunks_by_branch
|
hunks_by_branch
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(branch, hunks)| {
|
.map(|(branch, hunks)| {
|
||||||
let mut files = hunks_to_files(project_repository, hunks);
|
let mut files = virtual_hunks_to_virtual_files(project_repository, hunks);
|
||||||
files.sort_by(|a, b| {
|
files.sort_by(|a, b| {
|
||||||
branch
|
branch
|
||||||
.ownership
|
.ownership
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { Branch, BranchData, BaseBranch, WritableReloadable } from './types';
|
import type { Branch, RemoteBranch, BaseBranch, WritableReloadable } from './types';
|
||||||
import * as toasts from '$lib/toasts';
|
import * as toasts from '$lib/toasts';
|
||||||
import { invoke } from '$lib/ipc';
|
import { invoke } from '$lib/ipc';
|
||||||
|
|
||||||
@ -6,7 +6,7 @@ export class BranchController {
|
|||||||
constructor(
|
constructor(
|
||||||
readonly projectId: string,
|
readonly projectId: string,
|
||||||
readonly virtualBranchStore: WritableReloadable<Branch[] | undefined>,
|
readonly virtualBranchStore: WritableReloadable<Branch[] | undefined>,
|
||||||
readonly remoteBranchStore: WritableReloadable<BranchData[] | undefined>,
|
readonly remoteBranchStore: WritableReloadable<RemoteBranch[] | undefined>,
|
||||||
readonly targetBranchStore: WritableReloadable<BaseBranch | undefined>
|
readonly targetBranchStore: WritableReloadable<BaseBranch | undefined>
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { asyncWritable, type Readable } from '@square/svelte-store';
|
import { asyncWritable, type Readable } from '@square/svelte-store';
|
||||||
import { BaseBranch, Branch, BranchData, type WritableReloadable } from './types';
|
import { BaseBranch, Branch, RemoteBranch, type WritableReloadable } from './types';
|
||||||
import { plainToInstance } from 'class-transformer';
|
import { plainToInstance } from 'class-transformer';
|
||||||
import { invoke } from '$lib/ipc';
|
import { invoke } from '$lib/ipc';
|
||||||
import { isDelete, isInsert, type Delta } from '$lib/api/ipc/deltas';
|
import { isDelete, isInsert, type Delta } from '$lib/api/ipc/deltas';
|
||||||
@ -36,13 +36,13 @@ export function getRemoteBranchStore(projectId: string, asyncStores: Readable<an
|
|||||||
async () => getRemoteBranchesData({ projectId }),
|
async () => getRemoteBranchesData({ projectId }),
|
||||||
async (newRemotes) => newRemotes,
|
async (newRemotes) => newRemotes,
|
||||||
{ reloadable: true, trackState: true }
|
{ reloadable: true, trackState: true }
|
||||||
) as WritableReloadable<BranchData[] | undefined>;
|
) as WritableReloadable<RemoteBranch[] | undefined>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getBaseBranchStore(projectId: string, asyncStores: Readable<any>[]) {
|
export function getBaseBranchStore(projectId: string, asyncStores: Readable<any>[]) {
|
||||||
return asyncWritable(
|
return asyncWritable(
|
||||||
asyncStores,
|
asyncStores,
|
||||||
async () => getBaseBranchData({ projectId }),
|
async () => getBaseBranch({ projectId }),
|
||||||
async (newBaseBranch) => newBaseBranch,
|
async (newBaseBranch) => newBaseBranch,
|
||||||
{ reloadable: true, trackState: true }
|
{ reloadable: true, trackState: true }
|
||||||
) as WritableReloadable<BaseBranch | undefined>;
|
) as WritableReloadable<BaseBranch | undefined>;
|
||||||
@ -56,11 +56,13 @@ export async function listVirtualBranches(params: { projectId: string }): Promis
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getRemoteBranchesData(params: { projectId: string }): Promise<BranchData[]> {
|
export async function getRemoteBranchesData(params: {
|
||||||
return plainToInstance(BranchData, await invoke<any[]>('git_remote_branches_data', params));
|
projectId: string;
|
||||||
|
}): Promise<RemoteBranch[]> {
|
||||||
|
return plainToInstance(RemoteBranch, await invoke<any[]>('git_remote_branches_data', params));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getBaseBranchData(params: { projectId: string }): Promise<BaseBranch> {
|
export async function getBaseBranch(params: { projectId: string }): Promise<BaseBranch> {
|
||||||
const baseBranch = plainToInstance(BaseBranch, await invoke<any>('get_base_branch_data', params));
|
const baseBranch = plainToInstance(BaseBranch, await invoke<any>('get_base_branch_data', params));
|
||||||
if (baseBranch) {
|
if (baseBranch) {
|
||||||
// The rust code performs a fetch when get_base_branch_data is invoked
|
// The rust code performs a fetch when get_base_branch_data is invoked
|
||||||
|
@ -57,6 +57,27 @@ export class Commit {
|
|||||||
files!: File[];
|
files!: File[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class RemoteCommit {
|
||||||
|
id!: string;
|
||||||
|
author!: Author;
|
||||||
|
description!: string;
|
||||||
|
@Transform((obj) => new Date(obj.value))
|
||||||
|
createdAt!: Date;
|
||||||
|
@Type(() => RemoteFile)
|
||||||
|
files!: RemoteFile[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RemoteHunk {
|
||||||
|
diff!: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RemoteFile {
|
||||||
|
path!: string;
|
||||||
|
@Type(() => RemoteHunk)
|
||||||
|
hunks!: RemoteHunk[];
|
||||||
|
binary!: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export class Author {
|
export class Author {
|
||||||
email!: string;
|
email!: string;
|
||||||
name!: string;
|
name!: string;
|
||||||
@ -65,14 +86,14 @@ export class Author {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: For consistency change Ts suffix to At, and return milliseconds from back end
|
// TODO: For consistency change Ts suffix to At, and return milliseconds from back end
|
||||||
export class BranchData {
|
export class RemoteBranch {
|
||||||
sha!: string;
|
sha!: string;
|
||||||
name!: string;
|
name!: string;
|
||||||
behind!: number;
|
behind!: number;
|
||||||
upstream?: string;
|
upstream?: string;
|
||||||
mergeable!: boolean;
|
mergeable!: boolean;
|
||||||
@Type(() => Commit)
|
@Type(() => RemoteCommit)
|
||||||
commits!: Commit[];
|
commits!: RemoteCommit[];
|
||||||
|
|
||||||
ahead(): number {
|
ahead(): number {
|
||||||
return this.commits.length;
|
return this.commits.length;
|
||||||
@ -98,10 +119,10 @@ export class BaseBranch {
|
|||||||
baseSha!: string;
|
baseSha!: string;
|
||||||
currentSha!: string;
|
currentSha!: string;
|
||||||
behind!: number;
|
behind!: number;
|
||||||
@Type(() => Commit)
|
@Type(() => RemoteCommit)
|
||||||
upstreamCommits!: Commit[];
|
upstreamCommits!: RemoteCommit[];
|
||||||
@Type(() => Commit)
|
@Type(() => RemoteCommit)
|
||||||
recentCommits!: Commit[];
|
recentCommits!: RemoteCommit[];
|
||||||
fetchedAt!: Date;
|
fetchedAt!: Date;
|
||||||
|
|
||||||
get repoBaseUrl(): string {
|
get repoBaseUrl(): string {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Commit } from '$lib/vbranches/types';
|
import type { Commit, RemoteCommit } from '$lib/vbranches/types';
|
||||||
import TimeAgo from '$lib/components/TimeAgo/TimeAgo.svelte';
|
import TimeAgo from '$lib/components/TimeAgo/TimeAgo.svelte';
|
||||||
import Tooltip from '$lib/components/Tooltip/Tooltip.svelte';
|
import Tooltip from '$lib/components/Tooltip/Tooltip.svelte';
|
||||||
import { getCommitDiff } from '$lib/api/git/diffs';
|
import { getCommitDiff } from '$lib/api/git/diffs';
|
||||||
@ -10,7 +10,7 @@
|
|||||||
import { IconExpandUpDown, IconExpandUp, IconExpandDown } from '$lib/icons';
|
import { IconExpandUpDown, IconExpandUp, IconExpandDown } from '$lib/icons';
|
||||||
import { Button, Modal } from '$lib/components';
|
import { Button, Modal } from '$lib/components';
|
||||||
|
|
||||||
export let commit: Commit;
|
export let commit: Commit | RemoteCommit;
|
||||||
export let isIntegrated = false;
|
export let isIntegrated = false;
|
||||||
export let url: string | undefined = undefined;
|
export let url: string | undefined = undefined;
|
||||||
export let projectId: string;
|
export let projectId: string;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
import IconChevronLeft from '$lib/icons/IconChevronLeft.svelte';
|
import IconChevronLeft from '$lib/icons/IconChevronLeft.svelte';
|
||||||
import { SETTINGS_CONTEXT, type SettingsStore } from '$lib/userSettings';
|
import { SETTINGS_CONTEXT, type SettingsStore } from '$lib/userSettings';
|
||||||
import type { BranchController } from '$lib/vbranches/branchController';
|
import type { BranchController } from '$lib/vbranches/branchController';
|
||||||
import { BaseBranch, Branch, BranchData } from '$lib/vbranches/types';
|
import { BaseBranch, Branch, RemoteBranch } from '$lib/vbranches/types';
|
||||||
import { getContext } from 'svelte';
|
import { getContext } from 'svelte';
|
||||||
import type { Readable } from '@square/svelte-store';
|
import type { Readable } from '@square/svelte-store';
|
||||||
import BaseBranchPeek from './BaseBranchPeek.svelte';
|
import BaseBranchPeek from './BaseBranchPeek.svelte';
|
||||||
@ -11,7 +11,7 @@
|
|||||||
import Lane from './BranchLane.svelte';
|
import Lane from './BranchLane.svelte';
|
||||||
import type { getCloudApiClient } from '$lib/api/cloud/api';
|
import type { getCloudApiClient } from '$lib/api/cloud/api';
|
||||||
|
|
||||||
export let item: Readable<BranchData | Branch | BaseBranch | undefined> | undefined;
|
export let item: Readable<RemoteBranch | Branch | BaseBranch | undefined> | undefined;
|
||||||
export let cloud: ReturnType<typeof getCloudApiClient>;
|
export let cloud: ReturnType<typeof getCloudApiClient>;
|
||||||
export let base: BaseBranch | undefined;
|
export let base: BaseBranch | undefined;
|
||||||
export let branchController: BranchController;
|
export let branchController: BranchController;
|
||||||
@ -68,7 +68,7 @@
|
|||||||
class="h-full max-h-full w-full flex-grow overflow-y-hidden"
|
class="h-full max-h-full w-full flex-grow overflow-y-hidden"
|
||||||
style:width={`${$userSettings.peekTrayWidth}px`}
|
style:width={`${$userSettings.peekTrayWidth}px`}
|
||||||
>
|
>
|
||||||
{#if $item instanceof BranchData}
|
{#if $item instanceof RemoteBranch}
|
||||||
<RemoteBranchPeek {branchController} {base} branch={$item} />
|
<RemoteBranchPeek {branchController} {base} branch={$item} />
|
||||||
{:else if $item instanceof Branch}
|
{:else if $item instanceof Branch}
|
||||||
<Lane
|
<Lane
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Button from '$lib/components/Button/Button.svelte';
|
import Button from '$lib/components/Button/Button.svelte';
|
||||||
import type { BranchController } from '$lib/vbranches/branchController';
|
import type { BranchController } from '$lib/vbranches/branchController';
|
||||||
import type { BaseBranch, BranchData } from '$lib/vbranches/types';
|
import type { BaseBranch, RemoteBranch } from '$lib/vbranches/types';
|
||||||
import CommitCard from './CommitCard.svelte';
|
import CommitCard from './CommitCard.svelte';
|
||||||
|
|
||||||
export let branch: BranchData | undefined;
|
export let branch: RemoteBranch | undefined;
|
||||||
export let base: BaseBranch | undefined;
|
export let base: BaseBranch | undefined;
|
||||||
export let branchController: BranchController;
|
export let branchController: BranchController;
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Link } from '$lib/components';
|
import { Link } from '$lib/components';
|
||||||
import { Branch, BaseBranch, BranchData } from '$lib/vbranches/types';
|
import { Branch, BaseBranch, RemoteBranch } from '$lib/vbranches/types';
|
||||||
import { IconBranch, IconGitBranch, IconRemote } from '$lib/icons';
|
import { IconBranch, IconGitBranch, IconRemote } from '$lib/icons';
|
||||||
import { IconTriangleDown, IconTriangleUp } from '$lib/icons';
|
import { IconTriangleDown, IconTriangleUp } from '$lib/icons';
|
||||||
import { accordion } from './accordion';
|
import { accordion } from './accordion';
|
||||||
@ -25,7 +25,7 @@
|
|||||||
import { computedAddedRemoved } from '$lib/vbranches/fileStatus';
|
import { computedAddedRemoved } from '$lib/vbranches/fileStatus';
|
||||||
|
|
||||||
export let vbranchStore: Loadable<Branch[] | undefined>;
|
export let vbranchStore: Loadable<Branch[] | undefined>;
|
||||||
export let remoteBranchStore: Loadable<BranchData[] | undefined>;
|
export let remoteBranchStore: Loadable<RemoteBranch[] | undefined>;
|
||||||
export let baseBranchStore: Readable<BaseBranch | undefined>;
|
export let baseBranchStore: Readable<BaseBranch | undefined>;
|
||||||
export let branchController: BranchController;
|
export let branchController: BranchController;
|
||||||
export let peekTransitionsDisabled = false;
|
export let peekTransitionsDisabled = false;
|
||||||
@ -50,11 +50,11 @@
|
|||||||
let rbSection: HTMLElement;
|
let rbSection: HTMLElement;
|
||||||
let baseContents: HTMLElement;
|
let baseContents: HTMLElement;
|
||||||
|
|
||||||
let selectedItem: Readable<Branch | BranchData | BaseBranch | undefined> | undefined;
|
let selectedItem: Readable<Branch | RemoteBranch | BaseBranch | undefined> | undefined;
|
||||||
let overlayOffsetTop = 0;
|
let overlayOffsetTop = 0;
|
||||||
let fetching = false;
|
let fetching = false;
|
||||||
|
|
||||||
function select(detail: Branch | BranchData | BaseBranch | undefined, i: number): void {
|
function select(detail: Branch | RemoteBranch | BaseBranch | undefined, i: number): void {
|
||||||
if (peekTrayExpanded && selectedItem && detail == get(selectedItem)) {
|
if (peekTrayExpanded && selectedItem && detail == get(selectedItem)) {
|
||||||
peekTrayExpanded = false;
|
peekTrayExpanded = false;
|
||||||
return;
|
return;
|
||||||
@ -65,7 +65,7 @@
|
|||||||
);
|
);
|
||||||
const element = vbContents.children[i] as HTMLDivElement;
|
const element = vbContents.children[i] as HTMLDivElement;
|
||||||
overlayOffsetTop = element.offsetTop + vbViewport.offsetTop - vbViewport.scrollTop;
|
overlayOffsetTop = element.offsetTop + vbViewport.offsetTop - vbViewport.scrollTop;
|
||||||
} else if (detail instanceof BranchData) {
|
} else if (detail instanceof RemoteBranch) {
|
||||||
selectedItem = derived(remoteBranchStore, (branches) =>
|
selectedItem = derived(remoteBranchStore, (branches) =>
|
||||||
branches?.find((remoteBranch) => remoteBranch.sha == detail.sha)
|
branches?.find((remoteBranch) => remoteBranch.sha == detail.sha)
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user