mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-11-25 07:45:41 +03:00
Merge pull request #772 from gitbutlerapp/return-commits-with-base-branch
Return commit list with base branch data
This commit is contained in:
commit
5732a1804f
@ -36,7 +36,7 @@ impl super::RunCommand for Setup {
|
||||
Some(index) => {
|
||||
println!("Setting target to: {}", items[index].red());
|
||||
app.gb_repository()
|
||||
.set_target_branch(&app.project_repository(), &items[index])
|
||||
.set_base_branch(&app.project_repository(), &items[index])
|
||||
.context("failed to set target branch")?;
|
||||
}
|
||||
None => println!("User did not select anything"),
|
||||
|
@ -261,22 +261,22 @@ impl App {
|
||||
.list_by_project_id_session_id(project_id, session_id, paths)
|
||||
}
|
||||
|
||||
pub fn get_target_data(
|
||||
pub fn get_base_branch_data(
|
||||
&self,
|
||||
project_id: &str,
|
||||
) -> Result<Option<virtual_branches::target::Target>> {
|
||||
) -> Result<Option<virtual_branches::BaseBranch>> {
|
||||
let gb_repository = self.gb_repository(project_id)?;
|
||||
let project = self.gb_project(project_id)?;
|
||||
let project_repository = project_repository::Repository::open(&project)
|
||||
.context("failed to open project repository")?;
|
||||
virtual_branches::get_target_data(&gb_repository, &project_repository)
|
||||
virtual_branches::get_base_branch_data(&gb_repository, &project_repository)
|
||||
}
|
||||
|
||||
pub async fn set_target_branch(
|
||||
pub async fn set_base_branch(
|
||||
&self,
|
||||
project_id: &str,
|
||||
target_branch: &str,
|
||||
) -> Result<Option<virtual_branches::target::Target>> {
|
||||
) -> Result<Option<virtual_branches::BaseBranch>> {
|
||||
let gb_repository = self.gb_repository(project_id)?;
|
||||
let project = self.gb_project(project_id)?;
|
||||
let project_repository = project_repository::Repository::open(&project)
|
||||
@ -288,11 +288,11 @@ impl App {
|
||||
.or_insert_with(|| Semaphore::new(1));
|
||||
let _permit = semaphore.acquire().await?;
|
||||
|
||||
let target = gb_repository.set_target_branch(&project_repository, target_branch)?;
|
||||
let target = gb_repository.set_base_branch(&project_repository, target_branch)?;
|
||||
Ok(Some(target))
|
||||
}
|
||||
|
||||
pub async fn update_branch_target(&self, project_id: &str) -> Result<()> {
|
||||
pub async fn update_base_branch(&self, project_id: &str) -> Result<()> {
|
||||
let gb_repository = self.gb_repository(project_id)?;
|
||||
let project = self.gb_project(project_id)?;
|
||||
let project_repository = project_repository::Repository::open(&project)
|
||||
@ -304,7 +304,7 @@ impl App {
|
||||
.or_insert_with(|| Semaphore::new(1));
|
||||
let _permit = semaphore.acquire().await?;
|
||||
|
||||
virtual_branches::update_branch_target(&gb_repository, &project_repository)?;
|
||||
virtual_branches::update_base_branch(&gb_repository, &project_repository)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,10 @@ use filetime::FileTime;
|
||||
use sha2::{Digest, Sha256};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{fs, projects, users, virtual_branches};
|
||||
use crate::{
|
||||
fs, projects, users,
|
||||
virtual_branches::{self},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
project_repository,
|
||||
@ -549,11 +552,11 @@ impl Repository {
|
||||
self.session_path().join("wd")
|
||||
}
|
||||
|
||||
pub fn set_target_branch(
|
||||
pub fn set_base_branch(
|
||||
&self,
|
||||
project_repository: &project_repository::Repository,
|
||||
target_branch: &str,
|
||||
) -> Result<virtual_branches::target::Target> {
|
||||
) -> Result<virtual_branches::BaseBranch> {
|
||||
let repo = &project_repository.git_repository;
|
||||
|
||||
// lookup a branch by name
|
||||
@ -604,7 +607,6 @@ impl Repository {
|
||||
remote_name: remote.name().unwrap().to_string(),
|
||||
remote_url: remote_url.to_string(),
|
||||
sha: commit_oid,
|
||||
behind: 0,
|
||||
};
|
||||
|
||||
let target_writer = virtual_branches::target::Writer::new(self);
|
||||
@ -635,8 +637,8 @@ impl Repository {
|
||||
}
|
||||
|
||||
virtual_branches::update_gitbutler_integration(self, project_repository)?;
|
||||
|
||||
Ok(target)
|
||||
let base = virtual_branches::target_to_base_branch(project_repository, &target)?;
|
||||
Ok(base)
|
||||
}
|
||||
|
||||
pub fn git_signatures(&self) -> Result<(git2::Signature<'_>, git2::Signature<'_>)> {
|
||||
|
@ -681,25 +681,25 @@ async fn fetch_from_target(handle: tauri::AppHandle, project_id: &str) -> Result
|
||||
|
||||
#[timed(duration(printer = "debug!"))]
|
||||
#[tauri::command(async)]
|
||||
async fn get_target_data(
|
||||
async fn get_base_branch_data(
|
||||
handle: tauri::AppHandle,
|
||||
project_id: &str,
|
||||
) -> Result<Option<virtual_branches::target::Target>, Error> {
|
||||
) -> Result<Option<virtual_branches::BaseBranch>, Error> {
|
||||
let app = handle.state::<app::App>();
|
||||
let target = app.get_target_data(project_id)?;
|
||||
let target = app.get_base_branch_data(project_id)?;
|
||||
Ok(target)
|
||||
}
|
||||
|
||||
#[timed(duration(printer = "debug!"))]
|
||||
#[tauri::command(async)]
|
||||
async fn set_target_branch(
|
||||
async fn set_base_branch(
|
||||
handle: tauri::AppHandle,
|
||||
project_id: &str,
|
||||
branch: &str,
|
||||
) -> Result<virtual_branches::target::Target, Error> {
|
||||
) -> Result<virtual_branches::BaseBranch, Error> {
|
||||
let app = handle.state::<app::App>();
|
||||
let target = app
|
||||
.set_target_branch(project_id, branch)
|
||||
.set_base_branch(project_id, branch)
|
||||
.await?
|
||||
.context("failed to get target data")?;
|
||||
Ok(target)
|
||||
@ -707,9 +707,9 @@ async fn set_target_branch(
|
||||
|
||||
#[timed(duration(printer = "debug!"))]
|
||||
#[tauri::command(async)]
|
||||
async fn update_branch_target(handle: tauri::AppHandle, project_id: &str) -> Result<(), Error> {
|
||||
async fn update_base_branch(handle: tauri::AppHandle, project_id: &str) -> Result<(), Error> {
|
||||
let app = handle.state::<app::App>();
|
||||
app.update_branch_target(project_id).await?;
|
||||
app.update_base_branch(project_id).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -944,9 +944,9 @@ fn main() {
|
||||
list_virtual_branches,
|
||||
create_virtual_branch,
|
||||
commit_virtual_branch,
|
||||
get_target_data,
|
||||
set_target_branch,
|
||||
update_branch_target,
|
||||
get_base_branch_data,
|
||||
set_base_branch,
|
||||
update_base_branch,
|
||||
update_virtual_branch,
|
||||
delete_virtual_branch,
|
||||
apply_branch,
|
||||
|
@ -114,7 +114,6 @@ mod tests {
|
||||
unsafe { TEST_TARGET_INDEX }
|
||||
))
|
||||
.unwrap(),
|
||||
behind: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,8 +63,7 @@ pub struct VirtualBranchCommit {
|
||||
pub id: String,
|
||||
pub description: String,
|
||||
pub created_at: u128,
|
||||
pub author_name: String,
|
||||
pub author_email: String,
|
||||
pub author: Author,
|
||||
pub is_remote: bool,
|
||||
}
|
||||
|
||||
@ -132,7 +131,19 @@ pub struct RemoteBranch {
|
||||
pub merge_conflicts: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Hash, PartialEq, Eq)]
|
||||
#[derive(Debug, Serialize, PartialEq, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BaseBranch {
|
||||
pub branch_name: String,
|
||||
pub remote_name: String,
|
||||
pub remote_url: String,
|
||||
pub base_sha: String,
|
||||
pub current_sha: String,
|
||||
pub behind: u32,
|
||||
pub upstream_commits: Vec<VirtualBranchCommit>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Hash, Clone, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Author {
|
||||
pub name: String,
|
||||
@ -825,22 +836,7 @@ pub fn list_virtual_branches(
|
||||
.log(branch.head, default_target.sha)
|
||||
.context(format!("failed to get log for branch {}", branch.name))?
|
||||
{
|
||||
let timestamp = commit.time().seconds() as u128;
|
||||
let signature = commit.author();
|
||||
let name = signature.name().unwrap().to_string();
|
||||
let email = signature.email().unwrap().to_string();
|
||||
let message = commit.message().unwrap().to_string();
|
||||
let sha = commit.id().to_string();
|
||||
let is_remote = upstream_commits.contains_key(&commit.id());
|
||||
|
||||
let commit = VirtualBranchCommit {
|
||||
id: sha,
|
||||
created_at: timestamp * 1000,
|
||||
author_name: name,
|
||||
author_email: email,
|
||||
description: message,
|
||||
is_remote,
|
||||
};
|
||||
let commit = commit_to_vbranch_commit(&commit, Some(&upstream_commits))?;
|
||||
commits.push(commit);
|
||||
}
|
||||
|
||||
@ -894,6 +890,31 @@ pub fn list_virtual_branches(
|
||||
Ok(branches)
|
||||
}
|
||||
|
||||
pub fn commit_to_vbranch_commit(
|
||||
commit: &git2::Commit,
|
||||
upstream_commits: Option<&HashMap<git2::Oid, bool>>,
|
||||
) -> Result<VirtualBranchCommit> {
|
||||
let timestamp = commit.time().seconds() as u128;
|
||||
let signature = commit.author();
|
||||
let message = commit.message().unwrap().to_string();
|
||||
let sha = commit.id().to_string();
|
||||
|
||||
let is_remote = match upstream_commits {
|
||||
Some(commits) => commits.contains_key(&commit.id()),
|
||||
None => true,
|
||||
};
|
||||
|
||||
let commit = VirtualBranchCommit {
|
||||
id: sha,
|
||||
created_at: timestamp * 1000,
|
||||
author: Author::from(signature),
|
||||
description: message,
|
||||
is_remote,
|
||||
};
|
||||
|
||||
Ok(commit)
|
||||
}
|
||||
|
||||
pub fn create_virtual_branch_from_branch(
|
||||
gb_repository: &gb_repository::Repository,
|
||||
project_repository: &project_repository::Repository,
|
||||
@ -1499,7 +1520,7 @@ pub fn get_status_by_branch(
|
||||
// determine if what the target branch is now pointing to is mergeable with our current working directory
|
||||
// merge the target branch into our current working directory
|
||||
// update the target sha
|
||||
pub fn update_branch_target(
|
||||
pub fn update_base_branch(
|
||||
gb_repository: &gb_repository::Repository,
|
||||
project_repository: &project_repository::Repository,
|
||||
) -> Result<()> {
|
||||
@ -2154,10 +2175,10 @@ pub fn push(
|
||||
.map_err(Error::Other)
|
||||
}
|
||||
|
||||
pub fn get_target_data(
|
||||
pub fn get_base_branch_data(
|
||||
gb_repository: &gb_repository::Repository,
|
||||
project_repository: &project_repository::Repository,
|
||||
) -> Result<Option<target::Target>> {
|
||||
) -> Result<Option<BaseBranch>> {
|
||||
let current_session = gb_repository
|
||||
.get_or_create_current_session()
|
||||
.context("failed to get or create current session")?;
|
||||
@ -2165,15 +2186,39 @@ pub fn get_target_data(
|
||||
.context("failed to open current session")?;
|
||||
match get_default_target(¤t_session_reader)? {
|
||||
None => Ok(None),
|
||||
Some(mut target) => {
|
||||
let repo = &project_repository.git_repository;
|
||||
let branch = repo.find_branch(&target.branch_name, git2::BranchType::Remote)?;
|
||||
let commit = branch.get().peel_to_commit()?;
|
||||
let oid = commit.id();
|
||||
target.behind = project_repository
|
||||
.distance(oid, target.sha)
|
||||
.context(format!("failed to get behind for {}", target.branch_name))?;
|
||||
Ok(Some(target))
|
||||
Some(target) => {
|
||||
let base = target_to_base_branch(&project_repository, &target)?;
|
||||
Ok(Some(base))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn target_to_base_branch(
|
||||
project_repository: &project_repository::Repository,
|
||||
target: &target::Target,
|
||||
) -> Result<BaseBranch> {
|
||||
let repo = &project_repository.git_repository;
|
||||
let branch = repo.find_branch(&target.branch_name, git2::BranchType::Remote)?;
|
||||
let commit = branch.get().peel_to_commit()?;
|
||||
let oid = commit.id();
|
||||
|
||||
// gather a list of commits between oid and target.sha
|
||||
let mut upstream_commits = vec![];
|
||||
for commit in project_repository
|
||||
.log(oid, target.sha)
|
||||
.context(format!("failed to get log for branch {:?}", branch.name()?))?
|
||||
{
|
||||
let commit = commit_to_vbranch_commit(&commit, None)?;
|
||||
upstream_commits.push(commit);
|
||||
}
|
||||
let base = BaseBranch {
|
||||
branch_name: target.branch_name.clone(),
|
||||
remote_name: target.remote_name.clone(),
|
||||
remote_url: target.remote_url.clone(),
|
||||
base_sha: target.sha.to_string(),
|
||||
current_sha: oid.to_string(),
|
||||
behind: upstream_commits.len() as u32,
|
||||
upstream_commits,
|
||||
};
|
||||
Ok(base)
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ pub struct Target {
|
||||
pub remote_name: String,
|
||||
pub remote_url: String,
|
||||
pub sha: git2::Oid,
|
||||
pub behind: u32,
|
||||
}
|
||||
|
||||
impl Serialize for Target {
|
||||
@ -25,7 +24,6 @@ impl Serialize for Target {
|
||||
state.serialize_field("branchName", &self.branch_name)?;
|
||||
state.serialize_field("remoteName", &self.remote_name)?;
|
||||
state.serialize_field("remoteUrl", &self.remote_url)?;
|
||||
state.serialize_field("behind", &self.behind)?;
|
||||
state.serialize_field("sha", &self.sha.to_string())?;
|
||||
state.end()
|
||||
}
|
||||
@ -97,7 +95,6 @@ impl TryFrom<&dyn crate::reader::Reader> for Target {
|
||||
remote_name,
|
||||
remote_url,
|
||||
sha,
|
||||
behind: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -189,7 +189,6 @@ mod tests {
|
||||
branch_name: "branch".to_string(),
|
||||
remote_url: "remote url".to_string(),
|
||||
sha: git2::Oid::from_str("fedcba9876543210fedcba9876543210fedcba98").unwrap(),
|
||||
behind: 0,
|
||||
};
|
||||
|
||||
let default_target = Target {
|
||||
@ -197,7 +196,6 @@ mod tests {
|
||||
branch_name: "default branch".to_string(),
|
||||
remote_url: "default remote url".to_string(),
|
||||
sha: git2::Oid::from_str("0123456789abcdef0123456789abcdef01234567").unwrap(),
|
||||
behind: 0,
|
||||
};
|
||||
|
||||
let branch_writer = branch::Writer::new(&gb_repo);
|
||||
|
@ -167,7 +167,6 @@ mod tests {
|
||||
remote_name: "remote name".to_string(),
|
||||
remote_url: "remote url".to_string(),
|
||||
sha: git2::Oid::from_str("0123456789abcdef0123456789abcdef01234567").unwrap(),
|
||||
behind: 0,
|
||||
};
|
||||
|
||||
let branch_writer = branch::Writer::new(&gb_repo);
|
||||
@ -260,7 +259,6 @@ mod tests {
|
||||
branch_name: "branch name".to_string(),
|
||||
remote_url: "remote url".to_string(),
|
||||
sha: git2::Oid::from_str("0123456789abcdef0123456789abcdef01234567").unwrap(),
|
||||
behind: 0,
|
||||
};
|
||||
|
||||
let branch_writer = branch::Writer::new(&gb_repo);
|
||||
@ -273,7 +271,6 @@ mod tests {
|
||||
branch_name: "updated branch name".to_string(),
|
||||
remote_url: "updated remote url".to_string(),
|
||||
sha: git2::Oid::from_str("fedcba9876543210fedcba9876543210fedcba98").unwrap(),
|
||||
behind: 0,
|
||||
};
|
||||
|
||||
target_writer.write(&branch.id, &updated_target)?;
|
||||
|
@ -79,7 +79,6 @@ fn test_commit_on_branch_then_change_file_then_get_status() -> Result<()> {
|
||||
branch_name: "master".to_string(),
|
||||
remote_url: "origin".to_string(),
|
||||
sha: repository.head().unwrap().target().unwrap(),
|
||||
behind: 0,
|
||||
})?;
|
||||
update_gitbutler_integration(&gb_repo, &project_repository)?;
|
||||
|
||||
@ -163,7 +162,6 @@ fn test_track_binary_files() -> Result<()> {
|
||||
branch_name: "master".to_string(),
|
||||
remote_url: "origin".to_string(),
|
||||
sha: repository.head().unwrap().target().unwrap(),
|
||||
behind: 0,
|
||||
})?;
|
||||
update_gitbutler_integration(&gb_repo, &project_repository)?;
|
||||
|
||||
@ -260,7 +258,6 @@ fn test_create_branch_with_ownership() -> Result<()> {
|
||||
branch_name: "master".to_string(),
|
||||
remote_url: "origin".to_string(),
|
||||
sha: repository.head().unwrap().target().unwrap(),
|
||||
behind: 0,
|
||||
})?;
|
||||
update_gitbutler_integration(&gb_repo, &project_repository)?;
|
||||
|
||||
@ -327,7 +324,6 @@ fn test_create_branch_in_the_middle() -> Result<()> {
|
||||
remote_name: "origin".to_string(),
|
||||
remote_url: "origin".to_string(),
|
||||
sha: repository.head().unwrap().target().unwrap(),
|
||||
behind: 0,
|
||||
})?;
|
||||
update_gitbutler_integration(&gb_repo, &project_repository)?;
|
||||
|
||||
@ -381,7 +377,6 @@ fn test_create_branch_no_arguments() -> Result<()> {
|
||||
remote_name: "origin".to_string(),
|
||||
remote_url: "origin".to_string(),
|
||||
sha: repository.head().unwrap().target().unwrap(),
|
||||
behind: 0,
|
||||
})?;
|
||||
update_gitbutler_integration(&gb_repo, &project_repository)?;
|
||||
|
||||
@ -425,7 +420,6 @@ fn test_hunk_expantion() -> Result<()> {
|
||||
remote_name: "origin".to_string(),
|
||||
remote_url: "origin".to_string(),
|
||||
sha: repository.head().unwrap().target().unwrap(),
|
||||
behind: 0,
|
||||
})?;
|
||||
update_gitbutler_integration(&gb_repo, &project_repository)?;
|
||||
|
||||
@ -514,7 +508,6 @@ fn test_get_status_files_by_branch_no_hunks_no_branches() -> Result<()> {
|
||||
remote_name: "origin".to_string(),
|
||||
remote_url: "origin".to_string(),
|
||||
sha: repository.head().unwrap().target().unwrap(),
|
||||
behind: 0,
|
||||
})?;
|
||||
update_gitbutler_integration(&gb_repo, &project_repository)?;
|
||||
|
||||
@ -548,7 +541,6 @@ fn test_get_status_files_by_branch() -> Result<()> {
|
||||
remote_name: "origin".to_string(),
|
||||
remote_url: "origin".to_string(),
|
||||
sha: repository.head().unwrap().target().unwrap(),
|
||||
behind: 0,
|
||||
})?;
|
||||
update_gitbutler_integration(&gb_repo, &project_repository)?;
|
||||
|
||||
@ -609,7 +601,6 @@ fn test_updated_ownership_should_bubble_up() -> Result<()> {
|
||||
remote_name: "origin".to_string(),
|
||||
remote_url: "origin".to_string(),
|
||||
sha: repository.head().unwrap().target().unwrap(),
|
||||
behind: 0,
|
||||
})?;
|
||||
update_gitbutler_integration(&gb_repo, &project_repository)?;
|
||||
|
||||
@ -732,7 +723,6 @@ fn test_move_hunks_multiple_sources() -> Result<()> {
|
||||
remote_name: "origin".to_string(),
|
||||
remote_url: "origin".to_string(),
|
||||
sha: repository.head().unwrap().target().unwrap(),
|
||||
behind: 0,
|
||||
})?;
|
||||
update_gitbutler_integration(&gb_repo, &project_repository)?;
|
||||
|
||||
@ -849,7 +839,6 @@ fn test_move_hunks_partial_explicitly() -> Result<()> {
|
||||
remote_name: "origin".to_string(),
|
||||
remote_url: "origin".to_string(),
|
||||
sha: repository.head().unwrap().target().unwrap(),
|
||||
behind: 0,
|
||||
})?;
|
||||
update_gitbutler_integration(&gb_repo, &project_repository)?;
|
||||
|
||||
@ -946,7 +935,6 @@ fn test_add_new_hunk_to_the_end() -> Result<()> {
|
||||
remote_name: "origin".to_string(),
|
||||
remote_url: "origin".to_string(),
|
||||
sha: repository.head().unwrap().target().unwrap(),
|
||||
behind: 0,
|
||||
})?;
|
||||
update_gitbutler_integration(&gb_repo, &project_repository)?;
|
||||
|
||||
@ -978,7 +966,7 @@ fn test_add_new_hunk_to_the_end() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update_branch_target_base() -> Result<()> {
|
||||
fn test_update_base_branch_base() -> Result<()> {
|
||||
let repository = test_repository()?;
|
||||
let project = projects::Project::try_from(&repository)?;
|
||||
let gb_repo_path = tempdir()?.path().to_str().unwrap().to_string();
|
||||
@ -1013,7 +1001,6 @@ fn test_update_branch_target_base() -> Result<()> {
|
||||
remote_name: "origin".to_string(),
|
||||
remote_url: "origin".to_string(),
|
||||
sha: repository.head().unwrap().target().unwrap(),
|
||||
behind: 0,
|
||||
})?;
|
||||
update_gitbutler_integration(&gb_repo, &project_repository)?;
|
||||
repository.set_head("refs/heads/master")?;
|
||||
@ -1072,7 +1059,7 @@ fn test_update_branch_target_base() -> Result<()> {
|
||||
// update the target branch
|
||||
// this should leave the work on file2, but update the contents of file1
|
||||
// and the branch diff should only be on file2
|
||||
update_branch_target(&gb_repo, &project_repository)?;
|
||||
update_base_branch(&gb_repo, &project_repository)?;
|
||||
|
||||
let contents = std::fs::read(std::path::Path::new(&project.path).join(file_path))?;
|
||||
assert_eq!(
|
||||
@ -1090,7 +1077,7 @@ fn test_update_branch_target_base() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update_branch_target_detect_integrated_branches() -> Result<()> {
|
||||
fn test_update_base_branch_detect_integrated_branches() -> Result<()> {
|
||||
let repository = test_repository()?;
|
||||
let project = projects::Project::try_from(&repository)?;
|
||||
let gb_repo_path = tempdir()?.path().to_str().unwrap().to_string();
|
||||
@ -1120,7 +1107,6 @@ fn test_update_branch_target_detect_integrated_branches() -> Result<()> {
|
||||
remote_name: "origin".to_string(),
|
||||
remote_url: "origin".to_string(),
|
||||
sha: repository.head().unwrap().target().unwrap(),
|
||||
behind: 0,
|
||||
})?;
|
||||
update_gitbutler_integration(&gb_repo, &project_repository)?;
|
||||
|
||||
@ -1166,7 +1152,7 @@ fn test_update_branch_target_detect_integrated_branches() -> Result<()> {
|
||||
|
||||
// update the target branch
|
||||
// this should notice that the trees are the same after the merge, so it should unapply the branch
|
||||
update_branch_target(&gb_repo, &project_repository)?;
|
||||
update_base_branch(&gb_repo, &project_repository)?;
|
||||
|
||||
// integrated branch should be deleted
|
||||
let branches = list_virtual_branches(&gb_repo, &project_repository, true)?;
|
||||
@ -1176,7 +1162,7 @@ fn test_update_branch_target_detect_integrated_branches() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update_branch_target_detect_integrated_branches_with_more_work() -> Result<()> {
|
||||
fn test_update_base_branch_detect_integrated_branches_with_more_work() -> Result<()> {
|
||||
let repository = test_repository()?;
|
||||
let project = projects::Project::try_from(&repository)?;
|
||||
let gb_repo_path = tempdir()?.path().to_str().unwrap().to_string();
|
||||
@ -1206,7 +1192,6 @@ fn test_update_branch_target_detect_integrated_branches_with_more_work() -> Resu
|
||||
remote_name: "origin".to_string(),
|
||||
remote_url: "origin".to_string(),
|
||||
sha: repository.head().unwrap().target().unwrap(),
|
||||
behind: 0,
|
||||
})?;
|
||||
update_gitbutler_integration(&gb_repo, &project_repository)?;
|
||||
|
||||
@ -1246,7 +1231,7 @@ fn test_update_branch_target_detect_integrated_branches_with_more_work() -> Resu
|
||||
|
||||
// update the target branch
|
||||
// this should notice that the trees are the same after the merge, but there are files on the branch, so do a merge and then leave the files there
|
||||
update_branch_target(&gb_repo, &project_repository)?;
|
||||
update_base_branch(&gb_repo, &project_repository)?;
|
||||
|
||||
// there should be a new vbranch created, but nothing is on it
|
||||
let branches = list_virtual_branches(&gb_repo, &project_repository, true)?;
|
||||
@ -1302,7 +1287,6 @@ fn test_update_target_with_conflicts_in_vbranches() -> Result<()> {
|
||||
remote_name: "origin".to_string(),
|
||||
remote_url: "origin".to_string(),
|
||||
sha: repository.head().unwrap().target().unwrap(),
|
||||
behind: 0,
|
||||
})?;
|
||||
update_gitbutler_integration(&gb_repo, &project_repository)?;
|
||||
|
||||
@ -1546,7 +1530,7 @@ fn test_update_target_with_conflicts_in_vbranches() -> Result<()> {
|
||||
)?;
|
||||
|
||||
// update the target branch
|
||||
update_branch_target(&gb_repo, &project_repository)?;
|
||||
update_base_branch(&gb_repo, &project_repository)?;
|
||||
|
||||
let branches = list_virtual_branches(&gb_repo, &project_repository, true)?;
|
||||
|
||||
@ -1623,7 +1607,6 @@ fn test_apply_unapply_branch() -> Result<()> {
|
||||
remote_name: "origin".to_string(),
|
||||
remote_url: "origin".to_string(),
|
||||
sha: repository.head().unwrap().target().unwrap(),
|
||||
behind: 0,
|
||||
})?;
|
||||
update_gitbutler_integration(&gb_repo, &project_repository)?;
|
||||
|
||||
@ -1730,7 +1713,6 @@ fn test_apply_unapply_added_deleted_files() -> Result<()> {
|
||||
remote_name: "origin".to_string(),
|
||||
remote_url: "origin".to_string(),
|
||||
sha: repository.head().unwrap().target().unwrap(),
|
||||
behind: 0,
|
||||
})?;
|
||||
update_gitbutler_integration(&gb_repo, &project_repository)?;
|
||||
|
||||
@ -1821,7 +1803,6 @@ fn test_detect_mergeable_branch() -> Result<()> {
|
||||
remote_name: "origin".to_string(),
|
||||
remote_url: "origin".to_string(),
|
||||
sha: repository.head().unwrap().target().unwrap(),
|
||||
behind: 0,
|
||||
})?;
|
||||
update_gitbutler_integration(&gb_repo, &project_repository)?;
|
||||
|
||||
@ -2000,7 +1981,6 @@ fn test_detect_remote_commits() -> Result<()> {
|
||||
remote_name: "origin".to_string(),
|
||||
remote_url: "http://origin.com/project".to_string(),
|
||||
sha: repository.head().unwrap().target().unwrap(),
|
||||
behind: 0,
|
||||
})?;
|
||||
update_gitbutler_integration(&gb_repo, &project_repository)?;
|
||||
|
||||
@ -2101,7 +2081,6 @@ fn test_create_vbranch_from_remote_branch() -> Result<()> {
|
||||
remote_name: "origin".to_string(),
|
||||
remote_url: "http://origin.com/project".to_string(),
|
||||
sha: repository.head().unwrap().target().unwrap(),
|
||||
behind: 0,
|
||||
})?;
|
||||
repository.remote("origin", "http://origin.com/project")?;
|
||||
update_gitbutler_integration(&gb_repo, &project_repository)?;
|
||||
@ -2257,7 +2236,6 @@ fn test_create_vbranch_from_behind_remote_branch() -> Result<()> {
|
||||
branch_name: "master".to_string(),
|
||||
remote_url: "http://origin.com/project".to_string(),
|
||||
sha: upstream_commit,
|
||||
behind: 0,
|
||||
})?;
|
||||
repository.remote("origin", "http://origin.com/project")?;
|
||||
update_gitbutler_integration(&gb_repo, &project_repository)?;
|
||||
@ -2363,7 +2341,6 @@ fn test_partial_commit() -> Result<()> {
|
||||
remote_name: "origin".to_string(),
|
||||
remote_url: "origin".to_string(),
|
||||
sha: repository.head().unwrap().target().unwrap(),
|
||||
behind: 0,
|
||||
})?;
|
||||
update_gitbutler_integration(&gb_repo, &project_repository)?;
|
||||
|
||||
@ -2506,7 +2483,6 @@ fn test_commit_add_and_delete_files() -> Result<()> {
|
||||
remote_name: "origin".to_string(),
|
||||
remote_url: "origin".to_string(),
|
||||
sha: commit1_oid,
|
||||
behind: 0,
|
||||
})?;
|
||||
update_gitbutler_integration(&gb_repo, &project_repository)?;
|
||||
|
||||
@ -2583,7 +2559,6 @@ fn test_commit_executable_and_symlinks() -> Result<()> {
|
||||
remote_name: "origin".to_string(),
|
||||
remote_url: "origin".to_string(),
|
||||
sha: commit1_oid,
|
||||
behind: 0,
|
||||
})?;
|
||||
update_gitbutler_integration(&gb_repo, &project_repository)?;
|
||||
|
||||
@ -2723,7 +2698,6 @@ fn test_apply_out_of_date_vbranch() -> Result<()> {
|
||||
remote_name: "origin".to_string(),
|
||||
remote_url: "http://origin.com/project".to_string(),
|
||||
sha: base_commit,
|
||||
behind: 0,
|
||||
})?;
|
||||
repository.remote("origin", "http://origin.com/project")?;
|
||||
update_gitbutler_integration(&gb_repo, &project_repository)?;
|
||||
@ -2801,7 +2775,7 @@ fn test_apply_out_of_date_vbranch() -> Result<()> {
|
||||
assert_eq!(contents, "line1\nline2\nline3\nline4\n");
|
||||
|
||||
// update target, this will update the wd and add an empty default branch
|
||||
update_branch_target(&gb_repo, &project_repository)?;
|
||||
update_base_branch(&gb_repo, &project_repository)?;
|
||||
|
||||
// updated the file
|
||||
let contents = std::fs::read_to_string(std::path::Path::new(&project.path).join(file_path))?;
|
||||
@ -2866,7 +2840,6 @@ fn test_apply_out_of_date_conflicting_vbranch() -> Result<()> {
|
||||
remote_name: "origin".to_string(),
|
||||
remote_url: "http://origin.com/project".to_string(),
|
||||
sha: base_commit,
|
||||
behind: 0,
|
||||
})?;
|
||||
repository.remote("origin", "http://origin.com/project")?;
|
||||
update_gitbutler_integration(&gb_repo, &project_repository)?;
|
||||
@ -2945,7 +2918,7 @@ fn test_apply_out_of_date_conflicting_vbranch() -> Result<()> {
|
||||
assert_eq!(contents, "line1\nline2\nline3\nline4\n");
|
||||
|
||||
// update target, this will update the wd and add an empty default branch
|
||||
update_branch_target(&gb_repo, &project_repository)?;
|
||||
update_base_branch(&gb_repo, &project_repository)?;
|
||||
|
||||
// updated the file
|
||||
let contents = std::fs::read_to_string(std::path::Path::new(&project.path).join(file_path))?;
|
||||
@ -3044,7 +3017,6 @@ fn test_apply_conflicting_vbranch() -> Result<()> {
|
||||
remote_name: "origin".to_string(),
|
||||
remote_url: "http://origin.com/project".to_string(),
|
||||
sha: base_commit,
|
||||
behind: 0,
|
||||
})?;
|
||||
update_gitbutler_integration(&gb_repo, &project_repository)?;
|
||||
|
||||
|
@ -221,7 +221,6 @@ mod test {
|
||||
unsafe { TEST_TARGET_INDEX }
|
||||
))
|
||||
.unwrap(),
|
||||
behind: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import type { Refreshable } from './branchStoresCache';
|
||||
import type { Readable } from '@square/svelte-store';
|
||||
import type { Loadable } from 'svelte-loadable-store';
|
||||
import type { Branch, BranchData, Target } from './types';
|
||||
import type { Branch, BranchData, BaseBranch } from './types';
|
||||
import { toasts } from '$lib';
|
||||
import { invoke } from '$lib/ipc';
|
||||
|
||||
@ -12,12 +12,12 @@ export class BranchController {
|
||||
readonly projectId: string,
|
||||
readonly virtualBranchStore: Refreshable & Readable<Loadable<Branch[]>>,
|
||||
readonly remoteBranchStore: Refreshable & Readable<Loadable<BranchData[]>>,
|
||||
readonly targetBranchStore: Refreshable & Readable<Loadable<Target | null>>
|
||||
readonly targetBranchStore: Refreshable & Readable<Loadable<BaseBranch | null>>
|
||||
) {}
|
||||
|
||||
async setTarget(branch: string) {
|
||||
try {
|
||||
await invoke<Target>('set_target_branch', { projectId: this.projectId, branch });
|
||||
await invoke<BaseBranch>('set_base_branch', { projectId: this.projectId, branch });
|
||||
await Promise.all([
|
||||
this.virtualBranchStore.refresh(),
|
||||
this.remoteBranchStore.refresh(),
|
||||
@ -119,9 +119,9 @@ export class BranchController {
|
||||
}
|
||||
}
|
||||
|
||||
async updateBranchTarget() {
|
||||
async updateBaseBranch() {
|
||||
try {
|
||||
await invoke<object>('update_branch_target', { projectId: this.projectId });
|
||||
await invoke<object>('update_base_branch', { projectId: this.projectId });
|
||||
await Promise.all([
|
||||
this.remoteBranchStore.refresh(),
|
||||
this.virtualBranchStore.refresh(),
|
||||
|
@ -4,7 +4,7 @@ import { Operation } from '$lib/api';
|
||||
import type { Readable } from '@square/svelte-store';
|
||||
import { deltas, git } from '$lib/api/ipc';
|
||||
import { stores } from '$lib';
|
||||
import { Target, Branch, BranchData } from './types';
|
||||
import { BaseBranch, Branch, BranchData } from './types';
|
||||
import { plainToInstance } from 'class-transformer';
|
||||
import { invoke } from '$lib/ipc';
|
||||
|
||||
@ -15,7 +15,7 @@ export interface Refreshable {
|
||||
export class BranchStoresCache {
|
||||
virtualBranchStores: Map<string, Refreshable & Readable<Loadable<Branch[]>>> = new Map();
|
||||
remoteBranchStores: Map<string, Refreshable & Readable<Loadable<BranchData[]>>> = new Map();
|
||||
targetBranchStores: Map<string, Refreshable & Readable<Loadable<Target | null>>> = new Map();
|
||||
targetBranchStores: Map<string, Refreshable & Readable<Loadable<BaseBranch | null>>> = new Map();
|
||||
|
||||
getVirtualBranchStore(projectId: string) {
|
||||
const cachedStore = this.virtualBranchStores.get(projectId);
|
||||
@ -86,21 +86,21 @@ export class BranchStoresCache {
|
||||
return refreshableStore;
|
||||
}
|
||||
|
||||
getTargetBranchStore(projectId: string) {
|
||||
getBaseBranchStore(projectId: string) {
|
||||
const cachedStore = this.targetBranchStores.get(projectId);
|
||||
if (cachedStore) {
|
||||
return cachedStore;
|
||||
}
|
||||
const writableStore = writable(getTargetData({ projectId }), (set) => {
|
||||
const writableStore = writable(getBaseBranchData({ projectId }), (set) => {
|
||||
git.fetches.subscribe({ projectId }, () => {
|
||||
getTargetData({ projectId }).then(set);
|
||||
getBaseBranchData({ projectId }).then(set);
|
||||
});
|
||||
});
|
||||
const refreshableStore = {
|
||||
subscribe: writableStore.subscribe,
|
||||
refresh: async () => {
|
||||
const newTarget = await getTargetData({ projectId });
|
||||
return writableStore.set({ isLoading: false, value: newTarget });
|
||||
const newBaseBranch = await getBaseBranchData({ projectId });
|
||||
return writableStore.set({ isLoading: false, value: newBaseBranch });
|
||||
}
|
||||
};
|
||||
this.targetBranchStores.set(projectId, refreshableStore);
|
||||
@ -116,8 +116,8 @@ export async function getRemoteBranchesData(params: { projectId: string }): Prom
|
||||
return plainToInstance(BranchData, await invoke<any[]>('git_remote_branches_data', params));
|
||||
}
|
||||
|
||||
export async function getTargetData(params: { projectId: string }): Promise<Target> {
|
||||
return plainToInstance(Target, invoke<any>('get_target_data', params));
|
||||
export async function getBaseBranchData(params: { projectId: string }): Promise<BaseBranch> {
|
||||
return plainToInstance(BaseBranch, invoke<any>('get_base_branch_data', params));
|
||||
}
|
||||
|
||||
async function branchesWithFileContent(projectId: string, sessionId: string, branches: Branch[]) {
|
||||
|
@ -4,12 +4,12 @@
|
||||
* There are three interesting data types coming from the rust IPC api:
|
||||
* - Branch, representing a virtual branch
|
||||
* - BranchData, representing a remote branch
|
||||
* - Target, representing a target remote branch
|
||||
* - BaseBranch, representing a target base remote branch
|
||||
*
|
||||
* The three types are obtained as reactive stores from the BranchStoresCache's methods:
|
||||
* - getVirtualBranchStore - List of Branch (virtual branches)
|
||||
* - getRemoteBranchStore - List of BranchData (remote branches)
|
||||
* - getTargetBranchStore - Target (single target branch)
|
||||
* - getBaseBranchStore - BaseBranch (single target branch)
|
||||
*
|
||||
* BranchController is a class where all virtual branch operations are performed
|
||||
* This class gets the three stores injected at construction so that any related updates can be peformed
|
||||
@ -20,6 +20,6 @@
|
||||
* so that it can take advantage of caching, making project navigation quicker.
|
||||
* - Create the BranchController at the level of a specific project and inject it to components that need it.
|
||||
*/
|
||||
export { Branch, File, Hunk, Commit, BranchData, Target } from './types';
|
||||
export { Branch, File, Hunk, Commit, BranchData, BaseBranch } from './types';
|
||||
export { BranchStoresCache } from './branchStoresCache';
|
||||
export { BranchController } from './branchController';
|
||||
|
@ -42,8 +42,7 @@ export class Branch {
|
||||
|
||||
export class Commit {
|
||||
id!: string;
|
||||
authorEmail!: string;
|
||||
authorName!: string;
|
||||
author!: Author;
|
||||
description!: string;
|
||||
@Transform((obj) => new Date(obj.value))
|
||||
createdAt!: Date;
|
||||
@ -70,10 +69,12 @@ export class BranchData {
|
||||
mergeConflicts!: string[];
|
||||
}
|
||||
|
||||
export class Target {
|
||||
sha!: string;
|
||||
export class BaseBranch {
|
||||
branchName!: string;
|
||||
remoteName!: string;
|
||||
remoteUrl!: string;
|
||||
baseSha!: string;
|
||||
currentSha!: string;
|
||||
behind!: number;
|
||||
upstreamCommits!: Commit[];
|
||||
}
|
||||
|
@ -46,7 +46,7 @@
|
||||
|
||||
{#if target}
|
||||
<div class="flex w-full max-w-full" role="group" on:dragover|preventDefault>
|
||||
<Tray {branches} {target} {remoteBranches} />
|
||||
<Tray {branches} {remoteBranches} />
|
||||
<div
|
||||
class="z-50 -ml-[0.250rem] w-[0.250rem] shrink-0 cursor-col-resize hover:bg-orange-200 dark:bg-dark-1000 dark:hover:bg-orange-700"
|
||||
draggable="true"
|
||||
@ -60,7 +60,7 @@
|
||||
/>
|
||||
<div class="flex w-full flex-col overflow-x-hidden">
|
||||
<Board {branches} {projectId} projectPath={project.path} {target} />
|
||||
<BottomPanel />
|
||||
<BottomPanel {target} />
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
|
@ -14,7 +14,7 @@ export async function load({ parent, params }: PageLoadEvent) {
|
||||
const { branchStoresCache } = await parent();
|
||||
const vbranchStore = branchStoresCache.getVirtualBranchStore(projectId);
|
||||
const remoteBranchStore = branchStoresCache.getRemoteBranchStore(projectId);
|
||||
const targetBranchStore = branchStoresCache.getTargetBranchStore(projectId);
|
||||
const targetBranchStore = branchStoresCache.getBaseBranchStore(projectId);
|
||||
|
||||
return {
|
||||
projectId,
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script lang="ts" async="true">
|
||||
import Lane from './BranchLane.svelte';
|
||||
import NewBranchDropZone from './NewBranchDropZone.svelte';
|
||||
import type { Branch, Target } from '$lib/vbranches';
|
||||
import type { Branch, BaseBranch } from '$lib/vbranches';
|
||||
import { dzHighlight } from './dropZone';
|
||||
import type { BranchController } from '$lib/vbranches';
|
||||
import { getContext } from 'svelte';
|
||||
@ -10,7 +10,7 @@
|
||||
export let projectId: string;
|
||||
export let projectPath: string;
|
||||
export let branches: Branch[];
|
||||
export let target: Target;
|
||||
export let target: BaseBranch;
|
||||
|
||||
const branchController = getContext<BranchController>(BRANCH_CONTROLLER_KEY);
|
||||
|
||||
|
@ -1,10 +1,25 @@
|
||||
<script lang="ts">
|
||||
import { Button, Modal } from '$lib/components';
|
||||
import { slide } from 'svelte/transition';
|
||||
import { IconTriangleUp, IconTriangleDown } from '$lib/icons';
|
||||
import type { BaseBranch } from '$lib/vbranches';
|
||||
import type { BranchController } from '$lib/vbranches';
|
||||
import { IconRefresh } from '$lib/icons';
|
||||
import { BRANCH_CONTROLLER_KEY } from '$lib/vbranches/branchController';
|
||||
import { getContext } from 'svelte';
|
||||
|
||||
export let target: BaseBranch;
|
||||
|
||||
let updateTargetModal: Modal;
|
||||
|
||||
const branchController = getContext<BranchController>(BRANCH_CONTROLLER_KEY);
|
||||
|
||||
$: console.log(target);
|
||||
let shown = false;
|
||||
$: behindMessage = target.behind > 0 ? `behind ${target.behind}` : 'up-to-date';
|
||||
</script>
|
||||
|
||||
<div class="flex border-t border-light-400 dark:border-dark-600">
|
||||
<div class="flex border-t border-light-400 p-2 dark:border-dark-600">
|
||||
<div class="ml-4 flex flex-col">
|
||||
<div
|
||||
role="button"
|
||||
@ -18,24 +33,72 @@
|
||||
{:else}
|
||||
<IconTriangleUp />
|
||||
{/if}
|
||||
<span class="text-sm font-bold uppercase"> Common base </span>
|
||||
<div class="flex flex-row justify-between space-x-2">
|
||||
<div class="font-bold uppercase">Common base</div>
|
||||
<div class="flex-grow pb-1 font-bold" title={behindMessage}>{target.branchName}</div>
|
||||
<div class="pb-1">{target.behind > 0 ? `behind ${target.behind}` : 'up-to-date'}</div>
|
||||
<div class="flex-shrink-0 text-light-700 dark:text-dark-100" title={behindMessage}>
|
||||
{#if target.behind == 0}
|
||||
<button
|
||||
class="p-0 hover:bg-light-200 disabled:text-light-300 dark:hover:bg-dark-800 disabled:dark:text-dark-300"
|
||||
on:click={() => branchController.fetchFromTarget()}
|
||||
title="click to fetch"
|
||||
>
|
||||
<IconRefresh />
|
||||
</button>
|
||||
{:else}
|
||||
<button
|
||||
class="p-0 disabled:text-light-300 disabled:dark:text-dark-300"
|
||||
on:click={updateTargetModal.show}
|
||||
disabled={target.behind == 0}
|
||||
title={target.behind > 0 ? 'click to update target' : 'already up-to-date'}
|
||||
>
|
||||
<IconRefresh />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{#if shown}
|
||||
<div class="h-64 py-2" transition:slide={{ duration: 150 }}>
|
||||
<div class="">
|
||||
<div>We're no strangers to love You know the rules and so do I</div>
|
||||
<div>
|
||||
A full commitment's what I'm thinking of You wouldn't get this from any other guy
|
||||
</div>
|
||||
<div>I just wanna tell you how I'm feeling Gotta make you understand</div>
|
||||
<div>Never gonna give you up</div>
|
||||
<div>Never gonna let you down</div>
|
||||
<div>Never gonna run around and desert you</div>
|
||||
<div>Never gonna make you cry</div>
|
||||
<div>Never gonna say goodbye</div>
|
||||
<div>Never gonna tell a lie and hurt you</div>
|
||||
<div>Upstream Commits:</div>
|
||||
<div class="flex flex-col">
|
||||
{#each target.upstreamCommits as commit}
|
||||
<div class="flex flex-row space-x-2">
|
||||
<img
|
||||
class="relative z-30 inline-block h-4 w-4 rounded-full ring-1 ring-white dark:ring-black"
|
||||
title="Gravatar for {commit.author.email}"
|
||||
alt="Gravatar for {commit.author.email}"
|
||||
srcset="{commit.author.gravatarUrl} 2x"
|
||||
width="100"
|
||||
height="100"
|
||||
on:error
|
||||
/>
|
||||
<div>{commit.description.substring(0, 50)}</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Confirm target update modal -->
|
||||
|
||||
<Modal width="small" bind:this={updateTargetModal}>
|
||||
<svelte:fragment slot="title">Update target</svelte:fragment>
|
||||
<p>You are about to update the base branch.</p>
|
||||
<svelte:fragment slot="controls" let:close>
|
||||
<Button height="small" kind="outlined" on:click={close}>Cancel</Button>
|
||||
<Button
|
||||
height="small"
|
||||
color="purple"
|
||||
on:click={() => {
|
||||
branchController.updateBaseBranch();
|
||||
close();
|
||||
}}
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { toasts } from '$lib';
|
||||
import type { Commit, File, Target } from '$lib/vbranches';
|
||||
import type { Commit, File, BaseBranch } from '$lib/vbranches';
|
||||
import { getContext, onMount } from 'svelte';
|
||||
import { IconAISparkles, IconBranch } from '$lib/icons';
|
||||
import { Button, Link, Modal } from '$lib/components';
|
||||
@ -25,7 +25,7 @@
|
||||
export let projectId: string;
|
||||
export let order: number;
|
||||
export let conflicted: boolean;
|
||||
export let target: Target;
|
||||
export let target: BaseBranch;
|
||||
|
||||
const branchController = getContext<BranchController>(BRANCH_CONTROLLER_KEY);
|
||||
|
||||
@ -110,7 +110,7 @@
|
||||
.join('');
|
||||
}
|
||||
|
||||
function url(target: Target, branchName: string) {
|
||||
function url(target: BaseBranch, branchName: string) {
|
||||
const baseBranchName = target.branchName.split('/')[1];
|
||||
const repoBaseUrl = target.remoteUrl
|
||||
.replace(':', '/')
|
||||
|
@ -12,7 +12,7 @@
|
||||
{commit.description}
|
||||
</div>
|
||||
<div class="flex text-light-700">
|
||||
<div class="flex-grow">{commit.authorName}</div>
|
||||
<div class="flex-grow">{commit.author.name}</div>
|
||||
<div>{formatDistanceToNow(commit.createdAt)} ago</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { Button, Checkbox, Modal } from '$lib/components';
|
||||
import type { Branch, BranchData, Target } from '$lib/vbranches';
|
||||
import type { Branch, BranchData, BaseBranch } from '$lib/vbranches';
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
import { IconGitBranch, IconRemote, IconRefresh } from '$lib/icons';
|
||||
import { IconGitBranch, IconRemote } from '$lib/icons';
|
||||
import { IconTriangleDown, IconTriangleUp } from '$lib/icons';
|
||||
import { accordion } from './accordion';
|
||||
import PopupMenu from '$lib/components/PopupMenu/PopupMenu.svelte';
|
||||
@ -12,7 +12,6 @@
|
||||
import { getContext } from 'svelte';
|
||||
import { BRANCH_CONTROLLER_KEY } from '$lib/vbranches/branchController';
|
||||
|
||||
export let target: Target;
|
||||
export let branches: Branch[];
|
||||
export let remoteBranches: BranchData[];
|
||||
|
||||
@ -24,12 +23,9 @@
|
||||
|
||||
let yourBranchContextMenu: PopupMenu;
|
||||
let remoteBranchContextMenu: PopupMenu;
|
||||
let updateTargetModal: Modal;
|
||||
let applyConflictedModal: Modal;
|
||||
let deleteBranchModal: Modal;
|
||||
|
||||
$: behindMessage = target.behind > 0 ? `behind ${target.behind}` : 'up-to-date';
|
||||
|
||||
function toggleBranch(branch: Branch) {
|
||||
if (branch.active) {
|
||||
branchController.unapplyBranch(branch.id);
|
||||
@ -64,37 +60,6 @@
|
||||
class="tray-scroll w-80 min-w-[216px] shrink-0 cursor-default resize-x overflow-y-scroll overscroll-y-none border-r border-light-400 bg-white text-light-800 dark:border-dark-600 dark:bg-dark-900 dark:text-dark-100"
|
||||
style:width={$userSettings.trayWidth ? `${$userSettings.trayWidth}px` : null}
|
||||
>
|
||||
<!-- Target branch -->
|
||||
<div class="pl-2 pr-4 pt-2 text-light-700 dark:bg-dark-700 dark:text-dark-200">Base branch</div>
|
||||
<div
|
||||
class="flex w-full flex-row items-center justify-between border-b border-light-400 pb-1 pl-2 pr-1 text-light-900 dark:border-dark-500 dark:bg-dark-700 dark:text-dark-100"
|
||||
>
|
||||
<div class="flex-grow pb-1 font-bold" title={behindMessage}>{target.branchName}</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<div class="pb-1">{target.behind > 0 ? `behind ${target.behind}` : 'up-to-date'}</div>
|
||||
<div class="flex-shrink-0 text-light-700 dark:text-dark-100" title={behindMessage}>
|
||||
{#if target.behind == 0}
|
||||
<button
|
||||
class="p-0 hover:bg-light-200 disabled:text-light-300 dark:hover:bg-dark-800 disabled:dark:text-dark-300"
|
||||
on:click={() => branchController.fetchFromTarget()}
|
||||
title="click to fetch"
|
||||
>
|
||||
<IconRefresh />
|
||||
</button>
|
||||
{:else}
|
||||
<button
|
||||
class="p-0 disabled:text-light-300 disabled:dark:text-dark-300"
|
||||
on:click={updateTargetModal.show}
|
||||
disabled={target.behind == 0}
|
||||
title={target.behind > 0 ? 'click to update target' : 'already up-to-date'}
|
||||
>
|
||||
<IconRefresh />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Your branches -->
|
||||
<div
|
||||
class="flex items-center justify-between border-b border-light-400 bg-light-100 px-2 py-1 pr-1 dark:border-dark-600 dark:bg-dark-800"
|
||||
@ -253,26 +218,6 @@
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
|
||||
<!-- Confirm target update modal -->
|
||||
|
||||
<Modal width="small" bind:this={updateTargetModal}>
|
||||
<svelte:fragment slot="title">Update target</svelte:fragment>
|
||||
<p>You are about to update the base branch.</p>
|
||||
<svelte:fragment slot="controls" let:close>
|
||||
<Button height="small" kind="outlined" on:click={close}>Cancel</Button>
|
||||
<Button
|
||||
height="small"
|
||||
color="purple"
|
||||
on:click={() => {
|
||||
branchController.updateBranchTarget();
|
||||
close();
|
||||
}}
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
|
||||
<!-- Delete branch confirmation modal -->
|
||||
|
||||
<Modal width="small" bind:this={deleteBranchModal} let:item>
|
||||
|
Loading…
Reference in New Issue
Block a user