mirror of
https://github.com/martinvonz/jj.git
synced 2025-02-01 00:50:57 +00:00
working_copy: plumbing to propagate untracked paths to caller
This commit is contained in:
parent
e1936a2e8b
commit
f4fdc19d9e
9 changed files with 70 additions and 21 deletions
|
@ -41,6 +41,7 @@ use jj_lib::working_copy::LockedWorkingCopy;
|
||||||
use jj_lib::working_copy::ResetError;
|
use jj_lib::working_copy::ResetError;
|
||||||
use jj_lib::working_copy::SnapshotError;
|
use jj_lib::working_copy::SnapshotError;
|
||||||
use jj_lib::working_copy::SnapshotOptions;
|
use jj_lib::working_copy::SnapshotOptions;
|
||||||
|
use jj_lib::working_copy::SnapshotStats;
|
||||||
use jj_lib::working_copy::WorkingCopy;
|
use jj_lib::working_copy::WorkingCopy;
|
||||||
use jj_lib::working_copy::WorkingCopyFactory;
|
use jj_lib::working_copy::WorkingCopyFactory;
|
||||||
use jj_lib::working_copy::WorkingCopyStateError;
|
use jj_lib::working_copy::WorkingCopyStateError;
|
||||||
|
@ -233,7 +234,10 @@ impl LockedWorkingCopy for LockedConflictsWorkingCopy {
|
||||||
self.inner.old_tree_id()
|
self.inner.old_tree_id()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn snapshot(&mut self, options: &SnapshotOptions) -> Result<MergedTreeId, SnapshotError> {
|
fn snapshot(
|
||||||
|
&mut self,
|
||||||
|
options: &SnapshotOptions,
|
||||||
|
) -> Result<(MergedTreeId, SnapshotStats), SnapshotError> {
|
||||||
let options = SnapshotOptions {
|
let options = SnapshotOptions {
|
||||||
base_ignores: options.base_ignores.chain("", "/.conflicts".as_bytes())?,
|
base_ignores: options.base_ignores.chain("", "/.conflicts".as_bytes())?,
|
||||||
..options.clone()
|
..options.clone()
|
||||||
|
|
|
@ -1806,7 +1806,8 @@ See https://martinvonz.github.io/jj/latest/working-copy/#stale-working-copy \
|
||||||
};
|
};
|
||||||
self.user_repo = ReadonlyUserRepo::new(repo);
|
self.user_repo = ReadonlyUserRepo::new(repo);
|
||||||
let progress = crate::progress::snapshot_progress(ui);
|
let progress = crate::progress::snapshot_progress(ui);
|
||||||
let new_tree_id = locked_ws
|
// TODO: print stats
|
||||||
|
let (new_tree_id, _stats) = locked_ws
|
||||||
.locked_wc()
|
.locked_wc()
|
||||||
.snapshot(&SnapshotOptions {
|
.snapshot(&SnapshotOptions {
|
||||||
base_ignores,
|
base_ignores,
|
||||||
|
|
|
@ -52,7 +52,8 @@ pub(crate) fn cmd_file_track(
|
||||||
let mut tx = workspace_command.start_transaction().into_inner();
|
let mut tx = workspace_command.start_transaction().into_inner();
|
||||||
let base_ignores = workspace_command.base_ignores()?;
|
let base_ignores = workspace_command.base_ignores()?;
|
||||||
let (mut locked_ws, _wc_commit) = workspace_command.start_working_copy_mutation()?;
|
let (mut locked_ws, _wc_commit) = workspace_command.start_working_copy_mutation()?;
|
||||||
locked_ws.locked_wc().snapshot(&SnapshotOptions {
|
// TODO: print stats
|
||||||
|
let (_tree_id, _stats) = locked_ws.locked_wc().snapshot(&SnapshotOptions {
|
||||||
base_ignores,
|
base_ignores,
|
||||||
fsmonitor_settings: command.settings().fsmonitor_settings()?,
|
fsmonitor_settings: command.settings().fsmonitor_settings()?,
|
||||||
progress: None,
|
progress: None,
|
||||||
|
|
|
@ -76,7 +76,8 @@ pub(crate) fn cmd_file_untrack(
|
||||||
locked_ws.locked_wc().reset(&new_commit)?;
|
locked_ws.locked_wc().reset(&new_commit)?;
|
||||||
// Commit the working copy again so we can inform the user if paths couldn't be
|
// Commit the working copy again so we can inform the user if paths couldn't be
|
||||||
// untracked because they're not ignored.
|
// untracked because they're not ignored.
|
||||||
let wc_tree_id = locked_ws.locked_wc().snapshot(&SnapshotOptions {
|
// TODO: print stats
|
||||||
|
let (wc_tree_id, _stats) = locked_ws.locked_wc().snapshot(&SnapshotOptions {
|
||||||
base_ignores,
|
base_ignores,
|
||||||
fsmonitor_settings: command.settings().fsmonitor_settings()?,
|
fsmonitor_settings: command.settings().fsmonitor_settings()?,
|
||||||
progress: None,
|
progress: None,
|
||||||
|
|
|
@ -108,6 +108,8 @@ use crate::working_copy::ResetError;
|
||||||
use crate::working_copy::SnapshotError;
|
use crate::working_copy::SnapshotError;
|
||||||
use crate::working_copy::SnapshotOptions;
|
use crate::working_copy::SnapshotOptions;
|
||||||
use crate::working_copy::SnapshotProgress;
|
use crate::working_copy::SnapshotProgress;
|
||||||
|
use crate::working_copy::SnapshotStats;
|
||||||
|
use crate::working_copy::UntrackedReason;
|
||||||
use crate::working_copy::WorkingCopy;
|
use crate::working_copy::WorkingCopy;
|
||||||
use crate::working_copy::WorkingCopyFactory;
|
use crate::working_copy::WorkingCopyFactory;
|
||||||
use crate::working_copy::WorkingCopyStateError;
|
use crate::working_copy::WorkingCopyStateError;
|
||||||
|
@ -908,7 +910,10 @@ impl TreeState {
|
||||||
/// Look for changes to the working copy. If there are any changes, create
|
/// Look for changes to the working copy. If there are any changes, create
|
||||||
/// a new tree from it.
|
/// a new tree from it.
|
||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
pub fn snapshot(&mut self, options: &SnapshotOptions) -> Result<bool, SnapshotError> {
|
pub fn snapshot(
|
||||||
|
&mut self,
|
||||||
|
options: &SnapshotOptions,
|
||||||
|
) -> Result<(bool, SnapshotStats), SnapshotError> {
|
||||||
let &SnapshotOptions {
|
let &SnapshotOptions {
|
||||||
ref base_ignores,
|
ref base_ignores,
|
||||||
ref fsmonitor_settings,
|
ref fsmonitor_settings,
|
||||||
|
@ -935,11 +940,12 @@ impl TreeState {
|
||||||
if matcher.visit(RepoPath::root()).is_nothing() {
|
if matcher.visit(RepoPath::root()).is_nothing() {
|
||||||
// No need to load the current tree, set up channels, etc.
|
// No need to load the current tree, set up channels, etc.
|
||||||
self.watchman_clock = watchman_clock;
|
self.watchman_clock = watchman_clock;
|
||||||
return Ok(is_dirty);
|
return Ok((is_dirty, SnapshotStats::default()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (tree_entries_tx, tree_entries_rx) = channel();
|
let (tree_entries_tx, tree_entries_rx) = channel();
|
||||||
let (file_states_tx, file_states_rx) = channel();
|
let (file_states_tx, file_states_rx) = channel();
|
||||||
|
let (untracked_paths_tx, untracked_paths_rx) = channel();
|
||||||
let (deleted_files_tx, deleted_files_rx) = channel();
|
let (deleted_files_tx, deleted_files_rx) = channel();
|
||||||
|
|
||||||
trace_span!("traverse filesystem").in_scope(|| -> Result<(), SnapshotError> {
|
trace_span!("traverse filesystem").in_scope(|| -> Result<(), SnapshotError> {
|
||||||
|
@ -951,6 +957,7 @@ impl TreeState {
|
||||||
// Move tx sides so they'll be dropped at the end of the scope.
|
// Move tx sides so they'll be dropped at the end of the scope.
|
||||||
tree_entries_tx,
|
tree_entries_tx,
|
||||||
file_states_tx,
|
file_states_tx,
|
||||||
|
untracked_paths_tx,
|
||||||
deleted_files_tx,
|
deleted_files_tx,
|
||||||
error: OnceLock::new(),
|
error: OnceLock::new(),
|
||||||
progress,
|
progress,
|
||||||
|
@ -972,6 +979,9 @@ impl TreeState {
|
||||||
snapshotter.into_result()
|
snapshotter.into_result()
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
let stats = SnapshotStats {
|
||||||
|
untracked_paths: untracked_paths_rx.into_iter().collect(),
|
||||||
|
};
|
||||||
let mut tree_builder = MergedTreeBuilder::new(self.tree_id.clone());
|
let mut tree_builder = MergedTreeBuilder::new(self.tree_id.clone());
|
||||||
trace_span!("process tree entries").in_scope(|| {
|
trace_span!("process tree entries").in_scope(|| {
|
||||||
for (path, tree_values) in &tree_entries_rx {
|
for (path, tree_values) in &tree_entries_rx {
|
||||||
|
@ -1011,7 +1021,7 @@ impl TreeState {
|
||||||
assert_eq!(state_paths, tree_paths);
|
assert_eq!(state_paths, tree_paths);
|
||||||
}
|
}
|
||||||
self.watchman_clock = watchman_clock;
|
self.watchman_clock = watchman_clock;
|
||||||
Ok(is_dirty)
|
Ok((is_dirty, stats))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
|
@ -1087,6 +1097,8 @@ struct FileSnapshotter<'a> {
|
||||||
start_tracking_matcher: &'a dyn Matcher,
|
start_tracking_matcher: &'a dyn Matcher,
|
||||||
tree_entries_tx: Sender<(RepoPathBuf, MergedTreeValue)>,
|
tree_entries_tx: Sender<(RepoPathBuf, MergedTreeValue)>,
|
||||||
file_states_tx: Sender<(RepoPathBuf, FileState)>,
|
file_states_tx: Sender<(RepoPathBuf, FileState)>,
|
||||||
|
#[allow(unused)] // TODO
|
||||||
|
untracked_paths_tx: Sender<(RepoPathBuf, UntrackedReason)>,
|
||||||
deleted_files_tx: Sender<RepoPathBuf>,
|
deleted_files_tx: Sender<RepoPathBuf>,
|
||||||
error: OnceLock<SnapshotError>,
|
error: OnceLock<SnapshotError>,
|
||||||
progress: Option<&'a SnapshotProgress<'a>>,
|
progress: Option<&'a SnapshotProgress<'a>>,
|
||||||
|
@ -2150,7 +2162,10 @@ impl LockedWorkingCopy for LockedLocalWorkingCopy {
|
||||||
&self.old_tree_id
|
&self.old_tree_id
|
||||||
}
|
}
|
||||||
|
|
||||||
fn snapshot(&mut self, options: &SnapshotOptions) -> Result<MergedTreeId, SnapshotError> {
|
fn snapshot(
|
||||||
|
&mut self,
|
||||||
|
options: &SnapshotOptions,
|
||||||
|
) -> Result<(MergedTreeId, SnapshotStats), SnapshotError> {
|
||||||
let tree_state = self
|
let tree_state = self
|
||||||
.wc
|
.wc
|
||||||
.tree_state_mut()
|
.tree_state_mut()
|
||||||
|
@ -2158,8 +2173,9 @@ impl LockedWorkingCopy for LockedLocalWorkingCopy {
|
||||||
message: "Failed to read the working copy state".to_string(),
|
message: "Failed to read the working copy state".to_string(),
|
||||||
err: err.into(),
|
err: err.into(),
|
||||||
})?;
|
})?;
|
||||||
self.tree_state_dirty |= tree_state.snapshot(options)?;
|
let (is_dirty, stats) = tree_state.snapshot(options)?;
|
||||||
Ok(tree_state.current_tree_id().clone())
|
self.tree_state_dirty |= is_dirty;
|
||||||
|
Ok((tree_state.current_tree_id().clone(), stats))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_out(
|
fn check_out(
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
//! default local-disk implementation.
|
//! default local-disk implementation.
|
||||||
|
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -113,8 +114,11 @@ pub trait LockedWorkingCopy {
|
||||||
/// The tree at the time the lock was taken
|
/// The tree at the time the lock was taken
|
||||||
fn old_tree_id(&self) -> &MergedTreeId;
|
fn old_tree_id(&self) -> &MergedTreeId;
|
||||||
|
|
||||||
/// Snapshot the working copy and return the tree id.
|
/// Snapshot the working copy. Returns the tree id and stats.
|
||||||
fn snapshot(&mut self, options: &SnapshotOptions) -> Result<MergedTreeId, SnapshotError>;
|
fn snapshot(
|
||||||
|
&mut self,
|
||||||
|
options: &SnapshotOptions,
|
||||||
|
) -> Result<(MergedTreeId, SnapshotStats), SnapshotError>;
|
||||||
|
|
||||||
/// Check out the specified commit in the working copy.
|
/// Check out the specified commit in the working copy.
|
||||||
fn check_out(
|
fn check_out(
|
||||||
|
@ -249,6 +253,25 @@ impl SnapshotOptions<'_> {
|
||||||
/// A callback for getting progress updates.
|
/// A callback for getting progress updates.
|
||||||
pub type SnapshotProgress<'a> = dyn Fn(&RepoPath) + 'a + Sync;
|
pub type SnapshotProgress<'a> = dyn Fn(&RepoPath) + 'a + Sync;
|
||||||
|
|
||||||
|
/// Stats about a snapshot operation on a working copy.
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct SnapshotStats {
|
||||||
|
/// List of new (previously untracked) files which are still untracked.
|
||||||
|
pub untracked_paths: BTreeMap<RepoPathBuf, UntrackedReason>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reason why the new path isn't tracked.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum UntrackedReason {
|
||||||
|
/// File was larger than the specified maximum file size.
|
||||||
|
FileTooLarge {
|
||||||
|
/// Actual size of the large file.
|
||||||
|
size: u64,
|
||||||
|
/// Maximum allowed size.
|
||||||
|
max_size: u64,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
/// Options used when checking out a tree in the working copy.
|
/// Options used when checking out a tree in the working copy.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CheckoutOptions {
|
pub struct CheckoutOptions {
|
||||||
|
|
|
@ -939,7 +939,7 @@ fn test_snapshot_racy_timestamps() {
|
||||||
.workspace
|
.workspace
|
||||||
.start_working_copy_mutation()
|
.start_working_copy_mutation()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let new_tree_id = locked_ws
|
let (new_tree_id, _stats) = locked_ws
|
||||||
.locked_wc()
|
.locked_wc()
|
||||||
.snapshot(&SnapshotOptions::empty_for_test())
|
.snapshot(&SnapshotOptions::empty_for_test())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -973,7 +973,7 @@ fn test_snapshot_special_file() {
|
||||||
|
|
||||||
// Snapshot the working copy with the socket file
|
// Snapshot the working copy with the socket file
|
||||||
let mut locked_ws = ws.start_working_copy_mutation().unwrap();
|
let mut locked_ws = ws.start_working_copy_mutation().unwrap();
|
||||||
let tree_id = locked_ws
|
let (tree_id, _stats) = locked_ws
|
||||||
.locked_wc()
|
.locked_wc()
|
||||||
.snapshot(&SnapshotOptions::empty_for_test())
|
.snapshot(&SnapshotOptions::empty_for_test())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -2052,7 +2052,7 @@ fn test_fsmonitor() {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|p| p.to_fs_path_unchecked(Path::new("")))
|
.map(|p| p.to_fs_path_unchecked(Path::new("")))
|
||||||
.collect();
|
.collect();
|
||||||
locked_ws
|
let (tree_id, _stats) = locked_ws
|
||||||
.locked_wc()
|
.locked_wc()
|
||||||
.snapshot(&SnapshotOptions {
|
.snapshot(&SnapshotOptions {
|
||||||
fsmonitor_settings: FsmonitorSettings::Test {
|
fsmonitor_settings: FsmonitorSettings::Test {
|
||||||
|
@ -2060,7 +2060,8 @@ fn test_fsmonitor() {
|
||||||
},
|
},
|
||||||
..SnapshotOptions::empty_for_test()
|
..SnapshotOptions::empty_for_test()
|
||||||
})
|
})
|
||||||
.unwrap()
|
.unwrap();
|
||||||
|
tree_id
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -169,7 +169,7 @@ fn test_checkout_parallel() {
|
||||||
// &CheckoutOptions::empty_for_test()) should never produce a
|
// &CheckoutOptions::empty_for_test()) should never produce a
|
||||||
// different tree.
|
// different tree.
|
||||||
let mut locked_ws = workspace.start_working_copy_mutation().unwrap();
|
let mut locked_ws = workspace.start_working_copy_mutation().unwrap();
|
||||||
let new_tree_id = locked_ws
|
let (new_tree_id, _stats) = locked_ws
|
||||||
.locked_wc()
|
.locked_wc()
|
||||||
.snapshot(&SnapshotOptions::empty_for_test())
|
.snapshot(&SnapshotOptions::empty_for_test())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -60,6 +60,7 @@ use jj_lib::tree::Tree;
|
||||||
use jj_lib::tree_builder::TreeBuilder;
|
use jj_lib::tree_builder::TreeBuilder;
|
||||||
use jj_lib::working_copy::SnapshotError;
|
use jj_lib::working_copy::SnapshotError;
|
||||||
use jj_lib::working_copy::SnapshotOptions;
|
use jj_lib::working_copy::SnapshotOptions;
|
||||||
|
use jj_lib::working_copy::SnapshotStats;
|
||||||
use jj_lib::workspace::Workspace;
|
use jj_lib::workspace::Workspace;
|
||||||
use pollster::FutureExt;
|
use pollster::FutureExt;
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
@ -302,17 +303,18 @@ impl TestWorkspace {
|
||||||
pub fn snapshot_with_options(
|
pub fn snapshot_with_options(
|
||||||
&mut self,
|
&mut self,
|
||||||
options: &SnapshotOptions,
|
options: &SnapshotOptions,
|
||||||
) -> Result<MergedTree, SnapshotError> {
|
) -> Result<(MergedTree, SnapshotStats), SnapshotError> {
|
||||||
let mut locked_ws = self.workspace.start_working_copy_mutation().unwrap();
|
let mut locked_ws = self.workspace.start_working_copy_mutation().unwrap();
|
||||||
let tree_id = locked_ws.locked_wc().snapshot(options)?;
|
let (tree_id, stats) = locked_ws.locked_wc().snapshot(options)?;
|
||||||
// arbitrary operation id
|
// arbitrary operation id
|
||||||
locked_ws.finish(self.repo.op_id().clone()).unwrap();
|
locked_ws.finish(self.repo.op_id().clone()).unwrap();
|
||||||
Ok(self.repo.store().get_root_tree(&tree_id).unwrap())
|
Ok((self.repo.store().get_root_tree(&tree_id).unwrap(), stats))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Like `snapshot_with_option()` but with default options
|
/// Like `snapshot_with_option()` but with default options
|
||||||
pub fn snapshot(&mut self) -> Result<MergedTree, SnapshotError> {
|
pub fn snapshot(&mut self) -> Result<MergedTree, SnapshotError> {
|
||||||
self.snapshot_with_options(&SnapshotOptions::empty_for_test())
|
let (tree_id, _stats) = self.snapshot_with_options(&SnapshotOptions::empty_for_test())?;
|
||||||
|
Ok(tree_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue