mirror of
https://github.com/martinvonz/jj.git
synced 2025-02-05 19:14:43 +00:00
4a50a35046
Based on the discussion in #3505, I think the sliding behavior isn't favored at least for "jj abandon". This patch changes the default to delete bookmarks. "jj rebase --skip-emptied" can also be updated if needed. It might be good for consistency. However, I'm skeptical about changing the default of the internal API. It's not easy to add generic reporting mechanism for deleted/abandoned bookmarks. If we added one in report_repo_changes(), redundant message would be printed on "jj git fetch". So I think callers should enable the deletion explicitly. Closes #3505
421 lines
15 KiB
Rust
421 lines
15 KiB
Rust
// Copyright 2023 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::path::Path;
|
|
|
|
use crate::common::TestEnvironment;
|
|
|
|
fn create_commit(test_env: &TestEnvironment, repo_path: &Path, name: &str, parents: &[&str]) {
|
|
if parents.is_empty() {
|
|
test_env.jj_cmd_ok(repo_path, &["new", "root()", "-m", name]);
|
|
} else {
|
|
let mut args = vec!["new", "-m", name];
|
|
args.extend(parents);
|
|
test_env.jj_cmd_ok(repo_path, &args);
|
|
}
|
|
std::fs::write(repo_path.join(name), format!("{name}\n")).unwrap();
|
|
test_env.jj_cmd_ok(repo_path, &["bookmark", "create", name]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_basics() {
|
|
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");
|
|
|
|
create_commit(&test_env, &repo_path, "a", &[]);
|
|
create_commit(&test_env, &repo_path, "b", &["a"]);
|
|
create_commit(&test_env, &repo_path, "c", &[]);
|
|
create_commit(&test_env, &repo_path, "d", &["c"]);
|
|
create_commit(&test_env, &repo_path, "e", &["a", "d"]);
|
|
// Test the setup
|
|
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
|
|
@ [znk] e
|
|
├─╮
|
|
│ ○ [vru] d
|
|
│ ○ [roy] c
|
|
│ │ ○ [zsu] b
|
|
├───╯
|
|
○ │ [rlv] a
|
|
├─╯
|
|
◆ [zzz]
|
|
"###);
|
|
|
|
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["abandon", "--retain-bookmarks", "d"]);
|
|
insta::assert_snapshot!(stdout, @"");
|
|
insta::assert_snapshot!(stderr, @r###"
|
|
Abandoned commit vruxwmqv b7c62f28 d | d
|
|
Rebased 1 descendant commits onto parents of abandoned commits
|
|
Working copy now at: znkkpsqq 11a2e10e e | e
|
|
Parent commit : rlvkpnrz 2443ea76 a | a
|
|
Parent commit : royxmykx fe2e8e8b c d | c
|
|
Added 0 files, modified 0 files, removed 1 files
|
|
"###);
|
|
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
|
|
@ [znk] e
|
|
├─╮
|
|
│ ○ [roy] c d
|
|
│ │ ○ [zsu] b
|
|
├───╯
|
|
○ │ [rlv] a
|
|
├─╯
|
|
◆ [zzz]
|
|
"###);
|
|
|
|
test_env.jj_cmd_ok(&repo_path, &["undo"]);
|
|
let (stdout, stderr) = test_env.jj_cmd_ok(
|
|
&repo_path,
|
|
&["abandon", "--retain-bookmarks"], /* abandons `e` */
|
|
);
|
|
insta::assert_snapshot!(stdout, @"");
|
|
insta::assert_snapshot!(stderr, @r###"
|
|
Abandoned commit znkkpsqq 5557ece3 e | e
|
|
Working copy now at: nkmrtpmo d4f8ea73 (empty) (no description set)
|
|
Parent commit : rlvkpnrz 2443ea76 a e?? | a
|
|
Parent commit : vruxwmqv b7c62f28 d e?? | d
|
|
Added 0 files, modified 0 files, removed 1 files
|
|
"###);
|
|
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
|
|
@ [nkm]
|
|
├─╮
|
|
│ ○ [vru] d e??
|
|
│ ○ [roy] c
|
|
│ │ ○ [zsu] b
|
|
├───╯
|
|
○ │ [rlv] a e??
|
|
├─╯
|
|
◆ [zzz]
|
|
"###);
|
|
|
|
test_env.jj_cmd_ok(&repo_path, &["undo"]);
|
|
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["abandon", "descendants(d)"]);
|
|
insta::assert_snapshot!(stdout, @"");
|
|
insta::assert_snapshot!(stderr, @r"
|
|
Abandoned the following commits:
|
|
znkkpsqq 5557ece3 e | e
|
|
vruxwmqv b7c62f28 d | d
|
|
Deleted bookmarks: d, e
|
|
Working copy now at: xtnwkqum fa4ee8e6 (empty) (no description set)
|
|
Parent commit : rlvkpnrz 2443ea76 a | a
|
|
Parent commit : royxmykx fe2e8e8b c | c
|
|
Added 0 files, modified 0 files, removed 2 files
|
|
");
|
|
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r"
|
|
@ [xtn]
|
|
├─╮
|
|
│ ○ [roy] c
|
|
│ │ ○ [zsu] b
|
|
├───╯
|
|
○ │ [rlv] a
|
|
├─╯
|
|
◆ [zzz]
|
|
");
|
|
|
|
// Test abandoning the same commit twice directly
|
|
test_env.jj_cmd_ok(&repo_path, &["undo"]);
|
|
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["abandon", "-rb", "b"]);
|
|
insta::assert_snapshot!(stdout, @"");
|
|
insta::assert_snapshot!(stderr, @r"
|
|
Abandoned commit zsuskuln 1394f625 b | b
|
|
Deleted bookmarks: b
|
|
");
|
|
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r"
|
|
@ [znk] e
|
|
├─╮
|
|
│ ○ [vru] d
|
|
│ ○ [roy] c
|
|
○ │ [rlv] a
|
|
├─╯
|
|
◆ [zzz]
|
|
");
|
|
|
|
// Test abandoning the same commit twice indirectly
|
|
test_env.jj_cmd_ok(&repo_path, &["undo"]);
|
|
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["abandon", "d::", "e"]);
|
|
insta::assert_snapshot!(stdout, @"");
|
|
insta::assert_snapshot!(stderr, @r"
|
|
Abandoned the following commits:
|
|
znkkpsqq 5557ece3 e | e
|
|
vruxwmqv b7c62f28 d | d
|
|
Deleted bookmarks: d, e
|
|
Working copy now at: xlzxqlsl 14991aec (empty) (no description set)
|
|
Parent commit : rlvkpnrz 2443ea76 a | a
|
|
Parent commit : royxmykx fe2e8e8b c | c
|
|
Added 0 files, modified 0 files, removed 2 files
|
|
");
|
|
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r"
|
|
@ [xlz]
|
|
├─╮
|
|
│ ○ [roy] c
|
|
│ │ ○ [zsu] b
|
|
├───╯
|
|
○ │ [rlv] a
|
|
├─╯
|
|
◆ [zzz]
|
|
");
|
|
|
|
let (_stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["abandon", "none()"]);
|
|
insta::assert_snapshot!(stderr, @r###"
|
|
No revisions to abandon.
|
|
"###);
|
|
}
|
|
|
|
// This behavior illustrates https://github.com/jj-vcs/jj/issues/2600.
|
|
// See also the corresponding test in `test_rebase_command`
|
|
#[test]
|
|
fn test_bug_2600() {
|
|
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");
|
|
|
|
// We will not touch "nottherootcommit". See the
|
|
// `test_bug_2600_rootcommit_special_case` for the one case where base being the
|
|
// child of the root commit changes the expected behavior.
|
|
create_commit(&test_env, &repo_path, "nottherootcommit", &[]);
|
|
create_commit(&test_env, &repo_path, "base", &["nottherootcommit"]);
|
|
create_commit(&test_env, &repo_path, "a", &["base"]);
|
|
create_commit(&test_env, &repo_path, "b", &["base", "a"]);
|
|
create_commit(&test_env, &repo_path, "c", &["b"]);
|
|
|
|
// Test the setup
|
|
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
|
|
@ [znk] c
|
|
○ [vru] b
|
|
├─╮
|
|
│ ○ [roy] a
|
|
├─╯
|
|
○ [zsu] base
|
|
○ [rlv] nottherootcommit
|
|
◆ [zzz]
|
|
"###);
|
|
let setup_opid = test_env.current_operation_id(&repo_path);
|
|
|
|
test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
|
|
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["abandon", "base"]);
|
|
insta::assert_snapshot!(stdout, @"");
|
|
insta::assert_snapshot!(stderr, @r"
|
|
Abandoned commit zsuskuln 73c929fc base | base
|
|
Deleted bookmarks: base
|
|
Rebased 3 descendant commits onto parents of abandoned commits
|
|
Working copy now at: znkkpsqq 86e31bec c | c
|
|
Parent commit : vruxwmqv fd6eb121 b | b
|
|
Added 0 files, modified 0 files, removed 1 files
|
|
");
|
|
// Commits "a" and "b" should both have "nottherootcommit" as parent, and "b"
|
|
// should keep "a" as second parent.
|
|
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r"
|
|
@ [znk] c
|
|
○ [vru] b
|
|
├─╮
|
|
│ ○ [roy] a
|
|
├─╯
|
|
○ [rlv] nottherootcommit
|
|
◆ [zzz]
|
|
");
|
|
|
|
test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
|
|
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["abandon", "a"]);
|
|
insta::assert_snapshot!(stdout, @"");
|
|
insta::assert_snapshot!(stderr, @r"
|
|
Abandoned commit royxmykx 98f3b9ba a | a
|
|
Deleted bookmarks: a
|
|
Rebased 2 descendant commits onto parents of abandoned commits
|
|
Working copy now at: znkkpsqq 683b9435 c | c
|
|
Parent commit : vruxwmqv c10cb7b4 b | b
|
|
Added 0 files, modified 0 files, removed 1 files
|
|
");
|
|
// Commit "b" should have "base" as parent. It should not have two parent
|
|
// pointers to that commit even though it was a merge commit before we abandoned
|
|
// "a".
|
|
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r"
|
|
@ [znk] c
|
|
○ [vru] b
|
|
○ [zsu] base
|
|
○ [rlv] nottherootcommit
|
|
◆ [zzz]
|
|
");
|
|
|
|
test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
|
|
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["abandon", "b"]);
|
|
insta::assert_snapshot!(stdout, @"");
|
|
insta::assert_snapshot!(stderr, @r"
|
|
Abandoned commit vruxwmqv 8c0dced0 b | b
|
|
Deleted bookmarks: b
|
|
Rebased 1 descendant commits onto parents of abandoned commits
|
|
Working copy now at: znkkpsqq 33a94991 c | c
|
|
Parent commit : zsuskuln 73c929fc base | base
|
|
Parent commit : royxmykx 98f3b9ba a | a
|
|
Added 0 files, modified 0 files, removed 1 files
|
|
");
|
|
// Commit "c" should inherit the parents from the abndoned commit "b".
|
|
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r"
|
|
@ [znk] c
|
|
├─╮
|
|
│ ○ [roy] a
|
|
├─╯
|
|
○ [zsu] base
|
|
○ [rlv] nottherootcommit
|
|
◆ [zzz]
|
|
");
|
|
|
|
test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
|
|
// ========= Reminder of the setup ===========
|
|
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
|
|
@ [znk] c
|
|
○ [vru] b
|
|
├─╮
|
|
│ ○ [roy] a
|
|
├─╯
|
|
○ [zsu] base
|
|
○ [rlv] nottherootcommit
|
|
◆ [zzz]
|
|
"###);
|
|
let (stdout, stderr) =
|
|
test_env.jj_cmd_ok(&repo_path, &["abandon", "--retain-bookmarks", "a", "b"]);
|
|
insta::assert_snapshot!(stdout, @"");
|
|
insta::assert_snapshot!(stderr, @r###"
|
|
Abandoned the following commits:
|
|
vruxwmqv 8c0dced0 b | b
|
|
royxmykx 98f3b9ba a | a
|
|
Rebased 1 descendant commits onto parents of abandoned commits
|
|
Working copy now at: znkkpsqq 84fac1f8 c | c
|
|
Parent commit : zsuskuln 73c929fc a b base | base
|
|
Added 0 files, modified 0 files, removed 2 files
|
|
"###);
|
|
// Commit "c" should have "base" as parent. As when we abandoned "a", it should
|
|
// not have two parent pointers to the same commit.
|
|
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
|
|
@ [znk] c
|
|
○ [zsu] a b base
|
|
○ [rlv] nottherootcommit
|
|
◆ [zzz]
|
|
"###);
|
|
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["bookmark", "list", "b"]);
|
|
insta::assert_snapshot!(stdout, @r###"
|
|
b: zsuskuln 73c929fc base
|
|
"###);
|
|
insta::assert_snapshot!(stderr, @"");
|
|
}
|
|
|
|
#[test]
|
|
fn test_bug_2600_rootcommit_special_case() {
|
|
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");
|
|
|
|
// Set up like `test_bug_2600`, but without the `nottherootcommit` commit.
|
|
create_commit(&test_env, &repo_path, "base", &[]);
|
|
create_commit(&test_env, &repo_path, "a", &["base"]);
|
|
create_commit(&test_env, &repo_path, "b", &["base", "a"]);
|
|
create_commit(&test_env, &repo_path, "c", &["b"]);
|
|
|
|
// Setup
|
|
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
|
|
@ [vru] c
|
|
○ [roy] b
|
|
├─╮
|
|
│ ○ [zsu] a
|
|
├─╯
|
|
○ [rlv] base
|
|
◆ [zzz]
|
|
"###);
|
|
|
|
// Now, the test
|
|
let stderr = test_env.jj_cmd_failure(&repo_path, &["abandon", "base"]);
|
|
insta::assert_snapshot!(stderr, @r###"
|
|
Error: The Git backend does not support creating merge commits with the root commit as one of the parents.
|
|
"###);
|
|
}
|
|
|
|
#[test]
|
|
fn test_double_abandon() {
|
|
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");
|
|
|
|
create_commit(&test_env, &repo_path, "a", &[]);
|
|
// Test the setup
|
|
insta::assert_snapshot!(
|
|
test_env.jj_cmd_success(&repo_path, &["log", "--no-graph", "-r", "a"])
|
|
, @r###"
|
|
rlvkpnrz test.user@example.com 2001-02-03 08:05:09 a 2443ea76
|
|
a
|
|
"###);
|
|
|
|
let commit_id = test_env.jj_cmd_success(
|
|
&repo_path,
|
|
&["log", "--no-graph", "--color=never", "-T=commit_id", "-r=a"],
|
|
);
|
|
|
|
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["abandon", &commit_id]);
|
|
insta::assert_snapshot!(stdout, @"");
|
|
insta::assert_snapshot!(stderr, @r"
|
|
Abandoned commit rlvkpnrz 2443ea76 a | a
|
|
Deleted bookmarks: a
|
|
Working copy now at: royxmykx f37b4afd (empty) (no description set)
|
|
Parent commit : zzzzzzzz 00000000 (empty) (no description set)
|
|
Added 0 files, modified 0 files, removed 1 files
|
|
");
|
|
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["abandon", &commit_id]);
|
|
insta::assert_snapshot!(stdout, @"");
|
|
insta::assert_snapshot!(stderr, @r###"
|
|
Abandoned commit rlvkpnrz hidden 2443ea76 a
|
|
Nothing changed.
|
|
"###);
|
|
}
|
|
|
|
#[test]
|
|
fn test_abandon_restore_descendants() {
|
|
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");
|
|
|
|
std::fs::write(repo_path.join("file"), "foo\n").unwrap();
|
|
test_env.jj_cmd_ok(&repo_path, &["new"]);
|
|
std::fs::write(repo_path.join("file"), "bar\n").unwrap();
|
|
test_env.jj_cmd_ok(&repo_path, &["new"]);
|
|
std::fs::write(repo_path.join("file"), "baz\n").unwrap();
|
|
|
|
// Remove the commit containing "bar"
|
|
let (stdout, stderr) =
|
|
test_env.jj_cmd_ok(&repo_path, &["abandon", "-r@-", "--restore-descendants"]);
|
|
insta::assert_snapshot!(stdout, @"");
|
|
insta::assert_snapshot!(stderr, @r#"
|
|
Abandoned commit rlvkpnrz 225adef1 (no description set)
|
|
Rebased 1 descendant commits (while preserving their content) onto parents of abandoned commits
|
|
Working copy now at: kkmpptxz a734deb0 (no description set)
|
|
Parent commit : qpvuntsm 485d52a9 (no description set)
|
|
"#);
|
|
let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "--git"]);
|
|
insta::assert_snapshot!(stdout, @r#"
|
|
diff --git a/file b/file
|
|
index 257cc5642c..76018072e0 100644
|
|
--- a/file
|
|
+++ b/file
|
|
@@ -1,1 +1,1 @@
|
|
-foo
|
|
+baz
|
|
"#);
|
|
}
|
|
|
|
fn get_log_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
|
|
test_env.jj_cmd_success(
|
|
repo_path,
|
|
&[
|
|
"log",
|
|
"-T",
|
|
r#"separate(" ", "[" ++ change_id.short(3) ++ "]", bookmarks)"#,
|
|
],
|
|
)
|
|
}
|