jujutsu_lib/
view.rs

1// Copyright 2020 The Jujutsu Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::collections::{BTreeMap, HashMap, HashSet};
16
17use itertools::Itertools;
18
19use crate::backend::CommitId;
20use crate::index::Index;
21use crate::op_store;
22use crate::op_store::{BranchTarget, RefTarget, WorkspaceId};
23use crate::refs::merge_ref_targets;
24
25#[derive(PartialEq, Eq, Clone, Hash, Debug)]
26pub enum RefName {
27    LocalBranch(String),
28    RemoteBranch { branch: String, remote: String },
29    Tag(String),
30    GitRef(String),
31}
32
33#[derive(PartialEq, Eq, Debug, Clone)]
34pub struct View {
35    data: op_store::View,
36}
37
38impl View {
39    pub fn new(op_store_view: op_store::View) -> Self {
40        View {
41            data: op_store_view,
42        }
43    }
44
45    pub fn wc_commit_ids(&self) -> &HashMap<WorkspaceId, CommitId> {
46        &self.data.wc_commit_ids
47    }
48
49    pub fn get_wc_commit_id(&self, workspace_id: &WorkspaceId) -> Option<&CommitId> {
50        self.data.wc_commit_ids.get(workspace_id)
51    }
52
53    pub fn workspaces_for_wc_commit_id(&self, commit_id: &CommitId) -> Vec<WorkspaceId> {
54        let mut workspaces_ids = vec![];
55        for (workspace_id, wc_commit_id) in &self.data.wc_commit_ids {
56            if wc_commit_id == commit_id {
57                workspaces_ids.push(workspace_id.clone());
58            }
59        }
60        workspaces_ids
61    }
62
63    pub fn is_wc_commit_id(&self, commit_id: &CommitId) -> bool {
64        self.data.wc_commit_ids.values().contains(commit_id)
65    }
66
67    pub fn heads(&self) -> &HashSet<CommitId> {
68        &self.data.head_ids
69    }
70
71    pub fn public_heads(&self) -> &HashSet<CommitId> {
72        &self.data.public_head_ids
73    }
74
75    pub fn branches(&self) -> &BTreeMap<String, BranchTarget> {
76        &self.data.branches
77    }
78
79    pub fn tags(&self) -> &BTreeMap<String, RefTarget> {
80        &self.data.tags
81    }
82
83    pub fn git_refs(&self) -> &BTreeMap<String, RefTarget> {
84        &self.data.git_refs
85    }
86
87    pub fn git_head(&self) -> Option<&RefTarget> {
88        self.data.git_head.as_ref()
89    }
90
91    pub fn set_wc_commit(&mut self, workspace_id: WorkspaceId, commit_id: CommitId) {
92        self.data.wc_commit_ids.insert(workspace_id, commit_id);
93    }
94
95    pub fn remove_wc_commit(&mut self, workspace_id: &WorkspaceId) {
96        self.data.wc_commit_ids.remove(workspace_id);
97    }
98
99    pub fn add_head(&mut self, head_id: &CommitId) {
100        self.data.head_ids.insert(head_id.clone());
101    }
102
103    pub fn remove_head(&mut self, head_id: &CommitId) {
104        self.data.head_ids.remove(head_id);
105    }
106
107    pub fn add_public_head(&mut self, head_id: &CommitId) {
108        self.data.public_head_ids.insert(head_id.clone());
109    }
110
111    pub fn remove_public_head(&mut self, head_id: &CommitId) {
112        self.data.public_head_ids.remove(head_id);
113    }
114
115    pub fn get_ref(&self, name: &RefName) -> Option<RefTarget> {
116        match &name {
117            RefName::LocalBranch(name) => self.get_local_branch(name),
118            RefName::RemoteBranch { branch, remote } => self.get_remote_branch(branch, remote),
119            RefName::Tag(name) => self.get_tag(name),
120            RefName::GitRef(name) => self.git_refs().get(name).cloned(),
121        }
122    }
123
124    pub fn set_or_remove_ref(&mut self, name: RefName, target: Option<RefTarget>) {
125        if let Some(target) = target {
126            match name {
127                RefName::LocalBranch(name) => {
128                    self.set_local_branch(name, target);
129                }
130                RefName::RemoteBranch { branch, remote } => {
131                    self.set_remote_branch(branch, remote, target);
132                }
133                RefName::Tag(name) => {
134                    self.set_tag(name, target);
135                }
136                RefName::GitRef(name) => {
137                    self.set_git_ref(name, target);
138                }
139            }
140        } else {
141            match name {
142                RefName::LocalBranch(name) => {
143                    self.remove_local_branch(&name);
144                }
145                RefName::RemoteBranch { branch, remote } => {
146                    self.remove_remote_branch(&branch, &remote);
147                }
148                RefName::Tag(name) => {
149                    self.remove_tag(&name);
150                }
151                RefName::GitRef(name) => {
152                    self.remove_git_ref(&name);
153                }
154            }
155        }
156    }
157
158    pub fn get_branch(&self, name: &str) -> Option<&BranchTarget> {
159        self.data.branches.get(name)
160    }
161
162    pub fn set_branch(&mut self, name: String, target: BranchTarget) {
163        self.data.branches.insert(name, target);
164    }
165
166    pub fn remove_branch(&mut self, name: &str) {
167        self.data.branches.remove(name);
168    }
169
170    pub fn get_local_branch(&self, name: &str) -> Option<RefTarget> {
171        self.data
172            .branches
173            .get(name)
174            .and_then(|branch_target| branch_target.local_target.clone())
175    }
176
177    pub fn set_local_branch(&mut self, name: String, target: RefTarget) {
178        self.data.branches.entry(name).or_default().local_target = Some(target);
179    }
180
181    pub fn remove_local_branch(&mut self, name: &str) {
182        if let Some(branch) = self.data.branches.get_mut(name) {
183            branch.local_target = None;
184            if branch.remote_targets.is_empty() {
185                self.remove_branch(name);
186            }
187        }
188    }
189
190    pub fn get_remote_branch(&self, name: &str, remote_name: &str) -> Option<RefTarget> {
191        self.data
192            .branches
193            .get(name)
194            .and_then(|branch_target| branch_target.remote_targets.get(remote_name).cloned())
195    }
196
197    pub fn set_remote_branch(&mut self, name: String, remote_name: String, target: RefTarget) {
198        self.data
199            .branches
200            .entry(name)
201            .or_default()
202            .remote_targets
203            .insert(remote_name, target);
204    }
205
206    pub fn remove_remote_branch(&mut self, name: &str, remote_name: &str) {
207        if let Some(branch) = self.data.branches.get_mut(name) {
208            branch.remote_targets.remove(remote_name);
209            if branch.remote_targets.is_empty() && branch.local_target.is_none() {
210                self.remove_branch(name);
211            }
212        }
213    }
214
215    pub fn rename_remote(&mut self, old: &str, new: &str) {
216        for branch in self.data.branches.values_mut() {
217            if let Some(target) = branch.remote_targets.remove(old) {
218                branch.remote_targets.insert(new.to_owned(), target);
219            }
220        }
221    }
222
223    pub fn get_tag(&self, name: &str) -> Option<RefTarget> {
224        self.data.tags.get(name).cloned()
225    }
226
227    pub fn set_tag(&mut self, name: String, target: RefTarget) {
228        self.data.tags.insert(name, target);
229    }
230
231    pub fn remove_tag(&mut self, name: &str) {
232        self.data.tags.remove(name);
233    }
234
235    pub fn get_git_ref(&self, name: &str) -> Option<RefTarget> {
236        self.data.git_refs.get(name).cloned()
237    }
238
239    pub fn set_git_ref(&mut self, name: String, target: RefTarget) {
240        self.data.git_refs.insert(name, target);
241    }
242
243    pub fn remove_git_ref(&mut self, name: &str) {
244        self.data.git_refs.remove(name);
245    }
246
247    pub fn set_git_head(&mut self, target: RefTarget) {
248        self.data.git_head = Some(target);
249    }
250
251    pub fn clear_git_head(&mut self) {
252        self.data.git_head = None;
253    }
254
255    pub fn set_view(&mut self, data: op_store::View) {
256        self.data = data;
257    }
258
259    pub fn store_view(&self) -> &op_store::View {
260        &self.data
261    }
262
263    pub fn store_view_mut(&mut self) -> &mut op_store::View {
264        &mut self.data
265    }
266
267    pub fn merge_single_ref(
268        &mut self,
269        index: &dyn Index,
270        ref_name: &RefName,
271        base_target: Option<&RefTarget>,
272        other_target: Option<&RefTarget>,
273    ) {
274        if base_target != other_target {
275            let self_target = self.get_ref(ref_name);
276            let new_target =
277                merge_ref_targets(index, self_target.as_ref(), base_target, other_target);
278            if new_target != self_target {
279                self.set_or_remove_ref(ref_name.clone(), new_target);
280            }
281        }
282    }
283}