just playing with tangled
at tmp-tutorial 303 lines 9.9 kB view raw
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 15#![allow(missing_docs)] 16 17use std::collections::{BTreeMap, HashMap, HashSet}; 18use std::fmt; 19 20use itertools::Itertools; 21 22use crate::backend::CommitId; 23use crate::index::Index; 24use crate::op_store; 25use crate::op_store::{BranchTarget, RefTarget, RefTargetOptionExt as _, WorkspaceId}; 26use crate::refs::merge_ref_targets; 27 28#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Debug)] 29pub enum RefName { 30 LocalBranch(String), 31 RemoteBranch { branch: String, remote: String }, 32 Tag(String), 33 GitRef(String), 34} 35 36impl fmt::Display for RefName { 37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 38 match self { 39 RefName::LocalBranch(name) => write!(f, "{name}"), 40 RefName::RemoteBranch { branch, remote } => write!(f, "{branch}@{remote}"), 41 RefName::Tag(name) => write!(f, "{name}"), 42 RefName::GitRef(name) => write!(f, "{name}"), 43 } 44 } 45} 46 47#[derive(PartialEq, Eq, Debug, Clone)] 48pub struct View { 49 data: op_store::View, 50} 51 52impl View { 53 pub fn new(op_store_view: op_store::View) -> Self { 54 View { 55 data: op_store_view, 56 } 57 } 58 59 pub fn wc_commit_ids(&self) -> &HashMap<WorkspaceId, CommitId> { 60 &self.data.wc_commit_ids 61 } 62 63 pub fn get_wc_commit_id(&self, workspace_id: &WorkspaceId) -> Option<&CommitId> { 64 self.data.wc_commit_ids.get(workspace_id) 65 } 66 67 pub fn workspaces_for_wc_commit_id(&self, commit_id: &CommitId) -> Vec<WorkspaceId> { 68 let mut workspaces_ids = vec![]; 69 for (workspace_id, wc_commit_id) in &self.data.wc_commit_ids { 70 if wc_commit_id == commit_id { 71 workspaces_ids.push(workspace_id.clone()); 72 } 73 } 74 workspaces_ids 75 } 76 77 pub fn is_wc_commit_id(&self, commit_id: &CommitId) -> bool { 78 self.data.wc_commit_ids.values().contains(commit_id) 79 } 80 81 pub fn heads(&self) -> &HashSet<CommitId> { 82 &self.data.head_ids 83 } 84 85 pub fn public_heads(&self) -> &HashSet<CommitId> { 86 &self.data.public_head_ids 87 } 88 89 pub fn branches(&self) -> &BTreeMap<String, BranchTarget> { 90 &self.data.branches 91 } 92 93 pub fn tags(&self) -> &BTreeMap<String, RefTarget> { 94 &self.data.tags 95 } 96 97 pub fn git_refs(&self) -> &BTreeMap<String, RefTarget> { 98 &self.data.git_refs 99 } 100 101 pub fn git_head(&self) -> &RefTarget { 102 &self.data.git_head 103 } 104 105 pub fn set_wc_commit(&mut self, workspace_id: WorkspaceId, commit_id: CommitId) { 106 self.data.wc_commit_ids.insert(workspace_id, commit_id); 107 } 108 109 pub fn remove_wc_commit(&mut self, workspace_id: &WorkspaceId) { 110 self.data.wc_commit_ids.remove(workspace_id); 111 } 112 113 pub fn add_head(&mut self, head_id: &CommitId) { 114 self.data.head_ids.insert(head_id.clone()); 115 } 116 117 pub fn remove_head(&mut self, head_id: &CommitId) { 118 self.data.head_ids.remove(head_id); 119 } 120 121 pub fn add_public_head(&mut self, head_id: &CommitId) { 122 self.data.public_head_ids.insert(head_id.clone()); 123 } 124 125 pub fn remove_public_head(&mut self, head_id: &CommitId) { 126 self.data.public_head_ids.remove(head_id); 127 } 128 129 pub fn get_ref(&self, name: &RefName) -> &RefTarget { 130 match &name { 131 RefName::LocalBranch(name) => self.get_local_branch(name), 132 RefName::RemoteBranch { branch, remote } => self.get_remote_branch(branch, remote), 133 RefName::Tag(name) => self.get_tag(name), 134 RefName::GitRef(name) => self.get_git_ref(name), 135 } 136 } 137 138 /// Sets reference of the specified kind to point to the given target. If 139 /// the target is absent, the reference will be removed. 140 pub fn set_ref_target(&mut self, name: &RefName, target: RefTarget) { 141 match name { 142 RefName::LocalBranch(name) => self.set_local_branch_target(name, target), 143 RefName::RemoteBranch { branch, remote } => { 144 self.set_remote_branch_target(branch, remote, target) 145 } 146 RefName::Tag(name) => self.set_tag_target(name, target), 147 RefName::GitRef(name) => self.set_git_ref_target(name, target), 148 } 149 } 150 151 pub fn get_branch(&self, name: &str) -> Option<&BranchTarget> { 152 self.data.branches.get(name) 153 } 154 155 pub fn set_branch(&mut self, name: String, target: BranchTarget) { 156 self.data.branches.insert(name, target); 157 } 158 159 pub fn remove_branch(&mut self, name: &str) { 160 self.data.branches.remove(name); 161 } 162 163 pub fn get_local_branch(&self, name: &str) -> &RefTarget { 164 if let Some(branch_target) = self.data.branches.get(name) { 165 &branch_target.local_target 166 } else { 167 RefTarget::absent_ref() 168 } 169 } 170 171 /// Sets local branch to point to the given target. If the target is absent, 172 /// and if no associated remote branches exist, the branch will be removed. 173 pub fn set_local_branch_target(&mut self, name: &str, target: RefTarget) { 174 if target.is_present() { 175 self.insert_local_branch(name.to_owned(), target); 176 } else { 177 self.remove_local_branch(name); 178 } 179 } 180 181 fn insert_local_branch(&mut self, name: String, target: RefTarget) { 182 assert!(target.is_present()); 183 self.data.branches.entry(name).or_default().local_target = target; 184 } 185 186 fn remove_local_branch(&mut self, name: &str) { 187 if let Some(branch) = self.data.branches.get_mut(name) { 188 branch.local_target = RefTarget::absent(); 189 if branch.remote_targets.is_empty() { 190 self.remove_branch(name); 191 } 192 } 193 } 194 195 pub fn get_remote_branch(&self, name: &str, remote_name: &str) -> &RefTarget { 196 if let Some(branch_target) = self.data.branches.get(name) { 197 let maybe_target = branch_target.remote_targets.get(remote_name); 198 maybe_target.flatten() 199 } else { 200 RefTarget::absent_ref() 201 } 202 } 203 204 /// Sets remote-tracking branch to point to the given target. If the target 205 /// is absent, the branch will be removed. 206 pub fn set_remote_branch_target(&mut self, name: &str, remote_name: &str, target: RefTarget) { 207 if target.is_present() { 208 self.insert_remote_branch(name.to_owned(), remote_name.to_owned(), target); 209 } else { 210 self.remove_remote_branch(name, remote_name); 211 } 212 } 213 214 fn insert_remote_branch(&mut self, name: String, remote_name: String, target: RefTarget) { 215 assert!(target.is_present()); 216 self.data 217 .branches 218 .entry(name) 219 .or_default() 220 .remote_targets 221 .insert(remote_name, target); 222 } 223 224 fn remove_remote_branch(&mut self, name: &str, remote_name: &str) { 225 if let Some(branch) = self.data.branches.get_mut(name) { 226 branch.remote_targets.remove(remote_name); 227 if branch.remote_targets.is_empty() && branch.local_target.is_absent() { 228 self.remove_branch(name); 229 } 230 } 231 } 232 233 pub fn rename_remote(&mut self, old: &str, new: &str) { 234 for branch in self.data.branches.values_mut() { 235 let target = branch.remote_targets.remove(old).flatten(); 236 if target.is_present() { 237 branch.remote_targets.insert(new.to_owned(), target); 238 } 239 } 240 } 241 242 pub fn get_tag(&self, name: &str) -> &RefTarget { 243 self.data.tags.get(name).flatten() 244 } 245 246 /// Sets tag to point to the given target. If the target is absent, the tag 247 /// will be removed. 248 pub fn set_tag_target(&mut self, name: &str, target: RefTarget) { 249 if target.is_present() { 250 self.data.tags.insert(name.to_owned(), target); 251 } else { 252 self.data.tags.remove(name); 253 } 254 } 255 256 pub fn get_git_ref(&self, name: &str) -> &RefTarget { 257 self.data.git_refs.get(name).flatten() 258 } 259 260 /// Sets the last imported Git ref to point to the given target. If the 261 /// target is absent, the reference will be removed. 262 pub fn set_git_ref_target(&mut self, name: &str, target: RefTarget) { 263 if target.is_present() { 264 self.data.git_refs.insert(name.to_owned(), target); 265 } else { 266 self.data.git_refs.remove(name); 267 } 268 } 269 270 /// Sets `HEAD@git` to point to the given target. If the target is absent, 271 /// the reference will be cleared. 272 pub fn set_git_head_target(&mut self, target: RefTarget) { 273 self.data.git_head = target; 274 } 275 276 pub fn set_view(&mut self, data: op_store::View) { 277 self.data = data; 278 } 279 280 pub fn store_view(&self) -> &op_store::View { 281 &self.data 282 } 283 284 pub fn store_view_mut(&mut self) -> &mut op_store::View { 285 &mut self.data 286 } 287 288 pub fn merge_single_ref( 289 &mut self, 290 index: &dyn Index, 291 ref_name: &RefName, 292 base_target: &RefTarget, 293 other_target: &RefTarget, 294 ) { 295 if base_target != other_target { 296 let self_target = self.get_ref(ref_name); 297 let new_target = merge_ref_targets(index, self_target, base_target, other_target); 298 if new_target != *self_target { 299 self.set_ref_target(ref_name, new_target); 300 } 301 } 302 } 303}