195 lines · 6.9 KB · Rust
RawDownload
// Copyright 2020-2023 The Jujutsu Authors//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// https://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License. mod create;mod delete;mod forget;mod list;mod r#move;mod rename;mod set;mod track;mod untrack; use itertools::Itertools as _;use jj_lib::backend::CommitId;use jj_lib::op_store::RefTarget;use jj_lib::op_store::RemoteRef;use jj_lib::ref_name::RefName;use jj_lib::ref_name::RemoteRefSymbol;use jj_lib::repo::Repo;use jj_lib::str_util::StringPattern;use jj_lib::view::View; use self::create::BookmarkCreateArgs;use self::create::cmd_bookmark_create;use self::delete::BookmarkDeleteArgs;use self::delete::cmd_bookmark_delete;use self::forget::BookmarkForgetArgs;use self::forget::cmd_bookmark_forget;use self::list::BookmarkListArgs;use self::list::cmd_bookmark_list;use self::r#move::BookmarkMoveArgs;use self::r#move::cmd_bookmark_move;use self::rename::BookmarkRenameArgs;use self::rename::cmd_bookmark_rename;use self::set::BookmarkSetArgs;use self::set::cmd_bookmark_set;use self::track::BookmarkTrackArgs;use self::track::cmd_bookmark_track;use self::untrack::BookmarkUntrackArgs;use self::untrack::cmd_bookmark_untrack;use crate::cli_util::CommandHelper;use crate::cli_util::RemoteBookmarkNamePattern;use crate::command_error::CommandError;use crate::command_error::user_error;use crate::ui::Ui; // Unlike most other aliases, `b` is defined in the config and can be overridden// by the user. /// Manage bookmarks [default alias: b]////// See [`jj help -k bookmarks`] for more information.////// [`jj help -k bookmarks`]:///     https://jj-vcs.github.io/jj/latest/bookmarks#[derive(clap::Subcommand, Clone, Debug)]pub enum BookmarkCommand {    #[command(visible_alias("c"))]    Create(BookmarkCreateArgs),    #[command(visible_alias("d"))]    Delete(BookmarkDeleteArgs),    #[command(visible_alias("f"))]    Forget(BookmarkForgetArgs),    #[command(visible_alias("l"))]    List(BookmarkListArgs),    #[command(visible_alias("m"))]    Move(BookmarkMoveArgs),    #[command(visible_alias("r"))]    Rename(BookmarkRenameArgs),    #[command(visible_alias("s"))]    Set(BookmarkSetArgs),    #[command(visible_alias("t"))]    Track(BookmarkTrackArgs),    Untrack(BookmarkUntrackArgs),} pub fn cmd_bookmark(    ui: &mut Ui,    command: &CommandHelper,    subcommand: &BookmarkCommand,) -> Result<(), CommandError> {    match subcommand {        BookmarkCommand::Create(args) => cmd_bookmark_create(ui, command, args),        BookmarkCommand::Delete(args) => cmd_bookmark_delete(ui, command, args),        BookmarkCommand::Forget(args) => cmd_bookmark_forget(ui, command, args),        BookmarkCommand::List(args) => cmd_bookmark_list(ui, command, args),        BookmarkCommand::Move(args) => cmd_bookmark_move(ui, command, args),        BookmarkCommand::Rename(args) => cmd_bookmark_rename(ui, command, args),        BookmarkCommand::Set(args) => cmd_bookmark_set(ui, command, args),        BookmarkCommand::Track(args) => cmd_bookmark_track(ui, command, args),        BookmarkCommand::Untrack(args) => cmd_bookmark_untrack(ui, command, args),    }} fn find_local_bookmarks<'a>(    view: &'a View,    name_patterns: &[StringPattern],) -> Result<Vec<(&'a RefName, &'a RefTarget)>, CommandError> {    find_bookmarks_with(name_patterns, |pattern| {        view.local_bookmarks_matching(pattern).map(Ok)    })} fn find_bookmarks_with<'a, 'b, V, I>(    name_patterns: &'b [StringPattern],    mut find_matches: impl FnMut(&'b StringPattern) -> I,) -> Result<Vec<(&'a RefName, V)>, CommandError>where    I: Iterator<Item = Result<(&'a RefName, V), CommandError>>,{    let mut matching_bookmarks: Vec<(&'a RefName, V)> = vec![];    let mut unmatched_patterns = vec![];    for pattern in name_patterns {        let mut matches = find_matches(pattern).peekable();        if matches.peek().is_none() {            unmatched_patterns.push(pattern);        }        matches.process_results(|iter| matching_bookmarks.extend(iter))?;    }    match &unmatched_patterns[..] {        [] => {            matching_bookmarks.sort_unstable_by_key(|(name, _)| *name);            matching_bookmarks.dedup_by_key(|(name, _)| *name);            Ok(matching_bookmarks)        }        [pattern] if pattern.is_exact() => Err(user_error(format!("No such bookmark: {pattern}"))),        patterns => Err(user_error(format!(            "No matching bookmarks for patterns: {}",            patterns.iter().join(", ")        ))),    }} fn find_trackable_remote_bookmarks<'a>(    view: &'a View,    name_patterns: &[RemoteBookmarkNamePattern],) -> Result<Vec<(RemoteRefSymbol<'a>, &'a RemoteRef)>, CommandError> {    let mut matching_bookmarks = vec![];    let mut unmatched_patterns = vec![];    for pattern in name_patterns {        let present_or_tracked_matches =            view.remote_bookmarks_matching(&pattern.bookmark, &pattern.remote);        let absent_matches =            view.remote_views_matching(&pattern.remote)                .flat_map(|(remote, remote_view)| {                    view.local_bookmarks_matching(&pattern.bookmark)                        .filter(|&(name, _)| !remote_view.bookmarks.contains_key(name))                        .map(|(name, _)| (name.to_remote_symbol(remote), RemoteRef::absent_ref()))                });        let mut matches = itertools::chain(present_or_tracked_matches, absent_matches).peekable();        if matches.peek().is_none() {            unmatched_patterns.push(pattern);        }        matching_bookmarks.extend(matches);    }    match &unmatched_patterns[..] {        [] => {            matching_bookmarks.sort_unstable_by(|(sym1, _), (sym2, _)| sym1.cmp(sym2));            matching_bookmarks.dedup_by(|(sym1, _), (sym2, _)| sym1 == sym2);            Ok(matching_bookmarks)        }        [pattern] if pattern.is_exact() => {            Err(user_error(format!("No such remote bookmark: {pattern}")))        }        patterns => Err(user_error(format!(            "No matching remote bookmarks for patterns: {}",            patterns.iter().join(", ")        ))),    }} fn is_fast_forward(repo: &dyn Repo, old_target: &RefTarget, new_target_id: &CommitId) -> bool {    if old_target.is_present() {        // Strictly speaking, "all" old targets should be ancestors, but we allow        // conflict resolution by setting bookmark to "any" of the old target        // descendants.        old_target            .added_ids()            .any(|old| repo.index().is_ancestor(old, new_target_id))    } else {        true    }}