Martin von Zweigbergkiter_util: make all fallible_*() async2daed5c3 months agoHistory
150 lines · 5.5 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. use std::collections::HashSet; use clap_complete::ArgValueCandidates;use clap_complete::ArgValueCompleter;use itertools::Itertools as _;use jj_lib::object_id::ObjectId as _;use jj_lib::op_store::RefTarget;use jj_lib::ref_name::RefNameBuf; use super::is_fast_forward;use crate::cli_util::CommandHelper;use crate::cli_util::RevisionArg;use crate::cli_util::has_tracked_remote_bookmarks;use crate::command_error::CommandError;use crate::command_error::user_error;use crate::complete;use crate::revset_util;use crate::ui::Ui; /// Create a new bookmark, or update an existing one by name////// If you want to move bookmarks based on their current location rather than/// by name, use `jj bookmark move --from <REVSETS>`.#[derive(clap::Args, Clone, Debug)]pub struct BookmarkSetArgs {    /// The bookmark's target revision    #[arg(        long,        short,        default_value = "@",        visible_alias = "to",        value_name = "REVSET"    )]    #[arg(add = ArgValueCompleter::new(complete::revset_expression_all))]    revision: RevisionArg,     /// Allow moving the bookmark backwards or sideways    #[arg(long, short = 'B')]    allow_backwards: bool,     /// The bookmarks to update    #[arg(required = true, value_parser = revset_util::parse_bookmark_name)]    #[arg(add = ArgValueCandidates::new(complete::local_bookmarks))]    names: Vec<RefNameBuf>,} pub async fn cmd_bookmark_set(    ui: &mut Ui,    command: &CommandHelper,    args: &BookmarkSetArgs,) -> Result<(), CommandError> {    let mut workspace_command = command.workspace_helper(ui).await?;    let target_commit = workspace_command        .resolve_single_rev(ui, &args.revision)        .await?;    let repo = workspace_command.repo().as_ref();    let bookmark_names = &args.names;    let mut new_bookmarks = HashSet::new();    let mut moved_bookmark_count = 0;    for name in bookmark_names {        let old_target = repo.view().get_local_bookmark(name);        // If a bookmark is absent locally but is still tracking remote bookmarks,        // we are resurrecting the local bookmark, not "creating" a new bookmark.        if old_target.is_absent() && !has_tracked_remote_bookmarks(repo, name) {            new_bookmarks.insert(name);        } else if old_target.as_normal() != Some(target_commit.id()) {            moved_bookmark_count += 1;        }        if !args.allow_backwards && !is_fast_forward(repo, old_target, target_commit.id()).await? {            return Err(user_error(format!(                "Refusing to move bookmark backwards or sideways: {name}",                name = name.as_symbol()            ))            .hinted("Use --allow-backwards to allow it."));        }    }    if target_commit.is_discardable(repo).await? {        writeln!(ui.warning_default(), "Target revision is empty.")?;    }     let mut tx = workspace_command.start_transaction();    let remote_settings = tx.settings().remote_settings()?;    let remote_auto_track_matchers =        revset_util::parse_remote_auto_track_bookmarks_map_for_new_bookmarks(ui, &remote_settings)?;    let readonly_repo = tx.base_repo().clone();    for name in bookmark_names {        tx.repo_mut()            .set_local_bookmark_target(name, RefTarget::normal(target_commit.id().clone()));        if new_bookmarks.contains(name) {            for (remote_name, matcher) in &remote_auto_track_matchers {                if !matcher.is_match(name.as_str()) {                    continue;                }                let Some(view) = readonly_repo.view().get_remote_view(remote_name) else {                    continue;                };                let symbol = name.to_remote_symbol(remote_name);                if view.bookmarks.contains_key(name) {                    writeln!(                        ui.warning_default(),                        "Auto-tracking bookmark that exists on the remote: {symbol}"                    )?;                }                tx.repo_mut().track_remote_bookmark(symbol).await?;            }        }    }     if let Some(mut formatter) = ui.status_formatter() {        let new_bookmark_count = new_bookmarks.len();        if new_bookmark_count > 0 {            write!(                formatter,                "Created {new_bookmark_count} bookmarks pointing to "            )?;            tx.write_commit_summary(formatter.as_mut(), &target_commit)?;            writeln!(formatter)?;        }        if moved_bookmark_count > 0 {            write!(formatter, "Moved {moved_bookmark_count} bookmarks to ")?;            tx.write_commit_summary(formatter.as_mut(), &target_commit)?;            writeln!(formatter)?;        }    }     tx.finish(        ui,        format!(            "point bookmark {names} to commit {id}",            names = bookmark_names.iter().map(|n| n.as_symbol()).join(", "),            id = target_commit.id().hex()        ),    )    .await?;    Ok(())}