Skip to content

Commit 9cebccf

Browse files
committed
fix: Allow negative refspecs when pushing
This effectively reverts 9c280b2
1 parent 8712aaf commit 9cebccf

File tree

7 files changed

+43
-11
lines changed

7 files changed

+43
-11
lines changed

gix-refspec/src/instruction.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ pub enum Push<'a> {
3737
/// If true, allow non-fast-forward updates of `dest`.
3838
allow_non_fast_forward: bool,
3939
},
40+
/// Exclude a single ref.
41+
Exclude {
42+
/// A full or partial ref name to exclude, or multiple if a single `*` is used.
43+
src: &'a BStr,
44+
},
4045
}
4146

4247
/// Any source can either be a ref name (full or partial) or a fully spelled out hex-sha for an object, on the remote side.

gix-refspec/src/parse.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ pub enum Error {
88
NegativeWithDestination,
99
#[error("Negative specs must not be empty")]
1010
NegativeEmpty,
11-
#[error("Negative specs are only supported when fetching")]
12-
NegativeUnsupported,
1311
#[error("Negative specs must be object hashes")]
1412
NegativeObjectHash,
1513
#[error("Negative specs must be full ref names, starting with \"refs/\"")]
@@ -62,9 +60,6 @@ pub(crate) mod function {
6260
let mode = match spec.first() {
6361
Some(&b'^') => {
6462
spec = &spec[1..];
65-
if operation == Operation::Push {
66-
return Err(Error::NegativeUnsupported);
67-
}
6863
Mode::Negative
6964
}
7065
Some(&b'+') => {

gix-refspec/src/spec.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ impl<'a> RefSpecRef<'a> {
209209
dst,
210210
allow_non_fast_forward: matches!(self.mode, Mode::Force),
211211
}),
212+
(Mode::Negative, Some(src), None) => Instruction::Push(Push::Exclude { src }),
212213
(mode, src, dest) => {
213214
unreachable!(
214215
"BUG: push instructions with {:?} {:?} {:?} are not possible",

gix-refspec/src/write.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ impl Instruction<'_> {
5353
out.write_all(ref_or_pattern)
5454
}
5555
Instruction::Fetch(Fetch::Only { src }) => out.write_all(src),
56-
Instruction::Fetch(Fetch::Exclude { src }) => {
56+
Instruction::Fetch(Fetch::Exclude { src }) | Instruction::Push(Push::Exclude { src }) => {
5757
out.write_all(b"^")?;
5858
out.write_all(src)
5959
}
Binary file not shown.

gix-refspec/tests/fixtures/parse_baseline.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ baseline fetch 'HEAD'
9797
baseline push '@'
9898
baseline fetch '@'
9999

100-
baseline push '^@' fail
100+
baseline push '^@'
101101
baseline fetch '^@'
102102
baseline fetch '^refs/heads/main'
103103
baseline fetch '^refs/heads/*'

gix-refspec/tests/refspec/parse/push.rs

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,52 @@
1+
use crate::parse::{assert_parse, b, try_parse};
12
use gix_refspec::{
23
instruction::Push,
34
parse::{Error, Operation},
45
Instruction,
56
};
67

7-
use crate::parse::{assert_parse, b, try_parse};
8+
#[test]
9+
fn negative_must_not_be_empty() {
10+
assert!(matches!(
11+
try_parse("^", Operation::Push).unwrap_err(),
12+
Error::NegativeEmpty
13+
));
14+
}
15+
16+
#[test]
17+
fn negative_must_not_be_object_hash() {
18+
assert!(matches!(
19+
try_parse("^e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", Operation::Push).unwrap_err(),
20+
Error::NegativeObjectHash
21+
));
22+
}
823

924
#[test]
10-
fn negative_unsupported() {
11-
for spec in ["^a:b", "^a:", "^:", "^:b", "^"] {
25+
fn negative_with_destination() {
26+
for spec in ["^a:b", "^a:", "^:", "^:b"] {
1227
assert!(matches!(
1328
try_parse(spec, Operation::Push).unwrap_err(),
14-
Error::NegativeUnsupported
29+
Error::NegativeWithDestination
1530
));
1631
}
1732
}
1833

34+
#[test]
35+
fn exclude() {
36+
assert!(matches!(
37+
try_parse("^a", Operation::Push).unwrap_err(),
38+
Error::NegativePartialName
39+
));
40+
assert!(matches!(
41+
try_parse("^a*", Operation::Push).unwrap_err(),
42+
Error::NegativeGlobPattern
43+
));
44+
assert_parse(
45+
"^refs/heads/a",
46+
Instruction::Push(Push::Exclude { src: b("refs/heads/a") }),
47+
);
48+
}
49+
1950
#[test]
2051
fn revspecs_with_ref_name_destination() {
2152
assert_parse(

0 commit comments

Comments
 (0)