fix: auto snapshotting on changed lines of code

Compare against the last snapshot
This commit is contained in:
Kiril Videlov 2024-05-05 20:08:34 +02:00
parent 66a21e8ec5
commit c59767651d
4 changed files with 38 additions and 40 deletions

View File

@ -29,7 +29,6 @@ pub mod path;
pub mod project_repository;
pub mod projects;
pub mod reader;
pub mod repo;
pub mod sessions;
pub mod snapshots;
pub mod ssh;

View File

@ -1,18 +0,0 @@
use anyhow::Result;
/// The GbRepository trait provides methods which are building blocks for Gitbutler functionality.
pub trait GbRepository {
/// Returns the number of uncommitted lines of code (added plus removed) in the repository. Included untracked files.
fn changed_lines_count(&self) -> Result<usize>;
}
impl GbRepository for git2::Repository {
fn changed_lines_count(&self) -> Result<usize> {
let head_tree = self.head()?.peel_to_commit()?.tree()?;
let mut opts = git2::DiffOptions::new();
opts.include_untracked(true);
let diff = self.diff_tree_to_workdir_with_index(Some(&head_tree), Some(&mut opts));
let stats = diff?.stats()?;
Ok(stats.deletions() + stats.insertions())
}
}

View File

@ -237,6 +237,38 @@ pub fn restore(project: &Project, sha: String) -> Result<Option<String>> {
create(project, details)
}
/// Returns the number of uncommitted lines of code (added plus removed) since the last snapshot. Included untracked files.
pub fn changed_lines_count(project: &Project) -> Result<usize> {
let repo_path = project.path.as_path();
let repo = git2::Repository::init(repo_path)?;
// Exclude files that are larger than the limit (eg. database.sql which may never be intended to be committed)
let files_to_exclude = get_exclude_list(&repo)?;
// In-memory, libgit2 internal ignore rule
repo.add_ignore_rule(&files_to_exclude)?;
let oplog_state = OplogHandle::new(&project.gb_dir());
let head_sha = oplog_state.get_oplog_head()?;
if head_sha.is_none() {
return Ok(0);
}
let head_sha = head_sha.unwrap();
let commit = repo.find_commit(git2::Oid::from_str(&head_sha)?)?;
let head_tree = commit.tree()?;
let wd_tree_entry = head_tree
.get_name("workdir")
.ok_or(anyhow!("failed to get workdir tree entry"))?;
let wd_tree = repo.find_tree(wd_tree_entry.id())?;
let mut opts = git2::DiffOptions::new();
opts.include_untracked(true);
let diff = repo.diff_tree_to_workdir_with_index(Some(&wd_tree), Some(&mut opts));
let stats = diff?.stats()?;
Ok(stats.deletions() + stats.insertions())
}
fn restore_conflicts_tree(
snapshot_tree: &git2::Tree,
repo: &git2::Repository,

View File

@ -8,7 +8,6 @@ use std::{path, time};
use anyhow::{bail, Context, Result};
use gitbutler_core::projects::ProjectId;
use gitbutler_core::repo::GbRepository;
use gitbutler_core::sessions::SessionId;
use gitbutler_core::snapshots::entry::{OperationType, SnapshotDetails, Trailer};
use gitbutler_core::snapshots::snapshot;
@ -17,7 +16,6 @@ use gitbutler_core::{
assets, deltas, gb_repository, git, project_repository, projects, reader, sessions, users,
virtual_branches,
};
use tokio::sync::Mutex;
use tracing::instrument;
use super::{events, Change};
@ -43,9 +41,6 @@ pub struct Handler {
/// A function to send events - decoupled from app-handle for testing purposes.
#[allow(clippy::type_complexity)]
send_event: Arc<dyn Fn(Change) -> Result<()> + Send + Sync + 'static>,
// The number of changed lines since the value was last reset
changed_lines_count: Arc<Mutex<usize>>,
}
impl Handler {
@ -70,7 +65,6 @@ impl Handler {
sessions_db,
deltas_db,
send_event: Arc::new(send_event),
changed_lines_count: Arc::new(Mutex::new(0)),
}
}
@ -295,32 +289,23 @@ impl Handler {
let this = self.clone();
move || this.calculate_deltas(paths, project_id)
});
let changed_lines_before = *self.changed_lines_count.lock().await;
// Create a snapshot every time there are more than 20 new lines of code
// Create a snapshot every time there are more than a configurable number of new lines of code (default 20)
let handle_snapshots = tokio::task::spawn_blocking({
let this = self.clone();
move || this.maybe_create_snapshot(project_id, changed_lines_before, paths_string)
move || this.maybe_create_snapshot(project_id, paths_string)
});
// Set changed lines count to the newly observed value
*self.changed_lines_count.lock().await = handle_snapshots.await??;
self.calculate_virtual_branches(project_id).await?;
let _ = handle_snapshots.await;
calc_deltas.await??;
Ok(())
}
fn maybe_create_snapshot(
&self,
project_id: ProjectId,
changed_lines_before: usize,
paths: String,
) -> anyhow::Result<usize> {
fn maybe_create_snapshot(&self, project_id: ProjectId, paths: String) -> anyhow::Result<()> {
let project = self
.projects
.get(&project_id)
.context("failed to get project")?;
let repo_path = project.path.as_path();
let repo = git2::Repository::init(repo_path)?;
let changed_lines = repo.changed_lines_count()? - changed_lines_before;
let changed_lines = snapshot::changed_lines_count(&project)?;
if changed_lines > project.snapshot_lines_threshold {
let details = SnapshotDetails {
version: Default::default(),
@ -334,7 +319,7 @@ impl Handler {
};
snapshot::create(&project, details)?;
}
Ok(changed_lines)
Ok(())
}
pub async fn git_file_change(