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
199199 }
200200 }
201201 fn follow_refs_to_objects_if_needed(&mut self) -> Option<()> {
202− assert_eq!(self.refs.len(), self.objs.len());
203202 let repo = self.repo;
204203 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) {
206205 if let Some(id) = ref_.target.try_id().map(ToOwned::to_owned).or_else(|| {
207206 ref_.clone()
208207 .attach(repo)
gix/src/revision/spec/parse/delegate/revision.rs+46−4
@@ -9,6 +9,7 @@ use gix_revision::spec::parse::{ · 8 unmodified lines
99 use crate::{
1010 bstr::{BStr, BString, ByteSlice},
1111 ext::ReferenceExt,
12+ remote,
1213 revision::spec::parse::{Delegate, Error, RefsHint},
1314 };
1415
@@ -106,6 +107,7 @@ impl<'repo> delegate::Revision for Delegate<'repo> { · 91 unmodified lines
106107 self.unset_disambiguate_call();
107108 match query {
108109 ReflogLookup::Date(_date) => {
110+ // TODO: actually do this - this should be possible now despite incomplete date parsing
109111 self.err.push(Error::Planned {
110112 dependency: "remote handling and ref-specs are fleshed out more",
111113 });
@@ -215,11 +217,51 @@ impl<'repo> delegate::Revision for Delegate<'repo> { · 103 unmodified lines
215217 }
216218 }
217219
218− fn sibling_branch(&mut self, _kind: SiblingBranch) -> Option<()> {
220+ fn sibling_branch(&mut self, kind: SiblingBranch) -> Option<()> {
219221 self.unset_disambiguate_call();
220− self.err.push(Error::Planned {
221− dependency: "remote handling and ref-specs are fleshed out more",
222− });
222+ let reference = match &mut self.refs[self.idx] {
223+ val @ None => match self.repo.head().map(crate::Head::try_into_referent) {
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+ };
223265 None
224266 }
225267 }
gix/src/revision/spec/parse/types.rs+14−1
@@ -1,4 +1,4 @@
1−use crate::{bstr::BString, object, reference};
1+use crate::{bstr::BString, object, reference, remote};
22
33 /// A hint to know what to do if refs and object names are equal.
44 #[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
@@ -59,6 +59,19 @@ pub enum Error { · 54 unmodified lines
5959 Malformed,
6060 #[error("Unborn heads do not have a reflog yet")]
6161 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+ },
6275 #[error("This feature will be implemented once {dependency}")]
6376 Planned { dependency: &'static str },
6477 #[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
338338 git add file && git commit -m A
339339 git branch a
340340
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+
341351 baseline ":/message" # finds 'message recent' instead of 'initial message'
342352 baseline ":/!-message" # above, negated
343353 baseline ":/mes.age" # regexes work too
@@ -391,6 +401,11 @@ git init complex_graph · 47 unmodified lines
391401 baseline "@:"
392402 baseline "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
393403
404+ baseline @{push}
405+ baseline main@{push}
406+ baseline main@{upstream}
407+ baseline @{upstream}
408+
394409 baseline "^"
395410 baseline "^!"
396411 baseline "..."
gix/tests/revision/spec/from_bytes/mod.rs+18−13
@@ -13,22 +13,27 @@ mod traverse; · 12 unmodified lines
1313 mod peel;
1414
1515 mod sibling_branch {
16− use gix::revision::spec::parse::Error;
17−
18− use crate::revision::spec::from_bytes::{parse_spec_no_baseline, repo};
16+ use crate::revision::spec::from_bytes::{parse_spec, repo};
17+ use crate::util::hex_to_id;
1918
2019 #[test]
21− fn is_planned_and_delayed_until_remotes_are_sorted() {
20+ fn push_and_upstream() -> crate::Result {
2221 let repo = repo("complex_graph").unwrap();
23− assert!(matches!(
24− parse_spec_no_baseline("main@{push}", &repo).unwrap_err(),
25− Error::Planned { .. }
26− ));
27−
28− assert!(matches!(
29− parse_spec_no_baseline("main@{upstream}", &repo).unwrap_err(),
30− Error::Planned { .. }
31− ));
22+ for op in ["upstream", "push"] {
23+ for branch in ["", "main"] {
24+ let actual = parse_spec(format!("{branch}@{{{op}}}"), &repo)?;
25+ assert_eq!(
26+ actual.first_reference().expect("set").name.as_bstr(),
27+ "refs/remotes/origin/main"
28+ );
29+ assert_eq!(actual.second_reference(), None);
30+ assert_eq!(
31+ actual.single().expect("just one"),
32+ hex_to_id("55e825ebe8fd2ff78cad3826afb696b96b576a7e")
33+ );
34+ }
35+ }
36+ Ok(())
3237 }
3338 }
3439
gix/tests/revision/spec/from_bytes/util.rs+5−5
@@ -189,11 +189,11 @@ fn compare_with_baseline( · 188 unmodified lines
189189 }
190190 }
191191
192−pub fn parse_spec<'a>(
193− spec: &str,
194− repo: &'a gix::Repository,
195−) -> Result<gix::revision::Spec<'a>, gix::revision::spec::parse::Error> {
196− parse_spec_opts(spec, repo, Default::default())
192+pub fn parse_spec(
193+ spec: impl AsRef<str>,
194+ repo: &gix::Repository,
195+) -> Result<gix::revision::Spec<'_>, gix::revision::spec::parse::Error> {
196+ parse_spec_opts(spec.as_ref(), repo, Default::default())
197197 }
198198
199199 pub fn repo(name: &str) -> crate::Result<gix::Repository> {