1use 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}