add an enpoint for getting the snapshot diff

This commit is contained in:
Kiril Videlov 2024-05-09 11:24:42 -07:00
parent 2006511a80
commit 58c124627a
No known key found for this signature in database
4 changed files with 68 additions and 3 deletions

View File

@ -218,7 +218,7 @@ pub fn without_large_files(
/// `repository` should be `None` if there is no reason to access the workdir, which it will do to
/// keep the binary data in the object database, which otherwise would be lost to the system
/// (it's not reconstructable from the delta, or it's not attempted).
fn hunks_by_filepath(repo: Option<&Repository>, diff: &git2::Diff) -> Result<DiffByPathMap> {
pub fn hunks_by_filepath(repo: Option<&Repository>, diff: &git2::Diff) -> Result<DiffByPathMap> {
enum LineOrHexHash<'a> {
Line(Cow<'a, BStr>),
HexHashOfBinaryBlob(String),

View File

@ -1,12 +1,14 @@
use anyhow::anyhow;
use git2::FileMode;
use itertools::Itertools;
use std::fs;
use std::collections::HashMap;
use std::str::FromStr;
use std::{fs, path::PathBuf};
use anyhow::Result;
use crate::projects::Project;
use crate::git::diff::FileDiff;
use crate::{git::diff::hunks_by_filepath, projects::Project};
use super::{
entry::{OperationType, Snapshot, SnapshotDetails, Trailer},
@ -54,6 +56,10 @@ pub trait Oplog {
///
/// If there are no snapshots, 0 is returned.
fn lines_since_snapshot(&self) -> Result<usize>;
/// 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: String) -> Result<HashMap<PathBuf, FileDiff>>;
}
impl Oplog for Project {
@ -324,6 +330,46 @@ impl Oplog for Project {
let stats = diff?.stats()?;
Ok(stats.deletions() + stats.insertions())
}
fn snapshot_diff(&self, sha: String) -> Result<HashMap<PathBuf, FileDiff>> {
let repo_path = self.path.as_path();
let repo = git2::Repository::init(repo_path)?;
let commit = repo.find_commit(git2::Oid::from_str(&sha)?)?;
// Top tree
let tree = commit.tree()?;
let old_tree = commit.parent(0)?.tree()?;
let wd_tree_entry = tree
.get_name("workdir")
.ok_or(anyhow!("failed to get workdir tree entry"))?;
let old_wd_tree_entry = old_tree
.get_name("workdir")
.ok_or(anyhow!("failed to get old workdir tree entry"))?;
// workdir tree
let wd_tree = repo.find_tree(wd_tree_entry.id())?;
let old_wd_tree = repo.find_tree(old_wd_tree_entry.id())?;
// 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 mut diff_opts = git2::DiffOptions::new();
diff_opts
.recurse_untracked_dirs(true)
.include_untracked(true)
.show_binary(true)
.ignore_submodules(true)
.show_untracked_content(true);
let diff =
repo.diff_tree_to_tree(Some(&old_wd_tree), Some(&wd_tree), Some(&mut diff_opts))?;
let hunks = hunks_by_filepath(None, &diff)?;
Ok(hunks)
}
}
fn restore_conflicts_tree(

View File

@ -234,6 +234,7 @@ fn main() {
virtual_branches::commands::move_commit,
undo::list_snapshots,
undo::restore_snapshot,
undo::snapshot_diff,
menu::menu_item_set_enabled,
keys::commands::get_public_key,
github::commands::init_device_oauth,

View File

@ -1,9 +1,12 @@
use crate::error::Error;
use anyhow::Context;
use gitbutler_core::git::diff::FileDiff;
use gitbutler_core::{
ops::{entry::Snapshot, oplog::Oplog},
projects::{self, ProjectId},
};
use std::collections::HashMap;
use std::path::PathBuf;
use tauri::Manager;
use tracing::instrument;
@ -37,3 +40,18 @@ pub async fn restore_snapshot(
project.restore_snapshot(sha)?;
Ok(())
}
#[tauri::command(async)]
#[instrument(skip(handle), err(Debug))]
pub async fn snapshot_diff(
handle: tauri::AppHandle,
project_id: ProjectId,
sha: String,
) -> Result<HashMap<PathBuf, FileDiff>, Error> {
let project = handle
.state::<projects::Controller>()
.get(&project_id)
.context("failed to get project")?;
let diff = project.snapshot_diff(sha)?;
Ok(diff)
}