diff --git a/cli/src/commands/debug/copy_detection.rs b/cli/src/commands/debug/copy_detection.rs new file mode 100644 index 000000000..613baf8e7 --- /dev/null +++ b/cli/src/commands/debug/copy_detection.rs @@ -0,0 +1,79 @@ +// Copyright 2024 The Jujutsu Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![allow(unused, dead_code)] + +use std::fmt::Debug; +use std::io::Write as _; +use std::path::{Path, PathBuf}; + +use futures::executor::block_on_stream; +use futures::StreamExt; +use jj_lib::backend::{Backend, CopyRecord, CopySource, CopySources}; +use jj_lib::default_index::{AsCompositeIndex as _, DefaultIndexStore}; +use jj_lib::op_walk; +use jj_lib::repo_path::{RepoPath, RepoPathBuf}; + +use crate::cli_util::{CommandHelper, RevisionArg}; +use crate::command_error::{internal_error, user_error, CommandError}; +use crate::ui::Ui; + +/// Rebuild commit index +#[derive(clap::Args, Clone, Debug)] +pub struct CopyDetectionArgs { + /// Show changes in this revision, compared to its parent(s) + #[arg(default_value = "@")] + revision: RevisionArg, +} + +pub fn cmd_debug_copy_detection( + ui: &mut Ui, + command: &CommandHelper, + args: &CopyDetectionArgs, +) -> Result<(), CommandError> { + let ws = command.workspace_helper(ui)?; + let Some(git) = ws.git_backend() else { + writeln!(ui.stderr(), "Not a git backend.")?; + return Ok(()); + }; + let commit = ws.resolve_single_rev(&args.revision)?; + let tree = commit.tree()?; + + let paths: Vec = tree.entries().map(|(path, _)| path).collect(); + let commits = [commit.id().clone()]; + let parents = commit.parent_ids(); + for copy_record in + block_on_stream(git.get_copy_records(&paths, parents, &commits)?).filter_map(|r| r.ok()) + { + match copy_record.sources { + CopySources::Resolved(CopySource { path, .. }) => { + write!(ui.stdout(), "{}", path.as_internal_file_string()); + } + CopySources::Conflict(conflicting) => { + let mut sorted: Vec<_> = conflicting + .iter() + .map(|s| s.path.as_internal_file_string()) + .collect(); + sorted.sort(); + write!(ui.stdout(), "{{ {} }}", sorted.join(", ")); + } + } + writeln!( + ui.stdout(), + " -> {}", + copy_record.target.as_internal_file_string() + ); + } + Ok(()) +} diff --git a/cli/src/commands/debug/mod.rs b/cli/src/commands/debug/mod.rs index 97138e2fa..3f09819d3 100644 --- a/cli/src/commands/debug/mod.rs +++ b/cli/src/commands/debug/mod.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +pub mod copy_detection; pub mod fileset; pub mod index; pub mod local_working_copy; @@ -30,6 +31,7 @@ use std::fmt::Debug; use clap::Subcommand; use jj_lib::local_working_copy::LocalWorkingCopy; +use self::copy_detection::{cmd_debug_copy_detection, CopyDetectionArgs}; use self::fileset::{cmd_debug_fileset, DebugFilesetArgs}; use self::index::{cmd_debug_index, DebugIndexArgs}; use self::local_working_copy::{cmd_debug_local_working_copy, DebugLocalWorkingCopyArgs}; @@ -49,6 +51,7 @@ use crate::ui::Ui; #[derive(Subcommand, Clone, Debug)] #[command(hide = true)] pub enum DebugCommand { + CopyDetection(CopyDetectionArgs), Fileset(DebugFilesetArgs), Index(DebugIndexArgs), LocalWorkingCopy(DebugLocalWorkingCopyArgs), @@ -75,6 +78,7 @@ pub fn cmd_debug( DebugCommand::LocalWorkingCopy(args) => cmd_debug_local_working_copy(ui, command, args), DebugCommand::Operation(args) => cmd_debug_operation(ui, command, args), DebugCommand::Reindex(args) => cmd_debug_reindex(ui, command, args), + DebugCommand::CopyDetection(args) => cmd_debug_copy_detection(ui, command, args), DebugCommand::Revset(args) => cmd_debug_revset(ui, command, args), DebugCommand::Snapshot(args) => cmd_debug_snapshot(ui, command, args), DebugCommand::Template(args) => cmd_debug_template(ui, command, args), diff --git a/cli/tests/runner.rs b/cli/tests/runner.rs index c143db097..1f6b87bd1 100644 --- a/cli/tests/runner.rs +++ b/cli/tests/runner.rs @@ -19,6 +19,7 @@ mod test_commit_command; mod test_commit_template; mod test_concurrent_operations; mod test_config_command; +mod test_copy_detection; mod test_debug_command; mod test_describe_command; mod test_diff_command; diff --git a/cli/tests/test_copy_detection.rs b/cli/tests/test_copy_detection.rs new file mode 100644 index 000000000..f72849306 --- /dev/null +++ b/cli/tests/test_copy_detection.rs @@ -0,0 +1,34 @@ +// Copyright 2022 The Jujutsu Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::common::TestEnvironment; + +#[test] +fn test_simple_rename() { + let test_env = TestEnvironment::default(); + test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]); + let repo_path = test_env.env_root().join("repo"); + + test_env.jj_cmd_ok(&repo_path, &["new"]); + std::fs::write(repo_path.join("original"), "original").unwrap(); + std::fs::write(repo_path.join("something"), "something").unwrap(); + test_env.jj_cmd_ok(&repo_path, &["commit", "-mfirst"]); + std::fs::remove_file(repo_path.join("original")).unwrap(); + std::fs::write(repo_path.join("modified"), "original").unwrap(); + std::fs::write(repo_path.join("something"), "changed").unwrap(); + insta::assert_snapshot!(test_env.jj_cmd_success(&repo_path, &["debug", "copy-detection"]).replace('\\', "/"), + @r###" + original -> modified + "###); +}