Skip to content

Commit 7afebb7

Browse files
committed
handle colon and empty on the right side (#450)
1 parent 966a9e9 commit 7afebb7

File tree

3 files changed

+96
-69
lines changed

3 files changed

+96
-69
lines changed

git-refspec/src/parse.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ pub enum Error {
44
Empty,
55
#[error("Negative refspecs cannot have destinations as they exclude sources")]
66
NegativeWithDestination,
7+
#[error("Cannot push into an empty destination")]
8+
PushToEmpty,
79
}
810

911
pub(crate) mod function {
@@ -33,14 +35,19 @@ pub(crate) mod function {
3335
if mode == Mode::Negative {
3436
return Err(Error::NegativeWithDestination);
3537
}
38+
3639
let src = (!src.is_empty()).then(|| src.as_bstr());
3740
let dst = (!dst.is_empty()).then(|| dst.as_bstr());
3841
match (src, dst) {
39-
(None, None) => (None, None), // match all
42+
(None, None) => (None, None),
4043
(None, Some(dst)) => match operation {
4144
Operation::Push => (None, Some(dst)),
4245
Operation::Fetch => (Some("HEAD".into()), Some(dst)),
4346
},
47+
(Some(src), None) => match operation {
48+
Operation::Push => return Err(Error::PushToEmpty),
49+
Operation::Fetch => (Some(src), None),
50+
},
4451
_ => todo!("src or dst handling"),
4552
}
4653
}

git-refspec/src/spec.rs

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ impl RefSpecRef<'_> {
1515
item.contains(&b'*')
1616
}
1717
match (self.op, self.mode, self.src, self.dst) {
18+
(Operation::Fetch, Mode::Normal | Mode::Force, Some(src), None) => Instruction::Fetch(Fetch::Only { src }),
1819
(Operation::Fetch, Mode::Normal | Mode::Force, Some(src), Some(dst)) => {
1920
Instruction::Fetch(Fetch::AndUpdateSingle {
2021
src,

git-refspec/tests/parse/mod.rs

+87-68
Original file line numberDiff line numberDiff line change
@@ -74,78 +74,97 @@ mod invalid {
7474
}
7575
}
7676

77-
mod fetch {
78-
use crate::parse::{assert_parse, b};
79-
use git_refspec::{Fetch, Instruction};
80-
81-
#[test]
82-
fn empty_lhs_colon_rhs_fetches_head_to_destination() {
83-
assert_parse(
84-
":a",
85-
Instruction::Fetch(Fetch::AndUpdateSingle {
86-
src: b("HEAD"),
87-
dst: b("a"),
88-
allow_non_fast_forward: false,
89-
}),
90-
);
91-
92-
assert_parse(
93-
"+:a",
94-
Instruction::Fetch(Fetch::AndUpdateSingle {
95-
src: b("HEAD"),
96-
dst: b("a"),
97-
allow_non_fast_forward: true,
98-
}),
99-
);
100-
}
77+
#[test]
78+
fn push_to_empty() {
79+
assert!(matches!(
80+
try_parse("HEAD:", Operation::Push).unwrap_err(),
81+
Error::PushToEmpty
82+
));
83+
}
84+
}
10185

102-
#[test]
103-
fn colon_alone_is_for_fetching_into_fetchhead() {
104-
assert_parse(
105-
":",
106-
Instruction::Fetch(Fetch::AllMatchingBranches {
107-
allow_non_fast_forward: false,
108-
}),
109-
);
110-
assert_parse(
111-
"+:",
112-
Instruction::Fetch(Fetch::AllMatchingBranches {
113-
allow_non_fast_forward: true,
114-
}),
115-
);
116-
}
86+
mod fetch {
87+
use crate::parse::{assert_parse, b};
88+
use git_refspec::{Fetch, Instruction, Mode};
89+
90+
#[test]
91+
fn lhs_colon_empty_fetches_only() {
92+
assert_parse("src:", Instruction::Fetch(Fetch::Only { src: b("src") }));
93+
let spec = assert_parse("+src:", Instruction::Fetch(Fetch::Only { src: b("src") }));
94+
assert_eq!(
95+
spec.mode(),
96+
Mode::Force,
97+
"force is set, even though it has no effect in the actual instruction"
98+
);
11799
}
118100

119-
mod push {
120-
use crate::parse::{assert_parse, b};
121-
use git_refspec::{Instruction, Mode, Push};
122-
123-
#[test]
124-
fn colon_alone_is_for_pushing_matching_refs() {
125-
assert_parse(
126-
":",
127-
Instruction::Push(Push::AllMatchingBranches {
128-
allow_non_fast_forward: false,
129-
}),
130-
);
131-
assert_parse(
132-
"+:",
133-
Instruction::Push(Push::AllMatchingBranches {
134-
allow_non_fast_forward: true,
135-
}),
136-
);
137-
}
101+
#[test]
102+
fn empty_lhs_colon_rhs_fetches_head_to_destination() {
103+
assert_parse(
104+
":a",
105+
Instruction::Fetch(Fetch::AndUpdateSingle {
106+
src: b("HEAD"),
107+
dst: b("a"),
108+
allow_non_fast_forward: false,
109+
}),
110+
);
138111

139-
#[test]
140-
fn delete() {
141-
assert_parse(":a", Instruction::Push(Push::Delete { ref_or_pattern: b("a") }));
142-
let spec = assert_parse("+:a", Instruction::Push(Push::Delete { ref_or_pattern: b("a") }));
143-
assert_eq!(
144-
spec.mode(),
145-
Mode::Force,
146-
"force is set, even though it has no effect in the actual instruction"
147-
);
148-
}
112+
assert_parse(
113+
"+:a",
114+
Instruction::Fetch(Fetch::AndUpdateSingle {
115+
src: b("HEAD"),
116+
dst: b("a"),
117+
allow_non_fast_forward: true,
118+
}),
119+
);
120+
}
121+
122+
#[test]
123+
fn colon_alone_is_for_fetching_into_fetchhead() {
124+
assert_parse(
125+
":",
126+
Instruction::Fetch(Fetch::AllMatchingBranches {
127+
allow_non_fast_forward: false,
128+
}),
129+
);
130+
assert_parse(
131+
"+:",
132+
Instruction::Fetch(Fetch::AllMatchingBranches {
133+
allow_non_fast_forward: true,
134+
}),
135+
);
136+
}
137+
}
138+
139+
mod push {
140+
use crate::parse::{assert_parse, b};
141+
use git_refspec::{Instruction, Mode, Push};
142+
143+
#[test]
144+
fn colon_alone_is_for_pushing_matching_refs() {
145+
assert_parse(
146+
":",
147+
Instruction::Push(Push::AllMatchingBranches {
148+
allow_non_fast_forward: false,
149+
}),
150+
);
151+
assert_parse(
152+
"+:",
153+
Instruction::Push(Push::AllMatchingBranches {
154+
allow_non_fast_forward: true,
155+
}),
156+
);
157+
}
158+
159+
#[test]
160+
fn delete() {
161+
assert_parse(":a", Instruction::Push(Push::Delete { ref_or_pattern: b("a") }));
162+
let spec = assert_parse("+:a", Instruction::Push(Push::Delete { ref_or_pattern: b("a") }));
163+
assert_eq!(
164+
spec.mode(),
165+
Mode::Force,
166+
"force is set, even though it has no effect in the actual instruction"
167+
);
149168
}
150169
}
151170

0 commit comments

Comments
 (0)