754 lines · 27.6 KB · Rust
RawDownload
// Copyright 2020 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. #![expect(missing_docs)] use std::collections::BTreeMap;use std::collections::HashSet; use itertools::Itertools as _;use thiserror::Error; use crate::backend::CommitId;use crate::op_store;use crate::op_store::LocalRemoteRefTarget;use crate::op_store::RefTarget;use crate::op_store::RefTargetOptionExt as _;use crate::op_store::RemoteRef;use crate::op_store::RemoteView;use crate::ref_name::GitRefName;use crate::ref_name::GitRefNameBuf;use crate::ref_name::RefName;use crate::ref_name::RemoteName;use crate::ref_name::RemoteRefSymbol;use crate::ref_name::WorkspaceName;use crate::ref_name::WorkspaceNameBuf;use crate::refs;use crate::refs::LocalAndRemoteRef;use crate::str_util::StringMatcher; /// A wrapper around [`op_store::View`] that defines additional methods.#[derive(PartialEq, Eq, Debug, Clone)]pub struct View {    data: op_store::View,} impl View {    pub fn new(op_store_view: op_store::View) -> Self {        Self {            data: op_store_view,        }    }     pub fn wc_commit_ids(&self) -> &BTreeMap<WorkspaceNameBuf, CommitId> {        &self.data.wc_commit_ids    }     pub fn get_wc_commit_id(&self, name: &WorkspaceName) -> Option<&CommitId> {        self.data.wc_commit_ids.get(name)    }     pub fn workspaces_for_wc_commit_id(&self, commit_id: &CommitId) -> Vec<WorkspaceNameBuf> {        let mut workspace_names = vec![];        for (name, wc_commit_id) in &self.data.wc_commit_ids {            if wc_commit_id == commit_id {                workspace_names.push(name.clone());            }        }        workspace_names    }     pub fn is_wc_commit_id(&self, commit_id: &CommitId) -> bool {        self.data.wc_commit_ids.values().contains(commit_id)    }     pub fn heads(&self) -> &HashSet<CommitId> {        &self.data.head_ids    }     /// Iterates pair of local and remote bookmarks by bookmark name.    pub fn bookmarks(&self) -> impl Iterator<Item = (&RefName, LocalRemoteRefTarget<'_>)> {        op_store::merge_join_ref_views(            &self.data.local_bookmarks,            &self.data.remote_views,            |view| &view.bookmarks,        )    }     /// Iterates pair of local and remote tags by tag name.    pub fn tags(&self) -> impl Iterator<Item = (&RefName, LocalRemoteRefTarget<'_>)> {        op_store::merge_join_ref_views(&self.data.local_tags, &self.data.remote_views, |view| {            &view.tags        })    }     pub fn git_refs(&self) -> &BTreeMap<GitRefNameBuf, RefTarget> {        &self.data.git_refs    }     pub fn git_head(&self) -> &RefTarget {        &self.data.git_head    }     /// Returns the Git HEAD target for the given workspace, or absent if not    /// tracked.    pub fn get_workspace_git_head(&self, name: &WorkspaceName) -> &RefTarget {        self.data.workspace_git_heads.get(name).flatten()    }     /// Returns all per-workspace Git HEAD targets.    pub fn workspace_git_heads(&self) -> &BTreeMap<WorkspaceNameBuf, RefTarget> {        &self.data.workspace_git_heads    }     pub fn set_wc_commit(&mut self, name: WorkspaceNameBuf, commit_id: CommitId) {        self.data.wc_commit_ids.insert(name, commit_id);    }     pub fn remove_wc_commit(&mut self, name: &WorkspaceName) {        self.data.wc_commit_ids.remove(name);    }     pub fn rename_workspace(        &mut self,        old_name: &WorkspaceName,        new_name: WorkspaceNameBuf,    ) -> Result<(), RenameWorkspaceError> {        if self.data.wc_commit_ids.contains_key(&new_name) {            return Err(RenameWorkspaceError::WorkspaceAlreadyExists {                name: new_name.clone(),            });        }        let wc_commit_id = self.data.wc_commit_ids.remove(old_name).ok_or_else(|| {            RenameWorkspaceError::WorkspaceDoesNotExist {                name: old_name.to_owned(),            }        })?;        self.data.wc_commit_ids.insert(new_name, wc_commit_id);        Ok(())    }     pub fn add_head(&mut self, head_id: &CommitId) {        self.data.head_ids.insert(head_id.clone());    }     pub fn remove_head(&mut self, head_id: &CommitId) {        self.data.head_ids.remove(head_id);    }     /// Iterates local bookmark `(name, target)`s in lexicographical order.    pub fn local_bookmarks(&self) -> impl Iterator<Item = (&RefName, &RefTarget)> {        self.data            .local_bookmarks            .iter()            .map(|(name, target)| (name.as_ref(), target))    }     /// Iterates local bookmarks `(name, target)` in lexicographical order where    /// the target adds `commit_id`.    pub fn local_bookmarks_for_commit(        &self,        commit_id: &CommitId,    ) -> impl Iterator<Item = (&RefName, &RefTarget)> {        self.local_bookmarks()            .filter(|(_, target)| target.added_ids().contains(commit_id))    }     /// Iterates local bookmark `(name, target)`s matching the given pattern.    /// Entries are sorted by `name`.    pub fn local_bookmarks_matching(        &self,        matcher: &StringMatcher,    ) -> impl Iterator<Item = (&RefName, &RefTarget)> {        matcher            .filter_btree_map_as_deref(&self.data.local_bookmarks)            .map(|(name, target)| (name.as_ref(), target))    }     pub fn get_local_bookmark(&self, name: &RefName) -> &RefTarget {        self.data.local_bookmarks.get(name).flatten()    }     /// Sets local bookmark to point to the given target. If the target is    /// absent, the local bookmark will be removed. If there are absent remote    /// bookmarks tracked by the newly-absent local bookmark, they will also be    /// removed.    pub fn set_local_bookmark_target(&mut self, name: &RefName, target: RefTarget) {        if target.is_present() {            self.data.local_bookmarks.insert(name.to_owned(), target);        } else {            self.data.local_bookmarks.remove(name);            for remote_view in self.data.remote_views.values_mut() {                let remote_refs = &mut remote_view.bookmarks;                if remote_refs.get(name).is_some_and(RemoteRef::is_absent) {                    remote_refs.remove(name);                }            }        }    }     /// Iterates over `(symbol, remote_ref)` for all remote bookmarks in    /// lexicographical order.    pub fn all_remote_bookmarks(&self) -> impl Iterator<Item = (RemoteRefSymbol<'_>, &RemoteRef)> {        op_store::flatten_remote_refs(&self.data.remote_views, |view| &view.bookmarks)    }     /// Iterates over `(name, remote_ref)`s for all remote bookmarks of the    /// specified remote in lexicographical order.    pub fn remote_bookmarks(        &self,        remote_name: &RemoteName,    ) -> impl Iterator<Item = (&RefName, &RemoteRef)> + use<'_> {        let maybe_remote_view = self.data.remote_views.get(remote_name);        maybe_remote_view            .map(|remote_view| {                remote_view                    .bookmarks                    .iter()                    .map(|(name, remote_ref)| (name.as_ref(), remote_ref))            })            .into_iter()            .flatten()    }     /// Iterates over `(symbol, remote_ref)`s for all remote bookmarks of the    /// specified remote that match the given pattern.    ///    /// Entries are sorted by `symbol`, which is `(name, remote)`.    pub fn remote_bookmarks_matching(        &self,        bookmark_matcher: &StringMatcher,        remote_matcher: &StringMatcher,    ) -> impl Iterator<Item = (RemoteRefSymbol<'_>, &RemoteRef)> {        // Use kmerge instead of flat_map for consistency with all_remote_bookmarks().        remote_matcher            .filter_btree_map_as_deref(&self.data.remote_views)            .map(|(remote, remote_view)| {                bookmark_matcher                    .filter_btree_map_as_deref(&remote_view.bookmarks)                    .map(|(name, remote_ref)| (name.to_remote_symbol(remote), remote_ref))            })            .kmerge_by(|(symbol1, _), (symbol2, _)| symbol1 < symbol2)    }     pub fn get_remote_bookmark(&self, symbol: RemoteRefSymbol<'_>) -> &RemoteRef {        if let Some(remote_view) = self.data.remote_views.get(symbol.remote) {            remote_view.bookmarks.get(symbol.name).flatten()        } else {            RemoteRef::absent_ref()        }    }     /// Sets remote-tracking bookmark to the given target and state. If the    /// target is absent and if no tracking local bookmark exists, the bookmark    /// will be removed.    pub fn set_remote_bookmark(&mut self, symbol: RemoteRefSymbol<'_>, remote_ref: RemoteRef) {        if remote_ref.is_present()            || (remote_ref.is_tracked() && self.get_local_bookmark(symbol.name).is_present())        {            let remote_view = self                .data                .remote_views                .entry(symbol.remote.to_owned())                .or_default();            remote_view                .bookmarks                .insert(symbol.name.to_owned(), remote_ref);        } else if let Some(remote_view) = self.data.remote_views.get_mut(symbol.remote) {            remote_view.bookmarks.remove(symbol.name);        }    }     /// Iterates over `(name, {local_ref, remote_ref})`s for every bookmark    /// present locally and/or on the specified remote, in lexicographical    /// order.    ///    /// Note that this does *not* take into account whether the local bookmark    /// tracks the remote bookmark or not. Missing values are represented as    /// RefTarget::absent_ref() or RemoteRef::absent_ref().    pub fn local_remote_bookmarks(        &self,        remote_name: &RemoteName,    ) -> impl Iterator<Item = (&RefName, LocalAndRemoteRef<'_>)> + use<'_> {        refs::iter_named_local_remote_refs(            self.local_bookmarks(),            self.remote_bookmarks(remote_name),        )        .map(|(name, (local_target, remote_ref))| {            let targets = LocalAndRemoteRef {                local_target,                remote_ref,            };            (name, targets)        })    }     /// Iterates over `(name, TrackingRefPair {local_ref, remote_ref})`s for    /// every bookmark with a name that matches the given pattern, and that is    /// present locally and/or on the specified remote.    ///    /// Entries are sorted by `name`.    ///    /// Note that this does *not* take into account whether the local bookmark    /// tracks the remote bookmark or not. Missing values are represented as    /// RefTarget::absent_ref() or RemoteRef::absent_ref().    pub fn local_remote_bookmarks_matching<'a, 'b>(        &'a self,        bookmark_matcher: &'b StringMatcher,        remote_name: &RemoteName,    ) -> impl Iterator<Item = (&'a RefName, LocalAndRemoteRef<'a>)> + use<'a, 'b> {        // Change remote_name to StringMatcher if needed, but merge-join adapter won't        // be usable.        let maybe_remote_view = self.data.remote_views.get(remote_name);        refs::iter_named_local_remote_refs(            bookmark_matcher.filter_btree_map_as_deref(&self.data.local_bookmarks),            maybe_remote_view                .map(|remote_view| {                    bookmark_matcher.filter_btree_map_as_deref(&remote_view.bookmarks)                })                .into_iter()                .flatten(),        )        .map(|(name, (local_target, remote_ref))| {            let targets = LocalAndRemoteRef {                local_target,                remote_ref,            };            (name.as_ref(), targets)        })    }     /// Iterates remote `(name, view)`s in lexicographical order.    pub fn remote_views(&self) -> impl Iterator<Item = (&RemoteName, &RemoteView)> {        self.data            .remote_views            .iter()            .map(|(name, view)| (name.as_ref(), view))    }     /// Iterates matching remote `(name, view)`s in lexicographical order.    pub fn remote_views_matching(        &self,        matcher: &StringMatcher,    ) -> impl Iterator<Item = (&RemoteName, &RemoteView)> {        matcher            .filter_btree_map_as_deref(&self.data.remote_views)            .map(|(name, view)| (name.as_ref(), view))    }     /// Returns the remote view for `name`.    pub fn get_remote_view(&self, name: &RemoteName) -> Option<&RemoteView> {        self.data.remote_views.get(name)    }     /// Adds remote view if it doesn't exist.    pub fn ensure_remote(&mut self, remote_name: &RemoteName) {        if self.data.remote_views.contains_key(remote_name) {            return;        }        self.data            .remote_views            .insert(remote_name.to_owned(), RemoteView::default());    }     pub fn remove_remote(&mut self, remote_name: &RemoteName) {        self.data.remote_views.remove(remote_name);    }     pub fn rename_remote(&mut self, old: &RemoteName, new: &RemoteName) {        if let Some(remote_view) = self.data.remote_views.remove(old) {            self.data.remote_views.insert(new.to_owned(), remote_view);        }    }     /// Iterates local tag `(name, target)`s in lexicographical order.    pub fn local_tags(&self) -> impl Iterator<Item = (&RefName, &RefTarget)> {        self.data            .local_tags            .iter()            .map(|(name, target)| (name.as_ref(), target))    }     pub fn get_local_tag(&self, name: &RefName) -> &RefTarget {        self.data.local_tags.get(name).flatten()    }     /// Iterates local tag `(name, target)`s matching the given pattern. Entries    /// are sorted by `name`.    pub fn local_tags_matching(        &self,        matcher: &StringMatcher,    ) -> impl Iterator<Item = (&RefName, &RefTarget)> {        matcher            .filter_btree_map_as_deref(&self.data.local_tags)            .map(|(name, target)| (name.as_ref(), target))    }     /// Sets local tag to point to the given target. If the target is absent,    /// the local tag will be removed. If there are absent remote tags tracked    /// by the newly-absent local tag, they will also be removed.    pub fn set_local_tag_target(&mut self, name: &RefName, target: RefTarget) {        if target.is_present() {            self.data.local_tags.insert(name.to_owned(), target);        } else {            self.data.local_tags.remove(name);            for remote_view in self.data.remote_views.values_mut() {                let remote_refs = &mut remote_view.tags;                if remote_refs.get(name).is_some_and(RemoteRef::is_absent) {                    remote_refs.remove(name);                }            }        }    }     /// Iterates over `(symbol, remote_ref)` for all remote tags in    /// lexicographical order.    pub fn all_remote_tags(&self) -> impl Iterator<Item = (RemoteRefSymbol<'_>, &RemoteRef)> {        op_store::flatten_remote_refs(&self.data.remote_views, |view| &view.tags)    }     /// Iterates over `(name, remote_ref)`s for all remote tags of the specified    /// remote in lexicographical order.    pub fn remote_tags(        &self,        remote_name: &RemoteName,    ) -> impl Iterator<Item = (&RefName, &RemoteRef)> + use<'_> {        let maybe_remote_view = self.data.remote_views.get(remote_name);        maybe_remote_view            .map(|remote_view| {                remote_view                    .tags                    .iter()                    .map(|(name, remote_ref)| (name.as_ref(), remote_ref))            })            .into_iter()            .flatten()    }     /// Iterates over `(symbol, remote_ref)`s for all remote tags of the    /// specified remote that match the given pattern.    ///    /// Entries are sorted by `symbol`, which is `(name, remote)`.    pub fn remote_tags_matching(        &self,        tag_matcher: &StringMatcher,        remote_matcher: &StringMatcher,    ) -> impl Iterator<Item = (RemoteRefSymbol<'_>, &RemoteRef)> {        // Use kmerge instead of flat_map for consistency with all_remote_tags().        remote_matcher            .filter_btree_map_as_deref(&self.data.remote_views)            .map(|(remote, remote_view)| {                tag_matcher                    .filter_btree_map_as_deref(&remote_view.tags)                    .map(|(name, remote_ref)| (name.to_remote_symbol(remote), remote_ref))            })            .kmerge_by(|(symbol1, _), (symbol2, _)| symbol1 < symbol2)    }     /// Returns remote-tracking tag target and state specified by `symbol`.    pub fn get_remote_tag(&self, symbol: RemoteRefSymbol<'_>) -> &RemoteRef {        if let Some(remote_view) = self.data.remote_views.get(symbol.remote) {            remote_view.tags.get(symbol.name).flatten()        } else {            RemoteRef::absent_ref()        }    }     /// Sets remote-tracking tag to the given target and state. If the target is    /// absent and if no tracking local tag exists, the tag will be removed.    pub fn set_remote_tag(&mut self, symbol: RemoteRefSymbol<'_>, remote_ref: RemoteRef) {        if remote_ref.is_present()            || (remote_ref.is_tracked() && self.get_local_tag(symbol.name).is_present())        {            let remote_view = self                .data                .remote_views                .entry(symbol.remote.to_owned())                .or_default();            remote_view.tags.insert(symbol.name.to_owned(), remote_ref);        } else if let Some(remote_view) = self.data.remote_views.get_mut(symbol.remote) {            remote_view.tags.remove(symbol.name);        }    }     /// Iterates over `(name, {local_ref, remote_ref})`s for every tag present    /// locally and/or on the specified remote, in lexicographical order.    ///    /// Note that this does *not* take into account whether the local tag tracks    /// the remote tag or not. Missing values are represented as    /// [`RefTarget::absent_ref()`] or [`RemoteRef::absent_ref()`].    pub fn local_remote_tags(        &self,        remote_name: &RemoteName,    ) -> impl Iterator<Item = (&RefName, LocalAndRemoteRef<'_>)> + use<'_> {        refs::iter_named_local_remote_refs(self.local_tags(), self.remote_tags(remote_name)).map(            |(name, (local_target, remote_ref))| {                let targets = LocalAndRemoteRef {                    local_target,                    remote_ref,                };

Lines 1–500 of 754.