mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-18 14:31:30 +03:00
Merge pull request #3704 from gitbutlerapp/add-workdir-snapshot-details
add-workdir-snapshot-details
This commit is contained in:
commit
a6e80d9d7f
@ -27,6 +27,9 @@
|
||||
};
|
||||
type Snapshot = {
|
||||
id: string;
|
||||
linesAdded: number;
|
||||
linesRemoved: number;
|
||||
filesChanged: string[];
|
||||
details: SnapshotDetails | undefined;
|
||||
createdAt: number;
|
||||
};
|
||||
@ -79,17 +82,23 @@
|
||||
</div>
|
||||
<div style="padding-left: 16px; hidden;">
|
||||
{#if entry.details?.operation === 'RestoreFromSnapshot'}
|
||||
from: {entry.details?.trailers
|
||||
restored_from: {entry.details?.trailers
|
||||
.find((t) => t.key === 'restored_from')
|
||||
?.value?.slice(0, 7)}
|
||||
{:else if entry.details?.operation === 'FileChanges'}
|
||||
{#each entry.details?.trailers
|
||||
.find((t) => t.key === 'files')
|
||||
?.value?.split(',') || [] as file}
|
||||
<div>{file}</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
<div>
|
||||
lines added: {entry.linesAdded}
|
||||
</div>
|
||||
<div>
|
||||
lines removed: {entry.linesRemoved}
|
||||
</div>
|
||||
<div>
|
||||
<div>files changed:</div>
|
||||
{#each entry.filesChanged as filePath}
|
||||
<div style="padding-left: 16px;">{filePath}</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
|
@ -5,6 +5,7 @@ use serde::Deserialize;
|
||||
use std::fmt;
|
||||
use std::fmt::Display;
|
||||
use std::fmt::Formatter;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use strum::EnumString;
|
||||
|
||||
@ -19,6 +20,12 @@ pub struct Snapshot {
|
||||
pub id: String,
|
||||
/// Snapshot creation time in epoch milliseconds
|
||||
pub created_at: i64,
|
||||
/// The number of working directory lines added in the snapshot
|
||||
pub lines_added: usize,
|
||||
/// The number of working directory lines removed in the snapshot
|
||||
pub lines_removed: usize,
|
||||
/// The list of working directory files that were changed in the snapshot
|
||||
pub files_changed: Vec<PathBuf>,
|
||||
/// Snapshot details as persisted in the commit message
|
||||
pub details: Option<SnapshotDetails>,
|
||||
}
|
||||
@ -298,6 +305,9 @@ mod tests {
|
||||
let snapshot = Snapshot {
|
||||
id: commit_sha.clone(),
|
||||
created_at,
|
||||
lines_added: 1,
|
||||
lines_removed: 1,
|
||||
files_changed: vec![PathBuf::from("foo.txt")],
|
||||
details: Some(details),
|
||||
};
|
||||
assert_eq!(snapshot.id, commit_sha);
|
||||
|
@ -173,6 +173,35 @@ impl Oplog for Project {
|
||||
break;
|
||||
}
|
||||
|
||||
let tree = commit.tree()?;
|
||||
let wd_tree_entry = tree.get_name("workdir");
|
||||
let tree = if let Some(wd_tree_entry) = wd_tree_entry {
|
||||
repo.find_tree(wd_tree_entry.id())?
|
||||
} else {
|
||||
// We reached a tree that is not a snapshot
|
||||
continue;
|
||||
};
|
||||
|
||||
let parent_tree = commit.parent(0)?.tree()?;
|
||||
let parent_tree_entry = parent_tree.get_name("workdir");
|
||||
let parent_tree = parent_tree_entry
|
||||
.map(|entry| repo.find_tree(entry.id()))
|
||||
.transpose()?;
|
||||
|
||||
let diff = repo.diff_tree_to_tree(parent_tree.as_ref(), Some(&tree), None)?;
|
||||
let stats = diff.stats()?;
|
||||
|
||||
let mut files_changed = Vec::new();
|
||||
diff.print(git2::DiffFormat::NameOnly, |delta, _, _| {
|
||||
if let Some(path) = delta.new_file().path() {
|
||||
files_changed.push(path.to_path_buf());
|
||||
}
|
||||
true
|
||||
})?;
|
||||
|
||||
let lines_added = stats.insertions();
|
||||
let lines_removed = stats.deletions();
|
||||
|
||||
let details = commit
|
||||
.message()
|
||||
.and_then(|msg| SnapshotDetails::from_str(msg).ok());
|
||||
@ -180,6 +209,9 @@ impl Oplog for Project {
|
||||
snapshots.push(Snapshot {
|
||||
id: commit_id.to_string(),
|
||||
details,
|
||||
lines_added,
|
||||
lines_removed,
|
||||
files_changed,
|
||||
created_at: commit.time().seconds() * 1000,
|
||||
});
|
||||
|
||||
@ -492,9 +524,33 @@ mod tests {
|
||||
.create_snapshot(SnapshotDetails::new(OperationType::UpdateWorkspaceBase))
|
||||
.unwrap();
|
||||
|
||||
let initial_snapshot = &project.list_snapshots(10).unwrap()[1];
|
||||
assert_eq!(
|
||||
initial_snapshot.files_changed,
|
||||
vec![
|
||||
PathBuf::from_str("1.txt").unwrap(),
|
||||
PathBuf::from_str("2.txt").unwrap(),
|
||||
PathBuf::from_str("uncommitted.txt").unwrap()
|
||||
]
|
||||
);
|
||||
assert_eq!(initial_snapshot.lines_added, 3);
|
||||
assert_eq!(initial_snapshot.lines_removed, 0);
|
||||
let second_snapshot = &project.list_snapshots(10).unwrap()[0];
|
||||
assert_eq!(
|
||||
second_snapshot.files_changed,
|
||||
vec![
|
||||
PathBuf::from_str("1.txt").unwrap(),
|
||||
PathBuf::from_str("2.txt").unwrap(),
|
||||
PathBuf::from_str("3.txt").unwrap(),
|
||||
PathBuf::from_str("uncommitted.txt").unwrap()
|
||||
]
|
||||
);
|
||||
assert_eq!(second_snapshot.lines_added, 3);
|
||||
assert_eq!(second_snapshot.lines_removed, 3);
|
||||
|
||||
// restore from the initial snapshot
|
||||
project
|
||||
.restore_snapshot(project.list_snapshots(10).unwrap()[1].id.clone())
|
||||
.restore_snapshot(initial_snapshot.id.clone())
|
||||
.unwrap();
|
||||
|
||||
let file_path = dir.path().join("1.txt");
|
||||
|
@ -9,7 +9,7 @@ use std::{path, time};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use gitbutler_core::projects::ProjectId;
|
||||
use gitbutler_core::sessions::SessionId;
|
||||
use gitbutler_core::snapshots::entry::{OperationType, SnapshotDetails, Trailer};
|
||||
use gitbutler_core::snapshots::entry::{OperationType, SnapshotDetails};
|
||||
use gitbutler_core::snapshots::snapshot::Oplog;
|
||||
use gitbutler_core::virtual_branches::VirtualBranches;
|
||||
use gitbutler_core::{
|
||||
@ -280,11 +280,6 @@ impl Handler {
|
||||
paths: Vec<PathBuf>,
|
||||
project_id: ProjectId,
|
||||
) -> Result<()> {
|
||||
let paths_string = paths
|
||||
.iter()
|
||||
.map(|p| p.to_string_lossy().to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
let calc_deltas = tokio::task::spawn_blocking({
|
||||
let this = self.clone();
|
||||
move || this.calculate_deltas(paths, project_id)
|
||||
@ -292,7 +287,7 @@ impl Handler {
|
||||
// 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, paths_string)
|
||||
move || this.maybe_create_snapshot(project_id)
|
||||
});
|
||||
self.calculate_virtual_branches(project_id).await?;
|
||||
let _ = handle_snapshots.await;
|
||||
@ -300,24 +295,14 @@ impl Handler {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn maybe_create_snapshot(&self, project_id: ProjectId, paths: String) -> anyhow::Result<()> {
|
||||
fn maybe_create_snapshot(&self, project_id: ProjectId) -> anyhow::Result<()> {
|
||||
let project = self
|
||||
.projects
|
||||
.get(&project_id)
|
||||
.context("failed to get project")?;
|
||||
let changed_lines = project.lines_since_snapshot()?;
|
||||
if changed_lines > project.snapshot_lines_threshold {
|
||||
let details = SnapshotDetails {
|
||||
version: Default::default(),
|
||||
operation: OperationType::FileChanges,
|
||||
title: OperationType::FileChanges.to_string(),
|
||||
body: None,
|
||||
trailers: vec![Trailer {
|
||||
key: "files".to_string(),
|
||||
value: paths,
|
||||
}],
|
||||
};
|
||||
project.create_snapshot(details)?;
|
||||
project.create_snapshot(SnapshotDetails::new(OperationType::FileChanges))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user