Skip to content

Commit 3fba5b8

Browse files
committed
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.
1 parent 270322e commit 3fba5b8

File tree

7 files changed

+99
-25
lines changed

7 files changed

+99
-25
lines changed

gix/src/revision/spec/parse/delegate/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,10 +199,9 @@ impl<'repo> Delegate<'repo> {
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

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use gix_revision::spec::parse::{
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> {
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> {
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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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 {
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}")]
Binary file not shown.

gix/tests/fixtures/make_rev_spec_parse_repos.sh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,16 @@ git init complex_graph
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
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

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,27 @@ mod traverse;
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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -189,11 +189,11 @@ fn compare_with_baseline(
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> {

0 commit comments

Comments
 (0)