mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-11-30 01:17:37 +03:00
Merge branch 'master' into refactor-project-setup-page
This commit is contained in:
commit
eb7f2674a5
@ -4,7 +4,7 @@ use anyhow::Result;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
// Returns an ordered list of relative paths for files inside a directory recursively.
|
||||
pub fn list_files<P: AsRef<Path>>(dir_path: P) -> Result<Vec<PathBuf>> {
|
||||
pub fn list_files<P: AsRef<Path>>(dir_path: P, ignore_prefixes: &[P]) -> Result<Vec<PathBuf>> {
|
||||
let mut files = vec![];
|
||||
let dir_path = dir_path.as_ref();
|
||||
if !dir_path.exists() {
|
||||
@ -16,6 +16,12 @@ pub fn list_files<P: AsRef<Path>>(dir_path: P) -> Result<Vec<PathBuf>> {
|
||||
let path = entry.path();
|
||||
let path = path.strip_prefix(dir_path)?;
|
||||
let path = path.to_path_buf();
|
||||
if ignore_prefixes
|
||||
.iter()
|
||||
.any(|prefix| path.starts_with(prefix.as_ref()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
files.push(path);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
collections::{HashMap, HashSet},
|
||||
fs::File,
|
||||
io::{BufReader, Read},
|
||||
os::unix::prelude::{MetadataExt, OsStrExt},
|
||||
@ -11,16 +11,17 @@ use filetime::FileTime;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
use crate::{
|
||||
fs, git, lock,
|
||||
deltas, fs, git, lock,
|
||||
paths::DataDir,
|
||||
project_repository,
|
||||
projects::{self, ProjectId},
|
||||
reader::{self, Reader},
|
||||
sessions,
|
||||
sessions::SessionId,
|
||||
users,
|
||||
virtual_branches::{self, target},
|
||||
};
|
||||
|
||||
use crate::{project_repository, reader, sessions};
|
||||
|
||||
pub struct Repository {
|
||||
git_repository: git::Repository,
|
||||
project: projects::Project,
|
||||
@ -429,6 +430,7 @@ impl Repository {
|
||||
sessions::Writer::new(self).write(session)?;
|
||||
|
||||
let mut tree_builder = self.git_repository.treebuilder(None);
|
||||
|
||||
tree_builder.upsert(
|
||||
"session",
|
||||
build_session_tree(self).context("failed to build session tree")?,
|
||||
@ -462,7 +464,7 @@ impl Repository {
|
||||
.context("failed to remove session directory")?;
|
||||
|
||||
let session = sessions::Session {
|
||||
hash: Some(commit_oid.to_string()),
|
||||
hash: Some(commit_oid),
|
||||
..session.clone()
|
||||
};
|
||||
|
||||
@ -532,9 +534,81 @@ impl Repository {
|
||||
}
|
||||
}
|
||||
|
||||
fn build_wd_tree(
|
||||
gb_repository: &Repository,
|
||||
project_repository: &project_repository::Repository,
|
||||
) -> Result<git::Oid> {
|
||||
match gb_repository
|
||||
.git_repository
|
||||
.find_reference(&"refs/heads/current".parse().unwrap())
|
||||
{
|
||||
Result::Ok(reference) => build_wd_tree_from_reference(gb_repository, &reference)
|
||||
.context("failed to build wd index"),
|
||||
Err(git::Error::NotFound(_)) => build_wd_tree_from_repo(gb_repository, project_repository)
|
||||
.context("failed to build wd index"),
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_wd_tree_from_reference(
|
||||
gb_repository: &Repository,
|
||||
reference: &git::Reference,
|
||||
) -> Result<git::Oid> {
|
||||
// start off with the last tree as a base
|
||||
let tree = reference.peel_to_tree()?;
|
||||
let wd_tree_entry = tree.get_name("wd").unwrap();
|
||||
let wd_tree = gb_repository.git_repository.find_tree(wd_tree_entry.id())?;
|
||||
let mut index = git::Index::try_from(&wd_tree)?;
|
||||
|
||||
// write updated files on top of the last tree
|
||||
for file_path in fs::list_files(gb_repository.session_wd_path(), &[]).with_context(|| {
|
||||
format!(
|
||||
"failed to session working directory files list files in {}",
|
||||
gb_repository.session_wd_path().display()
|
||||
)
|
||||
})? {
|
||||
add_wd_path(
|
||||
&mut index,
|
||||
&gb_repository.session_wd_path(),
|
||||
&file_path,
|
||||
gb_repository,
|
||||
)
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"failed to add session working directory path {}",
|
||||
file_path.display()
|
||||
)
|
||||
})?;
|
||||
}
|
||||
|
||||
let session_reader = reader::DirReader::open(gb_repository.root());
|
||||
let deltas = deltas::Reader::new(&session_reader)
|
||||
.read(None)
|
||||
.context("failed to read deltas")?;
|
||||
let wd_files = session_reader.list_files(path::Path::new("session/wd"))?;
|
||||
let wd_files = wd_files.iter().collect::<HashSet<_>>();
|
||||
|
||||
// if a file has delta, but doesn't exist in wd, it was deleted
|
||||
let deleted_files = deltas
|
||||
.keys()
|
||||
.filter(|key| !wd_files.contains(key))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for deleted_file in deleted_files {
|
||||
index
|
||||
.remove_path(deleted_file)
|
||||
.context("failed to remove path")?;
|
||||
}
|
||||
|
||||
let wd_tree_oid = index
|
||||
.write_tree_to(&gb_repository.git_repository)
|
||||
.context("failed to write wd tree")?;
|
||||
Ok(wd_tree_oid)
|
||||
}
|
||||
|
||||
// build wd index from the working directory files new session wd files
|
||||
// this is important because we want to make sure session files are in sync with session deltas
|
||||
fn build_wd_tree(
|
||||
fn build_wd_tree_from_repo(
|
||||
gb_repository: &Repository,
|
||||
project_repository: &project_repository::Repository,
|
||||
) -> Result<git::Oid> {
|
||||
@ -544,16 +618,15 @@ fn build_wd_tree(
|
||||
|
||||
// first, add session/wd files. session/wd are written at the same time as deltas, so it's important to add them first
|
||||
// to make sure they are in sync with the deltas
|
||||
for file_path in fs::list_files(gb_repository.session_wd_path()).with_context(|| {
|
||||
for file_path in fs::list_files(gb_repository.session_wd_path(), &[]).with_context(|| {
|
||||
format!(
|
||||
"failed to session working directory files list files in {}",
|
||||
gb_repository.session_wd_path().display()
|
||||
)
|
||||
})? {
|
||||
let file_path = std::path::Path::new(&file_path);
|
||||
if project_repository
|
||||
.git_repository
|
||||
.is_path_ignored(file_path)
|
||||
.is_path_ignored(&file_path)
|
||||
.unwrap_or(true)
|
||||
{
|
||||
continue;
|
||||
@ -562,7 +635,7 @@ fn build_wd_tree(
|
||||
add_wd_path(
|
||||
&mut index,
|
||||
&gb_repository.session_wd_path(),
|
||||
file_path,
|
||||
&file_path,
|
||||
gb_repository,
|
||||
)
|
||||
.with_context(|| {
|
||||
@ -575,21 +648,21 @@ fn build_wd_tree(
|
||||
}
|
||||
|
||||
// finally, add files from the working directory if they aren't already in the index
|
||||
for file_path in fs::list_files(project_repository.root()).with_context(|| {
|
||||
format!(
|
||||
"failed to working directory list files in {}",
|
||||
project_repository.root().display()
|
||||
)
|
||||
})? {
|
||||
for file_path in fs::list_files(project_repository.root(), &[path::Path::new(".git")])
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"failed to working directory list files in {}",
|
||||
project_repository.root().display()
|
||||
)
|
||||
})?
|
||||
{
|
||||
if added.contains_key(&file_path.to_string_lossy().to_string()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let file_path = std::path::Path::new(&file_path);
|
||||
|
||||
if project_repository
|
||||
.git_repository
|
||||
.is_path_ignored(file_path)
|
||||
.is_path_ignored(&file_path)
|
||||
.unwrap_or(true)
|
||||
{
|
||||
continue;
|
||||
@ -598,7 +671,7 @@ fn build_wd_tree(
|
||||
add_wd_path(
|
||||
&mut index,
|
||||
project_repository.root(),
|
||||
file_path,
|
||||
&file_path,
|
||||
gb_repository,
|
||||
)
|
||||
.with_context(|| {
|
||||
@ -720,7 +793,9 @@ fn build_branches_tree(gb_repository: &Repository) -> Result<git::Oid> {
|
||||
let mut index = git::Index::new()?;
|
||||
|
||||
let branches_dir = gb_repository.root().join("branches");
|
||||
for file_path in fs::list_files(&branches_dir).context("failed to find branches directory")? {
|
||||
for file_path in
|
||||
fs::list_files(&branches_dir, &[]).context("failed to find branches directory")?
|
||||
{
|
||||
let file_path = std::path::Path::new(&file_path);
|
||||
add_file_to_index(
|
||||
gb_repository,
|
||||
@ -742,18 +817,17 @@ fn build_session_tree(gb_repository: &Repository) -> Result<git::Oid> {
|
||||
let mut index = git::Index::new()?;
|
||||
|
||||
// add all files in the working directory to the in-memory index, skipping for matching entries in the repo index
|
||||
for file_path in
|
||||
fs::list_files(gb_repository.session_path()).context("failed to list session files")?
|
||||
for file_path in fs::list_files(
|
||||
gb_repository.session_path(),
|
||||
&[path::Path::new("wd").to_path_buf()],
|
||||
)
|
||||
.context("failed to list session files")?
|
||||
{
|
||||
let file_path = std::path::Path::new(&file_path);
|
||||
if file_path.starts_with("wd/") {
|
||||
continue;
|
||||
}
|
||||
add_file_to_index(
|
||||
gb_repository,
|
||||
&mut index,
|
||||
file_path,
|
||||
&gb_repository.session_path().join(file_path),
|
||||
&file_path,
|
||||
&gb_repository.session_path().join(&file_path),
|
||||
)
|
||||
.with_context(|| format!("failed to add session file: {}", file_path.display()))?;
|
||||
}
|
||||
@ -809,7 +883,7 @@ fn write_gb_commit(
|
||||
let comitter = git::Signature::now("gitbutler", "gitbutler@localhost")?;
|
||||
let author = match user {
|
||||
None => comitter.clone(),
|
||||
Some(user) => git::Signature::now(user.name.as_str(), user.email.as_str())?,
|
||||
Some(user) => git::Signature::try_from(user)?,
|
||||
};
|
||||
|
||||
let current_refname: git::Refname = "refs/heads/current".parse().unwrap();
|
||||
|
@ -34,9 +34,19 @@ impl TryFrom<&users::User> for Signature<'_> {
|
||||
type Error = super::Error;
|
||||
|
||||
fn try_from(value: &users::User) -> Result<Self, Self::Error> {
|
||||
git2::Signature::now(&value.name, &value.email)
|
||||
.map(Into::into)
|
||||
.map_err(Into::into)
|
||||
if let Some(name) = &value.name {
|
||||
git2::Signature::now(name, &value.email)
|
||||
.map(Into::into)
|
||||
.map_err(Into::into)
|
||||
} else if let Some(name) = &value.given_name {
|
||||
git2::Signature::now(name, &value.email)
|
||||
.map(Into::into)
|
||||
.map_err(Into::into)
|
||||
} else {
|
||||
git2::Signature::now(&value.email, &value.email)
|
||||
.map(Into::into)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,12 +53,10 @@ impl Reader for DirReader {
|
||||
}
|
||||
|
||||
fn list_files(&self, dir_path: &path::Path) -> Result<Vec<path::PathBuf>> {
|
||||
fs::list_files(self.root.join(dir_path)).map(|files| {
|
||||
files
|
||||
.into_iter()
|
||||
.filter(|f| !f.starts_with(".git"))
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
fs::list_files(
|
||||
self.root.join(dir_path),
|
||||
&[path::Path::new(".git").to_path_buf()],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,10 +40,18 @@ pub fn init(package_info: &PackageInfo) -> ClientInitGuard {
|
||||
/// Sets the current user in the Sentry scope.
|
||||
/// There is only one scope in the application, so this will overwrite any previous user.
|
||||
pub fn configure_scope(user: Option<&users::User>) {
|
||||
let name = match user {
|
||||
Some(user) => match &user.name {
|
||||
Some(name) => Some(name.clone()),
|
||||
None => user.given_name.as_ref().cloned(),
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
|
||||
sentry::configure_scope(|scope| {
|
||||
scope.set_user(user.map(|user| sentry::User {
|
||||
id: Some(user.id.to_string()),
|
||||
username: Some(user.name.clone()),
|
||||
username: name,
|
||||
email: Some(user.email.clone()),
|
||||
..Default::default()
|
||||
}));
|
||||
|
@ -32,7 +32,7 @@ impl Database {
|
||||
stmt.execute(rusqlite::named_params! {
|
||||
":id": session.id,
|
||||
":project_id": project_id,
|
||||
":hash": session.hash,
|
||||
":hash": session.hash.map(|hash| hash.to_string()),
|
||||
":branch": session.meta.branch,
|
||||
":commit": session.meta.commit,
|
||||
":start_timestamp_ms": session.meta.start_timestamp_ms.to_string(),
|
||||
@ -127,7 +127,11 @@ impl Database {
|
||||
fn parse_row(row: &rusqlite::Row) -> Result<session::Session> {
|
||||
Ok(session::Session {
|
||||
id: row.get(0).context("Failed to get id")?,
|
||||
hash: row.get(2).context("Failed to get hash")?,
|
||||
hash: row
|
||||
.get::<usize, Option<String>>(2)
|
||||
.context("Failed to get hash")?
|
||||
.map(|hash| hash.parse().context("Failed to parse hash"))
|
||||
.transpose()?,
|
||||
meta: session::Meta {
|
||||
branch: row.get(3).context("Failed to get branch")?,
|
||||
commit: row.get(4).context("Failed to get commit")?,
|
||||
@ -212,7 +216,7 @@ mod tests {
|
||||
};
|
||||
let session2 = session::Session {
|
||||
id: SessionId::generate(),
|
||||
hash: Some("hash2".to_string()),
|
||||
hash: Some("08f23df1b9c2dec3d0c826a3ae745f9b821a1a26".parse().unwrap()),
|
||||
meta: session::Meta {
|
||||
branch: Some("branch2".to_string()),
|
||||
commit: Some("commit2".to_string()),
|
||||
@ -253,7 +257,7 @@ mod tests {
|
||||
};
|
||||
let session_updated = session::Session {
|
||||
id: session.id,
|
||||
hash: Some("hash2".to_string()),
|
||||
hash: Some("08f23df1b9c2dec3d0c826a3ae745f9b821a1a26".parse().unwrap()),
|
||||
meta: session::Meta {
|
||||
branch: Some("branch2".to_string()),
|
||||
commit: Some("commit2".to_string()),
|
||||
|
@ -3,7 +3,7 @@ use std::{collections::HashMap, path};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
|
||||
use crate::{
|
||||
gb_repository, git,
|
||||
gb_repository,
|
||||
reader::{self, CommitReader, Reader},
|
||||
};
|
||||
|
||||
@ -62,13 +62,9 @@ impl<'reader> SessionReader<'reader> {
|
||||
));
|
||||
};
|
||||
|
||||
let oid: git::Oid = session_hash
|
||||
.parse()
|
||||
.context(format!("failed to parse commit hash {}", session_hash))?;
|
||||
|
||||
let commit = repository
|
||||
.git_repository()
|
||||
.find_commit(oid)
|
||||
.find_commit(*session_hash)
|
||||
.context("failed to get commit")?;
|
||||
let commit_reader =
|
||||
reader::CommitReader::from_commit(repository.git_repository(), &commit)?;
|
||||
|
@ -4,7 +4,7 @@ use anyhow::{Context, Result};
|
||||
use serde::Serialize;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{id::Id, reader};
|
||||
use crate::{git, id::Id, reader};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@ -26,7 +26,7 @@ pub type SessionId = Id<Session>;
|
||||
pub struct Session {
|
||||
pub id: SessionId,
|
||||
// if hash is not set, the session is not saved aka current
|
||||
pub hash: Option<String>,
|
||||
pub hash: Option<git::Oid>,
|
||||
pub meta: Meta,
|
||||
}
|
||||
|
||||
@ -107,7 +107,7 @@ impl<'reader> TryFrom<reader::CommitReader<'reader>> for Session {
|
||||
type Error = SessionError;
|
||||
|
||||
fn try_from(reader: reader::CommitReader<'reader>) -> Result<Self, Self::Error> {
|
||||
let commit_oid = reader.get_commit_oid().to_string();
|
||||
let commit_oid = reader.get_commit_oid();
|
||||
let session = Session::try_from(&reader as &dyn reader::Reader)?;
|
||||
Ok(Session {
|
||||
hash: Some(commit_oid),
|
||||
|
@ -13,7 +13,7 @@ fn test_should_not_write_session_with_hash() {
|
||||
|
||||
let session = sessions::Session {
|
||||
id: SessionId::generate(),
|
||||
hash: Some("hash".to_string()),
|
||||
hash: Some("08f23df1b9c2dec3d0c826a3ae745f9b821a1a26".parse().unwrap()),
|
||||
meta: sessions::Meta {
|
||||
start_timestamp_ms: 0,
|
||||
last_timestamp_ms: 1,
|
||||
|
@ -35,7 +35,7 @@ impl Default for Suite {
|
||||
impl Suite {
|
||||
pub fn sign_in(&self) -> users::User {
|
||||
let user = users::User {
|
||||
name: "test".to_string(),
|
||||
name: Some("test".to_string()),
|
||||
email: "test@email.com".to_string(),
|
||||
access_token: "token".to_string(),
|
||||
..Default::default()
|
||||
|
@ -5,7 +5,7 @@ use crate::git;
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
|
||||
pub struct User {
|
||||
pub id: u64,
|
||||
pub name: String,
|
||||
pub name: Option<String>,
|
||||
pub given_name: Option<String>,
|
||||
pub family_name: Option<String>,
|
||||
pub email: String,
|
||||
@ -23,6 +23,12 @@ impl TryFrom<User> for git::Signature<'_> {
|
||||
type Error = git::Error;
|
||||
|
||||
fn try_from(value: User) -> Result<Self, Self::Error> {
|
||||
git::Signature::now(&value.name, &value.email)
|
||||
if let Some(name) = value.name {
|
||||
git::Signature::now(&name, &value.email)
|
||||
} else if let Some(name) = value.given_name {
|
||||
git::Signature::now(&name, &value.email)
|
||||
} else {
|
||||
git::Signature::now(&value.email, &value.email)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,6 @@ pub fn list_remote_branches(
|
||||
.context("failed to convert branches")?
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.filter(|branch| branch.name.branch() != Some(default_target.branch.branch()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Ok(remote_branches)
|
||||
|
@ -191,7 +191,9 @@ mod test {
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::{
|
||||
deltas, sessions,
|
||||
deltas,
|
||||
reader::Reader,
|
||||
sessions,
|
||||
test_utils::{self, Case, Suite},
|
||||
virtual_branches::{self, branch},
|
||||
};
|
||||
@ -923,4 +925,262 @@ mod test {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
mod flush_wd {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn should_add_new_files_to_session_wd() {
|
||||
let suite = Suite::default();
|
||||
let Case {
|
||||
gb_repository,
|
||||
project,
|
||||
project_repository,
|
||||
..
|
||||
} = suite.new_case();
|
||||
let listener = Handler::from(&suite.local_app_data);
|
||||
|
||||
// write a file into session
|
||||
std::fs::write(project.path.join("test.txt"), "hello world!").unwrap();
|
||||
listener.handle("test.txt", &project.id).unwrap();
|
||||
|
||||
let flushed_session = gb_repository
|
||||
.flush(&project_repository, None)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
{
|
||||
// after flush it should be flushed into the commit
|
||||
let session_commit = gb_repository
|
||||
.git_repository()
|
||||
.find_commit(flushed_session.hash.unwrap())
|
||||
.unwrap();
|
||||
let commit_reader = reader::CommitReader::from_commit(
|
||||
gb_repository.git_repository(),
|
||||
&session_commit,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
commit_reader.list_files(path::Path::new("wd")).unwrap(),
|
||||
vec![path::Path::new("test.txt")]
|
||||
);
|
||||
assert_eq!(
|
||||
commit_reader.read(path::Path::new("wd/test.txt")).unwrap(),
|
||||
reader::Content::UTF8("hello world!".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
// write another file into session
|
||||
std::fs::create_dir_all(project.path.join("one/two")).unwrap();
|
||||
std::fs::write(project.path.join("one/two/test2.txt"), "hello world!").unwrap();
|
||||
listener.handle("one/two/test2.txt", &project.id).unwrap();
|
||||
|
||||
let flushed_session = gb_repository
|
||||
.flush(&project_repository, None)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
{
|
||||
// after flush it should be flushed into the commit next to the previous one
|
||||
let session_commit = gb_repository
|
||||
.git_repository()
|
||||
.find_commit(flushed_session.hash.unwrap())
|
||||
.unwrap();
|
||||
let commit_reader = reader::CommitReader::from_commit(
|
||||
gb_repository.git_repository(),
|
||||
&session_commit,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
commit_reader.list_files(path::Path::new("wd")).unwrap(),
|
||||
vec![
|
||||
path::Path::new("one/two/test2.txt"),
|
||||
path::Path::new("test.txt"),
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
commit_reader.read(path::Path::new("wd/test.txt")).unwrap(),
|
||||
reader::Content::UTF8("hello world!".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
commit_reader
|
||||
.read(path::Path::new("wd/one/two/test2.txt"))
|
||||
.unwrap(),
|
||||
reader::Content::UTF8("hello world!".to_string())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_remove_deleted_files_from_session_wd() {
|
||||
let suite = Suite::default();
|
||||
let Case {
|
||||
gb_repository,
|
||||
project,
|
||||
project_repository,
|
||||
..
|
||||
} = suite.new_case();
|
||||
let listener = Handler::from(&suite.local_app_data);
|
||||
|
||||
// write a file into session
|
||||
std::fs::write(project.path.join("test.txt"), "hello world!").unwrap();
|
||||
listener.handle("test.txt", &project.id).unwrap();
|
||||
std::fs::create_dir_all(project.path.join("one/two")).unwrap();
|
||||
std::fs::write(project.path.join("one/two/test2.txt"), "hello world!").unwrap();
|
||||
listener.handle("one/two/test2.txt", &project.id).unwrap();
|
||||
|
||||
let flushed_session = gb_repository
|
||||
.flush(&project_repository, None)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
{
|
||||
// after flush it should be flushed into the commit
|
||||
let session_commit = gb_repository
|
||||
.git_repository()
|
||||
.find_commit(flushed_session.hash.unwrap())
|
||||
.unwrap();
|
||||
let commit_reader = reader::CommitReader::from_commit(
|
||||
gb_repository.git_repository(),
|
||||
&session_commit,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
commit_reader.list_files(path::Path::new("wd")).unwrap(),
|
||||
vec![
|
||||
path::Path::new("one/two/test2.txt"),
|
||||
path::Path::new("test.txt"),
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
commit_reader.read(path::Path::new("wd/test.txt")).unwrap(),
|
||||
reader::Content::UTF8("hello world!".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
commit_reader
|
||||
.read(path::Path::new("wd/one/two/test2.txt"))
|
||||
.unwrap(),
|
||||
reader::Content::UTF8("hello world!".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
// rm the files
|
||||
std::fs::remove_file(project.path.join("test.txt")).unwrap();
|
||||
listener.handle("test.txt", &project.id).unwrap();
|
||||
std::fs::remove_file(project.path.join("one/two/test2.txt")).unwrap();
|
||||
listener.handle("one/two/test2.txt", &project.id).unwrap();
|
||||
|
||||
let flushed_session = gb_repository
|
||||
.flush(&project_repository, None)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
{
|
||||
// after flush it should be removed from the commit
|
||||
let session_commit = gb_repository
|
||||
.git_repository()
|
||||
.find_commit(flushed_session.hash.unwrap())
|
||||
.unwrap();
|
||||
let commit_reader = reader::CommitReader::from_commit(
|
||||
gb_repository.git_repository(),
|
||||
&session_commit,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(commit_reader
|
||||
.list_files(path::Path::new("wd"))
|
||||
.unwrap()
|
||||
.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_update_updated_files_in_session_wd() {
|
||||
let suite = Suite::default();
|
||||
let Case {
|
||||
gb_repository,
|
||||
project,
|
||||
project_repository,
|
||||
..
|
||||
} = suite.new_case();
|
||||
let listener = Handler::from(&suite.local_app_data);
|
||||
|
||||
// write a file into session
|
||||
std::fs::write(project.path.join("test.txt"), "hello world!").unwrap();
|
||||
listener.handle("test.txt", &project.id).unwrap();
|
||||
std::fs::create_dir_all(project.path.join("one/two")).unwrap();
|
||||
std::fs::write(project.path.join("one/two/test2.txt"), "hello world!").unwrap();
|
||||
listener.handle("one/two/test2.txt", &project.id).unwrap();
|
||||
|
||||
let flushed_session = gb_repository
|
||||
.flush(&project_repository, None)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
{
|
||||
// after flush it should be flushed into the commit
|
||||
let session_commit = gb_repository
|
||||
.git_repository()
|
||||
.find_commit(flushed_session.hash.unwrap())
|
||||
.unwrap();
|
||||
let commit_reader = reader::CommitReader::from_commit(
|
||||
gb_repository.git_repository(),
|
||||
&session_commit,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
commit_reader.list_files(path::Path::new("wd")).unwrap(),
|
||||
vec![
|
||||
path::Path::new("one/two/test2.txt"),
|
||||
path::Path::new("test.txt"),
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
commit_reader.read(path::Path::new("wd/test.txt")).unwrap(),
|
||||
reader::Content::UTF8("hello world!".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
commit_reader
|
||||
.read(path::Path::new("wd/one/two/test2.txt"))
|
||||
.unwrap(),
|
||||
reader::Content::UTF8("hello world!".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
// update the file
|
||||
std::fs::write(project.path.join("test.txt"), "hello world!2").unwrap();
|
||||
listener.handle("test.txt", &project.id).unwrap();
|
||||
|
||||
std::fs::write(project.path.join("one/two/test2.txt"), "hello world!2").unwrap();
|
||||
listener.handle("one/two/test2.txt", &project.id).unwrap();
|
||||
|
||||
let flushed_session = gb_repository
|
||||
.flush(&project_repository, None)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
{
|
||||
// after flush it should be updated in the commit
|
||||
let session_commit = gb_repository
|
||||
.git_repository()
|
||||
.find_commit(flushed_session.hash.unwrap())
|
||||
.unwrap();
|
||||
let commit_reader = reader::CommitReader::from_commit(
|
||||
gb_repository.git_repository(),
|
||||
&session_commit,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
commit_reader.list_files(path::Path::new("wd")).unwrap(),
|
||||
vec![
|
||||
path::Path::new("one/two/test2.txt"),
|
||||
path::Path::new("test.txt"),
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
commit_reader.read(path::Path::new("wd/test.txt")).unwrap(),
|
||||
reader::Content::UTF8("hello world!2".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
commit_reader
|
||||
.read(path::Path::new("wd/one/two/test2.txt"))
|
||||
.unwrap(),
|
||||
reader::Content::UTF8("hello world!2".to_string())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,11 @@
|
||||
use anyhow::{Context, Result};
|
||||
use tauri::{AppHandle, Manager};
|
||||
|
||||
use crate::{assets, events as app_events, projects::ProjectId, virtual_branches};
|
||||
use crate::{
|
||||
assets, events as app_events,
|
||||
projects::ProjectId,
|
||||
virtual_branches::{self, controller::ControllerError},
|
||||
};
|
||||
|
||||
use super::events;
|
||||
|
||||
@ -26,16 +30,19 @@ impl TryFrom<&AppHandle> for Handler {
|
||||
|
||||
impl Handler {
|
||||
pub async fn handle(&self, project_id: &ProjectId) -> Result<Vec<events::Event>> {
|
||||
let branches = self
|
||||
match self
|
||||
.vbranch_controller
|
||||
.list_virtual_branches(project_id)
|
||||
.await
|
||||
.context("failed to list virtual branches")?;
|
||||
|
||||
let branches = self.assets_proxy.proxy_virtual_branches(branches).await;
|
||||
|
||||
Ok(vec![events::Event::Emit(
|
||||
app_events::Event::virtual_branches(project_id, &branches),
|
||||
)])
|
||||
{
|
||||
Ok(branches) => Ok(vec![events::Event::Emit(
|
||||
app_events::Event::virtual_branches(
|
||||
project_id,
|
||||
&self.assets_proxy.proxy_virtual_branches(branches).await,
|
||||
),
|
||||
)]),
|
||||
Err(ControllerError::VerifyError(_)) => Ok(vec![]),
|
||||
Err(error) => Err(error).context("failed to list virtual branches"),
|
||||
}
|
||||
}
|
||||
}
|
@ -179,7 +179,8 @@ mod test {
|
||||
|
||||
fn create_new_session_via_new_file(project: &projects::Project, suite: &Suite) {
|
||||
fs::write(project.path.join("test.txt"), "test").unwrap();
|
||||
let file_change_listener = handlers::session_handler::Handler::from(&suite.local_app_data);
|
||||
let file_change_listener =
|
||||
handlers::calculate_deltas_handler::Handler::from(&suite.local_app_data);
|
||||
file_change_listener
|
||||
.handle("test.txt", &project.id)
|
||||
.unwrap();
|
||||
|
@ -1,4 +1,6 @@
|
||||
mod analytics_handler;
|
||||
mod calculate_deltas_handler;
|
||||
mod caltulate_virtual_branches_handler;
|
||||
mod fetch_gitbutler_data;
|
||||
mod fetch_project_data;
|
||||
mod flush_session;
|
||||
@ -7,9 +9,7 @@ mod index_handler;
|
||||
mod project_file_change;
|
||||
mod push_gitbutler_data;
|
||||
mod push_project_to_gitbutler;
|
||||
mod session_handler;
|
||||
mod tick_handler;
|
||||
mod vbranch_handler;
|
||||
|
||||
use std::time;
|
||||
|
||||
@ -33,8 +33,8 @@ pub struct Handler {
|
||||
analytics_handler: analytics_handler::Handler,
|
||||
index_handler: index_handler::Handler,
|
||||
push_project_to_gitbutler: push_project_to_gitbutler::Handler,
|
||||
virtual_branch_handler: vbranch_handler::Handler,
|
||||
session_processing_handler: session_handler::Handler,
|
||||
calculate_vbranches_handler: caltulate_virtual_branches_handler::Handler,
|
||||
calculate_deltas_handler: calculate_deltas_handler::Handler,
|
||||
|
||||
events_sender: app_events::Sender,
|
||||
}
|
||||
@ -55,8 +55,10 @@ impl TryFrom<&AppHandle> for Handler {
|
||||
fetch_gitbutler_handler: fetch_gitbutler_data::Handler::try_from(value)?,
|
||||
analytics_handler: analytics_handler::Handler::from(value),
|
||||
push_project_to_gitbutler: push_project_to_gitbutler::Handler::try_from(value)?,
|
||||
virtual_branch_handler: vbranch_handler::Handler::try_from(value)?,
|
||||
session_processing_handler: session_handler::Handler::try_from(value)?,
|
||||
calculate_vbranches_handler: caltulate_virtual_branches_handler::Handler::try_from(
|
||||
value,
|
||||
)?,
|
||||
calculate_deltas_handler: calculate_deltas_handler::Handler::try_from(value)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -138,13 +140,13 @@ impl Handler {
|
||||
}
|
||||
|
||||
events::Event::CalculateVirtualBranches(project_id) => self
|
||||
.virtual_branch_handler
|
||||
.calculate_vbranches_handler
|
||||
.handle(project_id)
|
||||
.await
|
||||
.context("failed to handle virtual branch event"),
|
||||
|
||||
events::Event::CalculateDeltas(project_id, path) => self
|
||||
.session_processing_handler
|
||||
.calculate_deltas_handler
|
||||
.handle(path, project_id)
|
||||
.context(format!(
|
||||
"failed to handle session processing event: {:?}",
|
||||
|
@ -29,7 +29,7 @@ export type LoginToken = {
|
||||
|
||||
export type User = {
|
||||
id: number;
|
||||
name: string;
|
||||
name: string | undefined;
|
||||
given_name: string | undefined;
|
||||
family_name: string | undefined;
|
||||
email: string;
|
||||
|
@ -52,7 +52,7 @@
|
||||
// We account for the NewBranchDropZone by subtracting 2
|
||||
for (let i = 0; i < children.length - 2; i++) {
|
||||
const pos = children[i].getBoundingClientRect();
|
||||
if (e.clientX > pos.left + pos.width) {
|
||||
if (e.clientX > pos.left + dragged.offsetWidth / 2) {
|
||||
dropPosition = i + 1; // Note that this is declared in the <script>
|
||||
} else {
|
||||
break;
|
||||
@ -91,6 +91,7 @@
|
||||
// We rely on elements with id `drag-handle` to initiate this drag
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
return;
|
||||
}
|
||||
clone = cloneNode(e.target);
|
||||
document.body.appendChild(clone);
|
||||
|
Loading…
Reference in New Issue
Block a user