History

feat: `Repository::rev_parse*()` now supports `branch@{upstream|push|u|p}`.

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.

Sebastian Thiel committed 2 years ago

Commit 3fba5b81 parent 270322e7 files · +98 −24Browse files

Unified · Split

7 files
  1. gix/src/revision/spec/parse/delegate/mod.rs +1 −2
  2. gix/src/revision/spec/parse/delegate/revision.rs +46 −4
  3. gix/src/revision/spec/parse/types.rs +14 −1
  4. gix/tests/fixtures/generated-archives/make_rev_spec_parse_repos.tar.xz +0 −0
  5. gix/tests/fixtures/make_rev_spec_parse_repos.sh +15 −0
  6. gix/tests/revision/spec/from_bytes/mod.rs +18 −13
  7. gix/tests/revision/spec/from_bytes/util.rs +5 −5
gix/src/revision/spec/parse/delegate/mod.rs+1−2
@@ -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)
gix/src/revision/spec/parse/delegate/revision.rs+46−4
@@ -9,6 +9,7 @@ use gix_revision::spec::parse::{ · 8 unmodified lines
9use crate::{9use 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 None265 None
224 }266 }
225}267}
gix/src/revision/spec/parse/types.rs+14−1
@@ -1,4 +1,4 @@
1use crate::{bstr::BString, object, reference};1use 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}")]
gix/tests/fixtures/generated-archives/make_rev_spec_parse_repos.tar.xz+0−0

Binary file not shown.

gix/tests/fixtures/make_rev_spec_parse_repos.sh+15−0
@@ -338,6 +338,16 @@ git init complex_graph · 337 unmodified lines
338 git add file && git commit -m A338 git add file && git commit -m A
339 git branch a339 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
347EOF
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, negated352 baseline ":/!-message" # above, negated
343 baseline ":/mes.age" # regexes work too353 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 "..."
gix/tests/revision/spec/from_bytes/mod.rs+18−13
@@ -13,22 +13,27 @@ mod traverse; · 12 unmodified lines
13mod peel;13mod peel;
14 14
15mod sibling_branch {15mod 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
gix/tests/revision/spec/from_bytes/util.rs+5−5
@@ -189,11 +189,11 @@ fn compare_with_baseline( · 188 unmodified lines
189 }189 }
190}190}
191 191
192pub fn parse_spec<'a>(192pub 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
199pub fn repo(name: &str) -> crate::Result<gix::Repository> {199pub fn repo(name: &str) -> crate::Result<gix::Repository> {