From c36f67f148f509d285f8fc3e2fdc96f31138e89d Mon Sep 17 00:00:00 2001 From: Kiril Videlov Date: Sun, 7 Jul 2024 20:00:01 +0200 Subject: [PATCH] move oplog to its own crate This protects us from cyclic dependencies. Unfortunatelly as part of this, i had to introduce the imp Project as trait implementations since now Project is foreign --- Cargo.lock | 22 +++ Cargo.toml | 2 + crates/gitbutler-branch/Cargo.toml | 1 + crates/gitbutler-branch/src/controller.rs | 6 +- crates/gitbutler-branch/src/virtual.rs | 1 + .../tests/virtual_branches/oplog.rs | 1 + crates/gitbutler-cli/Cargo.toml | 1 + crates/gitbutler-cli/src/main.rs | 1 + crates/gitbutler-core/src/fs.rs | 7 +- crates/gitbutler-core/src/lib.rs | 1 - crates/gitbutler-core/tests/core.rs | 1 - crates/gitbutler-core/tests/ops/mod.rs | 1 - crates/gitbutler-oplog/Cargo.toml | 25 +++ .../src/ops => gitbutler-oplog/src}/entry.rs | 4 +- .../ops/mod.rs => gitbutler-oplog/src/lib.rs} | 4 +- .../src/ops => gitbutler-oplog/src}/oplog.rs | 166 ++++++++++-------- .../src/ops => gitbutler-oplog/src}/reflog.rs | 4 +- .../ops => gitbutler-oplog/src}/snapshot.rs | 54 ++++-- .../src/ops => gitbutler-oplog/src}/state.rs | 6 +- .../entry.rs => gitbutler-oplog/tests/mod.rs} | 9 +- crates/gitbutler-sync/Cargo.toml | 1 + crates/gitbutler-sync/src/cloud.rs | 1 + crates/gitbutler-tauri/Cargo.toml | 1 + crates/gitbutler-tauri/src/undo.rs | 7 +- crates/gitbutler-watcher/Cargo.toml | 1 + crates/gitbutler-watcher/src/file_monitor.rs | 2 +- crates/gitbutler-watcher/src/handler.rs | 5 +- 27 files changed, 226 insertions(+), 109 deletions(-) delete mode 100644 crates/gitbutler-core/tests/ops/mod.rs create mode 100644 crates/gitbutler-oplog/Cargo.toml rename crates/{gitbutler-core/src/ops => gitbutler-oplog/src}/entry.rs (97%) rename crates/{gitbutler-core/src/ops/mod.rs => gitbutler-oplog/src/lib.rs} (83%) rename crates/{gitbutler-core/src/ops => gitbutler-oplog/src}/oplog.rs (95%) rename crates/{gitbutler-core/src/ops => gitbutler-oplog/src}/reflog.rs (99%) rename crates/{gitbutler-core/src/ops => gitbutler-oplog/src}/snapshot.rs (82%) rename crates/{gitbutler-core/src/ops => gitbutler-oplog/src}/state.rs (93%) rename crates/{gitbutler-core/tests/ops/entry.rs => gitbutler-oplog/tests/mod.rs} (94%) diff --git a/Cargo.lock b/Cargo.lock index e41f715e1..49b6da752 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2114,6 +2114,7 @@ dependencies = [ "git2-hooks", "gitbutler-core", "gitbutler-git", + "gitbutler-oplog", "gitbutler-testsupport", "glob", "hex", @@ -2138,6 +2139,7 @@ dependencies = [ "chrono", "clap", "gitbutler-core", + "gitbutler-oplog", "pager", ] @@ -2226,6 +2228,23 @@ dependencies = [ "walkdir", ] +[[package]] +name = "gitbutler-oplog" +version = "0.0.0" +dependencies = [ + "anyhow", + "git2", + "gitbutler-core", + "gix", + "itertools 0.13.0", + "pretty_assertions", + "serde", + "strum", + "tempfile", + "toml 0.8.14", + "tracing", +] + [[package]] name = "gitbutler-sync" version = "0.0.0" @@ -2233,6 +2252,7 @@ dependencies = [ "anyhow", "git2", "gitbutler-core", + "gitbutler-oplog", "itertools 0.13.0", "tracing", ] @@ -2250,6 +2270,7 @@ dependencies = [ "git2", "gitbutler-branch", "gitbutler-core", + "gitbutler-oplog", "gitbutler-testsupport", "gitbutler-watcher", "log", @@ -2300,6 +2321,7 @@ dependencies = [ "gitbutler-branch", "gitbutler-core", "gitbutler-notify-debouncer", + "gitbutler-oplog", "gitbutler-sync", "gix", "notify", diff --git a/Cargo.toml b/Cargo.toml index bf31d7dde..ae3ceaa66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "crates/gitbutler-cli", "crates/gitbutler-branch", "crates/gitbutler-sync", + "crates/gitbutler-oplog", ] resolver = "2" @@ -29,6 +30,7 @@ gitbutler-testsupport = { path = "crates/gitbutler-testsupport" } gitbutler-cli ={ path = "crates/gitbutler-cli" } gitbutler-branch = { path = "crates/gitbutler-branch" } gitbutler-sync = { path = "crates/gitbutler-sync" } +gitbutler-oplog = { path = "crates/gitbutler-oplog" } [profile.release] codegen-units = 1 # Compile crates one after another so the compiler can optimize better diff --git a/crates/gitbutler-branch/Cargo.toml b/crates/gitbutler-branch/Cargo.toml index eacc09349..911979e19 100644 --- a/crates/gitbutler-branch/Cargo.toml +++ b/crates/gitbutler-branch/Cargo.toml @@ -11,6 +11,7 @@ anyhow = "1.0.86" git2.workspace = true tokio.workspace = true gitbutler-core.workspace = true +gitbutler-oplog.workspace = true serde = { workspace = true, features = ["std"]} bstr = "1.9.1" diffy = "0.3.0" diff --git a/crates/gitbutler-branch/src/controller.rs b/crates/gitbutler-branch/src/controller.rs index 4a12533f0..7f0bb57ee 100644 --- a/crates/gitbutler-branch/src/controller.rs +++ b/crates/gitbutler-branch/src/controller.rs @@ -1,11 +1,15 @@ use anyhow::Result; use gitbutler_core::{ git::{credentials::Helper, BranchExt}, - ops::entry::{OperationKind, SnapshotDetails}, project_repository::Repository, projects::FetchResult, types::ReferenceName, }; +use gitbutler_oplog::{ + entry::{OperationKind, SnapshotDetails}, + oplog::Oplog, + snapshot::Snapshot, +}; use std::{path::Path, sync::Arc}; use tokio::sync::Semaphore; diff --git a/crates/gitbutler-branch/src/virtual.rs b/crates/gitbutler-branch/src/virtual.rs index ebadd7281..3f61bd3e4 100644 --- a/crates/gitbutler-branch/src/virtual.rs +++ b/crates/gitbutler-branch/src/virtual.rs @@ -1,3 +1,4 @@ +use gitbutler_oplog::snapshot::Snapshot; use std::borrow::Borrow; #[cfg(target_family = "unix")] use std::os::unix::prelude::PermissionsExt; diff --git a/crates/gitbutler-branch/tests/virtual_branches/oplog.rs b/crates/gitbutler-branch/tests/virtual_branches/oplog.rs index 942230694..483ab36c9 100644 --- a/crates/gitbutler-branch/tests/virtual_branches/oplog.rs +++ b/crates/gitbutler-branch/tests/virtual_branches/oplog.rs @@ -1,4 +1,5 @@ use super::*; +use gitbutler_oplog::oplog::Oplog; use itertools::Itertools; use std::io::Write; use std::path::Path; diff --git a/crates/gitbutler-cli/Cargo.toml b/crates/gitbutler-cli/Cargo.toml index 1bd203fef..92f114051 100644 --- a/crates/gitbutler-cli/Cargo.toml +++ b/crates/gitbutler-cli/Cargo.toml @@ -11,6 +11,7 @@ path = "src/main.rs" [dependencies] gitbutler-core.workspace = true +gitbutler-oplog.workspace = true clap = "4.5.4" anyhow = "1.0.86" chrono = "0.4.10" diff --git a/crates/gitbutler-cli/src/main.rs b/crates/gitbutler-cli/src/main.rs index eb90c3a92..0c8d993eb 100644 --- a/crates/gitbutler-cli/src/main.rs +++ b/crates/gitbutler-cli/src/main.rs @@ -1,5 +1,6 @@ use anyhow::Result; use gitbutler_core::projects::Project; +use gitbutler_oplog::oplog::Oplog; use clap::{arg, Command}; #[cfg(not(windows))] diff --git a/crates/gitbutler-core/src/fs.rs b/crates/gitbutler-core/src/fs.rs index 9c2174b7b..ddcc119bb 100644 --- a/crates/gitbutler-core/src/fs.rs +++ b/crates/gitbutler-core/src/fs.rs @@ -60,10 +60,7 @@ pub fn iter_worktree_files( /// Write a single file so that the write either fully succeeds, or fully fails, /// assuming the containing directory already exists. -pub(crate) fn write>( - file_path: P, - contents: impl AsRef<[u8]>, -) -> anyhow::Result<()> { +pub fn write>(file_path: P, contents: impl AsRef<[u8]>) -> anyhow::Result<()> { let mut temp_file = gix::tempfile::new( file_path.as_ref().parent().unwrap(), ContainingDirectory::Exists, @@ -104,7 +101,7 @@ fn persist_tempfile( /// Reads and parses the state file. /// /// If the file does not exist, it will be created. -pub(crate) fn read_toml_file_or_default(path: &Path) -> Result { +pub fn read_toml_file_or_default(path: &Path) -> Result { let mut file = match File::open(path) { Ok(f) => f, Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(T::default()), diff --git a/crates/gitbutler-core/src/lib.rs b/crates/gitbutler-core/src/lib.rs index 6acd009e4..474d96528 100644 --- a/crates/gitbutler-core/src/lib.rs +++ b/crates/gitbutler-core/src/lib.rs @@ -23,7 +23,6 @@ pub mod git; pub mod id; pub mod keys; pub mod lock; -pub mod ops; pub mod path; pub mod project_repository; pub mod projects; diff --git a/crates/gitbutler-core/tests/core.rs b/crates/gitbutler-core/tests/core.rs index 0b7bdb58b..5709a1d81 100644 --- a/crates/gitbutler-core/tests/core.rs +++ b/crates/gitbutler-core/tests/core.rs @@ -5,7 +5,6 @@ mod suite { mod git; mod keys; mod lock; -mod ops; mod types; pub mod virtual_branches; mod zip; diff --git a/crates/gitbutler-core/tests/ops/mod.rs b/crates/gitbutler-core/tests/ops/mod.rs deleted file mode 100644 index f8eef2635..000000000 --- a/crates/gitbutler-core/tests/ops/mod.rs +++ /dev/null @@ -1 +0,0 @@ -mod entry; diff --git a/crates/gitbutler-oplog/Cargo.toml b/crates/gitbutler-oplog/Cargo.toml new file mode 100644 index 000000000..4b2eb7b1d --- /dev/null +++ b/crates/gitbutler-oplog/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "gitbutler-oplog" +version = "0.0.0" +edition = "2021" +authors = ["GitButler "] +publish = false + +[dependencies] +anyhow = "1.0.86" +git2.workspace = true +gitbutler-core.workspace = true +serde = { workspace = true, features = ["std"]} +itertools = "0.13" +strum = { version = "0.26", features = ["derive"] } +tracing = "0.1.40" +gix = { workspace = true, features = ["dirwalk", "credentials", "parallel"] } +toml = "0.8.13" + +[[test]] +name="oplog" +path = "tests/mod.rs" + +[dev-dependencies] +pretty_assertions = "1.4" +tempfile = "3.10" diff --git a/crates/gitbutler-core/src/ops/entry.rs b/crates/gitbutler-oplog/src/entry.rs similarity index 97% rename from crates/gitbutler-core/src/ops/entry.rs rename to crates/gitbutler-oplog/src/entry.rs index 5e3210f9e..687cfbc30 100644 --- a/crates/gitbutler-core/src/ops/entry.rs +++ b/crates/gitbutler-oplog/src/entry.rs @@ -17,10 +17,10 @@ use serde::Serialize; #[serde(rename_all = "camelCase")] pub struct Snapshot { /// The id of the commit that represents the snapshot - #[serde(rename = "id", with = "crate::serde::oid")] + #[serde(rename = "id", with = "gitbutler_core::serde::oid")] pub commit_id: git2::Oid, /// Snapshot creation time in seconds from Unix epoch seconds, based on a commit as `commit_id`. - #[serde(serialize_with = "crate::serde::as_time_seconds_from_unix_epoch")] + #[serde(serialize_with = "gitbutler_core::serde::as_time_seconds_from_unix_epoch")] pub created_at: git2::Time, /// The number of working directory lines added in the snapshot pub lines_added: usize, diff --git a/crates/gitbutler-core/src/ops/mod.rs b/crates/gitbutler-oplog/src/lib.rs similarity index 83% rename from crates/gitbutler-core/src/ops/mod.rs rename to crates/gitbutler-oplog/src/lib.rs index efa364088..6d98b72ad 100644 --- a/crates/gitbutler-core/src/ops/mod.rs +++ b/crates/gitbutler-oplog/src/lib.rs @@ -1,7 +1,7 @@ pub mod entry; -mod oplog; +pub mod oplog; mod reflog; -mod snapshot; +pub mod snapshot; mod state; /// The name of the file holding our state, useful for watching for changes. diff --git a/crates/gitbutler-core/src/ops/oplog.rs b/crates/gitbutler-oplog/src/oplog.rs similarity index 95% rename from crates/gitbutler-core/src/ops/oplog.rs rename to crates/gitbutler-oplog/src/oplog.rs index 56fde841f..f5dfd855e 100644 --- a/crates/gitbutler-core/src/ops/oplog.rs +++ b/crates/gitbutler-oplog/src/oplog.rs @@ -9,11 +9,11 @@ use std::{fs, path::PathBuf}; use anyhow::Result; use tracing::instrument; -use crate::git::diff::FileDiff; -use crate::virtual_branches::{ +use gitbutler_core::git::diff::FileDiff; +use gitbutler_core::virtual_branches::{ Branch, GITBUTLER_INTEGRATION_COMMIT_AUTHOR_EMAIL, GITBUTLER_INTEGRATION_COMMIT_AUTHOR_NAME, }; -use crate::{git::diff::hunks_by_filepath, git::RepositoryExt, projects::Project}; +use gitbutler_core::{git::diff::hunks_by_filepath, git::RepositoryExt, projects::Project}; use super::{ entry::{OperationKind, Snapshot, SnapshotDetails, Trailer}, @@ -41,11 +41,93 @@ const SNAPSHOT_FILE_LIMIT_BYTES: u64 = 32 * 1024 * 1024; /// │ └── tree (subtree) /// └── virtual_branches.toml /// ``` -impl Project { +pub trait Oplog { /// Prepares a snapshot of the current state of the working directory as well as GitButler data. /// Returns a tree hash of the snapshot. The snapshot is not discoverable until it is committed with [`commit_snapshot`](Self::commit_snapshot()) /// If there are files that are untracked and larger than `SNAPSHOT_FILE_LIMIT_BYTES`, they are excluded from snapshot creation and restoring. - pub fn prepare_snapshot(&self) -> Result { + fn prepare_snapshot(&self) -> Result; + + /// Commits the snapshot tree that is created with the [`prepare_snapshot`](Self::prepare_snapshot) method, + /// which yielded the `snapshot_tree_id` for the entire snapshot state. + /// Use `details` to provide metadata about the snapshot. + /// + /// Committing it makes the snapshot discoverable in [`list_snapshots`](Self::list_snapshots) as well as + /// restorable with [`restore_snapshot`](Self::restore_snapshot). + /// + /// Returns `Some(snapshot_commit_id)` if it was created or `None` if nothing changed between the previous oplog + /// commit and the current one (after comparing trees). + fn commit_snapshot( + &self, + snapshot_tree_id: git2::Oid, + details: SnapshotDetails, + ) -> Result>; + + /// Creates a snapshot of the current state of the working directory as well as GitButler data. + /// This is a convenience method that combines [`prepare_snapshot`](Self::prepare_snapshot) and + /// [`commit_snapshot`](Self::commit_snapshot). + /// + /// Returns `Some(snapshot_commit_id)` if it was created or `None` if nothing changed between the previous oplog + /// commit and the current one (after comparing trees). + /// + /// Note that errors in snapshot creation is typically ignored, so we want to learn about them. + fn create_snapshot(&self, details: SnapshotDetails) -> Result>; + + /// Lists the snapshots that have been created for the given repository, up to the given limit, + /// and with the most recent snapshot first, and at the end of the vec. + /// + /// Use `oplog_commit_id` if the traversal root for snapshot discovery should be the specified commit, which + /// is usually obtained from a previous iteration. Useful along with `limit` to allow starting where the iteration + /// left off. Note that the `oplog_commit_id` is always returned as first item in the result vec. + /// + /// An alternative way of retrieving the snapshots would be to manually the oplog head `git log ` available in `.git/gitbutler/operations-log.toml`. + /// + /// If there are no snapshots, an empty list is returned. + fn list_snapshots( + &self, + limit: usize, + oplog_commit_id: Option, + ) -> Result>; + + /// Reverts to a previous state of the working directory, virtual branches and commits. + /// The provided `snapshot_commit_id` must refer to a valid snapshot commit, as returned by [`create_snapshot`](Self::create_snapshot). + /// Upon success, a new snapshot is created representing the state right before this call. + /// + /// This will restore the following: + /// - The state of the working directory is checked out from the subtree `workdir` in the snapshot. + /// - The state of virtual branches is restored from the blob `virtual_branches.toml` in the snapshot. + /// - The state of conflicts (.git/base_merge_parent and .git/conflicts) is restored from the subtree `conflicts` in the snapshot (if not present, existing files are deleted). + /// + /// If there are files that are untracked and larger than `SNAPSHOT_FILE_LIMIT_BYTES`, they are excluded from snapshot creation and restoring. + /// Returns the sha of the created revert snapshot commit or None if snapshots are disabled. + fn restore_snapshot(&self, snapshot_commit_id: git2::Oid) -> Result>; + + /// Determines if a new snapshot should be created due to file changes being created since the last snapshot. + /// The needs for the automatic snapshotting are: + /// - It needs to facilitate backup of work in progress code + /// - The snapshots should not be too frequent or small - both for UX and performance reasons + /// - Checking if an automatic snapshot is needed should be fast and efficient since it is called on filesystem events + /// + /// Use `check_if_last_snapshot_older_than` as a way to control if the check should be performed at all, i.e. + /// if this is 10s but the last snapshot was done 9s ago, no check if performed and the return value is `false`. + /// + /// This implementation returns `true` on the following conditions: + /// - Head is pointing to the integration branch. + /// - If it's been more than 5 minutes since the last snapshot, + /// check the sum of added and removed lines since the last snapshot, otherwise return `false`. + /// * If the sum of added and removed lines is greater than a configured threshold, return `true`, otherwise return `false`. + fn should_auto_snapshot(&self, check_if_last_snapshot_older_than: Duration) -> Result; + + /// Returns the diff of the snapshot and it's parent. It only includes the workdir changes. + /// + /// This is useful to show what has changed in this particular snapshot + fn snapshot_diff(&self, sha: git2::Oid) -> Result>; + + /// Gets the sha of the last snapshot commit if present. + fn oplog_head(&self) -> Result>; +} + +impl Oplog for Project { + fn prepare_snapshot(&self) -> Result { let worktree_dir = self.path.as_path(); let repo = git2::Repository::open(worktree_dir)?; @@ -162,16 +244,7 @@ impl Project { Ok(tree_id) } - /// Commits the snapshot tree that is created with the [`prepare_snapshot`](Self::prepare_snapshot) method, - /// which yielded the `snapshot_tree_id` for the entire snapshot state. - /// Use `details` to provide metadata about the snapshot. - /// - /// Committing it makes the snapshot discoverable in [`list_snapshots`](Self::list_snapshots) as well as - /// restorable with [`restore_snapshot`](Self::restore_snapshot). - /// - /// Returns `Some(snapshot_commit_id)` if it was created or `None` if nothing changed between the previous oplog - /// commit and the current one (after comparing trees). - pub fn commit_snapshot( + fn commit_snapshot( &self, snapshot_tree_id: git2::Oid, details: SnapshotDetails, @@ -212,31 +285,13 @@ impl Project { Ok(Some(snapshot_commit_id)) } - /// Creates a snapshot of the current state of the working directory as well as GitButler data. - /// This is a convenience method that combines [`prepare_snapshot`](Self::prepare_snapshot) and - /// [`commit_snapshot`](Self::commit_snapshot). - /// - /// Returns `Some(snapshot_commit_id)` if it was created or `None` if nothing changed between the previous oplog - /// commit and the current one (after comparing trees). - /// - /// Note that errors in snapshot creation is typically ignored, so we want to learn about them. #[instrument(skip(details), err(Debug))] - pub fn create_snapshot(&self, details: SnapshotDetails) -> Result> { + fn create_snapshot(&self, details: SnapshotDetails) -> Result> { let tree_id = self.prepare_snapshot()?; self.commit_snapshot(tree_id, details) } - /// Lists the snapshots that have been created for the given repository, up to the given limit, - /// and with the most recent snapshot first, and at the end of the vec. - /// - /// Use `oplog_commit_id` if the traversal root for snapshot discovery should be the specified commit, which - /// is usually obtained from a previous iteration. Useful along with `limit` to allow starting where the iteration - /// left off. Note that the `oplog_commit_id` is always returned as first item in the result vec. - /// - /// An alternative way of retrieving the snapshots would be to manually the oplog head `git log ` available in `.git/gitbutler/operations-log.toml`. - /// - /// If there are no snapshots, an empty list is returned. - pub fn list_snapshots( + fn list_snapshots( &self, limit: usize, oplog_commit_id: Option, @@ -340,18 +395,7 @@ impl Project { Ok(snapshots) } - /// Reverts to a previous state of the working directory, virtual branches and commits. - /// The provided `snapshot_commit_id` must refer to a valid snapshot commit, as returned by [`create_snapshot`](Self::create_snapshot). - /// Upon success, a new snapshot is created representing the state right before this call. - /// - /// This will restore the following: - /// - The state of the working directory is checked out from the subtree `workdir` in the snapshot. - /// - The state of virtual branches is restored from the blob `virtual_branches.toml` in the snapshot. - /// - The state of conflicts (.git/base_merge_parent and .git/conflicts) is restored from the subtree `conflicts` in the snapshot (if not present, existing files are deleted). - /// - /// If there are files that are untracked and larger than `SNAPSHOT_FILE_LIMIT_BYTES`, they are excluded from snapshot creation and restoring. - /// Returns the sha of the created revert snapshot commit or None if snapshots are disabled. - pub fn restore_snapshot(&self, snapshot_commit_id: git2::Oid) -> Result> { + fn restore_snapshot(&self, snapshot_commit_id: git2::Oid) -> Result> { let worktree_dir = self.path.as_path(); let repo = git2::Repository::open(worktree_dir)?; @@ -501,24 +545,7 @@ impl Project { self.commit_snapshot(before_restore_snapshot_tree_id, details) } - /// Determines if a new snapshot should be created due to file changes being created since the last snapshot. - /// The needs for the automatic snapshotting are: - /// - It needs to facilitate backup of work in progress code - /// - The snapshots should not be too frequent or small - both for UX and performance reasons - /// - Checking if an automatic snapshot is needed should be fast and efficient since it is called on filesystem events - /// - /// Use `check_if_last_snapshot_older_than` as a way to control if the check should be performed at all, i.e. - /// if this is 10s but the last snapshot was done 9s ago, no check if performed and the return value is `false`. - /// - /// This implementation returns `true` on the following conditions: - /// - Head is pointing to the integration branch. - /// - If it's been more than 5 minutes since the last snapshot, - /// check the sum of added and removed lines since the last snapshot, otherwise return `false`. - /// * If the sum of added and removed lines is greater than a configured threshold, return `true`, otherwise return `false`. - pub fn should_auto_snapshot( - &self, - check_if_last_snapshot_older_than: Duration, - ) -> Result { + fn should_auto_snapshot(&self, check_if_last_snapshot_older_than: Duration) -> Result { let last_snapshot_time = OplogHandle::new(&self.gb_dir()).modified_at()?; if last_snapshot_time.elapsed()? <= check_if_last_snapshot_older_than { return Ok(false); @@ -531,10 +558,7 @@ impl Project { Ok(lines_since_snapshot(self, &repo)? > self.snapshot_lines_threshold()) } - /// Returns the diff of the snapshot and it's parent. It only includes the workdir changes. - /// - /// This is useful to show what has changed in this particular snapshot - pub fn snapshot_diff(&self, sha: git2::Oid) -> Result> { + fn snapshot_diff(&self, sha: git2::Oid) -> Result> { let worktree_dir = self.path.as_path(); let repo = git2::Repository::init(worktree_dir)?; @@ -567,7 +591,7 @@ impl Project { } /// Gets the sha of the last snapshot commit if present. - pub fn oplog_head(&self) -> Result> { + fn oplog_head(&self) -> Result> { let oplog_state = OplogHandle::new(&self.gb_dir()); oplog_state.oplog_head() } @@ -783,7 +807,7 @@ fn tree_from_applied_vbranches( .find_blob(vb_toml_entry.id()) .context("failed to convert virtual_branches tree entry to blob")?; - let vbs_from_toml: crate::virtual_branches::VirtualBranchesState = + let vbs_from_toml: gitbutler_core::virtual_branches::VirtualBranchesState = toml::from_str(from_utf8(vb_toml_blob.content())?)?; let applied_branch_trees: Vec = vbs_from_toml .branches diff --git a/crates/gitbutler-core/src/ops/reflog.rs b/crates/gitbutler-oplog/src/reflog.rs similarity index 99% rename from crates/gitbutler-core/src/ops/reflog.rs rename to crates/gitbutler-oplog/src/reflog.rs index ea479496b..920a21b9c 100644 --- a/crates/gitbutler-core/src/ops/reflog.rs +++ b/crates/gitbutler-oplog/src/reflog.rs @@ -1,10 +1,10 @@ -use crate::{ +use anyhow::{Context, Result}; +use gitbutler_core::{ fs::write, virtual_branches::{ GITBUTLER_INTEGRATION_COMMIT_AUTHOR_EMAIL, GITBUTLER_INTEGRATION_COMMIT_AUTHOR_NAME, }, }; -use anyhow::{Context, Result}; use gix::config::tree::Key; use std::path::Path; diff --git a/crates/gitbutler-core/src/ops/snapshot.rs b/crates/gitbutler-oplog/src/snapshot.rs similarity index 82% rename from crates/gitbutler-core/src/ops/snapshot.rs rename to crates/gitbutler-oplog/src/snapshot.rs index ced31aa88..5c385fbd6 100644 --- a/crates/gitbutler-core/src/ops/snapshot.rs +++ b/crates/gitbutler-oplog/src/snapshot.rs @@ -1,17 +1,51 @@ use anyhow::Result; use std::vec; -use crate::projects::Project; use crate::{ - ops::entry::{OperationKind, SnapshotDetails}, - virtual_branches::{branch::BranchUpdateRequest, Branch}, + entry::{OperationKind, SnapshotDetails}, + oplog::Oplog, }; +use gitbutler_core::projects::Project; +use gitbutler_core::virtual_branches::{branch::BranchUpdateRequest, Branch}; use super::entry::Trailer; +pub trait Snapshot { + fn snapshot_branch_unapplied( + &self, + snapshot_tree: git2::Oid, + result: Result<&git2::Branch, &anyhow::Error>, + ) -> anyhow::Result<()>; + + fn snapshot_commit_undo( + &self, + snapshot_tree: git2::Oid, + result: Result<&(), &anyhow::Error>, + commit_sha: git2::Oid, + ) -> anyhow::Result<()>; + + fn snapshot_commit_creation( + &self, + snapshot_tree: git2::Oid, + error: Option<&anyhow::Error>, + commit_message: String, + sha: Option, + ) -> anyhow::Result<()>; + + fn snapshot_branch_creation(&self, branch_name: String) -> anyhow::Result<()>; + fn snapshot_branch_deletion(&self, branch_name: String) -> anyhow::Result<()>; + fn snapshot_branch_update( + &self, + snapshot_tree: git2::Oid, + old_branch: &Branch, + update: &BranchUpdateRequest, + error: Option<&anyhow::Error>, + ) -> anyhow::Result<()>; +} + /// Snapshot functionality -impl Project { - pub fn snapshot_branch_unapplied( +impl Snapshot for Project { + fn snapshot_branch_unapplied( &self, snapshot_tree: git2::Oid, result: Result<&git2::Branch, &anyhow::Error>, @@ -22,7 +56,7 @@ impl Project { self.commit_snapshot(snapshot_tree, details)?; Ok(()) } - pub fn snapshot_commit_undo( + fn snapshot_commit_undo( &self, snapshot_tree: git2::Oid, result: Result<&(), &anyhow::Error>, @@ -34,7 +68,7 @@ impl Project { self.commit_snapshot(snapshot_tree, details)?; Ok(()) } - pub fn snapshot_commit_creation( + fn snapshot_commit_creation( &self, snapshot_tree: git2::Oid, error: Option<&anyhow::Error>, @@ -60,7 +94,7 @@ impl Project { self.commit_snapshot(snapshot_tree, details)?; Ok(()) } - pub fn snapshot_branch_creation(&self, branch_name: String) -> anyhow::Result<()> { + fn snapshot_branch_creation(&self, branch_name: String) -> anyhow::Result<()> { let details = SnapshotDetails::new(OperationKind::CreateBranch).with_trailers(vec![Trailer { key: "name".to_string(), @@ -69,7 +103,7 @@ impl Project { self.create_snapshot(details)?; Ok(()) } - pub fn snapshot_branch_deletion(&self, branch_name: String) -> anyhow::Result<()> { + fn snapshot_branch_deletion(&self, branch_name: String) -> anyhow::Result<()> { let details = SnapshotDetails::new(OperationKind::DeleteBranch).with_trailers(vec![Trailer { key: "name".to_string(), @@ -79,7 +113,7 @@ impl Project { self.create_snapshot(details)?; Ok(()) } - pub fn snapshot_branch_update( + fn snapshot_branch_update( &self, snapshot_tree: git2::Oid, old_branch: &Branch, diff --git a/crates/gitbutler-core/src/ops/state.rs b/crates/gitbutler-oplog/src/state.rs similarity index 93% rename from crates/gitbutler-core/src/ops/state.rs rename to crates/gitbutler-oplog/src/state.rs index 2550e097a..993f82f8e 100644 --- a/crates/gitbutler-core/src/ops/state.rs +++ b/crates/gitbutler-oplog/src/state.rs @@ -4,7 +4,7 @@ use std::{ time::SystemTime, }; -use crate::fs::read_toml_file_or_default; +use gitbutler_core::fs::read_toml_file_or_default; use serde::{Deserialize, Deserializer, Serialize}; use super::OPLOG_FILE_NAME; @@ -26,7 +26,7 @@ fn unix_epoch() -> SystemTime { #[derive(Serialize, Deserialize, Debug)] pub struct Oplog { /// This is the sha of the last oplog commit - #[serde(with = "crate::serde::oid_opt", default)] + #[serde(with = "gitbutler_core::serde::oid_opt", default)] pub head_sha: Option, /// The time when the last snapshot was created. Seconds since Epoch #[serde( @@ -92,6 +92,6 @@ impl OplogHandle { fn write_file(&self, mut oplog: Oplog) -> Result<()> { oplog.modified_at = SystemTime::now(); - crate::fs::write(&self.file_path, toml::to_string(&oplog)?) + gitbutler_core::fs::write(&self.file_path, toml::to_string(&oplog)?) } } diff --git a/crates/gitbutler-core/tests/ops/entry.rs b/crates/gitbutler-oplog/tests/mod.rs similarity index 94% rename from crates/gitbutler-core/tests/ops/entry.rs rename to crates/gitbutler-oplog/tests/mod.rs index 835349094..8dc508c44 100644 --- a/crates/gitbutler-core/tests/ops/entry.rs +++ b/crates/gitbutler-oplog/tests/mod.rs @@ -1,5 +1,6 @@ mod trailer { - use gitbutler_core::ops::entry::Trailer; + use gitbutler_oplog::entry::Trailer; + // use gitbutler_core::ops::entry::Trailer; use std::str::FromStr; #[test] @@ -28,7 +29,7 @@ mod trailer { } mod version { - use gitbutler_core::ops::entry::{Trailer, Version}; + use gitbutler_oplog::entry::{Trailer, Version}; use std::str::FromStr; #[test] @@ -57,7 +58,7 @@ mod version { } mod operation_kind { - use gitbutler_core::ops::entry::{OperationKind, SnapshotDetails, Trailer, Version}; + use gitbutler_oplog::entry::{OperationKind, SnapshotDetails, Trailer, Version}; use std::str::FromStr; #[test] @@ -90,7 +91,7 @@ mod operation_kind { } mod snapshot_details { - use gitbutler_core::ops::entry::{OperationKind, Snapshot, SnapshotDetails, Trailer, Version}; + use gitbutler_oplog::entry::{OperationKind, Snapshot, SnapshotDetails, Trailer, Version}; use std::path::PathBuf; use std::str::FromStr; diff --git a/crates/gitbutler-sync/Cargo.toml b/crates/gitbutler-sync/Cargo.toml index 55d6e5796..baee0c00f 100644 --- a/crates/gitbutler-sync/Cargo.toml +++ b/crates/gitbutler-sync/Cargo.toml @@ -11,3 +11,4 @@ tracing = "0.1.40" itertools = "0.13" git2.workspace = true gitbutler-core.workspace = true +gitbutler-oplog.workspace = true diff --git a/crates/gitbutler-sync/src/cloud.rs b/crates/gitbutler-sync/src/cloud.rs index d6b443c53..70a695fd5 100644 --- a/crates/gitbutler-sync/src/cloud.rs +++ b/crates/gitbutler-sync/src/cloud.rs @@ -8,6 +8,7 @@ use gitbutler_core::{ projects::{self, CodePushState}, users, }; +use gitbutler_oplog::oplog::Oplog; use itertools::Itertools; pub async fn sync_with_gitbutler( diff --git a/crates/gitbutler-tauri/Cargo.toml b/crates/gitbutler-tauri/Cargo.toml index 665d7941d..0e5a88747 100644 --- a/crates/gitbutler-tauri/Cargo.toml +++ b/crates/gitbutler-tauri/Cargo.toml @@ -49,6 +49,7 @@ tracing-subscriber = "0.3.17" gitbutler-core.workspace = true gitbutler-watcher.workspace = true gitbutler-branch.workspace = true +gitbutler-oplog.workspace = true open = "5" [dependencies.tauri] diff --git a/crates/gitbutler-tauri/src/undo.rs b/crates/gitbutler-tauri/src/undo.rs index f2a70fd7d..6bed1c523 100644 --- a/crates/gitbutler-tauri/src/undo.rs +++ b/crates/gitbutler-tauri/src/undo.rs @@ -1,10 +1,9 @@ use crate::error::Error; use anyhow::Context; use gitbutler_core::git::diff::FileDiff; -use gitbutler_core::{ - ops::entry::Snapshot, - projects::{self, ProjectId}, -}; +use gitbutler_core::projects::{self, ProjectId}; +use gitbutler_oplog::entry::Snapshot; +use gitbutler_oplog::oplog::Oplog; use std::collections::HashMap; use std::path::PathBuf; use tauri::Manager; diff --git a/crates/gitbutler-watcher/Cargo.toml b/crates/gitbutler-watcher/Cargo.toml index 0e704aa9e..ac0906cfe 100644 --- a/crates/gitbutler-watcher/Cargo.toml +++ b/crates/gitbutler-watcher/Cargo.toml @@ -12,6 +12,7 @@ doctest = false gitbutler-core.workspace = true gitbutler-branch.workspace = true gitbutler-sync.workspace = true +gitbutler-oplog.workspace = true thiserror.workspace = true anyhow = "1.0.86" futures = "0.3.30" diff --git a/crates/gitbutler-watcher/src/file_monitor.rs b/crates/gitbutler-watcher/src/file_monitor.rs index a8ea3ccf0..ee3a3b950 100644 --- a/crates/gitbutler-watcher/src/file_monitor.rs +++ b/crates/gitbutler-watcher/src/file_monitor.rs @@ -4,9 +4,9 @@ use std::time::Duration; use crate::events::InternalEvent; use anyhow::{anyhow, Context, Result}; -use gitbutler_core::ops::OPLOG_FILE_NAME; use gitbutler_core::projects::ProjectId; use gitbutler_notify_debouncer::{new_debouncer, Debouncer, NoCache}; +use gitbutler_oplog::OPLOG_FILE_NAME; use notify::RecommendedWatcher; use notify::Watcher; use tokio::task; diff --git a/crates/gitbutler-watcher/src/handler.rs b/crates/gitbutler-watcher/src/handler.rs index 6254923ed..650e0a22b 100644 --- a/crates/gitbutler-watcher/src/handler.rs +++ b/crates/gitbutler-watcher/src/handler.rs @@ -4,9 +4,12 @@ use std::sync::Arc; use anyhow::{Context, Result}; use gitbutler_branch::VirtualBranches; use gitbutler_core::error::Marker; -use gitbutler_core::ops::entry::{OperationKind, SnapshotDetails}; use gitbutler_core::projects::ProjectId; use gitbutler_core::{assets, git, project_repository, projects, users}; +use gitbutler_oplog::{ + entry::{OperationKind, SnapshotDetails}, + oplog::Oplog, +}; use gitbutler_sync::cloud::sync_with_gitbutler; use tracing::instrument;