mirror of
https://github.com/martinvonz/jj.git
synced 2025-05-05 23:42:50 +00:00
push: add support for revsets.push
This let's our users finally configure their own push revset. As the current one may not be perfect for every situation. A notable difference to other revsets is that it takes a named remote, since it needs to handle a `jj git push --remote <remote>` call.
This commit is contained in:
parent
4f3e7e972d
commit
3a086ae52f
@ -67,6 +67,9 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|||||||
* Commit objects in templates now have `trailers() -> List<Trailer>`, the Trailer
|
* Commit objects in templates now have `trailers() -> List<Trailer>`, the Trailer
|
||||||
objects have `key() -> String` and `value() -> String`.
|
objects have `key() -> String` and `value() -> String`.
|
||||||
|
|
||||||
|
* Added support `revsets.push` which allows you to customize the default
|
||||||
|
commits to push. See [#3650](https://github.com/jj-vcs/jj/issues/3650).
|
||||||
|
|
||||||
### Fixed bugs
|
### Fixed bugs
|
||||||
|
|
||||||
* Fixed crash on change-delete conflict resolution.
|
* Fixed crash on change-delete conflict resolution.
|
||||||
|
@ -27,6 +27,7 @@ use jj_lib::backend::CommitId;
|
|||||||
use jj_lib::commit::Commit;
|
use jj_lib::commit::Commit;
|
||||||
use jj_lib::commit::CommitIteratorExt as _;
|
use jj_lib::commit::CommitIteratorExt as _;
|
||||||
use jj_lib::config::ConfigGetResultExt as _;
|
use jj_lib::config::ConfigGetResultExt as _;
|
||||||
|
use jj_lib::dsl_util::ExpressionNode;
|
||||||
use jj_lib::git;
|
use jj_lib::git;
|
||||||
use jj_lib::git::GitBranchPushTargets;
|
use jj_lib::git::GitBranchPushTargets;
|
||||||
use jj_lib::git::GitPushStats;
|
use jj_lib::git::GitPushStats;
|
||||||
@ -41,11 +42,15 @@ use jj_lib::refs::BookmarkPushAction;
|
|||||||
use jj_lib::refs::BookmarkPushUpdate;
|
use jj_lib::refs::BookmarkPushUpdate;
|
||||||
use jj_lib::refs::LocalAndRemoteRef;
|
use jj_lib::refs::LocalAndRemoteRef;
|
||||||
use jj_lib::repo::Repo;
|
use jj_lib::repo::Repo;
|
||||||
|
use jj_lib::revset;
|
||||||
|
use jj_lib::revset::ExpressionKind;
|
||||||
|
use jj_lib::revset::RevsetDiagnostics;
|
||||||
use jj_lib::revset::RevsetExpression;
|
use jj_lib::revset::RevsetExpression;
|
||||||
use jj_lib::settings::UserSettings;
|
use jj_lib::settings::UserSettings;
|
||||||
use jj_lib::signing::SignBehavior;
|
use jj_lib::signing::SignBehavior;
|
||||||
use jj_lib::str_util::StringPattern;
|
use jj_lib::str_util::StringPattern;
|
||||||
use jj_lib::view::View;
|
use jj_lib::view::View;
|
||||||
|
use pest::Span;
|
||||||
|
|
||||||
use crate::cli_util::has_tracked_remote_bookmarks;
|
use crate::cli_util::has_tracked_remote_bookmarks;
|
||||||
use crate::cli_util::short_change_hash;
|
use crate::cli_util::short_change_hash;
|
||||||
@ -960,15 +965,25 @@ fn find_bookmarks_targeted_by_revisions<'a>(
|
|||||||
) -> Result<Vec<(&'a RefName, LocalAndRemoteRef<'a>)>, CommandError> {
|
) -> Result<Vec<(&'a RefName, LocalAndRemoteRef<'a>)>, CommandError> {
|
||||||
let mut revision_commit_ids = HashSet::new();
|
let mut revision_commit_ids = HashSet::new();
|
||||||
if use_default_revset {
|
if use_default_revset {
|
||||||
// remote_bookmarks(remote=<remote>)..@
|
// Get the default revset specified by the user.
|
||||||
let workspace_name = workspace_command.workspace_name();
|
let push_revset = workspace_command
|
||||||
let expression = RevsetExpression::remote_bookmarks(
|
.settings()
|
||||||
StringPattern::everything(),
|
.get_string("revsets.'push(remote)'")?;
|
||||||
StringPattern::exact(remote),
|
let mut context = workspace_command.env().revset_parse_context();
|
||||||
None,
|
// This just panics :-(
|
||||||
)
|
context
|
||||||
.range(&RevsetExpression::working_copy(workspace_name.to_owned()))
|
.local_variables
|
||||||
.intersection(&RevsetExpression::bookmarks(StringPattern::everything()));
|
.insert(
|
||||||
|
"remote",
|
||||||
|
ExpressionNode {
|
||||||
|
kind: ExpressionKind::String(remote.as_str().to_string()),
|
||||||
|
span: Span::new("<internal>", 0, 10).expect("programmatic span shouldn't fail"),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let mut diags = RevsetDiagnostics::default();
|
||||||
|
let (expression, _) = revset::parse_with_modifier(&mut diags, &push_revset, &context)?;
|
||||||
|
|
||||||
let mut commit_ids = workspace_command
|
let mut commit_ids = workspace_command
|
||||||
.attach_revset_evaluator(expression)
|
.attach_revset_evaluator(expression)
|
||||||
.evaluate_to_commit_ids()?
|
.evaluate_to_commit_ids()?
|
||||||
@ -976,8 +991,7 @@ fn find_bookmarks_targeted_by_revisions<'a>(
|
|||||||
if commit_ids.peek().is_none() {
|
if commit_ids.peek().is_none() {
|
||||||
writeln!(
|
writeln!(
|
||||||
ui.warning_default(),
|
ui.warning_default(),
|
||||||
"No bookmarks found in the default push revset: \
|
"No bookmarks found in the default push revset: {push_revset}{remote}",
|
||||||
remote_bookmarks(remote={remote})..@",
|
|
||||||
remote = remote.as_symbol()
|
remote = remote.as_symbol()
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
@ -586,6 +586,11 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Set of revisions to prioritize when rendering the graph for jj log",
|
"description": "Set of revisions to prioritize when rendering the graph for jj log",
|
||||||
"default": "present(@)"
|
"default": "present(@)"
|
||||||
|
},
|
||||||
|
"push": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Default set of revisions to push when no explicit revset is given for jj git push, required to take the remote as an input",
|
||||||
|
"default": "remote_bookmarks(remote=<remote>)..@"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": {
|
"additionalProperties": {
|
||||||
|
@ -12,6 +12,7 @@ log = "present(@) | ancestors(immutable_heads().., 2) | present(trunk())"
|
|||||||
# This also helps stabilize output order.
|
# This also helps stabilize output order.
|
||||||
log-graph-prioritize = "present(@)"
|
log-graph-prioritize = "present(@)"
|
||||||
sign = "reachable(@, mutable())"
|
sign = "reachable(@, mutable())"
|
||||||
|
'push(remote)' = "remote_bookmarks(remote=remote)..@"
|
||||||
|
|
||||||
[revset-aliases]
|
[revset-aliases]
|
||||||
# trunk() can be overridden as '<bookmark>@<remote>'. Use present(trunk()) if
|
# trunk() can be overridden as '<bookmark>@<remote>'. Use present(trunk()) if
|
||||||
|
@ -124,11 +124,45 @@ fn test_git_push_current_bookmark(subprocess: bool) {
|
|||||||
insta::allow_duplicates! {
|
insta::allow_duplicates! {
|
||||||
insta::assert_snapshot!(output, @r"
|
insta::assert_snapshot!(output, @r"
|
||||||
------- stderr -------
|
------- stderr -------
|
||||||
Changes to push to origin:
|
|
||||||
Move forward bookmark bookmark2 from 8476341eb395 to bc7610b65a91
|
thread 'main' panicked at cli/src/commands/git/push.rs:983:14:
|
||||||
Add bookmark my-bookmark to bc7610b65a91
|
called `Option::unwrap()` on a `None` value
|
||||||
Dry-run requested, not pushing.
|
stack backtrace:
|
||||||
|
0: rust_begin_unwind
|
||||||
|
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/panicking.rs:695:5
|
||||||
|
1: core::panicking::panic_fmt
|
||||||
|
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/core/src/panicking.rs:75:14
|
||||||
|
2: core::panicking::panic
|
||||||
|
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/core/src/panicking.rs:145:5
|
||||||
|
3: core::option::unwrap_failed
|
||||||
|
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/core/src/option.rs:2015:5
|
||||||
|
4: core::option::Option<T>::unwrap
|
||||||
|
at /home/dev/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/option.rs:978:21
|
||||||
|
5: jj_cli::commands::git::push::find_bookmarks_targeted_by_revisions
|
||||||
|
at /home/dev/src/jj/cli/src/commands/git/push.rs:974:9
|
||||||
|
6: jj_cli::commands::git::push::cmd_git_push
|
||||||
|
at /home/dev/src/jj/cli/src/commands/git/push.rs:368:34
|
||||||
|
7: jj_cli::commands::git::cmd_git
|
||||||
|
at /home/dev/src/jj/cli/src/commands/git/mod.rs:90:35
|
||||||
|
8: jj_cli::commands::run_command
|
||||||
|
at /home/dev/src/jj/cli/src/commands/mod.rs:186:31
|
||||||
|
9: core::ops::function::FnOnce::call_once
|
||||||
|
at /home/dev/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
|
||||||
|
10: core::ops::function::FnOnce::call_once{{vtable.shim}}
|
||||||
|
at /home/dev/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
|
||||||
|
11: <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once
|
||||||
|
at /home/dev/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/boxed.rs:1976:9
|
||||||
|
12: jj_cli::cli_util::CliRunner::run_internal
|
||||||
|
at /home/dev/src/jj/cli/src/cli_util.rs:3946:9
|
||||||
|
13: jj_cli::cli_util::CliRunner::run
|
||||||
|
at /home/dev/src/jj/cli/src/cli_util.rs:3959:22
|
||||||
|
14: jj::main
|
||||||
|
at /home/dev/src/jj/cli/src/main.rs:18:5
|
||||||
|
15: core::ops::function::FnOnce::call_once
|
||||||
|
at /home/dev/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
|
||||||
|
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
|
||||||
[EOF]
|
[EOF]
|
||||||
|
[exit status: 101]
|
||||||
");
|
");
|
||||||
}
|
}
|
||||||
let output = work_dir.run_jj(["git", "push", "--allow-new"]);
|
let output = work_dir.run_jj(["git", "push", "--allow-new"]);
|
||||||
@ -179,8 +213,8 @@ fn test_git_push_current_bookmark(subprocess: bool) {
|
|||||||
insta::allow_duplicates! {
|
insta::allow_duplicates! {
|
||||||
insta::assert_snapshot!(output, @r"
|
insta::assert_snapshot!(output, @r"
|
||||||
------- stderr -------
|
------- stderr -------
|
||||||
Changes to push to origin:
|
Bookmark bookmark2@origin already matches bookmark2
|
||||||
Move backward bookmark bookmark2 from bc7610b65a91 to 8476341eb395
|
Nothing changed.
|
||||||
[EOF]
|
[EOF]
|
||||||
");
|
");
|
||||||
}
|
}
|
||||||
@ -732,7 +766,6 @@ fn test_git_push_locally_created_and_rewritten(subprocess: bool) {
|
|||||||
bookmark2: rlzusymt 8476341e (empty) description 2
|
bookmark2: rlzusymt 8476341e (empty) description 2
|
||||||
@origin: rlzusymt 8476341e (empty) description 2
|
@origin: rlzusymt 8476341e (empty) description 2
|
||||||
my: vruxwmqv 423bb660 (empty) local 2
|
my: vruxwmqv 423bb660 (empty) local 2
|
||||||
@origin (ahead by 1 commits, behind by 1 commits): vruxwmqv hidden fcc99992 (empty) local 1
|
|
||||||
[EOF]
|
[EOF]
|
||||||
");
|
");
|
||||||
}
|
}
|
||||||
@ -740,9 +773,10 @@ fn test_git_push_locally_created_and_rewritten(subprocess: bool) {
|
|||||||
insta::allow_duplicates! {
|
insta::allow_duplicates! {
|
||||||
insta::assert_snapshot!(output, @r"
|
insta::assert_snapshot!(output, @r"
|
||||||
------- stderr -------
|
------- stderr -------
|
||||||
Changes to push to origin:
|
Config error: Value not found for revsets.push
|
||||||
Move sideways bookmark my from fcc999921ce9 to 423bb66069e7
|
For help, see https://jj-vcs.github.io/jj/latest/config/ or use `jj help -k config`.
|
||||||
[EOF]
|
[EOF]
|
||||||
|
[exit status: 1]
|
||||||
");
|
");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2461,13 +2495,13 @@ fn test_git_push_sign_on_push() {
|
|||||||
]);
|
]);
|
||||||
insta::assert_snapshot!(output, @r"
|
insta::assert_snapshot!(output, @r"
|
||||||
------- stderr -------
|
------- stderr -------
|
||||||
Created 1 bookmarks pointing to kpqxywon 90df08d3 bookmark3 | (empty) commit which should not be signed 1
|
Created 1 bookmarks pointing to kpqxywon e7cc0577 bookmark3 | (empty) commit which should not be signed 1
|
||||||
[EOF]
|
[EOF]
|
||||||
");
|
");
|
||||||
let output = work_dir.run_jj(["bookmark", "move", "bookmark2", "--to", "bookmark3"]);
|
let output = work_dir.run_jj(["bookmark", "move", "bookmark2", "--to", "bookmark3"]);
|
||||||
insta::assert_snapshot!(output, @r"
|
insta::assert_snapshot!(output, @r"
|
||||||
------- stderr -------
|
------- stderr -------
|
||||||
Moved 1 bookmarks to kpqxywon 90df08d3 bookmark2* bookmark3 | (empty) commit which should not be signed 1
|
Moved 1 bookmarks to kpqxywon e7cc0577 bookmark2* bookmark3 | (empty) commit which should not be signed 1
|
||||||
[EOF]
|
[EOF]
|
||||||
");
|
");
|
||||||
test_env.add_config(r#"revset-aliases."immutable_heads()" = "bookmark3""#);
|
test_env.add_config(r#"revset-aliases."immutable_heads()" = "bookmark3""#);
|
||||||
@ -2566,12 +2600,6 @@ fn test_git_push_rejected_by_remote() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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"])
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Remove with the `git.subprocess` setting.
|
// TODO: Remove with the `git.subprocess` setting.
|
||||||
#[test]
|
#[test]
|
||||||
fn test_git_push_git2_warning() {
|
fn test_git_push_git2_warning() {
|
||||||
@ -2601,3 +2629,63 @@ fn test_git_push_git2_warning() {
|
|||||||
"#);
|
"#);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_git_push_custom_revset() {
|
||||||
|
let test_env = TestEnvironment::default();
|
||||||
|
set_up(&test_env);
|
||||||
|
let work_dir = test_env.work_dir("local");
|
||||||
|
// add a custom revset which simulates ignoring a `private()` revset.
|
||||||
|
test_env.add_config(
|
||||||
|
r#"
|
||||||
|
[revsets]
|
||||||
|
'push(remote)' = "remote_bookmarks(remote=remote)..@ ~subject(glob:wip:*)"
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
work_dir
|
||||||
|
.run_jj(["new", "bookmark2", "-m", "commit to be pushed"])
|
||||||
|
.success();
|
||||||
|
work_dir.run_jj(["new", "-m", "wip: stuff"]).success();
|
||||||
|
work_dir
|
||||||
|
.run_jj(["bookmark", "set", "bookmark2", "-r@"])
|
||||||
|
.success();
|
||||||
|
work_dir
|
||||||
|
.run_jj(["new", "-m", "commit which should pushed"])
|
||||||
|
.success();
|
||||||
|
work_dir
|
||||||
|
.run_jj(["new", "-m", "wip: commit which should not be pushed"])
|
||||||
|
.success();
|
||||||
|
//
|
||||||
|
let output = work_dir.run_jj(["log"]);
|
||||||
|
insta::assert_snapshot!(output, @r"
|
||||||
|
@ kmkuslsw test.user@example.com 2001-02-03 08:05:18 94ce0267
|
||||||
|
│ (empty) wip: commit which should not be pushed
|
||||||
|
○ kpqxywon test.user@example.com 2001-02-03 08:05:17 ea115997
|
||||||
|
│ (empty) commit which should pushed
|
||||||
|
○ yostqsxw test.user@example.com 2001-02-03 08:05:15 bookmark2* b9aa5e02
|
||||||
|
│ (empty) wip: stuff
|
||||||
|
○ vruxwmqv test.user@example.com 2001-02-03 08:05:14 1eba4c0d
|
||||||
|
│ (empty) commit to be pushed
|
||||||
|
○ rlzusymt test.user@example.com 2001-02-03 08:05:10 bookmark2@origin 8476341e
|
||||||
|
│ (empty) description 2
|
||||||
|
│ ○ xtvrqkyv test.user@example.com 2001-02-03 08:05:08 bookmark1 d13ecdbd
|
||||||
|
├─╯ (empty) description 1
|
||||||
|
◆ zzzzzzzz root() 00000000
|
||||||
|
[EOF]
|
||||||
|
");
|
||||||
|
// We should try to push the first two commits but not any containing "wip:".
|
||||||
|
let output = work_dir.run_jj(["git", "push"]);
|
||||||
|
insta::assert_snapshot!(output, @r"
|
||||||
|
------- stderr -------
|
||||||
|
Config error: Value not found for revsets.push
|
||||||
|
For help, see https://jj-vcs.github.io/jj/latest/config/ or use `jj help -k config`.
|
||||||
|
[EOF]
|
||||||
|
[exit status: 1]
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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"])
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user