mirror of
https://github.com/jj-vcs/jj.git
synced 2026-07-06 08:12:39 +08:00
347 lines
10 KiB
Rust
347 lines
10 KiB
Rust
// Copyright 2026 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 std::ffi::OsStr;
|
|
|
|
use crate::common::CommandOutput;
|
|
use crate::common::TestEnvironment;
|
|
use crate::common::TestWorkDir;
|
|
|
|
fn normalize_git_hashes(output: CommandOutput) -> CommandOutput {
|
|
output
|
|
.normalize_stdout_with(normalize_git_hashes_in_text)
|
|
.normalize_stderr_with(normalize_git_hashes_in_text)
|
|
}
|
|
|
|
fn normalize_git_hashes_in_text(text: String) -> String {
|
|
let text = regex::Regex::new(r"[0-9a-f]{10,40}")
|
|
.unwrap()
|
|
.replace_all(&text, "<git-commit>")
|
|
.into_owned();
|
|
regex::Regex::new(r"[k-z]{8} [0-9a-f]{8}")
|
|
.unwrap()
|
|
.replace_all(&text, "<jj-commit>")
|
|
.into_owned()
|
|
}
|
|
|
|
fn machine_completion_values(output: CommandOutput) -> Vec<String> {
|
|
let output = output.success();
|
|
let candidates: Vec<serde_json::Value> = serde_json::from_str(output.stdout.raw()).unwrap();
|
|
candidates
|
|
.into_iter()
|
|
.map(|candidate| candidate["value"].as_str().unwrap().to_owned())
|
|
.collect()
|
|
}
|
|
|
|
fn run_git<I, S>(work_dir: &TestWorkDir<'_>, args: I)
|
|
where
|
|
I: IntoIterator<Item = S>,
|
|
S: AsRef<OsStr>,
|
|
{
|
|
let output = std::process::Command::new("git")
|
|
.current_dir(work_dir.root())
|
|
.args(args)
|
|
.output()
|
|
.unwrap();
|
|
assert!(
|
|
output.status.success(),
|
|
"git command failed\nstdout:\n{}\nstderr:\n{}",
|
|
String::from_utf8_lossy(&output.stdout),
|
|
String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_submodule_bind_updates_existing_gitlink() {
|
|
let test_env = TestEnvironment::default();
|
|
|
|
test_env
|
|
.run_jj_in(".", ["git", "init", "--colocate", "submodule"])
|
|
.success();
|
|
let submodule_dir = test_env.work_dir("submodule");
|
|
submodule_dir.write_file("file", "first");
|
|
submodule_dir
|
|
.run_jj(["commit", "-m", "first submodule commit"])
|
|
.success();
|
|
let old_target = submodule_dir
|
|
.run_jj(["log", "-r@-", "--no-graph", "-T", "commit_id"])
|
|
.success()
|
|
.stdout
|
|
.raw()
|
|
.trim()
|
|
.to_owned();
|
|
submodule_dir.write_file("file", "second");
|
|
submodule_dir
|
|
.run_jj(["commit", "-m", "second submodule commit"])
|
|
.success();
|
|
let new_target = submodule_dir
|
|
.run_jj(["log", "-r@-", "--no-graph", "-T", "commit_id"])
|
|
.success()
|
|
.stdout
|
|
.raw()
|
|
.trim()
|
|
.to_owned();
|
|
|
|
test_env
|
|
.run_jj_in(".", ["git", "init", "--colocate", "repo"])
|
|
.success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
let submodule_url = format!("{}/submodule", test_env.env_root().display());
|
|
run_git(
|
|
&work_dir,
|
|
[
|
|
"-c",
|
|
"protocol.file.allow=always",
|
|
"submodule",
|
|
"add",
|
|
submodule_url.as_str(),
|
|
"sub",
|
|
],
|
|
);
|
|
run_git(&work_dir, ["-C", "sub", "checkout", &old_target]);
|
|
run_git(
|
|
&work_dir,
|
|
["-C", "sub", "branch", "new-target", &new_target],
|
|
);
|
|
run_git(&work_dir, ["add", "sub"]);
|
|
run_git(
|
|
&work_dir,
|
|
[
|
|
"-c",
|
|
"user.email=test@example.com",
|
|
"-c",
|
|
"user.name=Test user",
|
|
"commit",
|
|
"-m",
|
|
"add submodule",
|
|
],
|
|
);
|
|
|
|
insta::assert_snapshot!(work_dir.run_jj(["diff", "--summary"]), @r#"
|
|
------- stderr -------
|
|
ignoring git submodule at "sub"
|
|
Done importing changes from the underlying Git repo.
|
|
[EOF]
|
|
"#);
|
|
|
|
let output = work_dir.run_jj(["submodule", "bind", "sub", "-r", "new-target"]);
|
|
insta::assert_snapshot!(normalize_git_hashes(output), @r#"
|
|
------- stderr -------
|
|
Updated Git submodule sub:
|
|
old: <git-commit>
|
|
new: <git-commit>
|
|
Rewritten superproject commit: <jj-commit> (no description set)
|
|
ignoring git submodule at "sub"
|
|
Working copy (@) now at: <jj-commit> (no description set)
|
|
Parent commit (@-) : <jj-commit> master | add submodule
|
|
Added 0 files, modified 1 files, removed 0 files
|
|
[EOF]
|
|
"#);
|
|
|
|
insta::assert_snapshot!(work_dir.run_jj(["diff", "--summary"]), @r#"
|
|
M sub
|
|
[EOF]
|
|
"#);
|
|
let diff_git = work_dir.run_jj(["diff", "--git"]);
|
|
assert!(diff_git.stdout.raw().contains(&new_target[..10]));
|
|
insta::assert_snapshot!(normalize_git_hashes(diff_git), @r#"
|
|
diff --git a/sub b/sub
|
|
index <git-commit>..<git-commit> 160000
|
|
[EOF]
|
|
"#);
|
|
}
|
|
|
|
#[test]
|
|
fn test_submodule_bind_revision_completion_uses_jj_submodule() {
|
|
let test_env = TestEnvironment::default();
|
|
|
|
test_env
|
|
.run_jj_in(".", ["git", "init", "--colocate", "repo"])
|
|
.success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "super-target", "-r", "@"])
|
|
.success();
|
|
|
|
work_dir.create_dir("sub");
|
|
test_env
|
|
.run_jj_in("repo/sub", ["git", "init", "--colocate", "."])
|
|
.success();
|
|
let submodule_dir = test_env.work_dir("repo/sub");
|
|
submodule_dir.write_file("file", "contents");
|
|
submodule_dir
|
|
.run_jj(["commit", "-m", "submodule commit"])
|
|
.success();
|
|
submodule_dir
|
|
.run_jj(["bookmark", "create", "sub-target", "-r", "@-"])
|
|
.success();
|
|
|
|
let values = machine_completion_values(work_dir.run_jj([
|
|
"util",
|
|
"complete",
|
|
"--index",
|
|
"5",
|
|
"--",
|
|
"jj",
|
|
"submodule",
|
|
"bind",
|
|
"sub",
|
|
"-r",
|
|
"sub-",
|
|
]));
|
|
assert!(values.contains(&"sub-target".to_owned()));
|
|
assert!(!values.contains(&"super-target".to_owned()));
|
|
}
|
|
|
|
#[test]
|
|
fn test_submodule_bind_revision_completion_uses_git_submodule() {
|
|
let test_env = TestEnvironment::default();
|
|
|
|
test_env
|
|
.run_jj_in(".", ["git", "init", "--colocate", "repo"])
|
|
.success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
work_dir
|
|
.run_jj(["bookmark", "create", "super-target", "-r", "@"])
|
|
.success();
|
|
|
|
work_dir.create_dir("sub");
|
|
work_dir.write_file("sub/file", "contents");
|
|
run_git(&work_dir, ["-C", "sub", "init"]);
|
|
run_git(&work_dir, ["-C", "sub", "add", "file"]);
|
|
run_git(
|
|
&work_dir,
|
|
[
|
|
"-C",
|
|
"sub",
|
|
"-c",
|
|
"user.email=test@example.com",
|
|
"-c",
|
|
"user.name=Test user",
|
|
"commit",
|
|
"-m",
|
|
"submodule commit",
|
|
],
|
|
);
|
|
run_git(&work_dir, ["-C", "sub", "branch", "sub-target"]);
|
|
|
|
let values = machine_completion_values(work_dir.run_jj([
|
|
"util",
|
|
"complete",
|
|
"--index",
|
|
"5",
|
|
"--",
|
|
"jj",
|
|
"submodule",
|
|
"bind",
|
|
"sub",
|
|
"-r",
|
|
"sub-",
|
|
]));
|
|
assert!(values.contains(&"sub-target".to_owned()));
|
|
assert!(!values.contains(&"super-target".to_owned()));
|
|
}
|
|
|
|
#[test]
|
|
fn test_submodule_bind_at_resolves_in_jj_submodule() {
|
|
let test_env = TestEnvironment::default();
|
|
|
|
test_env
|
|
.run_jj_in(".", ["git", "init", "--colocate", "submodule"])
|
|
.success();
|
|
let submodule_dir = test_env.work_dir("submodule");
|
|
submodule_dir.write_file("file", "contents");
|
|
submodule_dir
|
|
.run_jj(["commit", "-m", "submodule commit"])
|
|
.success();
|
|
let old_target = submodule_dir
|
|
.run_jj(["log", "-r@-", "--no-graph", "-T", "commit_id"])
|
|
.success()
|
|
.stdout
|
|
.raw()
|
|
.trim()
|
|
.to_owned();
|
|
|
|
test_env
|
|
.run_jj_in(".", ["git", "init", "--colocate", "repo"])
|
|
.success();
|
|
let work_dir = test_env.work_dir("repo");
|
|
let submodule_url = format!("{}/submodule", test_env.env_root().display());
|
|
run_git(
|
|
&work_dir,
|
|
[
|
|
"-c",
|
|
"protocol.file.allow=always",
|
|
"submodule",
|
|
"add",
|
|
submodule_url.as_str(),
|
|
"sub",
|
|
],
|
|
);
|
|
run_git(&work_dir, ["-C", "sub", "checkout", &old_target]);
|
|
run_git(&work_dir, ["add", "sub"]);
|
|
run_git(
|
|
&work_dir,
|
|
[
|
|
"-c",
|
|
"user.email=test@example.com",
|
|
"-c",
|
|
"user.name=Test user",
|
|
"commit",
|
|
"-m",
|
|
"add submodule",
|
|
],
|
|
);
|
|
insta::assert_snapshot!(work_dir.run_jj(["diff", "--summary"]), @r#"
|
|
------- stderr -------
|
|
ignoring git submodule at "sub"
|
|
Done importing changes from the underlying Git repo.
|
|
[EOF]
|
|
"#);
|
|
|
|
test_env
|
|
.run_jj_in("repo/sub", ["git", "init", "--git-repo=."])
|
|
.success();
|
|
let jj_wc_target = test_env
|
|
.run_jj_in("repo/sub", ["log", "-r@", "--no-graph", "-T", "commit_id"])
|
|
.success()
|
|
.stdout
|
|
.raw()
|
|
.trim()
|
|
.to_owned();
|
|
assert_ne!(jj_wc_target, old_target);
|
|
|
|
let output = work_dir.run_jj(["submodule", "bind", "sub", "-r", "@"]);
|
|
insta::assert_snapshot!(normalize_git_hashes(output), @r#"
|
|
------- stderr -------
|
|
Updated Git submodule sub:
|
|
old: <git-commit>
|
|
new: <git-commit>
|
|
Rewritten superproject commit: <jj-commit> (no description set)
|
|
ignoring git submodule at "sub"
|
|
Working copy (@) now at: <jj-commit> (no description set)
|
|
Parent commit (@-) : <jj-commit> master | add submodule
|
|
Added 0 files, modified 1 files, removed 0 files
|
|
[EOF]
|
|
"#);
|
|
|
|
let diff_git = work_dir.run_jj(["diff", "--git"]);
|
|
assert!(diff_git.stdout.raw().contains(&jj_wc_target[..10]));
|
|
insta::assert_snapshot!(normalize_git_hashes(diff_git), @r#"
|
|
diff --git a/sub b/sub
|
|
index <git-commit>..<git-commit> 160000
|
|
[EOF]
|
|
"#);
|
|
}
|