mirror of
https://github.com/jj-vcs/jj.git
synced 2025-12-23 06:01:01 +00:00
cli_util: enable automatic update of stale workspaces if config is set
This significantly reduces toil for multi-workspace users, resolving issue #3820
This commit is contained in:
parent
2c54848e63
commit
49890fa2d9
6 changed files with 331 additions and 115 deletions
|
|
@ -31,6 +31,9 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|||
[documentation](https://martinvonz.github.io/jj/latest/install-and-setup/#command-line-completion)
|
||||
to activate them.
|
||||
|
||||
* Added the config setting `snapshot.auto-update-stale` for automatically
|
||||
running `jj workspace update-stale` when applicable.
|
||||
|
||||
### Fixed bugs
|
||||
|
||||
## [0.23.0] - 2024-11-06
|
||||
|
|
|
|||
|
|
@ -372,7 +372,49 @@ impl CommandHelper {
|
|||
#[instrument(skip(self, ui))]
|
||||
pub fn workspace_helper(&self, ui: &Ui) -> Result<WorkspaceCommandHelper, CommandError> {
|
||||
let mut workspace_command = self.workspace_helper_no_snapshot(ui)?;
|
||||
workspace_command.maybe_snapshot(ui)?;
|
||||
|
||||
let workspace_command = match workspace_command.maybe_snapshot_impl(ui) {
|
||||
Ok(()) => workspace_command,
|
||||
Err(SnapshotWorkingCopyError::Command(err)) => return Err(err),
|
||||
Err(SnapshotWorkingCopyError::StaleWorkingCopy(err)) => {
|
||||
let auto_update_stale = self
|
||||
.settings()
|
||||
.config()
|
||||
.get_bool("snapshot.auto-update-stale")?;
|
||||
if !auto_update_stale {
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
// We detected the working copy was stale and the client is configured to
|
||||
// auto-update-stale, so let's do that now. We need to do it up here, not at a
|
||||
// lower level (e.g. inside snapshot_working_copy()) to avoid recursive locking
|
||||
// of the working copy.
|
||||
match self.load_stale_working_copy_commit(ui)? {
|
||||
StaleWorkingCopy::Recovered(workspace_command) => workspace_command,
|
||||
StaleWorkingCopy::Snapshotted((repo, stale_commit)) => {
|
||||
let mut workspace_command = self.workspace_helper_no_snapshot(ui)?;
|
||||
let (locked_ws, new_commit) =
|
||||
workspace_command.unchecked_start_working_copy_mutation()?;
|
||||
let stats = update_stale_working_copy(
|
||||
locked_ws,
|
||||
repo.op_id().clone(),
|
||||
&stale_commit,
|
||||
&new_commit,
|
||||
)?;
|
||||
writeln!(
|
||||
ui.warning_default(),
|
||||
"Automatically updated to fresh commit {}",
|
||||
short_commit_hash(stale_commit.id())
|
||||
)?;
|
||||
workspace_command.write_stale_commit_stats(ui, &new_commit, stats)?;
|
||||
|
||||
workspace_command.user_repo = ReadonlyUserRepo::new(repo);
|
||||
workspace_command
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(workspace_command)
|
||||
}
|
||||
|
||||
|
|
@ -854,6 +896,27 @@ pub struct WorkspaceCommandHelper {
|
|||
working_copy_shared_with_git: bool,
|
||||
}
|
||||
|
||||
enum SnapshotWorkingCopyError {
|
||||
Command(CommandError),
|
||||
StaleWorkingCopy(CommandError),
|
||||
}
|
||||
|
||||
impl SnapshotWorkingCopyError {
|
||||
fn into_command_error(self) -> CommandError {
|
||||
match self {
|
||||
Self::Command(err) => err,
|
||||
Self::StaleWorkingCopy(err) => err,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn snapshot_command_error<E>(err: E) -> SnapshotWorkingCopyError
|
||||
where
|
||||
E: Into<CommandError>,
|
||||
{
|
||||
SnapshotWorkingCopyError::Command(err.into())
|
||||
}
|
||||
|
||||
impl WorkspaceCommandHelper {
|
||||
#[instrument(skip_all)]
|
||||
fn new(
|
||||
|
|
@ -911,27 +974,34 @@ impl WorkspaceCommandHelper {
|
|||
}
|
||||
}
|
||||
|
||||
/// Snapshot the working copy if allowed, and import Git refs if the working
|
||||
/// copy is collocated with Git.
|
||||
#[instrument(skip_all)]
|
||||
pub fn maybe_snapshot(&mut self, ui: &Ui) -> Result<(), CommandError> {
|
||||
fn maybe_snapshot_impl(&mut self, ui: &Ui) -> Result<(), SnapshotWorkingCopyError> {
|
||||
if self.may_update_working_copy {
|
||||
if self.working_copy_shared_with_git {
|
||||
self.import_git_head(ui)?;
|
||||
self.import_git_head(ui).map_err(snapshot_command_error)?;
|
||||
}
|
||||
// Because the Git refs (except HEAD) aren't imported yet, the ref
|
||||
// pointing to the new working-copy commit might not be exported.
|
||||
// In that situation, the ref would be conflicted anyway, so export
|
||||
// failure is okay.
|
||||
self.snapshot_working_copy(ui)?;
|
||||
|
||||
// import_git_refs() can rebase the working-copy commit.
|
||||
if self.working_copy_shared_with_git {
|
||||
self.import_git_refs(ui)?;
|
||||
self.import_git_refs(ui).map_err(snapshot_command_error)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Snapshot the working copy if allowed, and import Git refs if the working
|
||||
/// copy is collocated with Git.
|
||||
#[instrument(skip_all)]
|
||||
pub fn maybe_snapshot(&mut self, ui: &Ui) -> Result<(), CommandError> {
|
||||
self.maybe_snapshot_impl(ui)
|
||||
.map_err(|err| err.into_command_error())
|
||||
}
|
||||
|
||||
/// Imports new HEAD from the colocated Git repo.
|
||||
///
|
||||
/// If the Git HEAD has changed, this function checks out the new Git HEAD.
|
||||
|
|
@ -1072,7 +1142,7 @@ impl WorkspaceCommandHelper {
|
|||
Ok((locked_ws, wc_commit))
|
||||
}
|
||||
|
||||
pub fn create_and_check_out_recovery_commit(&mut self, ui: &Ui) -> Result<(), CommandError> {
|
||||
fn create_and_check_out_recovery_commit(&mut self, ui: &Ui) -> Result<(), CommandError> {
|
||||
self.check_working_copy_writable()?;
|
||||
|
||||
let workspace_id = self.workspace_id().clone();
|
||||
|
|
@ -1098,10 +1168,9 @@ to the current parents may contain changes from multiple commits.
|
|||
short_commit_hash(new_commit.id())
|
||||
)?;
|
||||
locked_ws.finish(repo.op_id().clone())?;
|
||||
|
||||
self.user_repo = ReadonlyUserRepo::new(repo);
|
||||
self.maybe_snapshot(ui)?;
|
||||
Ok(())
|
||||
|
||||
self.maybe_snapshot(ui)
|
||||
}
|
||||
|
||||
pub fn workspace_root(&self) -> &Path {
|
||||
|
|
@ -1620,13 +1689,14 @@ to the current parents may contain changes from multiple commits.
|
|||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
fn snapshot_working_copy(&mut self, ui: &Ui) -> Result<(), CommandError> {
|
||||
fn snapshot_working_copy(&mut self, ui: &Ui) -> Result<(), SnapshotWorkingCopyError> {
|
||||
let workspace_id = self.workspace_id().to_owned();
|
||||
let get_wc_commit = |repo: &ReadonlyRepo| -> Result<Option<_>, _> {
|
||||
repo.view()
|
||||
.get_wc_commit_id(&workspace_id)
|
||||
.map(|id| repo.store().get_commit(id))
|
||||
.transpose()
|
||||
.map_err(snapshot_command_error)
|
||||
};
|
||||
let repo = self.repo().clone();
|
||||
let Some(wc_commit) = get_wc_commit(&repo)? else {
|
||||
|
|
@ -1634,20 +1704,34 @@ to the current parents may contain changes from multiple commits.
|
|||
// committing the working copy.
|
||||
return Ok(());
|
||||
};
|
||||
let base_ignores = self.base_ignores()?;
|
||||
let auto_tracking_matcher = self.auto_tracking_matcher(ui)?;
|
||||
let base_ignores = self.base_ignores().map_err(snapshot_command_error)?;
|
||||
let auto_tracking_matcher = self
|
||||
.auto_tracking_matcher(ui)
|
||||
.map_err(snapshot_command_error)?;
|
||||
|
||||
// Compare working-copy tree and operation with repo's, and reload as needed.
|
||||
let fsmonitor_settings = self.settings().fsmonitor_settings()?;
|
||||
let max_new_file_size = self.settings().max_new_file_size()?;
|
||||
let fsmonitor_settings = self
|
||||
.settings()
|
||||
.fsmonitor_settings()
|
||||
.map_err(snapshot_command_error)?;
|
||||
let max_new_file_size = self
|
||||
.settings()
|
||||
.max_new_file_size()
|
||||
.map_err(snapshot_command_error)?;
|
||||
let command = self.env.command.clone();
|
||||
let mut locked_ws = self.workspace.start_working_copy_mutation()?;
|
||||
let mut locked_ws = self
|
||||
.workspace
|
||||
.start_working_copy_mutation()
|
||||
.map_err(snapshot_command_error)?;
|
||||
let old_op_id = locked_ws.locked_wc().old_operation_id().clone();
|
||||
|
||||
let (repo, wc_commit) =
|
||||
match WorkingCopyFreshness::check_stale(locked_ws.locked_wc(), &wc_commit, &repo) {
|
||||
Ok(WorkingCopyFreshness::Fresh) => (repo, wc_commit),
|
||||
Ok(WorkingCopyFreshness::Updated(wc_operation)) => {
|
||||
let repo = repo.reload_at(&wc_operation)?;
|
||||
let repo = repo
|
||||
.reload_at(&wc_operation)
|
||||
.map_err(snapshot_command_error)?;
|
||||
let wc_commit = if let Some(wc_commit) = get_wc_commit(&repo)? {
|
||||
wc_commit
|
||||
} else {
|
||||
|
|
@ -1657,43 +1741,52 @@ to the current parents may contain changes from multiple commits.
|
|||
(repo, wc_commit)
|
||||
}
|
||||
Ok(WorkingCopyFreshness::WorkingCopyStale) => {
|
||||
return Err(user_error_with_hint(
|
||||
format!(
|
||||
"The working copy is stale (not updated since operation {}).",
|
||||
short_operation_hash(&old_op_id)
|
||||
),
|
||||
"Run `jj workspace update-stale` to update it.
|
||||
return Err(SnapshotWorkingCopyError::StaleWorkingCopy(
|
||||
user_error_with_hint(
|
||||
format!(
|
||||
"The working copy is stale (not updated since operation {}).",
|
||||
short_operation_hash(&old_op_id)
|
||||
),
|
||||
"Run `jj workspace update-stale` to update it.
|
||||
See https://martinvonz.github.io/jj/latest/working-copy/#stale-working-copy \
|
||||
for more information.",
|
||||
for more information.",
|
||||
),
|
||||
));
|
||||
}
|
||||
Ok(WorkingCopyFreshness::SiblingOperation) => {
|
||||
return Err(internal_error(format!(
|
||||
"The repo was loaded at operation {}, which seems to be a sibling of the \
|
||||
working copy's operation {}",
|
||||
short_operation_hash(repo.op_id()),
|
||||
short_operation_hash(&old_op_id)
|
||||
return Err(SnapshotWorkingCopyError::StaleWorkingCopy(internal_error(
|
||||
format!(
|
||||
"The repo was loaded at operation {}, which seems to be a sibling of \
|
||||
the working copy's operation {}",
|
||||
short_operation_hash(repo.op_id()),
|
||||
short_operation_hash(&old_op_id)
|
||||
),
|
||||
)));
|
||||
}
|
||||
Err(OpStoreError::ObjectNotFound { .. }) => {
|
||||
return Err(user_error_with_hint(
|
||||
"Could not read working copy's operation.",
|
||||
"Run `jj workspace update-stale` to recover.
|
||||
return Err(SnapshotWorkingCopyError::StaleWorkingCopy(
|
||||
user_error_with_hint(
|
||||
"Could not read working copy's operation.",
|
||||
"Run `jj workspace update-stale` to recover.
|
||||
See https://martinvonz.github.io/jj/latest/working-copy/#stale-working-copy \
|
||||
for more information.",
|
||||
for more information.",
|
||||
),
|
||||
));
|
||||
}
|
||||
Err(e) => return Err(e.into()),
|
||||
Err(e) => return Err(snapshot_command_error(e)),
|
||||
};
|
||||
self.user_repo = ReadonlyUserRepo::new(repo);
|
||||
let progress = crate::progress::snapshot_progress(ui);
|
||||
let new_tree_id = locked_ws.locked_wc().snapshot(&SnapshotOptions {
|
||||
base_ignores,
|
||||
fsmonitor_settings,
|
||||
progress: progress.as_ref().map(|x| x as _),
|
||||
start_tracking_matcher: &auto_tracking_matcher,
|
||||
max_new_file_size,
|
||||
})?;
|
||||
let new_tree_id = locked_ws
|
||||
.locked_wc()
|
||||
.snapshot(&SnapshotOptions {
|
||||
base_ignores,
|
||||
fsmonitor_settings,
|
||||
progress: progress.as_ref().map(|x| x as _),
|
||||
start_tracking_matcher: &auto_tracking_matcher,
|
||||
max_new_file_size,
|
||||
})
|
||||
.map_err(snapshot_command_error)?;
|
||||
drop(progress);
|
||||
if new_tree_id != *wc_commit.tree_id() {
|
||||
let mut tx = start_repo_transaction(
|
||||
|
|
@ -1706,26 +1799,37 @@ See https://martinvonz.github.io/jj/latest/working-copy/#stale-working-copy \
|
|||
let commit = mut_repo
|
||||
.rewrite_commit(command.settings(), &wc_commit)
|
||||
.set_tree_id(new_tree_id)
|
||||
.write()?;
|
||||
mut_repo.set_wc_commit(workspace_id, commit.id().clone())?;
|
||||
.write()
|
||||
.map_err(snapshot_command_error)?;
|
||||
mut_repo
|
||||
.set_wc_commit(workspace_id, commit.id().clone())
|
||||
.map_err(snapshot_command_error)?;
|
||||
|
||||
// Rebase descendants
|
||||
let num_rebased = mut_repo.rebase_descendants(command.settings())?;
|
||||
let num_rebased = mut_repo
|
||||
.rebase_descendants(command.settings())
|
||||
.map_err(snapshot_command_error)?;
|
||||
if num_rebased > 0 {
|
||||
writeln!(
|
||||
ui.status(),
|
||||
"Rebased {num_rebased} descendant commits onto updated working copy"
|
||||
)?;
|
||||
)
|
||||
.map_err(snapshot_command_error)?;
|
||||
}
|
||||
|
||||
if self.working_copy_shared_with_git {
|
||||
let refs = git::export_refs(mut_repo)?;
|
||||
print_failed_git_export(ui, &refs)?;
|
||||
let refs = git::export_refs(mut_repo).map_err(snapshot_command_error)?;
|
||||
print_failed_git_export(ui, &refs).map_err(snapshot_command_error)?;
|
||||
}
|
||||
|
||||
self.user_repo = ReadonlyUserRepo::new(tx.commit("snapshot working copy")?);
|
||||
let repo = tx
|
||||
.commit("snapshot working copy")
|
||||
.map_err(snapshot_command_error)?;
|
||||
self.user_repo = ReadonlyUserRepo::new(repo);
|
||||
}
|
||||
locked_ws.finish(self.user_repo.repo.op_id().clone())?;
|
||||
locked_ws
|
||||
.finish(self.user_repo.repo.op_id().clone())
|
||||
.map_err(snapshot_command_error)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ pub fn cmd_workspace_update_stale(
|
|||
// (since we just updated it), so we can return early.
|
||||
return Ok(());
|
||||
}
|
||||
StaleWorkingCopy::Snapshotted((_workspace_command, commit)) => commit,
|
||||
StaleWorkingCopy::Snapshotted((_repo, commit)) => commit,
|
||||
};
|
||||
let mut workspace_command = command.workspace_helper_no_snapshot(ui)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -457,6 +457,11 @@
|
|||
"description": "Fileset pattern describing what new files to automatically track on snapshotting. By default all new files are tracked.",
|
||||
"default": "all()"
|
||||
},
|
||||
"auto-update-stale": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to automatically update the working copy if it is stale. See https://martinvonz.github.io/jj/latest/working-copy/#stale-working-copy",
|
||||
"default": "false"
|
||||
},
|
||||
"max-new-file-size": {
|
||||
"type": [
|
||||
"integer",
|
||||
|
|
|
|||
|
|
@ -30,3 +30,4 @@ edit = false
|
|||
[snapshot]
|
||||
max-new-file-size = "1MiB"
|
||||
auto-track = "all()"
|
||||
auto-update-stale = false
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
use std::path::Path;
|
||||
|
||||
use test_case::test_case;
|
||||
|
||||
use crate::common::TestEnvironment;
|
||||
|
||||
/// Test adding a second workspace
|
||||
|
|
@ -609,9 +611,79 @@ fn test_workspaces_updated_by_other() {
|
|||
"###);
|
||||
}
|
||||
|
||||
/// Test a clean working copy that gets rewritten from another workspace
|
||||
#[test]
|
||||
fn test_workspaces_current_op_discarded_by_other() {
|
||||
fn test_workspaces_updated_by_other_automatic() {
|
||||
let test_env = TestEnvironment::default();
|
||||
test_env.add_config("[snapshot]\nauto-update-stale = true\n");
|
||||
|
||||
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "main"]);
|
||||
let main_path = test_env.env_root().join("main");
|
||||
let secondary_path = test_env.env_root().join("secondary");
|
||||
|
||||
std::fs::write(main_path.join("file"), "contents\n").unwrap();
|
||||
test_env.jj_cmd_ok(&main_path, &["new"]);
|
||||
|
||||
test_env.jj_cmd_ok(&main_path, &["workspace", "add", "../secondary"]);
|
||||
|
||||
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###"
|
||||
○ 3224de8ae048 secondary@
|
||||
│ @ 06b57f44a3ca default@
|
||||
├─╯
|
||||
○ 506f4ec3c2c6
|
||||
◆ 000000000000
|
||||
"###);
|
||||
|
||||
// Rewrite the check-out commit in one workspace.
|
||||
std::fs::write(main_path.join("file"), "changed in main\n").unwrap();
|
||||
let (stdout, stderr) = test_env.jj_cmd_ok(&main_path, &["squash"]);
|
||||
insta::assert_snapshot!(stdout, @"");
|
||||
insta::assert_snapshot!(stderr, @r###"
|
||||
Rebased 1 descendant commits
|
||||
Working copy now at: mzvwutvl a58c9a9b (empty) (no description set)
|
||||
Parent commit : qpvuntsm d4124476 (no description set)
|
||||
"###);
|
||||
|
||||
// The secondary workspace's working-copy commit was updated.
|
||||
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###"
|
||||
@ a58c9a9b19ce default@
|
||||
│ ○ e82cd4ee8faa secondary@
|
||||
├─╯
|
||||
○ d41244767d45
|
||||
◆ 000000000000
|
||||
"###);
|
||||
|
||||
// The first working copy gets automatically updated.
|
||||
let (stdout, stderr) = test_env.jj_cmd_ok(&secondary_path, &["st"]);
|
||||
insta::assert_snapshot!(stdout, @r###"
|
||||
The working copy is clean
|
||||
Working copy : pmmvwywv 3224de8a (empty) (no description set)
|
||||
Parent commit: qpvuntsm 506f4ec3 (no description set)
|
||||
"###);
|
||||
insta::assert_snapshot!(stderr, @r###"
|
||||
Warning: Automatically updated to fresh commit 3224de8ae048
|
||||
Working copy now at: pmmvwywv e82cd4ee (empty) (no description set)
|
||||
Added 0 files, modified 1 files, removed 0 files
|
||||
"###);
|
||||
|
||||
insta::assert_snapshot!(get_log_output(&test_env, &secondary_path),
|
||||
@r###"
|
||||
○ a58c9a9b19ce default@
|
||||
│ @ e82cd4ee8faa secondary@
|
||||
├─╯
|
||||
○ d41244767d45
|
||||
◆ 000000000000
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test_case(false; "manual")]
|
||||
#[test_case(true; "automatic")]
|
||||
fn test_workspaces_current_op_discarded_by_other(automatic: bool) {
|
||||
let test_env = TestEnvironment::default();
|
||||
if automatic {
|
||||
test_env.add_config("[snapshot]\nauto-update-stale = true\n");
|
||||
}
|
||||
|
||||
// Use the local backend because GitBackend::gc() depends on the git CLI.
|
||||
test_env.jj_cmd_ok(
|
||||
test_env.env_root(),
|
||||
|
|
@ -657,84 +729,115 @@ fn test_workspaces_current_op_discarded_by_other() {
|
|||
r#"id.short(10) ++ " " ++ description"#,
|
||||
],
|
||||
);
|
||||
insta::assert_snapshot!(stdout, @r#"
|
||||
@ 757bc1140b abandon commit 20dd439c4bd12c6ad56c187ac490bd0141804618f638dc5c4dc92ff9aecba20f152b23160db9dcf61beb31a5cb14091d9def5a36d11c9599cc4d2e5689236af1
|
||||
○ 8d4abed655 create initial working-copy commit in workspace secondary
|
||||
○ 3de27432e5 add workspace 'secondary'
|
||||
○ bcf69de808 new empty commit
|
||||
○ a36b99a15c snapshot working copy
|
||||
○ ddf023d319 new empty commit
|
||||
○ 829c93f6a3 snapshot working copy
|
||||
○ 2557266dd2 add workspace 'default'
|
||||
○ 0000000000
|
||||
"#);
|
||||
insta::allow_duplicates! {
|
||||
insta::assert_snapshot!(stdout, @r#"
|
||||
@ 757bc1140b abandon commit 20dd439c4bd12c6ad56c187ac490bd0141804618f638dc5c4dc92ff9aecba20f152b23160db9dcf61beb31a5cb14091d9def5a36d11c9599cc4d2e5689236af1
|
||||
○ 8d4abed655 create initial working-copy commit in workspace secondary
|
||||
○ 3de27432e5 add workspace 'secondary'
|
||||
○ bcf69de808 new empty commit
|
||||
○ a36b99a15c snapshot working copy
|
||||
○ ddf023d319 new empty commit
|
||||
○ 829c93f6a3 snapshot working copy
|
||||
○ 2557266dd2 add workspace 'default'
|
||||
○ 0000000000
|
||||
"#);
|
||||
}
|
||||
|
||||
// Abandon ops, including the one the secondary workspace is currently on.
|
||||
test_env.jj_cmd_ok(&main_path, &["operation", "abandon", "..@-"]);
|
||||
test_env.jj_cmd_ok(&main_path, &["util", "gc", "--expire=now"]);
|
||||
|
||||
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###"
|
||||
○ 96b31dafdc41 secondary@
|
||||
│ @ 6c051bd1ccd5 default@
|
||||
├─╯
|
||||
○ 7c5b25a4fc8f
|
||||
◆ 000000000000
|
||||
"###);
|
||||
insta::allow_duplicates! {
|
||||
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###"
|
||||
○ 96b31dafdc41 secondary@
|
||||
│ @ 6c051bd1ccd5 default@
|
||||
├─╯
|
||||
○ 7c5b25a4fc8f
|
||||
◆ 000000000000
|
||||
"###);
|
||||
}
|
||||
|
||||
let stderr = test_env.jj_cmd_failure(&secondary_path, &["st"]);
|
||||
insta::assert_snapshot!(stderr, @r###"
|
||||
Error: Could not read working copy's operation.
|
||||
Hint: Run `jj workspace update-stale` to recover.
|
||||
See https://martinvonz.github.io/jj/latest/working-copy/#stale-working-copy for more information.
|
||||
"###);
|
||||
if automatic {
|
||||
// Run a no-op command to set the randomness seed for commit hashes.
|
||||
test_env.jj_cmd_success(&secondary_path, &["help"]);
|
||||
|
||||
let (stdout, stderr) = test_env.jj_cmd_ok(&secondary_path, &["workspace", "update-stale"]);
|
||||
insta::assert_snapshot!(stderr, @r###"
|
||||
Failed to read working copy's current operation; attempting recovery. Error message from read attempt: Object 8d4abed655badb70b1bab62aa87136619dbc3c8015a8ce8dfb7abfeca4e2f36c713d8f84e070a0613907a6cee7e1cc05323fe1205a319b93fe978f11a060c33c of type operation not found
|
||||
Created and checked out recovery commit 76d0126b3e5c
|
||||
"###);
|
||||
insta::assert_snapshot!(stdout, @"");
|
||||
let (stdout, stderr) = test_env.jj_cmd_ok(&secondary_path, &["st"]);
|
||||
insta::assert_snapshot!(stdout, @r###"
|
||||
Working copy changes:
|
||||
A added
|
||||
D deleted
|
||||
M modified
|
||||
Working copy : kmkuslsw 15df8cb5 RECOVERY COMMIT FROM `jj workspace update-stale`
|
||||
Parent commit: rzvqmyuk 96b31daf (empty) (no description set)
|
||||
"###);
|
||||
insta::assert_snapshot!(stderr, @r###"
|
||||
Failed to read working copy's current operation; attempting recovery. Error message from read attempt: Object 8d4abed655badb70b1bab62aa87136619dbc3c8015a8ce8dfb7abfeca4e2f36c713d8f84e070a0613907a6cee7e1cc05323fe1205a319b93fe978f11a060c33c of type operation not found
|
||||
Created and checked out recovery commit 76d0126b3e5c
|
||||
"###);
|
||||
} else {
|
||||
let stderr = test_env.jj_cmd_failure(&secondary_path, &["st"]);
|
||||
insta::assert_snapshot!(stderr, @r###"
|
||||
Error: Could not read working copy's operation.
|
||||
Hint: Run `jj workspace update-stale` to recover.
|
||||
See https://martinvonz.github.io/jj/latest/working-copy/#stale-working-copy for more information.
|
||||
"###);
|
||||
|
||||
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###"
|
||||
○ 15df8cb57d3f secondary@
|
||||
○ 96b31dafdc41
|
||||
│ @ 6c051bd1ccd5 default@
|
||||
├─╯
|
||||
○ 7c5b25a4fc8f
|
||||
◆ 000000000000
|
||||
"###);
|
||||
let (stdout, stderr) = test_env.jj_cmd_ok(&secondary_path, &["workspace", "update-stale"]);
|
||||
insta::assert_snapshot!(stderr, @r###"
|
||||
Failed to read working copy's current operation; attempting recovery. Error message from read attempt: Object 8d4abed655badb70b1bab62aa87136619dbc3c8015a8ce8dfb7abfeca4e2f36c713d8f84e070a0613907a6cee7e1cc05323fe1205a319b93fe978f11a060c33c of type operation not found
|
||||
Created and checked out recovery commit 76d0126b3e5c
|
||||
"###);
|
||||
insta::assert_snapshot!(stdout, @"");
|
||||
}
|
||||
|
||||
insta::allow_duplicates! {
|
||||
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###"
|
||||
○ 15df8cb57d3f secondary@
|
||||
○ 96b31dafdc41
|
||||
│ @ 6c051bd1ccd5 default@
|
||||
├─╯
|
||||
○ 7c5b25a4fc8f
|
||||
◆ 000000000000
|
||||
"###);
|
||||
}
|
||||
|
||||
// The sparse patterns should remain
|
||||
let stdout = test_env.jj_cmd_success(&secondary_path, &["sparse", "list"]);
|
||||
insta::assert_snapshot!(stdout, @r###"
|
||||
added
|
||||
deleted
|
||||
modified
|
||||
"###);
|
||||
insta::allow_duplicates! {
|
||||
insta::assert_snapshot!(stdout, @r###"
|
||||
added
|
||||
deleted
|
||||
modified
|
||||
"###);
|
||||
}
|
||||
let (stdout, stderr) = test_env.jj_cmd_ok(&secondary_path, &["st"]);
|
||||
insta::assert_snapshot!(stderr, @"");
|
||||
insta::assert_snapshot!(stdout, @r###"
|
||||
Working copy changes:
|
||||
A added
|
||||
D deleted
|
||||
M modified
|
||||
Working copy : kmkuslsw 15df8cb5 RECOVERY COMMIT FROM `jj workspace update-stale`
|
||||
Parent commit: rzvqmyuk 96b31daf (empty) (no description set)
|
||||
"###);
|
||||
// The modified file should have the same contents it had before (not reset to
|
||||
// the base contents)
|
||||
insta::assert_snapshot!(std::fs::read_to_string(secondary_path.join("modified")).unwrap(), @r###"
|
||||
secondary
|
||||
"###);
|
||||
insta::allow_duplicates! {
|
||||
insta::assert_snapshot!(stderr, @"");
|
||||
insta::assert_snapshot!(stdout, @r###"
|
||||
Working copy changes:
|
||||
A added
|
||||
D deleted
|
||||
M modified
|
||||
Working copy : kmkuslsw 15df8cb5 RECOVERY COMMIT FROM `jj workspace update-stale`
|
||||
Parent commit: rzvqmyuk 96b31daf (empty) (no description set)
|
||||
"###);
|
||||
// The modified file should have the same contents it had before (not reset to
|
||||
// the base contents)
|
||||
insta::assert_snapshot!(std::fs::read_to_string(secondary_path.join("modified")).unwrap(), @r###"
|
||||
secondary
|
||||
"###);
|
||||
}
|
||||
|
||||
let (stdout, stderr) = test_env.jj_cmd_ok(&secondary_path, &["evolog"]);
|
||||
insta::assert_snapshot!(stderr, @"");
|
||||
insta::assert_snapshot!(stdout, @r###"
|
||||
@ kmkuslsw test.user@example.com 2001-02-03 08:05:18 secondary@ 15df8cb5
|
||||
│ RECOVERY COMMIT FROM `jj workspace update-stale`
|
||||
○ kmkuslsw hidden test.user@example.com 2001-02-03 08:05:18 76d0126b
|
||||
(empty) RECOVERY COMMIT FROM `jj workspace update-stale`
|
||||
"###);
|
||||
insta::allow_duplicates! {
|
||||
insta::assert_snapshot!(stderr, @"");
|
||||
insta::assert_snapshot!(stdout, @r###"
|
||||
@ kmkuslsw test.user@example.com 2001-02-03 08:05:18 secondary@ 15df8cb5
|
||||
│ RECOVERY COMMIT FROM `jj workspace update-stale`
|
||||
○ kmkuslsw hidden test.user@example.com 2001-02-03 08:05:18 76d0126b
|
||||
(empty) RECOVERY COMMIT FROM `jj workspace update-stale`
|
||||
"###);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue