|
| 1 | +use crate::Worktree; |
| 2 | +use crate::init::walk::WorktreeByBranch; |
| 3 | +use crate::init::{Entrypoint, Overlay, walk::RefsById}; |
| 4 | +use anyhow::bail; |
| 5 | +use but_core::{RefMetadata, ref_metadata}; |
| 6 | +use gix::{prelude::ReferenceExt, refs::Target}; |
1 | 7 | use std::{ |
2 | 8 | borrow::Cow, |
3 | 9 | collections::{BTreeMap, BTreeSet}, |
4 | 10 | }; |
5 | 11 |
|
6 | | -use but_core::{RefMetadata, ref_metadata}; |
7 | | -use gix::{prelude::ReferenceExt, refs::Target}; |
8 | | - |
9 | | -use crate::init::{Entrypoint, Overlay, walk::RefsById}; |
10 | | - |
11 | 12 | impl Overlay { |
12 | 13 | /// Serve the given `refs` from memory, as if they would exist. |
13 | 14 | /// This is true only, however, if a real reference doesn't exist. |
@@ -103,10 +104,12 @@ impl Overlay { |
103 | 104 | } |
104 | 105 | } |
105 | 106 |
|
| 107 | +type NameToReference = BTreeMap<gix::refs::FullName, gix::refs::Reference>; |
| 108 | + |
106 | 109 | pub(crate) struct OverlayRepo<'repo> { |
107 | 110 | inner: &'repo gix::Repository, |
108 | | - nonoverriding_references: BTreeMap<gix::refs::FullName, gix::refs::Reference>, |
109 | | - overriding_references: BTreeMap<gix::refs::FullName, gix::refs::Reference>, |
| 111 | + nonoverriding_references: NameToReference, |
| 112 | + overriding_references: NameToReference, |
110 | 113 | } |
111 | 114 |
|
112 | 115 | /// Note that functions with `'repo` in their return value technically leak the bare repo, and it's |
@@ -182,10 +185,6 @@ impl<'repo> OverlayRepo<'repo> { |
182 | 185 | self.inner |
183 | 186 | } |
184 | 187 |
|
185 | | - pub fn for_worktree_only(&self) -> &'repo gix::Repository { |
186 | | - self.inner |
187 | | - } |
188 | | - |
189 | 188 | pub fn remote_names(&self) -> gix::remote::Names<'repo> { |
190 | 189 | self.inner.remote_names() |
191 | 190 | } |
@@ -269,6 +268,93 @@ impl<'repo> OverlayRepo<'repo> { |
269 | 268 | all_refs_by_id.values_mut().for_each(|v| v.sort()); |
270 | 269 | Ok(all_refs_by_id) |
271 | 270 | } |
| 271 | + |
| 272 | + /// This is a bit tricky but aims to map the `HEAD` targets of the main worktree to what possibly was overridden |
| 273 | + /// via `main_head_referent`. The idea is that this is the entrypoint, which is assumed to be `HEAD` |
| 274 | + /// |
| 275 | + /// ### Shortcoming |
| 276 | + /// |
| 277 | + /// For now, it can only remap the first HEAD reference. For this to really work, we need proper in-memory overrides |
| 278 | + /// or a way to have overrides 'for real'. |
| 279 | + /// Also, we don't want `main_head_referent` to be initialised from the entrypoint, which we equal to be `HEAD`. |
| 280 | + /// But this invariant can fall apart easily and is caller dependent, as we use it to see the graph *as if* `HEAD` would |
| 281 | + /// be in another position - but that doesn't affect the worktree ref at all. |
| 282 | + pub fn worktree_branches( |
| 283 | + &self, |
| 284 | + main_head_referent: Option<&gix::refs::FullNameRef>, |
| 285 | + ) -> anyhow::Result<WorktreeByBranch> { |
| 286 | + /// If `main_head_referent` is set, it means this is an overridden reference of the `HEAD` of the repo the graph is built in. |
| 287 | + /// If `None`, `head` belongs to another worktree. Completely unrelated to linked or main. |
| 288 | + fn maybe_insert_head( |
| 289 | + head: Option<gix::Head<'_>>, |
| 290 | + main_head_referent: Option<&gix::refs::FullNameRef>, |
| 291 | + overriding: &NameToReference, |
| 292 | + out: &mut WorktreeByBranch, |
| 293 | + ) -> anyhow::Result<()> { |
| 294 | + let Some((head, wd)) = head.and_then(|head| { |
| 295 | + head.repo.worktree().map(|wt| { |
| 296 | + ( |
| 297 | + head, |
| 298 | + match wt.id() { |
| 299 | + None => Worktree::Main, |
| 300 | + Some(id) => Worktree::LinkedId(id.to_owned()), |
| 301 | + }, |
| 302 | + ) |
| 303 | + }) |
| 304 | + }) else { |
| 305 | + return Ok(()); |
| 306 | + }; |
| 307 | + |
| 308 | + out.entry("HEAD".try_into().expect("valid")) |
| 309 | + .or_default() |
| 310 | + .push(wd.clone()); |
| 311 | + let mut ref_chain = Vec::new(); |
| 312 | + // Is this the repo that the overrides were applied on? |
| 313 | + let mut cursor = if let Some(head_name) = main_head_referent { |
| 314 | + overriding |
| 315 | + .get(head_name) |
| 316 | + .map(|overridden_head| overridden_head.clone().attach(head.repo)) |
| 317 | + .or_else(|| head.try_into_referent()) |
| 318 | + } else { |
| 319 | + head.try_into_referent() |
| 320 | + }; |
| 321 | + while let Some(ref_) = cursor { |
| 322 | + ref_chain.push(ref_.name().to_owned()); |
| 323 | + if overriding |
| 324 | + .get(ref_.name()) |
| 325 | + .is_some_and(|r| r.target.try_name() != ref_.target().try_name()) |
| 326 | + { |
| 327 | + bail!( |
| 328 | + "SHORTCOMING: cannot deal with {ref_:?} overridden to a different symbolic name to follow" |
| 329 | + ) |
| 330 | + } |
| 331 | + cursor = ref_.follow().transpose()?; |
| 332 | + } |
| 333 | + for name in ref_chain { |
| 334 | + out.entry(name).or_default().push(wd.clone()); |
| 335 | + } |
| 336 | + |
| 337 | + Ok(()) |
| 338 | + } |
| 339 | + |
| 340 | + let mut map = BTreeMap::new(); |
| 341 | + maybe_insert_head( |
| 342 | + self.inner.head().ok(), |
| 343 | + main_head_referent, |
| 344 | + &self.overriding_references, |
| 345 | + &mut map, |
| 346 | + )?; |
| 347 | + for proxy in self.inner.worktrees()? { |
| 348 | + let repo = proxy.into_repo_with_possibly_inaccessible_worktree()?; |
| 349 | + maybe_insert_head( |
| 350 | + repo.head().ok(), |
| 351 | + None, |
| 352 | + &self.overriding_references, |
| 353 | + &mut map, |
| 354 | + )?; |
| 355 | + } |
| 356 | + Ok(map) |
| 357 | + } |
272 | 358 | } |
273 | 359 |
|
274 | 360 | pub(crate) struct OverlayMetadata<'meta, T> { |
|
0 commit comments