mirror of
https://github.com/jj-vcs/jj.git
synced 2026-07-03 14:02:54 +08:00
Note: we use longer short change-IDs in test_abandon_command to avoid false codebook detection.
2952 lines
103 KiB
Rust
2952 lines
103 KiB
Rust
// Copyright 2022 The Jujutsu Authors
|
||
//
|
||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
// you may not use this file except in compliance with the License.
|
||
// You may obtain a copy of the License at
|
||
//
|
||
// https://www.apache.org/licenses/LICENSE-2.0
|
||
//
|
||
// Unless required by applicable law or agreed to in writing, software
|
||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
// See the License for the specific language governing permissions and
|
||
// limitations under the License.
|
||
|
||
use 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");
|
||
// Show the setup. `insta` has trouble if this is done inside `set_up()`
|
||
insta::assert_snapshot!(get_bookmark_output(&work_dir), @"
|
||
bookmark1: qpvuntsm 9b2e76de (empty) description 1
|
||
@origin: qpvuntsm 9b2e76de (empty) description 1
|
||
bookmark2: psuskuln 7ac95915 (empty) description 2
|
||
@origin: psuskuln 7ac95915 (empty) description 2
|
||
[EOF]
|
||
");
|
||
|
||
// No bookmarks to push yet
|
||
let output = work_dir.run_jj(["git", "push", "--all"]);
|
||
insta::assert_snapshot!(output, @"
|
||
------- stderr -------
|
||
Nothing changed.
|
||
[EOF]
|
||
");
|
||
|
||
// Tracked bookmark is up to date
|
||
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");
|
||
|
||
// add a second remote (actually the same)
|
||
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();
|
||
|
||
// select remote based on git.push config
|
||
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]
|
||
");
|
||
|
||
// remove origin, should select other (with hint)
|
||
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()""#);
|
||
// Update some bookmarks. `bookmark1` is not a current bookmark, but
|
||
// `bookmark2` and `my-bookmark` are.
|
||
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();
|
||
// Check the setup
|
||
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: rostqsxw b1fc3a6f (empty) foo
|
||
@origin (behind by 1 commits): psuskuln 7ac95915 (empty) description 2
|
||
my-bookmark: rostqsxw b1fc3a6f (empty) foo
|
||
@origin (not created yet)
|
||
[EOF]
|
||
");
|
||
// First dry-run. `bookmark1` should not get pushed.
|
||
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 7ac95915695d to b1fc3a6f6cd9]
|
||
bookmark: my-bookmark [add to b1fc3a6f6cd9]
|
||
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 7ac95915695d to b1fc3a6f6cd9]
|
||
bookmark: my-bookmark [add to b1fc3a6f6cd9]
|
||
[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: rostqsxw b1fc3a6f (empty) foo
|
||
@origin: rostqsxw b1fc3a6f (empty) foo
|
||
my-bookmark: rostqsxw b1fc3a6f (empty) foo
|
||
@origin: rostqsxw b1fc3a6f (empty) foo
|
||
[EOF]
|
||
");
|
||
|
||
// Try pushing backwards
|
||
work_dir
|
||
.run_jj([
|
||
"bookmark",
|
||
"set",
|
||
"bookmark2",
|
||
"-rbookmark2-",
|
||
"--allow-backwards",
|
||
])
|
||
.success();
|
||
// This behavior is a strangeness of our definition of the default push revset.
|
||
// We could consider changing it.
|
||
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]
|
||
");
|
||
// We can move a bookmark backwards
|
||
let output = work_dir.run_jj(["git", "push", "-bbookmark2"]);
|
||
insta::assert_snapshot!(output, @"
|
||
------- stderr -------
|
||
Changes to push to origin:
|
||
bookmark: bookmark2 [move backward from b1fc3a6f6cd9 to 7ac95915695d]
|
||
[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 that `jj git push` without arguments pushes a bookmark to the specified
|
||
/// remote even if it's already up to date on another remote
|
||
/// (`remote_bookmarks(remote=<remote>)..@` vs. `remote_bookmarks()..@`).
|
||
#[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()""#);
|
||
// Create another remote (but actually the same)
|
||
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();
|
||
// Modify bookmark1 and push it to `origin`
|
||
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]
|
||
");
|
||
// Since it's already pushed to origin, nothing will happen if push again
|
||
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]
|
||
");
|
||
// The bookmark was moved on the "other" remote as well (since it's actually the
|
||
// same remote), but `jj` is not aware of that since it thinks this is a
|
||
// different remote. So, the push should fail.
|
||
//
|
||
// But it succeeds! That's because the bookmark is created at the same location
|
||
// as it is on the remote. This would also work for a descendant.
|
||
//
|
||
// TODO: Saner test?
|
||
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");
|
||
|
||
// Move bookmark1 forward on the remote
|
||
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();
|
||
|
||
// Move bookmark1 forward to another commit locally
|
||
work_dir.run_jj(["new", "bookmark1", "-m=local"]).success();
|
||
work_dir.write_file("local", "local");
|
||
work_dir
|
||
.run_jj(["bookmark", "set", "bookmark1", "-r@"])
|
||
.success();
|
||
|
||
// Pushing should fail
|
||
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]
|
||
");
|
||
|
||
// The ref name should be colorized
|
||
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");
|
||
|
||
// Move bookmark1 forward on the remote
|
||
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: uruxwmqv 9ab45f12 remote
|
||
@git (behind by 1 commits): qpvuntsm 9b2e76de (empty) description 1
|
||
bookmark2: psuskuln 7ac95915 (empty) description 2
|
||
@git: psuskuln 7ac95915 (empty) description 2
|
||
[EOF]
|
||
");
|
||
origin_dir.run_jj(["git", "export"]).success();
|
||
|
||
// Move bookmark1 sideways to another commit locally
|
||
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: wmkuslsw db695c28 local
|
||
@origin (ahead by 1 commits, behind by 1 commits): qpvuntsm 9b2e76de (empty) description 1
|
||
bookmark2: psuskuln 7ac95915 (empty) description 2
|
||
@origin: psuskuln 7ac95915 (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 db695c2833a1]
|
||
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]
|
||
");
|
||
|
||
// The ref name should be colorized
|
||
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 db695c2833a1]
|
||
[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]
|
||
");
|
||
}
|
||
|
||
// This tests whether the push checks that the remote bookmarks are in expected
|
||
// positions.
|
||
#[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");
|
||
|
||
// Move bookmark1 forward on the remote
|
||
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: uruxwmqv 9ab45f12 remote
|
||
@git (behind by 1 commits): qpvuntsm 9b2e76de (empty) description 1
|
||
bookmark2: psuskuln 7ac95915 (empty) description 2
|
||
@git: psuskuln 7ac95915 (empty) description 2
|
||
[EOF]
|
||
");
|
||
origin_dir.run_jj(["git", "export"]).success();
|
||
|
||
// Delete bookmark1 locally
|
||
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: psuskuln 7ac95915 (empty) description 2
|
||
@origin: psuskuln 7ac95915 (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");
|
||
|
||
// Delete bookmark1 forward on the remote
|
||
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: psuskuln 7ac95915 (empty) description 2
|
||
@git: psuskuln 7ac95915 (empty) description 2
|
||
[EOF]
|
||
");
|
||
origin_dir.run_jj(["git", "export"]).success();
|
||
|
||
// Move bookmark1 sideways to another commit locally
|
||
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: psuskuln 7ac95915 (empty) description 2
|
||
@origin: psuskuln 7ac95915 (empty) description 2
|
||
[EOF]
|
||
");
|
||
|
||
// Pushing a moved bookmark fails if deleted on remote
|
||
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: psuskuln 7ac95915 (empty) description 2
|
||
@origin: psuskuln 7ac95915 (empty) description 2
|
||
[EOF]
|
||
");
|
||
|
||
// git does not allow to push a deleted bookmark if we expect it to exist even
|
||
// though it was already deleted
|
||
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");
|
||
|
||
// Forget bookmark1 locally
|
||
work_dir
|
||
.run_jj(["bookmark", "forget", "--include-remotes", "bookmark1"])
|
||
.success();
|
||
|
||
// Create a new bookmark1
|
||
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: rostqsxw 132d1b1b new bookmark1
|
||
@origin (not created yet)
|
||
bookmark2: psuskuln 7ac95915 (empty) description 2
|
||
@origin: psuskuln 7ac95915 (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 132d1b1b45fd]
|
||
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");
|
||
// Ensure that remote bookmarks aren't tracked automatically
|
||
test_env.add_config("remotes.origin.auto-track-bookmarks = '~*'");
|
||
|
||
// Push locally-created bookmark
|
||
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]
|
||
");
|
||
// Absent-tracked bookmark can be pushed
|
||
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 2901efb0a58e]
|
||
[EOF]
|
||
");
|
||
|
||
// Rewrite it and push again, which would fail if the pushed bookmark weren't
|
||
// set to "tracking"
|
||
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: psuskuln 7ac95915 (empty) description 2
|
||
@origin: psuskuln 7ac95915 (empty) description 2
|
||
my: truxwmqv 9de3639a (empty) local 2
|
||
@origin (ahead by 1 commits, behind by 1 commits): truxwmqv/1 2901efb0 (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 2901efb0a58e to 9de3639a5039]
|
||
[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");
|
||
// Add tracked tag
|
||
origin_dir
|
||
.run_jj(["tag", "set", "tag1", "-rbookmark1"])
|
||
.success();
|
||
origin_dir.run_jj(["git", "export"]).success();
|
||
work_dir.run_jj(["git", "fetch", "--tag=*"]).success();
|
||
// Update bookmarks and tags
|
||
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();
|
||
// Make conflicting changes on bookmark3
|
||
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();
|
||
// Check the setup
|
||
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): psuskuln 7ac95915 (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]
|
||
");
|
||
|
||
// First dry-run
|
||
// TODO: untracked tags should be included when gets stabilized (#7528)
|
||
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 7ac95915695d 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]
|
||
");
|
||
// Dry run requesting two specific bookmarks and one tag
|
||
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]
|
||
");
|
||
// Dry run requesting two specific bookmarks twice
|
||
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]
|
||
");
|
||
// Dry run with glob pattern
|
||
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 7ac95915695d to 0cb91ecd4965]
|
||
bookmark: bookmark3 [add to 0cb91ecd4965]
|
||
Dry-run requested, not pushing.
|
||
[EOF]
|
||
");
|
||
|
||
// Unmatched bookmark/tag name
|
||
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]
|
||
");
|
||
|
||
// --deleted is required to push deleted bookmarks even with --all
|
||
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 7ac95915695d 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 7ac95915695d 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]
|
||
");
|
||
|
||
// All pushed bookmarks/tags should be committed to the operation.
|
||
// Therefore, the subsequent "git import" should be noop.
|
||
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 7ac95915695d 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
|
||
│ ○ psuskuln test.user@example.com 2001-02-03 08:05:10 7ac95915
|
||
├─╯ (empty) description 2
|
||
│ ○ qpvuntsm test.user@example.com 2001-02-03 08:05:08 9b2e76de
|
||
├─╯ (empty) description 1
|
||
◆ zzzzzzzz root() 00000000
|
||
[EOF]
|
||
");
|
||
|
||
// Specified branch is up to date
|
||
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-rostqsxwqrlt for revision rostqsxwqrlt
|
||
Changes to push to origin:
|
||
bookmark: push-rostqsxwqrlt [add to bf0ba55ca017]
|
||
[EOF]
|
||
");
|
||
|
||
// specified bookmark is up to date
|
||
let output = work_dir.run_jj(["git", "push", "--change", "@"]);
|
||
insta::assert_snapshot!(output, @"
|
||
------- stderr -------
|
||
Bookmark push-rostqsxwqrlt@origin already matches push-rostqsxwqrlt
|
||
Nothing changed.
|
||
[EOF]
|
||
");
|
||
|
||
// test pushing two changes at once
|
||
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-rostqsxwqrlt [move sideways from bf0ba55ca017 to a8cf969f69c3]
|
||
bookmark: push-yqosqzytrlsw [add to 0f8164cd580b]
|
||
[EOF]
|
||
");
|
||
|
||
// specifying the same change twice doesn't break things
|
||
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-rostqsxwqrlt [move sideways from a8cf969f69c3 to 141495e3e47f]
|
||
[EOF]
|
||
");
|
||
|
||
// specifying the same bookmark with --change/--bookmark doesn't break things
|
||
work_dir.write_file("file", "modified4");
|
||
let output = work_dir.run_jj(["git", "push", "-c=@", "-b=push-rostqsxwqrlt"]);
|
||
insta::assert_snapshot!(output, @"
|
||
------- stderr -------
|
||
Changes to push to origin:
|
||
bookmark: push-rostqsxwqrlt [move sideways from 141495e3e47f to c5c0eb67f8fc]
|
||
[EOF]
|
||
");
|
||
|
||
// try again with --change that could move the bookmark forward
|
||
work_dir.write_file("file", "modified5");
|
||
work_dir
|
||
.run_jj([
|
||
"bookmark",
|
||
"set",
|
||
"-r=@-",
|
||
"--allow-backwards",
|
||
"push-rostqsxwqrlt",
|
||
])
|
||
.success();
|
||
let output = work_dir.run_jj(["status"]);
|
||
insta::assert_snapshot!(output, @"
|
||
Working copy changes:
|
||
M file
|
||
Working copy (@) : rostqsxw cabf33c8 bar
|
||
Parent commit (@-): yqosqzyt 0f8164cd push-rostqsxwqrlt* push-yqosqzytrlsw | foo
|
||
[EOF]
|
||
");
|
||
let output = work_dir.run_jj(["git", "push", "-c=@", "-b=push-rostqsxwqrlt"]);
|
||
insta::assert_snapshot!(output, @"
|
||
------- stderr -------
|
||
Error: Bookmark already exists: push-rostqsxwqrlt
|
||
Hint: Use 'jj bookmark move' to move it, and 'jj git push -b push-rostqsxwqrlt' 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 (@) : rostqsxw cabf33c8 bar
|
||
Parent commit (@-): yqosqzyt 0f8164cd push-rostqsxwqrlt* push-yqosqzytrlsw | foo
|
||
[EOF]
|
||
");
|
||
|
||
// Test changing `git_push_bookmark` template. It causes us to push again.
|
||
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-rostqsxwqrlt for revision rostqsxwqrlt
|
||
Changes to push to origin:
|
||
bookmark: test-rostqsxwqrlt [add to cabf33c82fed]
|
||
[EOF]
|
||
");
|
||
|
||
// Generating duplicate bookmarks should not be allowed
|
||
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 rostqsxwqrlt
|
||
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]
|
||
");
|
||
|
||
// Bad `git_push_bookmark` templates
|
||
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]
|
||
");
|
||
|
||
// Generate a bookmark name with non-utf8 content
|
||
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");
|
||
|
||
// Normal behavior.
|
||
let output = work_dir.run_jj(["git", "push", "--named", "b1=@"]);
|
||
insta::assert_snapshot!(output, @"
|
||
------- stderr -------
|
||
Changes to push to origin:
|
||
bookmark: b1 [add to 89ea5ca5f04c]
|
||
[EOF]
|
||
");
|
||
// Spaces before the = sign are treated like part of the bookmark name and such
|
||
// bookmarks cannot be pushed.
|
||
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]
|
||
");
|
||
// test pushing a change with an empty name
|
||
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]
|
||
");
|
||
// Unparsable name
|
||
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]
|
||
");
|
||
// test pushing a change with an empty revision
|
||
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]
|
||
");
|
||
// test pushing a change with no equals sign
|
||
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]
|
||
");
|
||
|
||
// test pushing the same change with the same name again
|
||
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]
|
||
");
|
||
// test pushing two changes at once
|
||
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:
|
||
rostqsxw 34bd1e21 b1* | pushed
|
||
yqosqzyt 0f8164cd foo
|
||
[EOF]
|
||
[exit status: 1]
|
||
");
|
||
|
||
// specifying the same bookmark with --named/--bookmark
|
||
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 5317293e9031]
|
||
[EOF]
|
||
");
|
||
}
|
||
|
||
#[test]
|
||
fn test_git_push_changes_with_name_deleted_tracked() {
|
||
let test_env = TestEnvironment::default();
|
||
set_up(&test_env);
|
||
// Unset immutable_heads so that untracking branches does not move the working
|
||
// copy
|
||
test_env.add_config(r#"revset-aliases."immutable_heads()" = "none()""#);
|
||
let work_dir = test_env.work_dir("local");
|
||
// Create a second empty remote `another_remote`
|
||
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");
|
||
// Normal push as part of the test setup
|
||
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();
|
||
|
||
// Test the setup
|
||
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]
|
||
");
|
||
|
||
// Can't push `b1` with --named to the same or another remote if it's deleted
|
||
// locally and still tracked on `origin`
|
||
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]
|
||
");
|
||
|
||
// OK to push to a different remote once the bookmark is no longer tracked on
|
||
// `origin`
|
||
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");
|
||
// Unset immutable_heads so that untracking branches does not move the working
|
||
// copy
|
||
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");
|
||
|
||
// Push a branch to a remote, but forget the local branch
|
||
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, @"
|
||
○ 366 child
|
||
@ 9e0 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: rostqsxw 9e0f2255 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]
|
||
");
|
||
|
||
// The bookmarked is still pushed to the remote, but let's entirely forget
|
||
// it. In other words, let's forget the remote-tracking bookmarks.
|
||
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]
|
||
");
|
||
|
||
// Make sure push still errors if we try to push a bookmark with the same name
|
||
// to a different location.
|
||
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]
|
||
");
|
||
|
||
// The bookmark is still forgotten
|
||
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 3668cedee47a]
|
||
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]
|
||
");
|
||
// In this case, pushing the bookmark to the same location where it already is
|
||
// succeeds. TODO: This seems pretty safe, but perhaps it should still show
|
||
// an error or some sort of warning?
|
||
let output = work_dir.run_jj(["git", "push", "--named", "b1=@"]);
|
||
insta::assert_snapshot!(output, @"
|
||
------- stderr -------
|
||
Changes to push to origin:
|
||
bookmark: b1 [add to 9e0f22551b20]
|
||
[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");
|
||
// Add tracked tags
|
||
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();
|
||
// Update bookmarks and tags
|
||
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"]), @"
|
||
@ mylxulpl test.user@example.com 2001-02-03 08:05:23 bookmark-2a* bookmark-2b* tag-2* 11142838
|
||
│ baz
|
||
○ wmkuslsw test.user@example.com 2001-02-03 08:05:20 bookmark-1* 2b2c6c07
|
||
│ bar
|
||
○ yqosqzyt test.user@example.com 2001-02-03 08:05:18 33b167f8
|
||
│ foo
|
||
│ ○ lznxytkn test.user@example.com 2001-02-03 08:05:24 tag-3* 44ea9da4
|
||
├─╯ (empty) new3
|
||
│ ○ psuskuln test.user@example.com 2001-02-03 08:05:10 bookmark2 7ac95915
|
||
├─╯ (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]
|
||
");
|
||
|
||
// Bookmark/tag at revision is up to date
|
||
let output = work_dir.run_jj(["git", "push", "--revisions", "bookmark1"]);
|
||
insta::assert_snapshot!(output, @"
|
||
------- stderr -------
|
||
Nothing changed.
|
||
[EOF]
|
||
");
|
||
// Push an empty set
|
||
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]
|
||
");
|
||
// Push a revision with no bookmarks/tags
|
||
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]
|
||
");
|
||
// Push a revision with a single bookmark
|
||
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 2b2c6c07cdf3]
|
||
Dry-run requested, not pushing.
|
||
[EOF]
|
||
");
|
||
// Push a revision with a single tag
|
||
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 44ea9da46261]
|
||
Dry-run requested, not pushing.
|
||
[EOF]
|
||
");
|
||
// Push multiple revisions of which some have bookmarks
|
||
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 2b2c6c07cdf3]
|
||
Dry-run requested, not pushing.
|
||
[EOF]
|
||
");
|
||
// Push a revision with a multiple bookmarks and tags
|
||
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 111428387a8b]
|
||
bookmark: bookmark-2b [add to 111428387a8b]
|
||
tag: tag-2 [move sideways from 9b2e76de3920 to 111428387a8b]
|
||
Dry-run requested, not pushing.
|
||
[EOF]
|
||
");
|
||
// Repeating a commit doesn't result in repeated messages about the bookmark
|
||
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 2b2c6c07cdf3]
|
||
Dry-run requested, not pushing.
|
||
[EOF]
|
||
");
|
||
// Repetition via name and commit doesn't result in repeated messages either
|
||
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 2b2c6c07cdf3]
|
||
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");
|
||
|
||
// New bookmark shouldn't be pushed by -r=..
|
||
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 27bd1dbb5995]
|
||
tag: tag-1 [add to 78f71024a5c3]
|
||
[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();
|
||
|
||
// Move bookmarks and tags
|
||
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: truxwmqv 0163b9ed (empty) local
|
||
@origin (behind by 1 commits): qpvuntsm 110db8ed (empty) origin
|
||
foo: truxwmqv 0163b9ed (empty) local
|
||
@origin (behind by 1 commits): qpvuntsm 110db8ed (empty) origin
|
||
[EOF]
|
||
");
|
||
insta::assert_snapshot!(get_tag_output(&work_dir), @"
|
||
bar: truxwmqv 0163b9ed (empty) local
|
||
@origin (behind by 1 commits): qpvuntsm 110db8ed (empty) origin
|
||
foo: truxwmqv 0163b9ed (empty) local
|
||
@origin (behind by 1 commits): qpvuntsm 110db8ed (empty) origin
|
||
[EOF]
|
||
");
|
||
|
||
// Both bookmark and tag of the same name can be selected
|
||
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 0163b9ed8732]
|
||
tag: foo [move forward from 110db8edfa5f to 0163b9ed8732]
|
||
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 0163b9ed8732]
|
||
bookmark: foo [move forward from 110db8edfa5f to 0163b9ed8732]
|
||
tag: bar [move forward from 110db8edfa5f to 0163b9ed8732]
|
||
tag: foo [move forward from 110db8edfa5f to 0163b9ed8732]
|
||
Dry-run requested, not pushing.
|
||
[EOF]
|
||
");
|
||
|
||
// Bookmark and tag can be selected individually
|
||
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 0163b9ed8732]
|
||
tag: bar [move forward from 110db8edfa5f to 0163b9ed8732]
|
||
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();
|
||
|
||
// Pushing untracked refs with --bookmark/--tag automatically tracks it.
|
||
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();
|
||
|
||
// ...unless the ref is already tracked with a different remote, in
|
||
// which case the user may have simply forgotten to specify --remote.
|
||
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 aed7f818ab04]
|
||
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 14c3571c4153 since it has no author and/or committer set
|
||
Hint: Rejected commit: truxwmqv 14c3571c 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 0f6d496a6e33 since it has no author and/or committer set
|
||
Hint: Rejected commit: rostqsxw 0f6d496a 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 1e056846da20]
|
||
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]
|
||
");
|
||
|
||
// Test message when there are multiple reasons (missing committer and
|
||
// description)
|
||
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 8356575dda50 since it has no author and/or committer set
|
||
Hint: Rejected commit: rostqsxw 8356575d 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 274d0f582429]
|
||
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");
|
||
// Add tracked tag
|
||
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)
|
||
│ ○ psuskuln test.user@example.com 2001-02-03 08:05:10 bookmark2 7ac95915
|
||
├─╯ (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)
|
||
};
|
||
|
||
// Forget remote ref, move local ref, then fetch to create conflict.
|
||
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):
|
||
+ postqsxw 602aa604 (empty) description 3
|
||
+ psuskuln 7ac95915 (empty) description 2
|
||
@origin (behind by 1 commits): psuskuln 7ac95915 (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();
|
||
};
|
||
|
||
// Conflicting bookmark at @
|
||
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]
|
||
");
|
||
|
||
// --bookmark should be blocked by conflicting bookmark
|
||
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]
|
||
");
|
||
|
||
// --all shouldn't be blocked by conflicting bookmark
|
||
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 2eb9ca9d923e]
|
||
[EOF]
|
||
");
|
||
|
||
// --revisions shouldn't be blocked by conflicting bookmark
|
||
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 2eb9ca9d923e to 7b78a524086f]
|
||
[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");
|
||
|
||
// Absent local bookmark shouldn't be considered "deleted" compared to
|
||
// non-tracking remote bookmark.
|
||
// TODO: include tag if we add "tag untrack" command
|
||
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();
|
||
|
||
// create origin
|
||
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();
|
||
|
||
// clone without colocation
|
||
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");
|
||
|
||
// advance the origin, fetch it
|
||
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();
|
||
|
||
// adjust the origin again, fetch concurrently
|
||
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
|
||
+ poyxmykx fe007030 (empty) description 2
|
||
+ onkkpsqq 110b71ca (empty) description 3
|
||
@origin (ahead by 2 commits, behind by 1 commits) (conflicted):
|
||
- qpvuntsm/0 9b2e76de (hidden) (empty) description 1
|
||
+ poyxmykx/1 a37f5af9 (hidden) (empty) description 2
|
||
+ onkkpsqq 110b71ca (empty) description 3
|
||
[EOF]
|
||
");
|
||
|
||
// fix the local conflict, unpushable remote conflict remains
|
||
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
|
||
+ poyxmykx/1 a37f5af9 (hidden) (empty) description 2
|
||
+ onkkpsqq 110b71ca (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");
|
||
// Add tracked tag
|
||
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();
|
||
// Update bookmarks and tags
|
||
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: wmkuslsw 36714d46 (empty) moved bookmark1
|
||
bookmark1@origin: qpvuntsm 9b2e76de (empty) description 1
|
||
bookmark2 (deleted)
|
||
@origin: psuskuln 7ac95915 (empty) description 2
|
||
bookmark3: mylxulpl 844b3f91 (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: psuskuln 7ac95915 (empty) description 2
|
||
tag3: mylxulpl 844b3f91 (empty) moved bookmark2
|
||
[EOF]
|
||
");
|
||
|
||
// At this point, only bookmark2 and tags are still tracked.
|
||
// `jj git push --tracked --deleted` would try to push them and no other
|
||
// bookmarks nor tags.
|
||
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 7ac95915695d]
|
||
tag: tag2 [delete from 7ac95915695d]
|
||
Dry-run requested, not pushing.
|
||
[EOF]
|
||
");
|
||
|
||
// Untrack the last remaining tracked bookmark.
|
||
work_dir
|
||
.run_jj(["bookmark", "untrack", "bookmark2"])
|
||
.success();
|
||
insta::assert_snapshot!(get_bookmark_output(&work_dir), @"
|
||
bookmark1: wmkuslsw 36714d46 (empty) moved bookmark1
|
||
bookmark1@origin: qpvuntsm 9b2e76de (empty) description 1
|
||
bookmark2@origin: psuskuln 7ac95915 (empty) description 2
|
||
bookmark3: mylxulpl 844b3f91 (empty) moved bookmark2
|
||
[EOF]
|
||
");
|
||
|
||
// Now, no bookmarks are tracked. --tracked would push tags only
|
||
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]
|
||
");
|
||
|
||
// All bookmarks are still untracked.
|
||
// - --all tries to push bookmark1, but fails because a bookmark with the same
|
||
// name exist on the remote.
|
||
// - --all succeeds in pushing bookmark3, since there is no bookmark of the same
|
||
// name on the remote.
|
||
// - It does not try to push bookmark2.
|
||
//
|
||
// TODO: Not trying to push bookmark2 could be considered correct, or perhaps
|
||
// we want to consider this as a deletion of the bookmark that failed because
|
||
// the bookmark was untracked. In the latter case, an error message should be
|
||
// printed. Some considerations:
|
||
// - Whatever we do should be consistent with what `jj bookmark list` does; it
|
||
// currently does *not* list bookmarks like bookmark2 as "about to be
|
||
// deleted", as can be seen above.
|
||
// - We could consider showing some hint on `jj bookmark untrack bookmark2
|
||
// --remote=origin` instead of showing an error here.
|
||
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 844b3f914c63]
|
||
[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 7ac95915695d]
|
||
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 7ac95915695d]
|
||
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();
|
||
|
||
// ready to push
|
||
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 7ac95915695d to e1f429e5205e]
|
||
Dry-run requested, not pushing.
|
||
[EOF]
|
||
");
|
||
|
||
// rejected with empty username
|
||
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 744d067f0b2d has no author and/or committer set
|
||
truxwmqv 744d067f bookmark2* | (empty) commit not suitable
|
||
Nothing changed.
|
||
[EOF]
|
||
");
|
||
|
||
// rejected for both private content and empty username
|
||
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 744d067f0b2d has no author and/or committer set and is private
|
||
truxwmqv 744d067f bookmark2* | (empty) commit not suitable
|
||
Hint: Configured git.private-commits: 'all()'
|
||
Nothing changed.
|
||
[EOF]
|
||
");
|
||
|
||
// still rejected even with --allow-private
|
||
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 744d067f0b2d has no author and/or committer set
|
||
truxwmqv 744d067f 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();
|
||
// There should be no signed commits initially
|
||
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 7ac95915695d to ab23dfb46418]
|
||
Dry-run requested, not pushing.
|
||
[EOF]
|
||
");
|
||
// There should be no signed commits after performing a dry run
|
||
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 7ac95915695d to adc71fb1a763]
|
||
Working copy (@) now at: xmkuslsw f36ecefd (empty) commit which should not be signed 2
|
||
Parent commit (@-) : kpqxywon a7a9ae6e (empty) commit which should not be signed 1
|
||
[EOF]
|
||
");
|
||
// Only commits which are being pushed should be signed
|
||
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]
|
||
");
|
||
|
||
// Immutable commits should not be signed
|
||
let output = work_dir.run_jj(["bookmark", "move", "bookmark2", "--to", "@-"]);
|
||
insta::assert_snapshot!(output, @"
|
||
------- stderr -------
|
||
Moved 1 bookmarks to kpqxywon a7a9ae6e 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 adc71fb1a763 to a7a9ae6eb373]
|
||
[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]
|
||
");
|
||
|
||
// Do not sign commits from other authors or re-sign unnecessarily
|
||
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 0f0dc241548c]
|
||
Working copy (@) now at: mzsxstzt 0f0dc241 bookmark1 | (empty) commit to be signed 3
|
||
Parent commit (@-) : xmkuslsw da79958d (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");
|
||
// show repo state
|
||
insta::assert_snapshot!(get_bookmark_output(&work_dir), @"
|
||
bookmark1: qpvuntsm 9b2e76de (empty) description 1
|
||
@origin: qpvuntsm 9b2e76de (empty) description 1
|
||
bookmark2: psuskuln 7ac95915 (empty) description 2
|
||
@origin: psuskuln 7ac95915 (empty) description 2
|
||
[EOF]
|
||
");
|
||
|
||
// create a hook on the remote that prevents pushing
|
||
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))?;
|
||
}
|
||
|
||
// create new commit on top of bookmark1
|
||
work_dir.run_jj(["new", "bookmark1"]).success();
|
||
work_dir.write_file("file", "file");
|
||
work_dir.run_jj(["describe", "-m=update"]).success();
|
||
|
||
// update bookmark
|
||
work_dir.run_jj(["bookmark", "move", "bookmark1"]).success();
|
||
|
||
// push bookmark
|
||
let output = work_dir.run_jj(["git", "push"]);
|
||
|
||
// The git remote sideband adds a dummy suffix of 8 spaces to attempt to clear
|
||
// any leftover data. This is done to help with cases where the line is
|
||
// rewritten.
|
||
//
|
||
// However, a common option in a lot of editors removes trailing whitespace.
|
||
// This means that anyone with that option that opens this file would make the
|
||
// following snapshot fail. Using the insta filter here normalizes the
|
||
// output.
|
||
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 e88165908698]
|
||
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");
|
||
|
||
// Set up remote with refspecs that map only specific branch
|
||
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();
|
||
|
||
// bookmark1 isn't mapped in Git, but can still be pushed
|
||
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]
|
||
");
|
||
|
||
// Remote bookmark for bookmark1 is created and exported by jj
|
||
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]
|
||
");
|
||
|
||
// Make export of bookmark2 fail by creating non-empty directory
|
||
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]
|
||
"#);
|
||
|
||
// Since export failed, bookmark2@origin isn't created. If it were created,
|
||
// this command would import "deletion" of bookmark2@origin as an external
|
||
// change, and abandon commit2.
|
||
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 {
|
||
// --quiet to suppress deleted bookmarks hint
|
||
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"])
|
||
}
|