mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2025-01-06 01:27:24 +03:00
remove files database
This commit is contained in:
parent
3baa937129
commit
33cf7e7e63
@ -6,7 +6,7 @@ use tauri::{AppHandle, Manager};
|
||||
use tokio::task;
|
||||
|
||||
use crate::{
|
||||
bookmarks, deltas, files, gb_repository,
|
||||
bookmarks, deltas, gb_repository,
|
||||
git::{self, diff},
|
||||
keys,
|
||||
project_repository::{self, activity, conflicts},
|
||||
@ -24,7 +24,6 @@ pub struct App {
|
||||
searcher: search::Searcher,
|
||||
watchers: watcher::Watchers,
|
||||
sessions_database: sessions::Database,
|
||||
files_database: files::Database,
|
||||
deltas_database: deltas::Database,
|
||||
bookmarks_database: bookmarks::Database,
|
||||
}
|
||||
@ -55,7 +54,6 @@ impl TryFrom<&AppHandle> for App {
|
||||
watchers: value.state::<watcher::Watchers>().inner().clone(),
|
||||
sessions_database: sessions::Database::try_from(value)?,
|
||||
deltas_database: deltas::Database::try_from(value)?,
|
||||
files_database: files::Database::try_from(value)?,
|
||||
bookmarks_database: bookmarks::Database::try_from(value)?,
|
||||
})
|
||||
}
|
||||
@ -242,8 +240,19 @@ impl App {
|
||||
session_id: &str,
|
||||
paths: Option<Vec<&str>>,
|
||||
) -> Result<HashMap<String, String>> {
|
||||
self.files_database
|
||||
.list_by_project_id_session_id(project_id, session_id, paths)
|
||||
let session = self
|
||||
.sessions_database
|
||||
.get_by_project_id_id(project_id, session_id)
|
||||
.context("failed to get session")?
|
||||
.context("session not found")?;
|
||||
let gb_repo = self
|
||||
.gb_repository(project_id)
|
||||
.context("failed to open repository")?;
|
||||
let session_reader =
|
||||
sessions::Reader::open(&gb_repo, &session).context("failed to open session reader")?;
|
||||
session_reader
|
||||
.files(paths)
|
||||
.context("failed to read session files")
|
||||
}
|
||||
|
||||
pub fn mark_resolved(&self, project_id: &str, path: &str) -> Result<()> {
|
||||
|
@ -0,0 +1 @@
|
||||
CREATE INDEX `sessions_project_id_id_index` ON `sessions` (`project_id`, `id`);
|
@ -0,0 +1,2 @@
|
||||
DROP TABLE files;
|
||||
DROP TABLE contents;
|
@ -1,228 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use sha1::{Digest, Sha1};
|
||||
use tauri::{AppHandle, Manager};
|
||||
|
||||
use crate::database;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Database {
|
||||
database: database::Database,
|
||||
}
|
||||
|
||||
impl From<database::Database> for Database {
|
||||
fn from(database: database::Database) -> Self {
|
||||
Self { database }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&AppHandle> for Database {
|
||||
fn from(value: &AppHandle) -> Self {
|
||||
Self::from(value.state::<database::Database>().inner().clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Database {
|
||||
pub fn insert(
|
||||
&self,
|
||||
project_id: &str,
|
||||
session_id: &str,
|
||||
file_path: &str,
|
||||
content: &str,
|
||||
) -> Result<()> {
|
||||
let mut hasher = Sha1::new();
|
||||
hasher.update(content);
|
||||
let sha1 = hasher.finalize();
|
||||
|
||||
self.database.transaction(|tx| -> Result<()> {
|
||||
let mut stmt = is_content_exist_by_sha1_stmt(tx)
|
||||
.context("Failed to prepare is_content_exist_by_sha1 statement")?;
|
||||
let mut rows = stmt
|
||||
.query(rusqlite::named_params! {
|
||||
":sha1": sha1.as_slice(),
|
||||
})
|
||||
.context("Failed to execute is_content_exist_by_sha1 statement")?;
|
||||
let is_content_exist: bool = rows
|
||||
.next()
|
||||
.context("Failed to iterate over is_content_exist_by_sha1 results")?
|
||||
.is_some();
|
||||
|
||||
if !is_content_exist {
|
||||
let mut stmt =
|
||||
insert_content_stmt(tx).context("Failed to prepare insert statement")?;
|
||||
stmt.execute(rusqlite::named_params! {
|
||||
":sha1": sha1.as_slice(),
|
||||
":content": content,
|
||||
})
|
||||
.context("Failed to execute insert statement")?;
|
||||
}
|
||||
|
||||
let mut stmt =
|
||||
insert_file_stmt(tx).context("Failed to prepare insert file statement")?;
|
||||
stmt.execute(rusqlite::named_params! {
|
||||
":project_id": project_id,
|
||||
":session_id": session_id,
|
||||
":file_path": file_path,
|
||||
":sha1": sha1.as_slice(),
|
||||
})
|
||||
.context("Failed to execute insert statement")?;
|
||||
Ok(())
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn list_by_project_id_session_id(
|
||||
&self,
|
||||
project_id: &str,
|
||||
session_id: &str,
|
||||
file_path_filter: Option<Vec<&str>>,
|
||||
) -> Result<HashMap<String, String>> {
|
||||
self.database
|
||||
.transaction(|tx| -> Result<HashMap<String, String>> {
|
||||
let mut stmt = list_by_project_id_session_id_stmt(tx)
|
||||
.context("Failed to prepare list_by_session_id statement")?;
|
||||
let mut rows = stmt
|
||||
.query(rusqlite::named_params! {
|
||||
":project_id": project_id,
|
||||
":session_id": session_id,
|
||||
})
|
||||
.context("Failed to execute list_by_session_id statement")?;
|
||||
|
||||
let mut files = HashMap::new();
|
||||
while let Some(row) = rows
|
||||
.next()
|
||||
.context("Failed to iterate over list_by_session_id results")?
|
||||
{
|
||||
let file_path: String = row.get(0)?;
|
||||
if let Some(file_path_filter) = &file_path_filter {
|
||||
if !file_path_filter.contains(&file_path.as_str()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let content: String = row.get(1)?;
|
||||
files.insert(file_path, content);
|
||||
}
|
||||
Ok(files)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn list_by_project_id_session_id_stmt<'conn>(
|
||||
tx: &'conn rusqlite::Transaction,
|
||||
) -> Result<rusqlite::CachedStatement<'conn>> {
|
||||
Ok(tx.prepare_cached(
|
||||
"SELECT `file_path`, `content`
|
||||
FROM `files`
|
||||
JOIN `contents` ON `files`.`sha1` = `contents`.`sha1`
|
||||
WHERE `project_id` = :project_id AND `session_id` = :session_id",
|
||||
)?)
|
||||
}
|
||||
|
||||
fn is_content_exist_by_sha1_stmt<'conn>(
|
||||
tx: &'conn rusqlite::Transaction,
|
||||
) -> Result<rusqlite::CachedStatement<'conn>> {
|
||||
Ok(tx.prepare_cached(
|
||||
"SELECT 1
|
||||
FROM `contents`
|
||||
WHERE `sha1` = :sha1",
|
||||
)?)
|
||||
}
|
||||
|
||||
fn insert_content_stmt<'conn>(
|
||||
tx: &'conn rusqlite::Transaction,
|
||||
) -> Result<rusqlite::CachedStatement<'conn>> {
|
||||
Ok(tx.prepare_cached(
|
||||
"INSERT INTO `contents` (
|
||||
`sha1`, `content`
|
||||
) VALUES (
|
||||
:sha1, :content
|
||||
)",
|
||||
)?)
|
||||
}
|
||||
|
||||
fn insert_file_stmt<'conn>(
|
||||
tx: &'conn rusqlite::Transaction,
|
||||
) -> Result<rusqlite::CachedStatement<'conn>> {
|
||||
Ok(tx.prepare_cached(
|
||||
"INSERT INTO `files` (
|
||||
`project_id`, `session_id`, `file_path`, `sha1`
|
||||
) VALUES (
|
||||
:project_id, :session_id, :file_path, :sha1
|
||||
) ON CONFLICT(`project_id`, `session_id`, `file_path`)
|
||||
DO UPDATE SET `sha1` = :sha1",
|
||||
)?)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_insert_query_with_filter() -> Result<()> {
|
||||
let db = database::Database::memory()?;
|
||||
let database = Database::from(db);
|
||||
|
||||
let project_id = "project_id";
|
||||
let session_id = "session_id";
|
||||
let file_path = "file_path";
|
||||
|
||||
let file = "file";
|
||||
database
|
||||
.insert(project_id, session_id, file_path, file)
|
||||
.context("Failed to insert file")?;
|
||||
|
||||
assert_eq!(
|
||||
database
|
||||
.list_by_project_id_session_id(project_id, session_id, Some(vec!["file_path"]))
|
||||
.context("filed to list by session id")?,
|
||||
{
|
||||
let mut files = HashMap::new();
|
||||
files.insert(String::from(file_path), file.to_string());
|
||||
files
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
database
|
||||
.list_by_project_id_session_id(project_id, session_id, Some(vec!["file_path2"]))
|
||||
.context("filed to list by session id")?,
|
||||
HashMap::new()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_upsert() -> Result<()> {
|
||||
let db = database::Database::memory()?;
|
||||
let database = Database::from(db);
|
||||
|
||||
let project_id = "project_id";
|
||||
let session_id = "session_id";
|
||||
let file_path = "file_path";
|
||||
|
||||
let file = "file";
|
||||
database
|
||||
.insert(project_id, session_id, file_path, file)
|
||||
.context("Failed to insert file1")?;
|
||||
|
||||
let file2 = "file2";
|
||||
database
|
||||
.insert(project_id, session_id, file_path, file2)
|
||||
.context("Failed to insert file2")?;
|
||||
|
||||
assert_eq!(
|
||||
database
|
||||
.list_by_project_id_session_id(project_id, session_id, None)
|
||||
.context("filed to list by session id")?,
|
||||
{
|
||||
let mut files = HashMap::new();
|
||||
files.insert(String::from(file_path), file2.to_string());
|
||||
files
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
mod database;
|
||||
|
||||
pub use database::Database;
|
@ -7,7 +7,6 @@ pub mod dedup;
|
||||
pub mod deltas;
|
||||
pub mod error;
|
||||
pub mod events;
|
||||
pub mod files;
|
||||
pub mod fs;
|
||||
pub mod gb_repository;
|
||||
pub mod git;
|
||||
|
@ -79,6 +79,31 @@ impl Database {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_by_project_id_id(
|
||||
&self,
|
||||
project_id: &str,
|
||||
id: &str,
|
||||
) -> Result<Option<session::Session>> {
|
||||
self.database.transaction(|tx| {
|
||||
let mut stmt = get_by_project_id_id_stmt(tx)
|
||||
.context("Failed to prepare get_by_project_id_id statement")?;
|
||||
let mut rows = stmt
|
||||
.query(rusqlite::named_params! {
|
||||
":project_id": project_id,
|
||||
":id": id,
|
||||
})
|
||||
.context("Failed to execute get_by_project_id_id statement")?;
|
||||
if let Some(row) = rows
|
||||
.next()
|
||||
.context("Failed to iterate over get_by_project_id_id results")?
|
||||
{
|
||||
Ok(Some(parse_row(row)?))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_by_id(&self, id: &str) -> Result<Option<session::Session>> {
|
||||
self.database.transaction(|tx| {
|
||||
let mut stmt = get_by_id_stmt(tx).context("Failed to prepare get_by_id statement")?;
|
||||
@ -128,6 +153,14 @@ fn list_by_project_id_stmt<'conn>(
|
||||
)?)
|
||||
}
|
||||
|
||||
fn get_by_project_id_id_stmt<'conn>(
|
||||
tx: &'conn rusqlite::Transaction,
|
||||
) -> Result<rusqlite::CachedStatement<'conn>> {
|
||||
Ok(tx.prepare_cached(
|
||||
"SELECT `id`, `project_id`, `hash`, `branch`, `commit`, `start_timestamp_ms`, `last_timestamp_ms` FROM `sessions` WHERE `project_id` = :project_id AND `id` = :id",
|
||||
)?)
|
||||
}
|
||||
|
||||
fn get_by_id_stmt<'conn>(
|
||||
tx: &'conn rusqlite::Transaction,
|
||||
) -> Result<rusqlite::CachedStatement<'conn>> {
|
||||
|
@ -4,8 +4,7 @@ use anyhow::{Context, Result};
|
||||
use tauri::{AppHandle, Manager};
|
||||
|
||||
use crate::{
|
||||
bookmarks, deltas, events as app_events, files, gb_repository, projects, search, sessions,
|
||||
users,
|
||||
bookmarks, deltas, events as app_events, gb_repository, projects, search, sessions, users,
|
||||
};
|
||||
|
||||
use super::events;
|
||||
@ -16,7 +15,6 @@ pub struct Handler {
|
||||
project_store: projects::Storage,
|
||||
user_store: users::Storage,
|
||||
deltas_searcher: search::Searcher,
|
||||
files_database: files::Database,
|
||||
sessions_database: sessions::Database,
|
||||
deltas_database: deltas::Database,
|
||||
bookmarks_database: bookmarks::Database,
|
||||
@ -35,7 +33,6 @@ impl TryFrom<&AppHandle> for Handler {
|
||||
project_store: projects::Storage::try_from(value)?,
|
||||
user_store: users::Storage::try_from(value)?,
|
||||
deltas_searcher: value.state::<search::Searcher>().inner().clone(),
|
||||
files_database: files::Database::try_from(value)?,
|
||||
sessions_database: sessions::Database::try_from(value)?,
|
||||
deltas_database: deltas::Database::try_from(value)?,
|
||||
bookmarks_database: bookmarks::Database::try_from(value)?,
|
||||
@ -57,19 +54,6 @@ impl Handler {
|
||||
Ok(vec![])
|
||||
}
|
||||
|
||||
pub fn index_file(
|
||||
&self,
|
||||
project_id: &str,
|
||||
session_id: &str,
|
||||
file_path: &str,
|
||||
content: &str,
|
||||
) -> Result<Vec<events::Event>> {
|
||||
self.files_database
|
||||
.insert(project_id, session_id, file_path, content)
|
||||
.context("failed to insert file into database")?;
|
||||
Ok(vec![])
|
||||
}
|
||||
|
||||
pub fn index_bookmark(
|
||||
&self,
|
||||
project_id: &str,
|
||||
@ -145,14 +129,6 @@ impl Handler {
|
||||
project_id, session,
|
||||
))];
|
||||
|
||||
for (file_path, content) in session_reader
|
||||
.files(None)
|
||||
.context("could not list files for session")?
|
||||
{
|
||||
let file_events = self.index_file(project_id, &session.id, &file_path, &content)?;
|
||||
events.extend(file_events);
|
||||
}
|
||||
|
||||
let deltas_reader = deltas::Reader::new(&session_reader);
|
||||
for (file_path, deltas) in deltas_reader
|
||||
.read(None)
|
||||
|
@ -92,22 +92,12 @@ impl Handler {
|
||||
.context("failed to handle flush session event"),
|
||||
|
||||
events::Event::SessionFile((project_id, session_id, file_path, contents)) => {
|
||||
let mut events = self
|
||||
.index_handler
|
||||
.index_file(
|
||||
project_id,
|
||||
session_id,
|
||||
file_path.to_str().unwrap(),
|
||||
contents,
|
||||
)
|
||||
.context("failed to index file")?;
|
||||
events.push(events::Event::Emit(app_events::Event::file(
|
||||
Ok(vec![events::Event::Emit(app_events::Event::file(
|
||||
project_id,
|
||||
session_id,
|
||||
&file_path.display().to_string(),
|
||||
contents,
|
||||
)));
|
||||
Ok(events)
|
||||
))])
|
||||
}
|
||||
|
||||
events::Event::Session(project_id, session) => self
|
||||
|
Loading…
Reference in New Issue
Block a user