Previously it would be parsed, but always error as the implementation didn't exist. Now it will return the fetch and push tracking branches respectively.
Unified · Split
| @@ -199,10 +199,9 @@ impl<'repo> Delegate<'repo> { · 198 unmodified lines | |||
| 199 | } | 199 | } |
| 200 | } | 200 | } |
| 201 | fn follow_refs_to_objects_if_needed(&mut self) -> Option<()> { | 201 | fn follow_refs_to_objects_if_needed(&mut self) -> Option<()> { |
| 202 | assert_eq!(self.refs.len(), self.objs.len()); | ||
| 203 | let repo = self.repo; | 202 | let repo = self.repo; |
| 204 | for (r, obj) in self.refs.iter().zip(self.objs.iter_mut()) { | 203 | for (r, obj) in self.refs.iter().zip(self.objs.iter_mut()) { |
| 205 | if let (_ref_opt @ Some(ref_), obj_opt @ None) = (r, obj) { | 204 | if let (Some(ref_), obj_opt @ None) = (r, obj) { |
| 206 | if let Some(id) = ref_.target.try_id().map(ToOwned::to_owned).or_else(|| { | 205 | if let Some(id) = ref_.target.try_id().map(ToOwned::to_owned).or_else(|| { |
| 207 | ref_.clone() | 206 | ref_.clone() |
| 208 | .attach(repo) | 207 | .attach(repo) |
| @@ -9,6 +9,7 @@ use gix_revision::spec::parse::{ · 8 unmodified lines | |||
| 9 | use crate::{ | 9 | use crate::{ |
| 10 | bstr::{BStr, BString, ByteSlice}, | 10 | bstr::{BStr, BString, ByteSlice}, |
| 11 | ext::ReferenceExt, | 11 | ext::ReferenceExt, |
| 12 | remote, | ||
| 12 | revision::spec::parse::{Delegate, Error, RefsHint}, | 13 | revision::spec::parse::{Delegate, Error, RefsHint}, |
| 13 | }; | 14 | }; |
| 14 | 15 | ||
| @@ -106,6 +107,7 @@ impl<'repo> delegate::Revision for Delegate<'repo> { · 91 unmodified lines | |||
| 106 | self.unset_disambiguate_call(); | 107 | self.unset_disambiguate_call(); |
| 107 | match query { | 108 | match query { |
| 108 | ReflogLookup::Date(_date) => { | 109 | ReflogLookup::Date(_date) => { |
| 110 | // TODO: actually do this - this should be possible now despite incomplete date parsing | ||
| 109 | self.err.push(Error::Planned { | 111 | self.err.push(Error::Planned { |
| 110 | dependency: "remote handling and ref-specs are fleshed out more", | 112 | dependency: "remote handling and ref-specs are fleshed out more", |
| 111 | }); | 113 | }); |
| @@ -215,11 +217,51 @@ impl<'repo> delegate::Revision for Delegate<'repo> { · 103 unmodified lines | |||
| 215 | } | 217 | } |
| 216 | } | 218 | } |
| 217 | 219 | ||
| 218 | fn sibling_branch(&mut self, _kind: SiblingBranch) -> Option<()> { | 220 | fn sibling_branch(&mut self, kind: SiblingBranch) -> Option<()> { |
| 219 | self.unset_disambiguate_call(); | 221 | self.unset_disambiguate_call(); |
| 220 | self.err.push(Error::Planned { | 222 | let reference = match &mut self.refs[self.idx] { |
| 221 | dependency: "remote handling and ref-specs are fleshed out more", | 223 | val @ None => match self.repo.head().map(crate::Head::try_into_referent) { |
| 222 | }); | 224 | Ok(Some(r)) => { |
| 225 | *val = Some(r.clone().detach()); | ||
| 226 | r | ||
| 227 | } | ||
| 228 | Ok(None) => { | ||
| 229 | self.err.push(Error::UnbornHeadForSibling); | ||
| 230 | return None; | ||
| 231 | } | ||
| 232 | Err(err) => { | ||
| 233 | self.err.push(err.into()); | ||
| 234 | return None; | ||
| 235 | } | ||
| 236 | }, | ||
| 237 | Some(r) => r.clone().attach(self.repo), | ||
| 238 | }; | ||
| 239 | let direction = match kind { | ||
| 240 | SiblingBranch::Upstream => remote::Direction::Fetch, | ||
| 241 | SiblingBranch::Push => remote::Direction::Push, | ||
| 242 | }; | ||
| 243 | match reference.remote_tracking_ref_name(direction) { | ||
| 244 | None => self.err.push(Error::NoTrackingBranch { | ||
| 245 | name: reference.inner.name, | ||
| 246 | direction, | ||
| 247 | }), | ||
| 248 | Some(Err(err)) => self.err.push(Error::GetTrackingBranch { | ||
| 249 | name: reference.inner.name, | ||
| 250 | direction, | ||
| 251 | source: Box::new(err), | ||
| 252 | }), | ||
| 253 | Some(Ok(name)) => match self.repo.find_reference(name.as_ref()) { | ||
| 254 | Err(err) => self.err.push(Error::GetTrackingBranch { | ||
| 255 | name: reference.inner.name, | ||
| 256 | direction, | ||
| 257 | source: Box::new(err), | ||
| 258 | }), | ||
| 259 | Ok(r) => { | ||
| 260 | self.refs[self.idx] = r.inner.into(); | ||
| 261 | return Some(()); | ||
| 262 | } | ||
| 263 | }, | ||
| 264 | }; | ||
| 223 | None | 265 | None |
| 224 | } | 266 | } |
| 225 | } | 267 | } |
| @@ -1,4 +1,4 @@ | |||
| 1 | use crate::{bstr::BString, object, reference}; | 1 | use crate::{bstr::BString, object, reference, remote}; |
| 2 | 2 | ||
| 3 | /// A hint to know what to do if refs and object names are equal. | 3 | /// A hint to know what to do if refs and object names are equal. |
| 4 | #[derive(Default, Debug, Copy, Clone, PartialEq, Eq)] | 4 | #[derive(Default, Debug, Copy, Clone, PartialEq, Eq)] |
| @@ -59,6 +59,19 @@ pub enum Error { · 54 unmodified lines | |||
| 59 | Malformed, | 59 | Malformed, |
| 60 | #[error("Unborn heads do not have a reflog yet")] | 60 | #[error("Unborn heads do not have a reflog yet")] |
| 61 | UnbornHeadsHaveNoRefLog, | 61 | UnbornHeadsHaveNoRefLog, |
| 62 | #[error("Unborn heads cannot have push or upstream tracking branches")] | ||
| 63 | UnbornHeadForSibling, | ||
| 64 | #[error("Branch named {name} does not have a {} tracking branch configured", direction.as_str())] | ||
| 65 | NoTrackingBranch { | ||
| 66 | name: gix_ref::FullName, | ||
| 67 | direction: remote::Direction, | ||
| 68 | }, | ||
| 69 | #[error("Error when obtaining {} tracking branch for {name}", direction.as_str())] | ||
| 70 | GetTrackingBranch { | ||
| 71 | name: gix_ref::FullName, | ||
| 72 | direction: remote::Direction, | ||
| 73 | source: Box<dyn std::error::Error + Send + Sync + 'static>, | ||
| 74 | }, | ||
| 62 | #[error("This feature will be implemented once {dependency}")] | 75 | #[error("This feature will be implemented once {dependency}")] |
| 63 | Planned { dependency: &'static str }, | 76 | Planned { dependency: &'static str }, |
| 64 | #[error("Reference {reference:?} does not have a reference log, cannot {action}")] | 77 | #[error("Reference {reference:?} does not have a reference log, cannot {action}")] |
Binary file not shown.
| @@ -338,6 +338,16 @@ git init complex_graph · 337 unmodified lines | |||
| 338 | git add file && git commit -m A | 338 | git add file && git commit -m A |
| 339 | git branch a | 339 | git branch a |
| 340 | 340 | ||
| 341 | git remote add origin . | ||
| 342 | cat <<EOF>>.git/config | ||
| 343 | |||
| 344 | [branch "main"] | ||
| 345 | remote = origin | ||
| 346 | merge = refs/heads/main | ||
| 347 | EOF | ||
| 348 | |||
| 349 | git fetch | ||
| 350 | |||
| 341 | baseline ":/message" # finds 'message recent' instead of 'initial message' | 351 | baseline ":/message" # finds 'message recent' instead of 'initial message' |
| 342 | baseline ":/!-message" # above, negated | 352 | baseline ":/!-message" # above, negated |
| 343 | baseline ":/mes.age" # regexes work too | 353 | baseline ":/mes.age" # regexes work too |
| @@ -391,6 +401,11 @@ git init complex_graph · 47 unmodified lines | |||
| 391 | baseline "@:" | 401 | baseline "@:" |
| 392 | baseline "4b825dc642cb6eb9a060e54bf8d69288fbee4904" | 402 | baseline "4b825dc642cb6eb9a060e54bf8d69288fbee4904" |
| 393 | 403 | ||
| 404 | baseline @{push} | ||
| 405 | baseline main@{push} | ||
| 406 | baseline main@{upstream} | ||
| 407 | baseline @{upstream} | ||
| 408 | |||
| 394 | baseline "^" | 409 | baseline "^" |
| 395 | baseline "^!" | 410 | baseline "^!" |
| 396 | baseline "..." | 411 | baseline "..." |
| @@ -13,22 +13,27 @@ mod traverse; · 12 unmodified lines | |||
| 13 | mod peel; | 13 | mod peel; |
| 14 | 14 | ||
| 15 | mod sibling_branch { | 15 | mod sibling_branch { |
| 16 | use gix::revision::spec::parse::Error; | 16 | use crate::revision::spec::from_bytes::{parse_spec, repo}; |
| 17 | 17 | use crate::util::hex_to_id; | |
| 18 | use crate::revision::spec::from_bytes::{parse_spec_no_baseline, repo}; | ||
| 19 | 18 | ||
| 20 | #[test] | 19 | #[test] |
| 21 | fn is_planned_and_delayed_until_remotes_are_sorted() { | 20 | fn push_and_upstream() -> crate::Result { |
| 22 | let repo = repo("complex_graph").unwrap(); | 21 | let repo = repo("complex_graph").unwrap(); |
| 23 | assert!(matches!( | 22 | for op in ["upstream", "push"] { |
| 24 | parse_spec_no_baseline("main@{push}", &repo).unwrap_err(), | 23 | for branch in ["", "main"] { |
| 25 | Error::Planned { .. } | 24 | let actual = parse_spec(format!("{branch}@{{{op}}}"), &repo)?; |
| 26 | )); | 25 | assert_eq!( |
| 27 | 26 | actual.first_reference().expect("set").name.as_bstr(), | |
| 28 | assert!(matches!( | 27 | "refs/remotes/origin/main" |
| 29 | parse_spec_no_baseline("main@{upstream}", &repo).unwrap_err(), | 28 | ); |
| 30 | Error::Planned { .. } | 29 | assert_eq!(actual.second_reference(), None); |
| 31 | )); | 30 | assert_eq!( |
| 31 | actual.single().expect("just one"), | ||
| 32 | hex_to_id("55e825ebe8fd2ff78cad3826afb696b96b576a7e") | ||
| 33 | ); | ||
| 34 | } | ||
| 35 | } | ||
| 36 | Ok(()) | ||
| 32 | } | 37 | } |
| 33 | } | 38 | } |
| 34 | 39 | ||
| @@ -189,11 +189,11 @@ fn compare_with_baseline( · 188 unmodified lines | |||
| 189 | } | 189 | } |
| 190 | } | 190 | } |
| 191 | 191 | ||
| 192 | pub fn parse_spec<'a>( | 192 | pub fn parse_spec( |
| 193 | spec: &str, | 193 | spec: impl AsRef<str>, |
| 194 | repo: &'a gix::Repository, | 194 | repo: &gix::Repository, |
| 195 | ) -> Result<gix::revision::Spec<'a>, gix::revision::spec::parse::Error> { | 195 | ) -> Result<gix::revision::Spec<'_>, gix::revision::spec::parse::Error> { |
| 196 | parse_spec_opts(spec, repo, Default::default()) | 196 | parse_spec_opts(spec.as_ref(), repo, Default::default()) |
| 197 | } | 197 | } |
| 198 | 198 | ||
| 199 | pub fn repo(name: &str) -> crate::Result<gix::Repository> { | 199 | pub fn repo(name: &str) -> crate::Result<gix::Repository> { |