jj_cli/commands/bookmark/
untrack.rs1use clap_complete::ArgValueCandidates;
16use itertools::Itertools as _;
17use jj_lib::repo::Repo as _;
18use jj_lib::str_util::StringExpression;
19
20use super::find_trackable_remote_bookmarks;
21use super::trackable_remote_bookmarks_matching;
22use super::warn_unmatched_local_or_remote_bookmarks;
23use super::warn_unmatched_remotes;
24use crate::cli_util::CommandHelper;
25use crate::cli_util::RemoteBookmarkNamePattern;
26use crate::cli_util::default_ignored_remote_name;
27use crate::command_error::CommandError;
28use crate::command_error::cli_error;
29use crate::complete;
30use crate::revset_util::parse_union_name_patterns;
31use crate::ui::Ui;
32
33#[derive(clap::Args, Clone, Debug)]
41pub struct BookmarkUntrackArgs {
42 #[arg(required = true, value_name = "BOOKMARK")]
50 #[arg(add = ArgValueCandidates::new(complete::tracked_bookmarks))]
51 names: Vec<String>,
52
53 #[arg(long = "remote", value_name = "REMOTE")]
64 #[arg(add = ArgValueCandidates::new(complete::git_remotes))]
66 remotes: Option<Vec<String>>,
67}
68
69pub async fn cmd_bookmark_untrack(
70 ui: &mut Ui,
71 command: &CommandHelper,
72 args: &BookmarkUntrackArgs,
73) -> Result<(), CommandError> {
74 let mut workspace_command = command.workspace_helper(ui)?;
75 let repo = workspace_command.repo().clone();
76 let view = repo.view();
77 let ignored_remote = default_ignored_remote_name(repo.store())
78 .filter(|name| view.get_remote_view(name).is_some());
80 let matched_refs = if args.remotes.is_none() && args.names.iter().all(|s| s.contains('@')) {
81 writeln!(
83 ui.warning_default(),
84 "<bookmark>@<remote> syntax is deprecated, use `<bookmark> --remote=<remote>` instead."
85 )?;
86 let name_patterns: Vec<RemoteBookmarkNamePattern> = args
87 .names
88 .iter()
89 .map(|s| s.parse())
90 .try_collect()
91 .map_err(cli_error)?;
92 find_trackable_remote_bookmarks(ui, view, &name_patterns)?
93 } else {
94 let bookmark_expr = parse_union_name_patterns(ui, &args.names)?;
95 let remote_expr = match (&args.remotes, ignored_remote) {
96 (Some(text), _) => parse_union_name_patterns(ui, text)?,
97 (None, Some(ignored)) => StringExpression::exact(ignored).negated(),
98 (None, None) => StringExpression::all(),
99 };
100 let bookmark_matcher = bookmark_expr.to_matcher();
101 let remote_matcher = remote_expr.to_matcher();
102 let matched_refs =
103 trackable_remote_bookmarks_matching(view, &bookmark_matcher, &remote_matcher).collect();
104 warn_unmatched_local_or_remote_bookmarks(ui, view, &bookmark_expr)?;
105 warn_unmatched_remotes(ui, view, &remote_expr)?;
106 matched_refs
107 };
108 let mut symbols = Vec::new();
109 for (symbol, remote_ref) in matched_refs {
110 if ignored_remote.is_some_and(|ignored| symbol.remote == ignored) {
111 writeln!(
114 ui.warning_default(),
115 "Git-tracking bookmark cannot be untracked: {symbol}"
116 )?;
117 } else if !remote_ref.is_tracked() {
118 writeln!(
119 ui.warning_default(),
120 "Remote bookmark not tracked yet: {symbol}"
121 )?;
122 } else {
123 symbols.push(symbol);
124 }
125 }
126 let mut tx = workspace_command.start_transaction();
127 for &symbol in &symbols {
128 tx.repo_mut().untrack_remote_bookmark(symbol);
129 }
130 if !symbols.is_empty() {
131 writeln!(
132 ui.status(),
133 "Stopped tracking {} remote bookmarks.",
134 symbols.len()
135 )?;
136 }
137 tx.finish(
138 ui,
139 format!("untrack remote bookmark {}", symbols.iter().join(", ")),
140 )?;
141 Ok(())
142}