use indoc::indoc;
use testutils::TestResult;
use testutils::git;
use crate::common::CommandOutput;
use crate::common::TestEnvironment;
use crate::common::TestWorkDir;
use crate::common::to_toml_value;
fn git_repo_dir_for_jj_repo(work_dir: &TestWorkDir<'_>) -> std::path::PathBuf {
work_dir
.root()
.join(".jj")
.join("repo")
.join("store")
.join("git")
}
fn set_up(test_env: &TestEnvironment) {
test_env.run_jj_in(".", ["git", "init", "origin"]).success();
let origin_dir = test_env.work_dir("origin");
let origin_git_repo_path = git_repo_dir_for_jj_repo(&origin_dir);
origin_dir
.run_jj(["describe", "-m=description 1"])
.success();
origin_dir
.run_jj(["bookmark", "create", "-r@", "bookmark1"])
.success();
origin_dir
.run_jj(["new", "root()", "-m=description 2"])
.success();
origin_dir
.run_jj(["bookmark", "create", "-r@", "bookmark2"])
.success();
origin_dir.run_jj(["git", "export"]).success();
test_env
.run_jj_in(
".",
[
"git",
"clone",
"--config=remotes.origin.auto-track-bookmarks='*'",
origin_git_repo_path.to_str().unwrap(),
"local",
],
)
.success();
}
#[test]
fn test_git_push_nothing() {
let test_env = TestEnvironment::default();
set_up(&test_env);
let work_dir = test_env.work_dir("local");
insta::assert_snapshot!(get_bookmark_output(&work_dir), @"
bookmark1: qpvuntsm 9b2e76de (empty) description 1
@origin: qpvuntsm 9b2e76de (empty) description 1
bookmark2: zsuskuln 38a20473 (empty) description 2
@origin: zsuskuln 38a20473 (empty) description 2
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--all"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Nothing changed.
[EOF]
");
work_dir
.run_jj(["bookmark", "track", "bookmark1@origin"])
.success();
let output = work_dir.run_jj(["git", "push", "--tracked"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Nothing changed.
[EOF]
");
}
#[test]
fn test_git_push_default_remote_selection() {
let test_env = TestEnvironment::default();
set_up(&test_env);
let work_dir = test_env.work_dir("local");
let other_remote_path = test_env
.env_root()
.join("origin")
.join(".jj")
.join("repo")
.join("store")
.join("git");
work_dir
.run_jj([
"git",
"remote",
"add",
"other",
other_remote_path.to_str().unwrap(),
])
.success();
let output = work_dir.run_jj(["git", "push", "--config=git.push=other", "-b", "bookmark1"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Refusing to create new remote bookmark bookmark1@other
Hint: Run `jj bookmark track bookmark1 --remote=other` and try again.
[EOF]
[exit status: 1]
");
work_dir
.run_jj(["git", "remote", "remove", "origin"])
.success();
let output = work_dir.run_jj(["git", "push", "-b", "bookmark1"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Hint: Pushing to the only existing remote: other
Changes to push to other:
bookmark: bookmark1 [add to 9b2e76de3920]
[EOF]
");
}
#[test]
fn test_git_push_current_bookmark() {
let test_env = TestEnvironment::default();
set_up(&test_env);
test_env.add_config("remotes.origin.auto-track-bookmarks = '*'");
let work_dir = test_env.work_dir("local");
test_env.add_config(r#"revset-aliases."immutable_heads()" = "none()""#);
work_dir
.run_jj(["describe", "bookmark1", "-m", "modified bookmark1 commit"])
.success();
work_dir.run_jj(["new", "bookmark2"]).success();
work_dir
.run_jj(["bookmark", "set", "bookmark2", "-r@"])
.success();
work_dir
.run_jj(["bookmark", "create", "-r@", "my-bookmark"])
.success();
work_dir.run_jj(["describe", "-m", "foo"]).success();
insta::assert_snapshot!(get_bookmark_output(&work_dir), @"
bookmark1: qpvuntsm e5ce6d9a (empty) modified bookmark1 commit
@origin (ahead by 1 commits, behind by 1 commits): qpvuntsm/1 9b2e76de (hidden) (empty) description 1
bookmark2: yostqsxw 88ca14a7 (empty) foo
@origin (behind by 1 commits): zsuskuln 38a20473 (empty) description 2
my-bookmark: yostqsxw 88ca14a7 (empty) foo
@origin (not created yet)
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--dry-run"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: bookmark2 [move forward from 38a204733702 to 88ca14a7d46f]
bookmark: my-bookmark [add to 88ca14a7d46f]
Dry-run requested, not pushing.
[EOF]
");
let output = work_dir.run_jj(["git", "push"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: bookmark2 [move forward from 38a204733702 to 88ca14a7d46f]
bookmark: my-bookmark [add to 88ca14a7d46f]
[EOF]
");
insta::assert_snapshot!(get_bookmark_output(&work_dir), @"
bookmark1: qpvuntsm e5ce6d9a (empty) modified bookmark1 commit
@origin (ahead by 1 commits, behind by 1 commits): qpvuntsm/1 9b2e76de (hidden) (empty) description 1
bookmark2: yostqsxw 88ca14a7 (empty) foo
@origin: yostqsxw 88ca14a7 (empty) foo
my-bookmark: yostqsxw 88ca14a7 (empty) foo
@origin: yostqsxw 88ca14a7 (empty) foo
[EOF]
");
work_dir
.run_jj([
"bookmark",
"set",
"bookmark2",
"-rbookmark2-",
"--allow-backwards",
])
.success();
let output = work_dir.run_jj(["git", "push"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: No bookmarks/tags found in the default push revset: remote_bookmarks(remote=origin)..@
Nothing changed.
[EOF]
");
let output = work_dir.run_jj(["git", "push", "-bbookmark2"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: bookmark2 [move backward from 88ca14a7d46f to 38a204733702]
[EOF]
");
}
#[test]
fn test_git_push_parent_bookmark() {
let test_env = TestEnvironment::default();
set_up(&test_env);
let work_dir = test_env.work_dir("local");
test_env.add_config(r#"revset-aliases."immutable_heads()" = "none()""#);
work_dir.run_jj(["edit", "bookmark1"]).success();
work_dir
.run_jj(["describe", "-m", "modified bookmark1 commit"])
.success();
work_dir
.run_jj(["new", "-m", "non-empty description"])
.success();
work_dir.write_file("file", "file");
let output = work_dir.run_jj(["git", "push"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: bookmark1 [move sideways from 9b2e76de3920 to 80560a3e08e2]
[EOF]
");
}
#[test]
fn test_git_push_tag_in_default_target() {
let test_env = TestEnvironment::default();
test_env
.run_jj_in(".", ["git", "init", "--colocate", "origin"])
.success();
let origin_dir = test_env.work_dir("origin");
origin_dir.run_jj(["commit", "-morigin"]).success();
origin_dir.run_jj(["tag", "set", "-r@-", "tag1"]).success();
test_env
.run_jj_in(".", ["git", "clone", "--tag=*", "origin", "local"])
.success();
let work_dir = test_env.work_dir("local");
work_dir.run_jj(["commit", "-mlocal"]).success();
work_dir
.run_jj(["tag", "set", "--allow-move", "-r@-", "tag1"])
.success();
let output = work_dir.run_jj(["git", "push"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
tag: tag1 [move sideways from 110db8edfa5f to b60842ac7691]
[EOF]
");
}
#[test]
fn test_git_push_no_matching_bookmark() {
let test_env = TestEnvironment::default();
set_up(&test_env);
let work_dir = test_env.work_dir("local");
work_dir.run_jj(["new"]).success();
let output = work_dir.run_jj(["git", "push"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: No bookmarks/tags found in the default push revset: remote_bookmarks(remote=origin)..@
Nothing changed.
[EOF]
");
}
#[test]
fn test_git_push_matching_bookmark_unchanged() {
let test_env = TestEnvironment::default();
set_up(&test_env);
let work_dir = test_env.work_dir("local");
work_dir.run_jj(["new", "bookmark1"]).success();
let output = work_dir.run_jj(["git", "push"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: No bookmarks/tags found in the default push revset: remote_bookmarks(remote=origin)..@
Nothing changed.
[EOF]
");
}
#[test]
fn test_git_push_other_remote_has_bookmark() {
let test_env = TestEnvironment::default();
set_up(&test_env);
let work_dir = test_env.work_dir("local");
test_env.add_config(r#"revset-aliases."immutable_heads()" = "none()""#);
let other_remote_path = test_env
.env_root()
.join("origin")
.join(".jj")
.join("repo")
.join("store")
.join("git");
work_dir
.run_jj([
"git",
"remote",
"add",
"other",
other_remote_path.to_str().unwrap(),
])
.success();
work_dir.run_jj(["edit", "bookmark1"]).success();
work_dir.run_jj(["describe", "-m=modified"]).success();
let output = work_dir.run_jj(["git", "push"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: bookmark1 [move sideways from 9b2e76de3920 to a843bfad2abb]
[EOF]
");
let output = work_dir.run_jj(["git", "push"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: No bookmarks/tags found in the default push revset: remote_bookmarks(remote=origin)..@
Nothing changed.
[EOF]
");
work_dir
.run_jj(["bookmark", "track", "bookmark1", "--remote=other"])
.success();
let output = work_dir.run_jj(["git", "push", "--remote=other"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to other:
bookmark: bookmark1 [add to a843bfad2abb]
[EOF]
");
}
#[test]
fn test_git_push_forward_unexpectedly_moved() {
let test_env = TestEnvironment::default();
set_up(&test_env);
let work_dir = test_env.work_dir("local");
let origin_dir = test_env.work_dir("origin");
origin_dir
.run_jj(["new", "bookmark1", "-m=remote"])
.success();
origin_dir.write_file("remote", "remote");
origin_dir
.run_jj(["bookmark", "set", "bookmark1", "-r@"])
.success();
origin_dir.run_jj(["git", "export"]).success();
work_dir.run_jj(["new", "bookmark1", "-m=local"]).success();
work_dir.write_file("local", "local");
work_dir
.run_jj(["bookmark", "set", "bookmark1", "-r@"])
.success();
let output = work_dir.run_jj(["git", "push"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: bookmark1 [move forward from 9b2e76de3920 to 624f94a35f00]
Warning: The following references unexpectedly moved on the remote:
refs/heads/bookmark1 (reason: stale info)
Hint: Try fetching from the remote, then make the bookmark point to where you want it to be, and push again.
Error: Failed to push some bookmarks
[EOF]
[exit status: 1]
");
let output = work_dir.run_jj(["git", "push", "--color=always"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: bookmark1 [move forward from 9b2e76de3920 to 624f94a35f00]
[1m[38;5;3mWarning: [39mThe following references unexpectedly moved on the remote:[0m
[38;5;2mrefs/heads/bookmark1[39m (reason: stale info)
[1m[38;5;6mHint: [0m[39mTry fetching from the remote, then make the bookmark point to where you want it to be, and push again.[39m
[1m[38;5;1mError: [39mFailed to push some bookmarks[0m
[EOF]
[exit status: 1]
");
}
#[test]
fn test_git_push_sideways_unexpectedly_moved() {
let test_env = TestEnvironment::default();
set_up(&test_env);
let work_dir = test_env.work_dir("local");
let origin_dir = test_env.work_dir("origin");
origin_dir
.run_jj(["new", "bookmark1", "-m=remote"])
.success();
origin_dir.write_file("remote", "remote");
origin_dir
.run_jj(["bookmark", "set", "bookmark1", "-r@"])
.success();
insta::assert_snapshot!(get_bookmark_output(&origin_dir), @"
bookmark1: vruxwmqv 7ce4029e remote
@git (behind by 1 commits): qpvuntsm 9b2e76de (empty) description 1
bookmark2: zsuskuln 38a20473 (empty) description 2
@git: zsuskuln 38a20473 (empty) description 2
[EOF]
");
origin_dir.run_jj(["git", "export"]).success();
work_dir.run_jj(["new", "root()", "-m=local"]).success();
work_dir.write_file("local", "local");
work_dir
.run_jj(["bookmark", "set", "bookmark1", "--allow-backwards", "-r@"])
.success();
insta::assert_snapshot!(get_bookmark_output(&work_dir), @"
bookmark1: kmkuslsw 827b8a38 local
@origin (ahead by 1 commits, behind by 1 commits): qpvuntsm 9b2e76de (empty) description 1
bookmark2: zsuskuln 38a20473 (empty) description 2
@origin: zsuskuln 38a20473 (empty) description 2
[EOF]
");
let output = work_dir.run_jj(["git", "push"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: bookmark1 [move sideways from 9b2e76de3920 to 827b8a385853]
Warning: The following references unexpectedly moved on the remote:
refs/heads/bookmark1 (reason: stale info)
Hint: Try fetching from the remote, then make the bookmark point to where you want it to be, and push again.
Error: Failed to push some bookmarks
[EOF]
[exit status: 1]
");
let output = work_dir.run_jj(["git", "push", "--color=always"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: bookmark1 [move sideways from 9b2e76de3920 to 827b8a385853]
[1m[38;5;3mWarning: [39mThe following references unexpectedly moved on the remote:[0m
[38;5;2mrefs/heads/bookmark1[39m (reason: stale info)
[1m[38;5;6mHint: [0m[39mTry fetching from the remote, then make the bookmark point to where you want it to be, and push again.[39m
[1m[38;5;1mError: [39mFailed to push some bookmarks[0m
[EOF]
[exit status: 1]
");
}
#[test]
fn test_git_push_deletion_unexpectedly_moved() {
let test_env = TestEnvironment::default();
set_up(&test_env);
let work_dir = test_env.work_dir("local");
let origin_dir = test_env.work_dir("origin");
origin_dir
.run_jj(["new", "bookmark1", "-m=remote"])
.success();
origin_dir.write_file("remote", "remote");
origin_dir
.run_jj(["bookmark", "set", "bookmark1", "-r@"])
.success();
insta::assert_snapshot!(get_bookmark_output(&origin_dir), @"
bookmark1: vruxwmqv 7ce4029e remote
@git (behind by 1 commits): qpvuntsm 9b2e76de (empty) description 1
bookmark2: zsuskuln 38a20473 (empty) description 2
@git: zsuskuln 38a20473 (empty) description 2
[EOF]
");
origin_dir.run_jj(["git", "export"]).success();
work_dir
.run_jj(["bookmark", "delete", "bookmark1"])
.success();
insta::assert_snapshot!(get_bookmark_output(&work_dir), @"
bookmark1 (deleted)
@origin: qpvuntsm 9b2e76de (empty) description 1
bookmark2: zsuskuln 38a20473 (empty) description 2
@origin: zsuskuln 38a20473 (empty) description 2
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--bookmark", "bookmark1"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: bookmark1 [delete from 9b2e76de3920]
Warning: The following references unexpectedly moved on the remote:
refs/heads/bookmark1 (reason: stale info)
Hint: Try fetching from the remote, then make the bookmark point to where you want it to be, and push again.
Error: Failed to push some bookmarks
[EOF]
[exit status: 1]
");
}
#[test]
fn test_git_push_unexpectedly_deleted() {
let test_env = TestEnvironment::default();
set_up(&test_env);
let work_dir = test_env.work_dir("local");
let origin_dir = test_env.work_dir("origin");
origin_dir
.run_jj(["bookmark", "delete", "bookmark1"])
.success();
insta::assert_snapshot!(get_bookmark_output(&origin_dir), @"
bookmark1 (deleted)
@git: qpvuntsm 9b2e76de (empty) description 1
bookmark2: zsuskuln 38a20473 (empty) description 2
@git: zsuskuln 38a20473 (empty) description 2
[EOF]
");
origin_dir.run_jj(["git", "export"]).success();
work_dir.run_jj(["new", "root()", "-m=local"]).success();
work_dir.write_file("local", "local");
work_dir
.run_jj(["bookmark", "set", "bookmark1", "--allow-backwards", "-r@"])
.success();
insta::assert_snapshot!(get_bookmark_output(&work_dir), @"
bookmark1: kpqxywon 09919fb0 local
@origin (ahead by 1 commits, behind by 1 commits): qpvuntsm 9b2e76de (empty) description 1
bookmark2: zsuskuln 38a20473 (empty) description 2
@origin: zsuskuln 38a20473 (empty) description 2
[EOF]
");
let output = work_dir.run_jj(["git", "push"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: bookmark1 [move sideways from 9b2e76de3920 to 09919fb051bf]
Warning: The following references unexpectedly moved on the remote:
refs/heads/bookmark1 (reason: stale info)
Hint: Try fetching from the remote, then make the bookmark point to where you want it to be, and push again.
Error: Failed to push some bookmarks
[EOF]
[exit status: 1]
");
work_dir
.run_jj(["bookmark", "delete", "bookmark1"])
.success();
insta::assert_snapshot!(get_bookmark_output(&work_dir), @"
bookmark1 (deleted)
@origin: qpvuntsm 9b2e76de (empty) description 1
bookmark2: zsuskuln 38a20473 (empty) description 2
@origin: zsuskuln 38a20473 (empty) description 2
[EOF]
");
let output = work_dir.run_jj(["git", "push", "-bbookmark1"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: bookmark1 [delete from 9b2e76de3920]
Warning: The following references unexpectedly moved on the remote:
refs/heads/bookmark1 (reason: stale info)
Hint: Try fetching from the remote, then make the bookmark point to where you want it to be, and push again.
Error: Failed to push some bookmarks
[EOF]
[exit status: 1]
");
}
#[test]
fn test_git_push_creation_unexpectedly_already_exists() {
let test_env = TestEnvironment::default();
set_up(&test_env);
test_env.add_config("remotes.origin.auto-track-bookmarks = '*'");
let work_dir = test_env.work_dir("local");
work_dir
.run_jj(["bookmark", "forget", "--include-remotes", "bookmark1"])
.success();
work_dir
.run_jj(["new", "root()", "-m=new bookmark1"])
.success();
work_dir.write_file("local", "local");
work_dir
.run_jj(["bookmark", "create", "-r@", "bookmark1"])
.success();
insta::assert_snapshot!(get_bookmark_output(&work_dir), @"
bookmark1: yostqsxw a43cb801 new bookmark1
@origin (not created yet)
bookmark2: zsuskuln 38a20473 (empty) description 2
@origin: zsuskuln 38a20473 (empty) description 2
[EOF]
");
let output = work_dir.run_jj(["git", "push"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: bookmark1 [add to a43cb8011c85]
Warning: The following references unexpectedly moved on the remote:
refs/heads/bookmark1 (reason: stale info)
Hint: Try fetching from the remote, then make the bookmark point to where you want it to be, and push again.
Error: Failed to push some bookmarks
[EOF]
[exit status: 1]
");
}
#[test]
fn test_git_push_locally_created_and_rewritten() {
let test_env = TestEnvironment::default();
set_up(&test_env);
let work_dir = test_env.work_dir("local");
test_env.add_config("remotes.origin.auto-track-bookmarks = '~*'");
work_dir.run_jj(["new", "root()", "-mlocal 1"]).success();
work_dir
.run_jj(["bookmark", "create", "-r@", "my"])
.success();
let output = work_dir.run_jj(["git", "push"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: Refusing to create new remote bookmark my@origin
Hint: Run `jj bookmark track my --remote=origin` and try again.
Nothing changed.
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--allow-new", "--dry-run"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: --allow-new is deprecated, track bookmarks manually or configure remotes.<name>.auto-track-bookmarks instead.
Changes to push to origin:
bookmark: my [add to e0cba5e497ee]
Dry-run requested, not pushing.
[EOF]
");
let output = work_dir.run_jj([
"git",
"push",
"--config=git.push-new-bookmarks=true",
"--dry-run",
]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: Deprecated CLI-provided config: `git.push-new-bookmarks` is deprecated; use `remotes.<name>.auto-track-bookmarks` instead.
Example: jj config set --user remotes.origin.auto-track-bookmarks '*'
For details, see: https://docs.jj-vcs.dev/latest/config/#automatic-tracking-of-bookmarks
Changes to push to origin:
bookmark: my [add to e0cba5e497ee]
Dry-run requested, not pushing.
[EOF]
");
work_dir
.run_jj(["bookmark", "track", "my", "--remote=origin"])
.success();
let output = work_dir.run_jj(["git", "push"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: my [add to e0cba5e497ee]
[EOF]
");
work_dir.run_jj(["describe", "-mlocal 2"]).success();
insta::assert_snapshot!(get_bookmark_output(&work_dir), @"
bookmark1: qpvuntsm 9b2e76de (empty) description 1
@origin: qpvuntsm 9b2e76de (empty) description 1
bookmark2: zsuskuln 38a20473 (empty) description 2
@origin: zsuskuln 38a20473 (empty) description 2
my: vruxwmqv 9ebc3217 (empty) local 2
@origin (ahead by 1 commits, behind by 1 commits): vruxwmqv/1 e0cba5e4 (hidden) (empty) local 1
[EOF]
");
let output = work_dir.run_jj(["git", "push"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: my [move sideways from e0cba5e497ee to 9ebc3217a0b8]
[EOF]
");
}
#[test]
fn test_git_push_multiple() {
let test_env = TestEnvironment::default();
set_up(&test_env);
test_env.add_config("remotes.origin.auto-track-bookmarks = '*'");
test_env.add_config("revset-aliases.'immutable_heads()' = 'none()'");
let origin_dir = test_env.work_dir("origin");
let work_dir = test_env.work_dir("local");
origin_dir
.run_jj(["tag", "set", "tag1", "-rbookmark1"])
.success();
origin_dir.run_jj(["git", "export"]).success();
work_dir.run_jj(["git", "fetch", "--tag=*"]).success();
work_dir
.run_jj(["bookmark", "delete", "bookmark1"])
.success();
work_dir
.run_jj(["bookmark", "set", "--allow-backwards", "bookmark2", "-r@"])
.success();
work_dir
.run_jj(["bookmark", "create", "-r@", "my-bookmark"])
.success();
work_dir.run_jj(["describe", "-m", "foo"]).success();
work_dir
.run_jj(["tag", "set", "--allow-move", "tag1", "-r@"])
.success();
work_dir.run_jj(["tag", "set", "tag2", "-r@"]).success();
work_dir
.run_jj(["bookmark", "set", "bookmark3", "-rbookmark2"])
.success();
origin_dir
.run_jj(["bookmark", "set", "bookmark3", "-rbookmark1"])
.success();
origin_dir.run_jj(["git", "export"]).success();
insta::assert_snapshot!(get_bookmark_output(&work_dir), @"
bookmark1 (deleted)
@origin: qpvuntsm 9b2e76de (empty) description 1
bookmark2: yqosqzyt 0cb91ecd (empty) foo
@origin (ahead by 1 commits, behind by 1 commits): zsuskuln 38a20473 (empty) description 2
bookmark3: yqosqzyt 0cb91ecd (empty) foo
@origin (not created yet)
my-bookmark: yqosqzyt 0cb91ecd (empty) foo
@origin (not created yet)
[EOF]
");
insta::assert_snapshot!(get_tag_output(&work_dir), @"
tag1: yqosqzyt 0cb91ecd (empty) foo
@origin (ahead by 1 commits, behind by 1 commits): qpvuntsm 9b2e76de (empty) description 1
tag2: yqosqzyt 0cb91ecd (empty) foo
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--all", "--deleted", "--dry-run"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: Refusing to create new remote tag tag2@origin
Changes to push to origin:
bookmark: bookmark1 [delete from 9b2e76de3920]
bookmark: bookmark2 [move sideways from 38a204733702 to 0cb91ecd4965]
bookmark: bookmark3 [add to 0cb91ecd4965]
bookmark: my-bookmark [add to 0cb91ecd4965]
tag: tag1 [move sideways from 9b2e76de3920 to 0cb91ecd4965]
Dry-run requested, not pushing.
[EOF]
");
let output = work_dir.run_jj([
"git",
"push",
"--bookmark=bookmark1|my-bookmark",
"--tag=tag2",
"--dry-run",
]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: bookmark1 [delete from 9b2e76de3920]
bookmark: my-bookmark [add to 0cb91ecd4965]
tag: tag2 [add to 0cb91ecd4965]
Dry-run requested, not pushing.
[EOF]
");
let output = work_dir.run_jj([
"git",
"push",
"-b=bookmark1",
"-b=my-bookmark",
"-b=bookmark1",
"-b=my-*",
"--dry-run",
]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: bookmark1 [delete from 9b2e76de3920]
bookmark: my-bookmark [add to 0cb91ecd4965]
Dry-run requested, not pushing.
[EOF]
");
let output = work_dir.run_jj(["git", "push", "-b='bookmark?'", "--dry-run"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: bookmark1 [delete from 9b2e76de3920]
bookmark: bookmark2 [move sideways from 38a204733702 to 0cb91ecd4965]
bookmark: bookmark3 [add to 0cb91ecd4965]
Dry-run requested, not pushing.
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--bookmark=foo", "--tag=bar"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: No matching bookmarks for names: foo
Warning: No matching tags for names: bar
Nothing changed.
[EOF]
");
let output = work_dir.run_jj(["git", "push", "-b=foo", "-b='?bookmark'"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: No matching bookmarks for names: foo
Nothing changed.
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--all", "--dry-run"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: Refusing to push deleted bookmark bookmark1
Hint: Push deleted bookmarks with --deleted or forget the bookmark to suppress this warning.
Warning: Refusing to create new remote tag tag2@origin
Changes to push to origin:
bookmark: bookmark2 [move sideways from 38a204733702 to 0cb91ecd4965]
bookmark: bookmark3 [add to 0cb91ecd4965]
bookmark: my-bookmark [add to 0cb91ecd4965]
tag: tag1 [move sideways from 9b2e76de3920 to 0cb91ecd4965]
Dry-run requested, not pushing.
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--all", "--deleted", "--dry-run"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: Refusing to create new remote tag tag2@origin
Changes to push to origin:
bookmark: bookmark1 [delete from 9b2e76de3920]
bookmark: bookmark2 [move sideways from 38a204733702 to 0cb91ecd4965]
bookmark: bookmark3 [add to 0cb91ecd4965]
bookmark: my-bookmark [add to 0cb91ecd4965]
tag: tag1 [move sideways from 9b2e76de3920 to 0cb91ecd4965]
Dry-run requested, not pushing.
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--all", "--deleted"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: Refusing to create new remote tag tag2@origin
Changes to push to origin:
bookmark: bookmark1 [delete from 9b2e76de3920]
bookmark: bookmark2 [move sideways from 38a204733702 to 0cb91ecd4965]
bookmark: bookmark3 [add to 0cb91ecd4965]
bookmark: my-bookmark [add to 0cb91ecd4965]
tag: tag1 [move sideways from 9b2e76de3920 to 0cb91ecd4965]
Warning: The following references unexpectedly moved on the remote:
refs/heads/bookmark3 (reason: stale info)
Hint: Try fetching from the remote, then make the bookmark point to where you want it to be, and push again.
Error: Failed to push some bookmarks
[EOF]
[exit status: 1]
");
let output = work_dir.run_jj(["git", "import"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Nothing changed.
[EOF]
");
insta::assert_snapshot!(get_bookmark_output(&work_dir), @"
bookmark2: yqosqzyt 0cb91ecd (empty) foo
@origin: yqosqzyt 0cb91ecd (empty) foo
bookmark3: yqosqzyt 0cb91ecd (empty) foo
@origin (not created yet)
my-bookmark: yqosqzyt 0cb91ecd (empty) foo
@origin: yqosqzyt 0cb91ecd (empty) foo
[EOF]
");
insta::assert_snapshot!(get_tag_output(&work_dir), @"
tag1: yqosqzyt 0cb91ecd (empty) foo
@origin: yqosqzyt 0cb91ecd (empty) foo
tag2: yqosqzyt 0cb91ecd (empty) foo
[EOF]
");
let output = work_dir.run_jj(["log", "-rall()"]);
insta::assert_snapshot!(output, @"
@ yqosqzyt test.user@example.com 2001-02-03 08:05:20 bookmark2 bookmark3* my-bookmark tag1 tag2 0cb91ecd
│ (empty) foo
│ ○ zsuskuln test.user@example.com 2001-02-03 08:05:10 38a20473
├─╯ (empty) description 2
│ ○ qpvuntsm test.user@example.com 2001-02-03 08:05:08 9b2e76de
├─╯ (empty) description 1
â—† zzzzzzzz root() 00000000
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--branch", "bookmark2"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Bookmark bookmark2@origin already matches bookmark2
Nothing changed.
[EOF]
");
}
#[test]
fn test_git_push_changes() {
let test_env = TestEnvironment::default();
set_up(&test_env);
let work_dir = test_env.work_dir("local");
work_dir.run_jj(["describe", "-m", "foo"]).success();
work_dir.write_file("file", "contents");
work_dir.run_jj(["new", "-m", "bar"]).success();
work_dir.write_file("file", "modified");
let output = work_dir.run_jj(["git", "push", "--change", "@"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Creating bookmark push-yostqsxwqrlt for revision yostqsxwqrlt
Changes to push to origin:
bookmark: push-yostqsxwqrlt [add to 916414184c47]
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--change", "@"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Bookmark push-yostqsxwqrlt@origin already matches push-yostqsxwqrlt
Nothing changed.
[EOF]
");
work_dir.write_file("file", "modified2");
let output = work_dir.run_jj(["git", "push", "-c=(@|@-)"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Creating bookmark push-yqosqzytrlsw for revision yqosqzytrlsw
Changes to push to origin:
bookmark: push-yostqsxwqrlt [move sideways from 916414184c47 to 107f11285524]
bookmark: push-yqosqzytrlsw [add to 0f8164cd580b]
[EOF]
");
work_dir.write_file("file", "modified3");
let output = work_dir.run_jj(["git", "push", "-c=(@|@)"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: push-yostqsxwqrlt [move sideways from 107f11285524 to 7436a8a600a4]
[EOF]
");
work_dir.write_file("file", "modified4");
let output = work_dir.run_jj(["git", "push", "-c=@", "-b=push-yostqsxwqrlt"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: push-yostqsxwqrlt [move sideways from 7436a8a600a4 to a8b93bdd0f68]
[EOF]
");
work_dir.write_file("file", "modified5");
work_dir
.run_jj([
"bookmark",
"set",
"-r=@-",
"--allow-backwards",
"push-yostqsxwqrlt",
])
.success();
let output = work_dir.run_jj(["status"]);
insta::assert_snapshot!(output, @"
Working copy changes:
M file
Working copy (@) : yostqsxw 4b18f5ea bar
Parent commit (@-): yqosqzyt 0f8164cd push-yostqsxwqrlt* push-yqosqzytrlsw | foo
[EOF]
");
let output = work_dir.run_jj(["git", "push", "-c=@", "-b=push-yostqsxwqrlt"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Bookmark already exists: push-yostqsxwqrlt
Hint: Use 'jj bookmark move' to move it, and 'jj git push -b push-yostqsxwqrlt' to push it
[EOF]
[exit status: 1]
");
let output = work_dir.run_jj(["status"]);
insta::assert_snapshot!(output, @"
Working copy changes:
M file
Working copy (@) : yostqsxw 4b18f5ea bar
Parent commit (@-): yqosqzyt 0f8164cd push-yostqsxwqrlt* push-yqosqzytrlsw | foo
[EOF]
");
let output = work_dir.run_jj([
"git",
"push",
&format!(
"--config=templates.git_push_bookmark={}",
to_toml_value("'test-' ++ change_id.short()")
),
"--change=@",
]);
insta::assert_snapshot!(output, @"
------- stderr -------
Creating bookmark test-yostqsxwqrlt for revision yostqsxwqrlt
Changes to push to origin:
bookmark: test-yostqsxwqrlt [add to 4b18f5ea2994]
[EOF]
");
let output = work_dir.run_jj([
"git",
"push",
"--config=templates.git_push_bookmark=\"'dupe-bookmark'\"",
"--change=(@|root()+)",
]);
insta::assert_snapshot!(output, @"
------- stderr -------
Creating bookmark dupe-bookmark for revision yostqsxwqrlt
Error: Bookmark already exists: dupe-bookmark
Hint: Use 'jj bookmark move' to move it, and 'jj git push -b dupe-bookmark' to push it
[EOF]
[exit status: 1]
");
let output = work_dir.run_jj([
"git",
"push",
r#"--config=templates.git_push_bookmark='""'"#,
"--change=@",
]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Empty bookmark name generated
[EOF]
[exit status: 1]
");
work_dir.write_file("file", vec![b'\xff']);
let output = work_dir.run_jj([
"git",
"push",
"--config=templates.git_push_bookmark=self.diff().git()",
"--change=@",
]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Invalid character in bookmark name
Caused by: invalid utf-8 sequence of 1 bytes from index 138
[EOF]
[exit status: 1]
");
}
#[test]
fn test_git_push_changes_with_name() {
let test_env = TestEnvironment::default();
set_up(&test_env);
let work_dir = test_env.work_dir("local");
work_dir.run_jj(["describe", "-m", "foo"]).success();
work_dir.write_file("file", "contents");
work_dir.run_jj(["new", "-m", "pushed"]).success();
work_dir.write_file("file", "modified");
let output = work_dir.run_jj(["git", "push", "--named", "b1=@"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: b1 [add to 5f4f9a466c96]
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--named", "b1 = @"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Could not parse 'b1 ' as a bookmark name
Caused by:
1: Failed to parse bookmark name: Syntax error
2: --> 1:3
|
1 | b1
| ^---
|
= expected <EOI>
Hint: For example, `--named myfeature=@` is valid syntax
[EOF]
[exit status: 2]
");
let output = work_dir.run_jj(["git", "push", "--named", "=@"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Argument '=@' must have the form NAME=REVISION, with both NAME and REVISION non-empty
Hint: For example, `--named myfeature=@` is valid syntax
[EOF]
[exit status: 2]
");
let output = work_dir.run_jj(["git", "push", "--named", ":!:=@"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Could not parse ':!:' as a bookmark name
Caused by:
1: Failed to parse bookmark name: Syntax error
2: --> 1:1
|
1 | :!:
| ^---
|
= expected <identifier>, <string_literal>, or <raw_string_literal>
Hint: For example, `--named myfeature=@` is valid syntax
[EOF]
[exit status: 2]
");
let output = work_dir.run_jj(["git", "push", "--named", "b2="]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Argument 'b2=' must have the form NAME=REVISION, with both NAME and REVISION non-empty
Hint: For example, `--named myfeature=@` is valid syntax
[EOF]
[exit status: 2]
");
let output = work_dir.run_jj(["git", "push", "--named", "b2"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Argument 'b2' must include '=' and have the form NAME=REVISION
Hint: For example, `--named myfeature=@` is valid syntax
[EOF]
[exit status: 2]
");
let output = work_dir.run_jj(["git", "push", "--named", "b1=@"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Bookmark already exists: b1
Hint: Use 'jj bookmark move' to move it, and 'jj git push -b b1' to push it
[EOF]
[exit status: 1]
");
work_dir.write_file("file", "modified2");
let output = work_dir.run_jj(["git", "push", "--named=b2=(@|@-)"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Revset `(@|@-)` resolved to more than one revision
Hint: The revset `(@|@-)` resolved to these revisions:
yostqsxw 1b2bd869 b1* | pushed
yqosqzyt 0f8164cd foo
[EOF]
[exit status: 1]
");
work_dir.write_file("file", "modified4");
let output = work_dir.run_jj(["git", "push", "--named=b2=@", "-b=b2"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: b2 [add to 95ba7bdacb38]
[EOF]
");
}
#[test]
fn test_git_push_changes_with_name_deleted_tracked() {
let test_env = TestEnvironment::default();
set_up(&test_env);
test_env.add_config(r#"revset-aliases."immutable_heads()" = "none()""#);
let work_dir = test_env.work_dir("local");
test_env
.run_jj_in(".", ["git", "init", "another_remote"])
.success();
let another_remote_git_repo_path =
git_repo_dir_for_jj_repo(&test_env.work_dir("another_remote"));
work_dir
.run_jj([
"git",
"remote",
"add",
"another_remote",
another_remote_git_repo_path.to_str().unwrap(),
])
.success();
work_dir.run_jj(["describe", "-m", "foo"]).success();
work_dir.write_file("file", "contents");
work_dir.run_jj(["new", "-m", "pushed"]).success();
work_dir.write_file("file", "modified");
let output = work_dir.run_jj(["git", "push", "--named", "b1=@"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: b1 [add to 08f401c17d51]
[EOF]
");
work_dir.run_jj(["bookmark", "delete", "b1"]).success();
let output = work_dir
.run_jj(["bookmark", "list", "--all", "b1"])
.success();
insta::assert_snapshot!(output, @"
b1 (deleted)
@origin: kpqxywon 08f401c1 pushed
[EOF]
------- stderr -------
Hint: Bookmarks marked as deleted can be *deleted permanently* on the remote by running `jj git push --deleted`. Use `jj bookmark forget` if you don't want that.
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--named", "b1=@", "--remote=another_remote"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Tracked remote bookmarks exist for deleted bookmark: b1
Hint: Use `jj bookmark set` to recreate the local bookmark. Run `jj bookmark untrack b1` to disassociate them.
[EOF]
[exit status: 1]
");
let output = work_dir.run_jj(["git", "push", "--named", "b1=@", "--remote=origin"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Tracked remote bookmarks exist for deleted bookmark: b1
Hint: Use `jj bookmark set` to recreate the local bookmark. Run `jj bookmark untrack b1` to disassociate them.
[EOF]
[exit status: 1]
");
work_dir
.run_jj(["bookmark", "untrack", "b1", "--remote=origin"])
.success();
let output = work_dir
.run_jj(["bookmark", "list", "--all", "b1"])
.success();
insta::assert_snapshot!(output, @"
b1@origin: kpqxywon 08f401c1 pushed
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--named", "b1=@", "--remote=another_remote"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to another_remote:
bookmark: b1 [add to 08f401c17d51]
[EOF]
");
let output = work_dir
.run_jj(["bookmark", "list", "--all", "b1"])
.success();
insta::assert_snapshot!(output, @"
b1: kpqxywon 08f401c1 pushed
@another_remote: kpqxywon 08f401c1 pushed
b1@origin: kpqxywon 08f401c1 pushed
[EOF]
");
}
#[test]
fn test_git_push_changes_with_name_untracked_or_forgotten() {
let test_env = TestEnvironment::default();
set_up(&test_env);
let work_dir = test_env.work_dir("local");
test_env.add_config(r#"revset-aliases."immutable_heads()" = "none()""#);
work_dir.run_jj(["describe", "-m", "parent"]).success();
work_dir.run_jj(["new", "-m", "pushed_to_remote"]).success();
work_dir.write_file("file", "contents");
work_dir
.run_jj(["new", "-m", "child", "--no-edit"])
.success();
work_dir.write_file("file", "modified");
work_dir
.run_jj(["git", "push", "--named", "b1=@"])
.success();
work_dir.run_jj(["bookmark", "untrack", "b1"]).success();
work_dir.run_jj(["bookmark", "delete", "b1"]).success();
let output = work_dir
.run_jj(&[
"log",
"-r=::@+",
r#"-T=separate(" ", commit_id.shortest(3), bookmarks, description)"#,
])
.success();
insta::assert_snapshot!(output, @"
â—‹ 9a0 child
@ 767 b1@origin pushed_to_remote
â—‹ aa9 parent
â—† 000
[EOF]
");
let output = work_dir
.run_jj(["bookmark", "list", "--all", "b1"])
.success();
insta::assert_snapshot!(output, @"
b1@origin: yostqsxw 767b63a5 pushed_to_remote
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--named", "b1=@"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Non-tracking remote bookmark b1@origin exists
Hint: Run `jj bookmark track b1 --remote=origin` to import the remote bookmark.
[EOF]
[exit status: 1]
");
let output = work_dir.run_jj(["git", "push", "--named", "b1=@+"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Non-tracking remote bookmark b1@origin exists
Hint: Run `jj bookmark track b1 --remote=origin` to import the remote bookmark.
[EOF]
[exit status: 1]
");
work_dir
.run_jj(&["bookmark", "forget", "b1", "--include-remotes"])
.success();
let output = work_dir
.run_jj(["bookmark", "list", "--all", "b1"])
.success();
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: No matching bookmarks for names: b1
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--named", "b1=@-"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: b1 [add to aa9ad64cb4ce]
Warning: The following references unexpectedly moved on the remote:
refs/heads/b1 (reason: stale info)
Hint: Try fetching from the remote, then make the bookmark point to where you want it to be, and push again.
Error: Failed to push some bookmarks
[EOF]
[exit status: 1]
");
let output = work_dir.run_jj(["bookmark", "list", "--all", "b1"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: No matching bookmarks for names: b1
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--named", "b1=@+"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: b1 [add to 9a0f76645905]
Warning: The following references unexpectedly moved on the remote:
refs/heads/b1 (reason: stale info)
Hint: Try fetching from the remote, then make the bookmark point to where you want it to be, and push again.
Error: Failed to push some bookmarks
[EOF]
[exit status: 1]
");
let output = work_dir.run_jj(["git", "push", "--named", "b1=@"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: b1 [add to 767b63a598e1]
[EOF]
");
}
#[test]
fn test_git_push_revisions() {
let test_env = TestEnvironment::default();
set_up(&test_env);
test_env.add_config("remotes.origin.auto-track-bookmarks = '*'");
test_env.add_config("revset-aliases.'immutable_heads()' = 'none()'");
let origin_dir = test_env.work_dir("origin");
let work_dir = test_env.work_dir("local");
origin_dir
.run_jj(["tag", "set", "tag-1", "tag-2", "tag-3", "-rbookmark1"])
.success();
origin_dir.run_jj(["git", "export"]).success();
work_dir.run_jj(["git", "fetch", "--tag=*"]).success();
work_dir.run_jj(["describe", "-m", "foo"]).success();
work_dir.write_file("file", "contents");
work_dir.run_jj(["new", "-m", "bar"]).success();
work_dir
.run_jj(["bookmark", "create", "-r@", "bookmark-1"])
.success();
work_dir.write_file("file", "modified");
work_dir.run_jj(["new", "-m", "baz"]).success();
work_dir
.run_jj(["bookmark", "create", "-r@", "bookmark-2a"])
.success();
work_dir
.run_jj(["bookmark", "create", "-r@", "bookmark-2b"])
.success();
work_dir.write_file("file", "modified again");
work_dir
.run_jj(["tag", "set", "--allow-move", "-r@", "tag-2"])
.success();
work_dir
.run_jj(["new", "root()", "--no-edit", "-mnew3"])
.success();
work_dir
.run_jj(["tag", "set", "--allow-move", "-rsubject(new3)", "tag-3"])
.success();
insta::assert_snapshot!(work_dir.run_jj(["log"]), @"
@ lylxulpl test.user@example.com 2001-02-03 08:05:23 bookmark-2a* bookmark-2b* tag-2* 6bc5860f
│ baz
â—‹ kmkuslsw test.user@example.com 2001-02-03 08:05:20 bookmark-1* 3c179a96
│ bar
â—‹ yqosqzyt test.user@example.com 2001-02-03 08:05:18 33b167f8
│ foo
│ ○ xznxytkn test.user@example.com 2001-02-03 08:05:24 tag-3* 14504d39
├─╯ (empty) new3
│ ○ zsuskuln test.user@example.com 2001-02-03 08:05:10 bookmark2 38a20473
├─╯ (empty) description 2
│ ○ qpvuntsm test.user@example.com 2001-02-03 08:05:08 bookmark1 tag-1 tag-2@origin tag-3@origin 9b2e76de
├─╯ (empty) description 1
â—† zzzzzzzz root() 00000000
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--revisions", "bookmark1"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Nothing changed.
[EOF]
");
let output = work_dir.run_jj(["git", "push", "-r=none()"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: No bookmarks/tags point to the specified revisions: none()
Nothing changed.
[EOF]
");
let output = work_dir.run_jj(["git", "push", "-r=@--"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: No bookmarks/tags point to the specified revisions: @--
Nothing changed.
[EOF]
");
let output = work_dir.run_jj(["git", "push", "-r=@-", "--dry-run"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: bookmark-1 [add to 3c179a96c972]
Dry-run requested, not pushing.
[EOF]
");
let output = work_dir.run_jj(["git", "push", "-r=subject(new3)", "--dry-run"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
tag: tag-3 [move sideways from 9b2e76de3920 to 14504d39da71]
Dry-run requested, not pushing.
[EOF]
");
let output = work_dir.run_jj(["git", "push", "-r=@--", "-r=@-", "--dry-run"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: No bookmarks/tags point to the specified revisions: @--
Changes to push to origin:
bookmark: bookmark-1 [add to 3c179a96c972]
Dry-run requested, not pushing.
[EOF]
");
let output = work_dir.run_jj(["git", "push", "-r=@", "--dry-run"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: bookmark-2a [add to 6bc5860f95fb]
bookmark: bookmark-2b [add to 6bc5860f95fb]
tag: tag-2 [move sideways from 9b2e76de3920 to 6bc5860f95fb]
Dry-run requested, not pushing.
[EOF]
");
let output = work_dir.run_jj(["git", "push", "-r=@-", "-r=@-", "--dry-run"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: bookmark-1 [add to 3c179a96c972]
Dry-run requested, not pushing.
[EOF]
");
let output = work_dir.run_jj(["git", "push", "-r=@-", "-b", "bookmark-1", "--dry-run"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: bookmark-1 [add to 3c179a96c972]
Dry-run requested, not pushing.
[EOF]
");
}
#[test]
fn test_git_push_mixed() {
let test_env = TestEnvironment::default();
set_up(&test_env);
test_env.add_config("revset-aliases.'immutable_heads()' = 'none()'");
let work_dir = test_env.work_dir("local");
work_dir.run_jj(["describe", "-m", "foo"]).success();
work_dir.write_file("file", "contents");
work_dir.run_jj(["new", "-m", "bar"]).success();
work_dir
.run_jj(["bookmark", "create", "-r@", "bookmark-1"])
.success();
work_dir.run_jj(["tag", "set", "-r@", "tag-1"]).success();
work_dir.write_file("file", "modified");
work_dir.run_jj(["new", "-m", "baz"]).success();
work_dir
.run_jj(["bookmark", "create", "-r@", "bookmark-2a"])
.success();
work_dir
.run_jj(["bookmark", "create", "-r@", "bookmark-2b"])
.success();
work_dir.write_file("file", "modified again");
let output = work_dir.run_jj([
"git",
"push",
"--change=@--",
"--bookmark=bookmark-1",
"--tag=tag-1",
"-r=@",
]);
insta::assert_snapshot!(output, @"
------- stderr -------
Creating bookmark push-yqosqzytrlsw for revision yqosqzytrlsw
Warning: Refusing to create new remote bookmark bookmark-2a@origin
Hint: Run `jj bookmark track bookmark-2a --remote=origin` and try again.
Warning: Refusing to create new remote bookmark bookmark-2b@origin
Hint: Run `jj bookmark track bookmark-2b --remote=origin` and try again.
Changes to push to origin:
bookmark: push-yqosqzytrlsw [add to 0f8164cd580b]
bookmark: bookmark-1 [add to bc7f5ebae839]
tag: tag-1 [add to 9b467a2a0cdf]
[EOF]
");
let output = work_dir.run_jj([
"git",
"push",
"--allow-new",
"--change=@--",
"--bookmark=bookmark-1",
"-r=@",
]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: --allow-new is deprecated, track bookmarks manually or configure remotes.<name>.auto-track-bookmarks instead.
Bookmark push-yqosqzytrlsw@origin already matches push-yqosqzytrlsw
Bookmark bookmark-1@origin already matches bookmark-1
Changes to push to origin:
bookmark: bookmark-2a [add to 0e5755c15447]
bookmark: bookmark-2b [add to 0e5755c15447]
[EOF]
");
}
#[test]
fn test_git_push_bookmarks_and_tags_of_same_name() {
let test_env = TestEnvironment::default();
test_env
.run_jj_in(".", ["git", "init", "--colocate", "origin"])
.success();
let origin_dir = test_env.work_dir("origin");
origin_dir.run_jj(["describe", "-morigin"]).success();
origin_dir.run_jj(["new"]).success();
origin_dir
.run_jj(["bookmark", "set", "-r@-", "foo", "bar"])
.success();
origin_dir
.run_jj(["tag", "set", "-r@-", "foo", "bar"])
.success();
test_env
.run_jj_in(
".",
["git", "clone", "--branch=*", "--tag=*", "origin", "local"],
)
.success();
let work_dir = test_env.work_dir("local");
work_dir.run_jj(["bookmark", "track", "*"]).success();
work_dir.run_jj(["new", "foo", "-mlocal"]).success();
work_dir.run_jj(["new"]).success();
work_dir
.run_jj(["bookmark", "set", "-r@-", "foo", "bar"])
.success();
work_dir
.run_jj(["tag", "set", "-r@-", "--allow-move", "foo", "bar"])
.success();
insta::assert_snapshot!(get_bookmark_output(&work_dir), @"
bar: vruxwmqv 5058996c (empty) local
@origin (behind by 1 commits): qpvuntsm 110db8ed (empty) origin
foo: vruxwmqv 5058996c (empty) local
@origin (behind by 1 commits): qpvuntsm 110db8ed (empty) origin
[EOF]
");
insta::assert_snapshot!(get_tag_output(&work_dir), @"
bar: vruxwmqv 5058996c (empty) local
@origin (behind by 1 commits): qpvuntsm 110db8ed (empty) origin
foo: vruxwmqv 5058996c (empty) local
@origin (behind by 1 commits): qpvuntsm 110db8ed (empty) origin
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--dry-run", "--bookmark=foo", "--tag=foo"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: foo [move forward from 110db8edfa5f to 5058996c52e2]
tag: foo [move forward from 110db8edfa5f to 5058996c52e2]
Dry-run requested, not pushing.
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--dry-run", "-r@-"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: bar [move forward from 110db8edfa5f to 5058996c52e2]
bookmark: foo [move forward from 110db8edfa5f to 5058996c52e2]
tag: bar [move forward from 110db8edfa5f to 5058996c52e2]
tag: foo [move forward from 110db8edfa5f to 5058996c52e2]
Dry-run requested, not pushing.
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--dry-run", "--bookmark=foo", "--tag=bar"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: foo [move forward from 110db8edfa5f to 5058996c52e2]
tag: bar [move forward from 110db8edfa5f to 5058996c52e2]
Dry-run requested, not pushing.
[EOF]
");
}
#[test]
fn test_git_push_allow_new_heuristics() {
let test_env = TestEnvironment::default();
set_up(&test_env);
let work_dir = test_env.work_dir("local");
test_env
.run_jj_in(".", ["git", "init", "--colocate", "upstream"])
.success();
work_dir
.run_jj([
"git",
"remote",
"add",
"upstream",
test_env
.work_dir("upstream")
.root()
.join(".git")
.to_str()
.unwrap(),
])
.success();
work_dir.run_jj(["describe", "-m", "foo"]).success();
work_dir.write_file("file", "contents");
work_dir
.run_jj(["bookmark", "create", "untracked"])
.success();
work_dir.run_jj(["tag", "set", "untracked"]).success();
let start_op = work_dir.current_operation_id();
let output = work_dir.run_jj(["git", "push", "--bookmark=untracked", "--tag=untracked"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: untracked [add to b9124726209b]
tag: untracked [add to b9124726209b]
[EOF]
");
work_dir.run_jj(["op", "restore", &start_op]).success();
work_dir
.run_jj([
"git",
"push",
"--bookmark=untracked",
"--tag=untracked",
"--remote=upstream",
])
.success();
let output = work_dir.run_jj(["git", "push", "--bookmark=untracked"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Refusing to create new remote bookmark untracked@origin
Hint: Run `jj bookmark track untracked --remote=origin` and try again.
[EOF]
[exit status: 1]
");
let output = work_dir.run_jj(["git", "push", "--tag=untracked"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Refusing to create new remote tag untracked@origin
[EOF]
[exit status: 1]
");
}
#[test]
fn test_git_push_unsnapshotted_change() {
let test_env = TestEnvironment::default();
set_up(&test_env);
let work_dir = test_env.work_dir("local");
work_dir.run_jj(["describe", "-m", "foo"]).success();
work_dir.write_file("file", "contents");
work_dir.run_jj(["git", "push", "--change", "@"]).success();
work_dir.write_file("file", "modified");
work_dir.run_jj(["git", "push", "--change", "@"]).success();
}
#[test]
fn test_git_push_conflict() {
let test_env = TestEnvironment::default();
set_up(&test_env);
let work_dir = test_env.work_dir("local");
work_dir.write_file("file", "first");
work_dir.run_jj(["commit", "-m", "first"]).success();
work_dir.write_file("file", "second");
work_dir.run_jj(["commit", "-m", "second"]).success();
work_dir.write_file("file", "third");
work_dir
.run_jj(["rebase", "-r", "@", "-o", "@--"])
.success();
work_dir
.run_jj(["bookmark", "create", "-r@", "my-bookmark"])
.success();
work_dir.run_jj(["describe", "-m", "third"]).success();
let output = work_dir.run_jj(["git", "push", "--all"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: Won't push bookmark my-bookmark: commit b96eaa9bb3d8 has conflicts
yostqsxw b96eaa9b my-bookmark | (conflict) third
Nothing changed.
[EOF]
");
}
#[test]
fn test_git_push_no_description() {
let test_env = TestEnvironment::default();
set_up(&test_env);
test_env.add_config("remotes.origin.auto-track-bookmarks = '*'");
let work_dir = test_env.work_dir("local");
work_dir
.run_jj(["bookmark", "create", "-r@", "my-bookmark"])
.success();
work_dir.run_jj(["describe", "-m="]).success();
let output = work_dir.run_jj(["git", "push", "--bookmark", "my-bookmark"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Won't push commit 8d23abddc924 since it has no description
Hint: Rejected commit: yqosqzyt 8d23abdd my-bookmark* | (empty) (no description set)
[EOF]
[exit status: 1]
");
work_dir
.run_jj([
"git",
"push",
"--bookmark",
"my-bookmark",
"--allow-empty-description",
])
.success();
}
#[test]
fn test_git_push_no_description_in_immutable() {
let test_env = TestEnvironment::default();
set_up(&test_env);
test_env.add_config("remotes.origin.auto-track-bookmarks = '*'");
let work_dir = test_env.work_dir("local");
work_dir
.run_jj(["bookmark", "create", "-r@", "imm"])
.success();
work_dir.run_jj(["describe", "-m="]).success();
work_dir.run_jj(["new", "-m", "foo"]).success();
work_dir.write_file("file", "contents");
work_dir
.run_jj(["bookmark", "create", "-r@", "my-bookmark"])
.success();
let output = work_dir.run_jj(["git", "push", "--bookmark=my-bookmark", "--dry-run"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Won't push commit 8d23abddc924 since it has no description
Hint: Rejected commit: yqosqzyt 8d23abdd imm* | (empty) (no description set)
[EOF]
[exit status: 1]
");
test_env.add_config(r#"revset-aliases."immutable_heads()" = "imm""#);
let output = work_dir.run_jj(["git", "push", "--bookmark=my-bookmark", "--dry-run"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: my-bookmark [add to 240e2e89abb2]
Dry-run requested, not pushing.
[EOF]
");
}
#[test]
fn test_git_push_missing_author() {
let test_env = TestEnvironment::default();
set_up(&test_env);
test_env.add_config("remotes.origin.auto-track-bookmarks = '*'");
let work_dir = test_env.work_dir("local");
let run_without_var = |var: &str, args: &[&str]| {
work_dir
.run_jj_with(|cmd| cmd.args(args).env_remove(var))
.success();
};
run_without_var("JJ_USER", &["new", "root()", "-m=initial"]);
run_without_var("JJ_USER", &["bookmark", "create", "-r@", "missing-name"]);
let output = work_dir.run_jj(["git", "push", "--bookmark", "missing-name"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Won't push commit 613adaba9d49 since it has no author and/or committer set
Hint: Rejected commit: vruxwmqv 613adaba missing-name* | (empty) initial
[EOF]
[exit status: 1]
");
run_without_var("JJ_EMAIL", &["new", "root()", "-m=initial"]);
run_without_var("JJ_EMAIL", &["bookmark", "create", "-r@", "missing-email"]);
let output = work_dir.run_jj(["git", "push", "--bookmark=missing-email"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Won't push commit bb4ea60fc9ba since it has no author and/or committer set
Hint: Rejected commit: kpqxywon bb4ea60f missing-email* | (empty) initial
[EOF]
[exit status: 1]
");
}
#[test]
fn test_git_push_missing_author_in_immutable() {
let test_env = TestEnvironment::default();
set_up(&test_env);
test_env.add_config("remotes.origin.auto-track-bookmarks = '*'");
let work_dir = test_env.work_dir("local");
let run_without_var = |var: &str, args: &[&str]| {
work_dir
.run_jj_with(|cmd| cmd.args(args).env_remove(var))
.success();
};
run_without_var("JJ_USER", &["new", "root()", "-m=no author name"]);
run_without_var("JJ_EMAIL", &["new", "-m=no author email"]);
work_dir
.run_jj(["bookmark", "create", "-r@", "imm"])
.success();
work_dir.run_jj(["new", "-m", "foo"]).success();
work_dir.write_file("file", "contents");
work_dir
.run_jj(["bookmark", "create", "-r@", "my-bookmark"])
.success();
let output = work_dir.run_jj(["git", "push", "--bookmark=my-bookmark", "--dry-run"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Won't push commit 5c3cc711907f since it has no author and/or committer set
Hint: Rejected commit: yostqsxw 5c3cc711 imm* | (empty) no author email
[EOF]
[exit status: 1]
");
test_env.add_config(r#"revset-aliases."immutable_heads()" = "imm""#);
let output = work_dir.run_jj(["git", "push", "--bookmark=my-bookmark", "--dry-run"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: my-bookmark [add to 96080b93b4ce]
Dry-run requested, not pushing.
[EOF]
");
}
#[test]
fn test_git_push_missing_committer() {
let test_env = TestEnvironment::default();
set_up(&test_env);
test_env.add_config("remotes.origin.auto-track-bookmarks = '*'");
let work_dir = test_env.work_dir("local");
let run_without_var = |var: &str, args: &[&str]| {
work_dir
.run_jj_with(|cmd| cmd.args(args).env_remove(var))
.success();
};
work_dir
.run_jj(["bookmark", "create", "-r@", "missing-name"])
.success();
run_without_var("JJ_USER", &["describe", "-m=no committer name"]);
let output = work_dir.run_jj(["git", "push", "--bookmark=missing-name"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Won't push commit e8a77cb24da9 since it has no author and/or committer set
Hint: Rejected commit: yqosqzyt e8a77cb2 missing-name* | (empty) no committer name
[EOF]
[exit status: 1]
");
work_dir.run_jj(["new", "root()"]).success();
work_dir
.run_jj(["bookmark", "create", "-r@", "missing-email"])
.success();
run_without_var("JJ_EMAIL", &["describe", "-m=no committer email"]);
let output = work_dir.run_jj(["git", "push", "--bookmark=missing-email"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Won't push commit 971c50fd8d1d since it has no author and/or committer set
Hint: Rejected commit: kpqxywon 971c50fd missing-email* | (empty) no committer email
[EOF]
[exit status: 1]
");
run_without_var("JJ_EMAIL", &["describe", "-m=", "missing-email"]);
let output = work_dir.run_jj(["git", "push", "--bookmark=missing-email"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Won't push commit 4bd3b55c7759 since it has no description and has no author and/or committer set
Hint: Rejected commit: kpqxywon 4bd3b55c missing-email* | (empty) (no description set)
[EOF]
[exit status: 1]
");
}
#[test]
fn test_git_push_missing_committer_in_immutable() {
let test_env = TestEnvironment::default();
set_up(&test_env);
test_env.add_config("remotes.origin.auto-track-bookmarks = '*'");
let work_dir = test_env.work_dir("local");
let run_without_var = |var: &str, args: &[&str]| {
work_dir
.run_jj_with(|cmd| cmd.args(args).env_remove(var))
.success();
};
run_without_var("JJ_USER", &["describe", "-m=no committer name"]);
work_dir.run_jj(["new"]).success();
run_without_var("JJ_EMAIL", &["describe", "-m=no committer email"]);
work_dir
.run_jj(["bookmark", "create", "-r@", "imm"])
.success();
work_dir.run_jj(["new", "-m", "foo"]).success();
work_dir.write_file("file", "contents");
work_dir
.run_jj(["bookmark", "create", "-r@", "my-bookmark"])
.success();
let output = work_dir.run_jj(["git", "push", "--bookmark=my-bookmark", "--dry-run"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Won't push commit ab230f98c812 since it has no author and/or committer set
Hint: Rejected commit: yostqsxw ab230f98 imm* | (empty) no committer email
[EOF]
[exit status: 1]
");
test_env.add_config(r#"revset-aliases."immutable_heads()" = "imm""#);
let output = work_dir.run_jj(["git", "push", "--bookmark=my-bookmark", "--dry-run"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: my-bookmark [add to e0dff9c29479]
Dry-run requested, not pushing.
[EOF]
");
}
#[test]
fn test_git_push_deleted() {
let test_env = TestEnvironment::default();
set_up(&test_env);
let origin_dir = test_env.work_dir("origin");
let work_dir = test_env.work_dir("local");
origin_dir
.run_jj(["tag", "set", "tag1", "-rbookmark1"])
.success();
origin_dir.run_jj(["git", "export"]).success();
work_dir.run_jj(["git", "fetch", "--tag=*"]).success();
work_dir
.run_jj(["bookmark", "delete", "bookmark1"])
.success();
work_dir.run_jj(["tag", "delete", "tag1"]).success();
let output = work_dir.run_jj(["git", "push", "--deleted"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: bookmark1 [delete from 9b2e76de3920]
tag: tag1 [delete from 9b2e76de3920]
[EOF]
");
let output = work_dir.run_jj(["log", "-rall()"]);
insta::assert_snapshot!(output, @"
@ yqosqzyt test.user@example.com 2001-02-03 08:05:13 8d23abdd
│ (empty) (no description set)
│ ○ zsuskuln test.user@example.com 2001-02-03 08:05:10 bookmark2 38a20473
├─╯ (empty) description 2
│ ○ qpvuntsm test.user@example.com 2001-02-03 08:05:08 9b2e76de
├─╯ (empty) description 1
â—† zzzzzzzz root() 00000000
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--deleted"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Nothing changed.
[EOF]
");
}
#[test]
fn test_git_push_conflicting_bookmarks() -> TestResult {
let test_env = TestEnvironment::default();
set_up(&test_env);
let work_dir = test_env.work_dir("local");
test_env.add_config("remotes.origin.auto-track-bookmarks = '*'");
let git_repo = {
let mut git_repo_path = work_dir.root().to_owned();
git_repo_path.extend([".jj", "repo", "store", "git"]);
git::open(&git_repo_path)
};
git_repo
.find_reference("refs/remotes/origin/bookmark2")?
.delete()?;
work_dir.run_jj(["git", "import"]).success();
work_dir
.run_jj(["new", "root()", "-m=description 3"])
.success();
work_dir
.run_jj(["bookmark", "create", "-r@", "bookmark2"])
.success();
work_dir.run_jj(["git", "fetch"]).success();
insta::assert_snapshot!(get_bookmark_output(&work_dir), @"
bookmark1: qpvuntsm 9b2e76de (empty) description 1
@origin: qpvuntsm 9b2e76de (empty) description 1
bookmark2 (conflicted):
+ yostqsxw ebedbe63 (empty) description 3
+ zsuskuln 38a20473 (empty) description 2
@origin (behind by 1 commits): zsuskuln 38a20473 (empty) description 2
[EOF]
");
let bump_bookmark1 = || {
work_dir.run_jj(["new", "bookmark1", "-m=bump"]).success();
work_dir
.run_jj(["bookmark", "set", "bookmark1", "-r@"])
.success();
};
let output = work_dir.run_jj(["git", "push"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: Bookmark bookmark2 is conflicted
Hint: Run `jj bookmark list` to inspect, and use `jj bookmark set` to fix it up.
Nothing changed.
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--bookmark", "bookmark2"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Error: Bookmark bookmark2 is conflicted
Hint: Run `jj bookmark list` to inspect, and use `jj bookmark set` to fix it up.
[EOF]
[exit status: 1]
");
bump_bookmark1();
let output = work_dir.run_jj(["git", "push", "--all"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: Bookmark bookmark2 is conflicted
Hint: Run `jj bookmark list` to inspect, and use `jj bookmark set` to fix it up.
Changes to push to origin:
bookmark: bookmark1 [move forward from 9b2e76de3920 to 749c2e6d999f]
[EOF]
");
bump_bookmark1();
let output = work_dir.run_jj(["git", "push", "-rall()"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: Bookmark bookmark2 is conflicted
Hint: Run `jj bookmark list` to inspect, and use `jj bookmark set` to fix it up.
Changes to push to origin:
bookmark: bookmark1 [move forward from 749c2e6d999f to 9bb0f427b517]
[EOF]
");
Ok(())
}
#[test]
fn test_git_push_deleted_untracked() {
let test_env = TestEnvironment::default();
set_up(&test_env);
let work_dir = test_env.work_dir("local");
work_dir
.run_jj(["bookmark", "delete", "bookmark1"])
.success();
work_dir
.run_jj(["bookmark", "untrack", "bookmark1"])
.success();
let output = work_dir.run_jj(["git", "push", "--deleted"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Nothing changed.
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--bookmark=bookmark1"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: No matching bookmarks for names: bookmark1
Nothing changed.
[EOF]
");
}
#[test]
fn test_git_push_deleted_remote_conflict() {
let test_env = TestEnvironment::default();
test_env.run_jj_in(".", ["git", "init", "origin"]).success();
let origin_dir = test_env.work_dir("origin");
let origin_git_repo_path = git_repo_dir_for_jj_repo(&origin_dir);
origin_dir
.run_jj(["describe", "-m=description 1"])
.success();
origin_dir
.run_jj(["bookmark", "create", "-r@", "my-bookmark"])
.success();
origin_dir.run_jj(["git", "export"]).success();
test_env
.run_jj_in(
".",
[
"git",
"clone",
"--no-colocate",
"--config=remotes.origin.auto-track-bookmarks='*'",
origin_git_repo_path.to_str().unwrap(),
"local",
],
)
.success();
let work_dir = test_env.work_dir("local");
let origin_dir = test_env.work_dir("origin");
origin_dir.run_jj(["new", "-m", "description 2"]).success();
origin_dir
.run_jj(["bookmark", "move", "my-bookmark", "--to", "@"])
.success();
origin_dir.run_jj(["git", "export"]).success();
work_dir.run_jj(["git", "fetch"]).success();
let origin_dir = test_env.work_dir("origin");
origin_dir
.run_jj(["new", "root()", "-m", "description 3"])
.success();
origin_dir
.run_jj([
"bookmark",
"move",
"my-bookmark",
"--to",
"@",
"--allow-backwards",
])
.success();
origin_dir.run_jj(["git", "export"]).success();
work_dir.run_jj(["git", "fetch", "--at-op=@-"]).success();
insta::assert_snapshot!(get_bookmark_output(&work_dir), @"
my-bookmark (conflicted):
- qpvuntsm/0 9b2e76de (hidden) (empty) description 1
+ royxmykx d7d20286 (empty) description 2
+ znkkpsqq 7b393945 (empty) description 3
@origin (ahead by 2 commits, behind by 1 commits) (conflicted):
- qpvuntsm/0 9b2e76de (hidden) (empty) description 1
+ royxmykx/1 8a615f93 (hidden) (empty) description 2
+ znkkpsqq 7b393945 (empty) description 3
[EOF]
");
work_dir
.run_jj(["bookmark", "delete", "my-bookmark"])
.success();
insta::assert_snapshot!(get_bookmark_output(&work_dir), @"
my-bookmark (deleted)
@origin (conflicted):
- qpvuntsm/0 9b2e76de (hidden) (empty) description 1
+ royxmykx/1 8a615f93 (hidden) (empty) description 2
+ znkkpsqq 7b393945 (empty) description 3
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--deleted"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: Bookmark my-bookmark@origin is conflicted
Hint: Run `jj git fetch` to update the conflicted remote bookmark.
Nothing changed.
[EOF]
");
}
#[test]
fn test_git_push_tracked_vs_all() {
let test_env = TestEnvironment::default();
set_up(&test_env);
test_env.add_config("revset-aliases.'immutable_heads()' = 'none()'");
let origin_dir = test_env.work_dir("origin");
let work_dir = test_env.work_dir("local");
origin_dir
.run_jj(["tag", "set", "tag1", "-rbookmark1"])
.success();
origin_dir
.run_jj(["tag", "set", "tag2", "-rbookmark2"])
.success();
origin_dir.run_jj(["git", "export"]).success();
work_dir.run_jj(["git", "fetch", "--tag=*"]).success();
work_dir
.run_jj(["new", "bookmark1", "-mmoved bookmark1"])
.success();
work_dir
.run_jj(["bookmark", "set", "bookmark1", "-r@"])
.success();
work_dir
.run_jj(["new", "bookmark2", "-mmoved bookmark2"])
.success();
work_dir
.run_jj(["bookmark", "delete", "bookmark2"])
.success();
work_dir.run_jj(["tag", "delete", "tag2"]).success();
work_dir
.run_jj(["bookmark", "untrack", "bookmark1"])
.success();
work_dir
.run_jj(["bookmark", "create", "-r@", "bookmark3"])
.success();
work_dir.run_jj(["tag", "set", "-r@", "tag3"]).success();
insta::assert_snapshot!(get_bookmark_output(&work_dir), @"
bookmark1: kmkuslsw f8cde667 (empty) moved bookmark1
bookmark1@origin: qpvuntsm 9b2e76de (empty) description 1
bookmark2 (deleted)
@origin: zsuskuln 38a20473 (empty) description 2
bookmark3: lylxulpl db4b9518 (empty) moved bookmark2
[EOF]
");
insta::assert_snapshot!(get_tag_output(&work_dir), @"
tag1: qpvuntsm 9b2e76de (empty) description 1
@origin: qpvuntsm 9b2e76de (empty) description 1
tag2 (deleted)
@origin: zsuskuln 38a20473 (empty) description 2
tag3: lylxulpl db4b9518 (empty) moved bookmark2
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--tracked", "--dry-run"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: Refusing to push deleted bookmark bookmark2
Hint: Push deleted bookmarks with --deleted or forget the bookmark to suppress this warning.
Warning: Refusing to push deleted tag tag2
Hint: Push deleted tags with --deleted.
Nothing changed.
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--tracked", "--deleted", "--dry-run"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: bookmark2 [delete from 38a204733702]
tag: tag2 [delete from 38a204733702]
Dry-run requested, not pushing.
[EOF]
");
work_dir
.run_jj(["bookmark", "untrack", "bookmark2"])
.success();
insta::assert_snapshot!(get_bookmark_output(&work_dir), @"
bookmark1: kmkuslsw f8cde667 (empty) moved bookmark1
bookmark1@origin: qpvuntsm 9b2e76de (empty) description 1
bookmark2@origin: zsuskuln 38a20473 (empty) description 2
bookmark3: lylxulpl db4b9518 (empty) moved bookmark2
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--tracked"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: Refusing to push deleted tag tag2
Hint: Push deleted tags with --deleted.
Nothing changed.
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--all"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: Non-tracking remote bookmark bookmark1@origin exists
Hint: Run `jj bookmark track bookmark1 --remote=origin` to import the remote bookmark.
Warning: Refusing to push deleted tag tag2
Hint: Push deleted tags with --deleted.
Warning: Refusing to create new remote tag tag3@origin
Changes to push to origin:
bookmark: bookmark3 [add to db4b95184aca]
[EOF]
");
}
#[test]
fn test_git_push_moved_forward_untracked() {
let test_env = TestEnvironment::default();
set_up(&test_env);
let work_dir = test_env.work_dir("local");
work_dir
.run_jj(["new", "bookmark1", "-mmoved bookmark1"])
.success();
work_dir
.run_jj(["bookmark", "set", "bookmark1", "-r@"])
.success();
work_dir
.run_jj(["bookmark", "untrack", "bookmark1"])
.success();
let output = work_dir.run_jj(["git", "push"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: Non-tracking remote bookmark bookmark1@origin exists
Hint: Run `jj bookmark track bookmark1 --remote=origin` to import the remote bookmark.
Nothing changed.
[EOF]
");
}
#[test]
fn test_git_push_moved_sideways_untracked() {
let test_env = TestEnvironment::default();
set_up(&test_env);
let work_dir = test_env.work_dir("local");
work_dir
.run_jj(["new", "root()", "-mmoved bookmark1"])
.success();
work_dir
.run_jj(["bookmark", "set", "--allow-backwards", "bookmark1", "-r@"])
.success();
work_dir
.run_jj(["bookmark", "untrack", "bookmark1"])
.success();
let output = work_dir.run_jj(["git", "push"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: Non-tracking remote bookmark bookmark1@origin exists
Hint: Run `jj bookmark track bookmark1 --remote=origin` to import the remote bookmark.
Nothing changed.
[EOF]
");
}
#[test]
fn test_git_push_to_remote_named_git() {
let test_env = TestEnvironment::default();
set_up(&test_env);
let work_dir = test_env.work_dir("local");
let git_repo_path = {
let mut git_repo_path = work_dir.root().to_owned();
git_repo_path.extend([".jj", "repo", "store", "git"]);
git_repo_path
};
git::rename_remote(&git_repo_path, "origin", "git");
let output = work_dir.run_jj(["git", "push", "--all", "--remote=git"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to git:
bookmark: bookmark1 [add to 9b2e76de3920]
bookmark: bookmark2 [add to 38a204733702]
Error: Git remote named 'git' is reserved for local Git repository
Hint: Run `jj git remote rename` to give a different name.
[EOF]
[exit status: 1]
");
}
#[test]
fn test_git_push_to_remote_with_slashes() {
let test_env = TestEnvironment::default();
set_up(&test_env);
let work_dir = test_env.work_dir("local");
let git_repo_path = {
let mut git_repo_path = work_dir.root().to_owned();
git_repo_path.extend([".jj", "repo", "store", "git"]);
git_repo_path
};
git::rename_remote(&git_repo_path, "origin", "slash/origin");
let output = work_dir.run_jj(["git", "push", "--all", "--remote=slash/origin"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to slash/origin:
bookmark: bookmark1 [add to 9b2e76de3920]
bookmark: bookmark2 [add to 38a204733702]
Error: Git remotes with slashes are incompatible with jj: slash/origin
Hint: Run `jj git remote rename` to give a different name.
[EOF]
[exit status: 1]
");
}
#[test]
fn test_git_push_commits_not_ready() {
let test_env = TestEnvironment::default();
set_up(&test_env);
let work_dir = test_env.work_dir("local");
work_dir
.run_jj(["new", "bookmark2", "-m", "commit not suitable"])
.success();
work_dir
.run_jj(["bookmark", "set", "bookmark2", "-r@"])
.success();
let output = work_dir.run_jj(["git", "push", "--dry-run"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: bookmark2 [move forward from 38a204733702 to 9e7e2853b980]
Dry-run requested, not pushing.
[EOF]
");
work_dir
.run_jj_with(|cmd| {
cmd.args(["metaedit", "--update-author"])
.env_remove("JJ_USER")
})
.success();
let output = work_dir.run_jj(["git", "push"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: Won't push bookmark bookmark2: commit b63db0e60f60 has no author and/or committer set
vruxwmqv b63db0e6 bookmark2* | (empty) commit not suitable
Nothing changed.
[EOF]
");
let output = work_dir.run_jj(["git", "push", "--config", "git.private-commits=all()"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: Won't push bookmark bookmark2: commit b63db0e60f60 has no author and/or committer set and is private
vruxwmqv b63db0e6 bookmark2* | (empty) commit not suitable
Hint: Configured git.private-commits: 'all()'
Nothing changed.
[EOF]
");
let output = work_dir.run_jj([
"git",
"push",
"--config",
"git.private-commits=all()",
"--allow-private",
]);
insta::assert_snapshot!(output, @"
------- stderr -------
Warning: Won't push bookmark bookmark2: commit b63db0e60f60 has no author and/or committer set
vruxwmqv b63db0e6 bookmark2* | (empty) commit not suitable
Nothing changed.
[EOF]
");
}
#[test]
fn test_git_push_sign_on_push() {
let test_env = TestEnvironment::default();
set_up(&test_env);
let work_dir = test_env.work_dir("local");
let template = r#"
separate("\n",
description.first_line(),
if(signature,
separate(", ",
"Signature: " ++ signature.display(),
"Status: " ++ signature.status(),
"Key: " ++ signature.key(),
)
)
)
"#;
work_dir
.run_jj(["new", "bookmark2", "-m", "commit to be signed 1"])
.success();
work_dir
.run_jj(["new", "-m", "commit to be signed 2"])
.success();
work_dir
.run_jj(["bookmark", "set", "bookmark2", "-r@"])
.success();
work_dir
.run_jj(["new", "-m", "commit which should not be signed 1"])
.success();
work_dir
.run_jj(["new", "-m", "commit which should not be signed 2"])
.success();
let output = work_dir.run_jj(["log", "-T", template]);
insta::assert_snapshot!(output, @"
@ commit which should not be signed 2
â—‹ commit which should not be signed 1
â—‹ commit to be signed 2
â—‹ commit to be signed 1
â—‹ description 2
│ ○ description 1
├─╯
â—†
[EOF]
");
test_env.add_config(
r#"
signing.backend = "test"
signing.key = "impeccable"
git.sign-on-push = true
"#,
);
let output = work_dir.run_jj(["git", "push", "--dry-run"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: bookmark2 [move forward from 38a204733702 to 3779ed7f18df]
Dry-run requested, not pushing.
[EOF]
");
let output = work_dir.run_jj(["log", "-T", template]);
insta::assert_snapshot!(output, @"
@ commit which should not be signed 2
â—‹ commit which should not be signed 1
â—‹ commit to be signed 2
â—‹ commit to be signed 1
â—‹ description 2
│ ○ description 1
├─╯
â—†
[EOF]
");
let output = work_dir.run_jj(["git", "push"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Updated signatures of 2 commits
Rebased 2 descendant commits
Changes to push to origin:
bookmark: bookmark2 [move forward from 38a204733702 to d45e2adce0ad]
Working copy (@) now at: kmkuslsw 3d5a9465 (empty) commit which should not be signed 2
Parent commit (@-) : kpqxywon 48ea83e9 (empty) commit which should not be signed 1
[EOF]
");
let output = work_dir.run_jj(["log", "-T", template]);
insta::assert_snapshot!(output, @"
@ commit which should not be signed 2
â—‹ commit which should not be signed 1
â—‹ commit to be signed 2
│ Signature: test-display, Status: good, Key: impeccable
â—‹ commit to be signed 1
│ Signature: test-display, Status: good, Key: impeccable
â—‹ description 2
│ ○ description 1
├─╯
â—†
[EOF]
");
let output = work_dir.run_jj(["bookmark", "move", "bookmark2", "--to", "@-"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Moved 1 bookmarks to kpqxywon 48ea83e9 bookmark2* | (empty) commit which should not be signed 1
[EOF]
");
test_env.add_config(r#"revset-aliases."immutable_heads()" = "bookmark2""#);
let output = work_dir.run_jj(["git", "push"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: bookmark2 [move forward from d45e2adce0ad to 48ea83e9499c]
[EOF]
");
let output = work_dir.run_jj(["log", "-T", template, "-r", "::"]);
insta::assert_snapshot!(output, @"
@ commit which should not be signed 2
â—† commit which should not be signed 1
â—† commit to be signed 2
│ Signature: test-display, Status: good, Key: impeccable
â—† commit to be signed 1
│ Signature: test-display, Status: good, Key: impeccable
â—† description 2
│ ○ description 1
├─╯
â—†
[EOF]
");
work_dir
.run_jj([
"metaedit",
"--author",
"Test User <someone.else@example.com>",
])
.success();
work_dir
.run_jj(["new", "-B@", "--no-edit", "-m", "pre-signed commit"])
.success();
work_dir.run_jj(["sign", "-r", "@-"]).success();
work_dir
.run_jj(["new", "-m", "commit to be signed 3"])
.success();
work_dir
.run_jj(["bookmark", "set", "bookmark1", "-r@", "--allow-backwards"])
.success();
let output = work_dir.run_jj(["log", "-T", template]);
insta::assert_snapshot!(output, @"
@ commit to be signed 3
â—‹ commit which should not be signed 2
â—‹ pre-signed commit
│ Signature: test-display, Status: good, Key: impeccable
â—† commit which should not be signed 1
~ (elided revisions)
│ ○ description 1
├─╯
â—†
[EOF]
");
let output = work_dir.run_jj(["git", "push"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Updated signatures of 1 commits
Changes to push to origin:
bookmark: bookmark1 [move sideways from 9b2e76de3920 to 0617b6813c01]
Working copy (@) now at: pzsxstzt 0617b681 bookmark1 | (empty) commit to be signed 3
Parent commit (@-) : kmkuslsw 5114df95 (empty) commit which should not be signed 2
[EOF]
");
let output = work_dir.run_jj(["log", "-T", template]);
insta::assert_snapshot!(output, @"
@ commit to be signed 3
│ Signature: test-display, Status: good, Key: impeccable
â—‹ commit which should not be signed 2
â—‹ pre-signed commit
│ Signature: test-display, Status: good, Key: impeccable
â—† commit which should not be signed 1
~ (elided revisions)
│ ○ description 1
├─╯
â—†
[EOF]
");
}
#[test]
fn test_git_push_rejected_by_remote() -> TestResult {
let test_env = TestEnvironment::default();
set_up(&test_env);
let work_dir = test_env.work_dir("local");
insta::assert_snapshot!(get_bookmark_output(&work_dir), @"
bookmark1: qpvuntsm 9b2e76de (empty) description 1
@origin: qpvuntsm 9b2e76de (empty) description 1
bookmark2: zsuskuln 38a20473 (empty) description 2
@origin: zsuskuln 38a20473 (empty) description 2
[EOF]
");
let hook_path = test_env
.env_root()
.join("origin")
.join(".jj")
.join("repo")
.join("store")
.join("git")
.join("hooks")
.join("update");
std::fs::write(&hook_path, "#!/bin/sh\nexit 1")?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt as _;
std::fs::set_permissions(&hook_path, std::fs::Permissions::from_mode(0o700))?;
}
work_dir.run_jj(["new", "bookmark1"]).success();
work_dir.write_file("file", "file");
work_dir.run_jj(["describe", "-m=update"]).success();
work_dir.run_jj(["bookmark", "move", "bookmark1"]).success();
let output = work_dir.run_jj(["git", "push"]);
let mut settings = insta::Settings::clone_current();
settings.add_filter(r"\s*\n", "\n");
settings.bind(|| {
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: bookmark1 [move forward from 9b2e76de3920 to 0fc4cf312e83]
remote: error: hook declined to update refs/heads/bookmark1
Warning: The remote rejected the following updates:
refs/heads/bookmark1 (reason: hook declined)
Hint: Try checking if you have permission to push to all the bookmarks.
Error: Failed to push some bookmarks
[EOF]
[exit status: 1]
");
});
Ok(())
}
#[test]
fn test_git_push_unmapped_refs() {
let test_env = TestEnvironment::default();
test_env
.run_jj_in(".", ["git", "init", "--colocate", "origin"])
.success();
test_env
.run_jj_in(".", ["git", "init", "--colocate", "local"])
.success();
let local_dir = test_env.work_dir("local");
let mut data = local_dir.read_file(".git/config");
data.extend(indoc! {br#"
[remote "origin"]
url = updated-later
fetch = +refs/heads/dummy:refs/remotes/origin/dummy
"#});
local_dir.write_file(".git/config", data);
local_dir
.run_jj(["git", "remote", "set-url", "origin", "../origin"])
.success();
local_dir.run_jj(["commit", "-mcommit1"]).success();
local_dir
.run_jj(["bookmark", "set", "-r@-", "bookmark1"])
.success();
local_dir.run_jj(["commit", "-mcommit2"]).success();
local_dir
.run_jj(["bookmark", "set", "-r@-", "bookmark2"])
.success();
local_dir.run_jj(["bookmark", "track", "*"]).success();
let output = local_dir.run_jj(["git", "push", "--bookmark=bookmark1"]);
insta::assert_snapshot!(output, @"
------- stderr -------
Changes to push to origin:
bookmark: bookmark1 [add to 5792bfac70a2]
[EOF]
");
insta::assert_snapshot!(get_bookmark_output(&local_dir), @"
bookmark1: rlvkpnrz 5792bfac (empty) commit1
@git: rlvkpnrz 5792bfac (empty) commit1
@origin: rlvkpnrz 5792bfac (empty) commit1
bookmark2: zsuskuln aa5df56a (empty) commit2
@git: zsuskuln aa5df56a (empty) commit2
@origin (not created yet)
[EOF]
");
local_dir.write_file(".git/refs/remotes/origin/bookmark2/dummy.lock", "");
let output = local_dir.run_jj(["git", "push", "--bookmark=bookmark2"]);
insta::assert_snapshot!(output, @r#"
------- stderr -------
Changes to push to origin:
bookmark: bookmark2 [add to aa5df56a071b]
Warning: The following bookmarks couldn't be updated locally:
bookmark2@origin: Failed to set: The change for reference "refs/remotes/origin/bookmark2" could not be committed: Directory not empty
Error: Failed to push some bookmarks
[EOF]
[exit status: 1]
"#);
insta::assert_snapshot!(get_bookmark_output(&local_dir), @"
bookmark1: rlvkpnrz 5792bfac (empty) commit1
@git: rlvkpnrz 5792bfac (empty) commit1
@origin: rlvkpnrz 5792bfac (empty) commit1
bookmark2: zsuskuln aa5df56a (empty) commit2
@git: zsuskuln aa5df56a (empty) commit2
@origin (not created yet)
[EOF]
");
}
#[must_use]
fn get_bookmark_output(work_dir: &TestWorkDir) -> CommandOutput {
work_dir.run_jj(["bookmark", "list", "--all-remotes", "--quiet"])
}
#[must_use]
fn get_tag_output(work_dir: &TestWorkDir) -> CommandOutput {
work_dir.run_jj(["tag", "list", "--all-remotes"])
}