Skip to content

Commit 9c280b2

Browse files
committed
disallow excludes in push mode (#450)
It's not documented in `git-push`, even though git parses it fine for some reason.
1 parent b889953 commit 9c280b2

File tree

7 files changed

+40
-32
lines changed

7 files changed

+40
-32
lines changed

git-refspec/src/parse.rs

+5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ pub enum Error {
66
NegativeWithDestination,
77
#[error("Negative specs must not be empty")]
88
NegativeEmpty,
9+
#[error("Negative specs are only supported when fetching")]
10+
NegativeUnsupported,
911
#[error("Negative specs must be object hashes")]
1012
NegativeObjectHash,
1113
#[error("Cannot push into an empty destination")]
@@ -39,6 +41,9 @@ pub(crate) mod function {
3941
let mode = match spec.get(0) {
4042
Some(&b'^') => {
4143
spec = &spec[1..];
44+
if operation == Operation::Push {
45+
return Err(Error::NegativeUnsupported);
46+
}
4247
Mode::Negative
4348
}
4449
Some(&b'+') => {

git-refspec/src/spec.rs

-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ impl RefSpecRef<'_> {
4343
dst,
4444
allow_non_fast_forward: matches!(self.mode, Mode::Force),
4545
}),
46-
(Mode::Negative, Some(src), None) => Instruction::Push(Push::Exclude { src }),
4746
(mode, src, dest) => {
4847
unreachable!(
4948
"BUG: push instructions with {:?} {:?} {:?} are not possible",

git-refspec/src/types.rs

+1-6
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,6 @@ pub enum Push<'a> {
4040
/// The reference or pattern to delete on the remote.
4141
ref_or_pattern: &'a BStr,
4242
},
43-
/// Exclude a single ref.
44-
Exclude {
45-
/// A full or partial ref name to exclude, or multiple if a single `*` is used.
46-
src: &'a BStr,
47-
},
4843
/// Push a single ref or refspec to a known destination ref.
4944
Matching {
5045
/// The source ref or refspec to push. If pattern, it contains a single `*`.
@@ -67,7 +62,7 @@ pub enum Fetch<'a> {
6762
},
6863
/// Exclude a single ref.
6964
Exclude {
70-
/// A single partial or full ref name to exclude on the remote, or a pattern with a single `*`.
65+
/// A single partial or full ref name to exclude on the remote, or a pattern with a single `*`. It cannot be a spelled out object hash.
7166
src: &'a BStr,
7267
},
7368
AndUpdate {
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
version https://git-lfs.github.com/spec/v1
2-
oid sha256:6d2478d9f90c1e68924743a45cf801b4420924751c2caf7fa9a6c14291c388cd
3-
size 9356
2+
oid sha256:3fe4f4ca2f442e58b594faaa0f512c6fdf1df8f052995928eca9bede909794e9
3+
size 9360

git-refspec/tests/fixtures/make_baseline.sh

+6-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ git init;
66
function baseline() {
77
local kind=$1
88
local refspec=$2
9+
local force_fail=${3:-}
910

1011
cat <<EOF >.git/config
1112
[remote "test"]
@@ -14,6 +15,10 @@ function baseline() {
1415
EOF
1516

1617
git ls-remote "test" && status=0 || status=$?
18+
if [ -n "$force_fail" ]; then
19+
status=128
20+
fi
21+
1722
{
1823
echo "$kind" "$refspec"
1924
echo "$status"
@@ -82,7 +87,7 @@ baseline fetch 'HEAD'
8287
baseline push '@'
8388
baseline fetch '@'
8489

85-
baseline push '^@'
90+
baseline push '^@' fail
8691
baseline fetch '^@'
8792

8893
baseline push '+@'

git-refspec/tests/parse/invalid.rs

+26-15
Original file line numberDiff line numberDiff line change
@@ -8,43 +8,54 @@ fn empty() {
88

99
#[test]
1010
fn negative_must_not_be_empty() {
11-
for op in [Operation::Fetch, Operation::Push] {
12-
assert!(matches!(try_parse("^", op).unwrap_err(), Error::NegativeEmpty));
13-
}
11+
assert!(matches!(
12+
try_parse("^", Operation::Fetch).unwrap_err(),
13+
Error::NegativeEmpty
14+
));
1415
}
1516

1617
#[test]
1718
fn negative_must_not_be_object_hash() {
18-
for op in [Operation::Fetch, Operation::Push] {
19+
assert!(matches!(
20+
try_parse("^e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", Operation::Fetch).unwrap_err(),
21+
Error::NegativeObjectHash
22+
));
23+
}
24+
25+
#[test]
26+
fn negative_with_destination() {
27+
for spec in ["^a:b", "^a:", "^:", "^:b"] {
1928
assert!(matches!(
20-
try_parse("^e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", op).unwrap_err(),
21-
Error::NegativeObjectHash
29+
try_parse(spec, Operation::Fetch).unwrap_err(),
30+
Error::NegativeWithDestination
2231
));
2332
}
2433
}
2534

2635
#[test]
27-
fn negative_with_destination() {
28-
for op in [Operation::Fetch, Operation::Push] {
29-
for spec in ["^a:b", "^a:", "^:", "^:b"] {
30-
assert!(matches!(
31-
try_parse(spec, op).unwrap_err(),
32-
Error::NegativeWithDestination
33-
));
34-
}
36+
fn negative_unsupported_when_pushing() {
37+
for spec in ["^a:b", "^a:", "^:", "^:b", "^"] {
38+
assert!(matches!(
39+
try_parse(spec, Operation::Push).unwrap_err(),
40+
Error::NegativeUnsupported
41+
));
3542
}
3643
}
3744

3845
#[test]
3946
fn complex_patterns_with_more_than_one_asterisk() {
4047
for op in [Operation::Fetch, Operation::Push] {
41-
for spec in ["^*/*", "a/*/c/*", "a**:**b", "+:**/"] {
48+
for spec in ["a/*/c/*", "a**:**b", "+:**/"] {
4249
assert!(matches!(
4350
try_parse(spec, op).unwrap_err(),
4451
Error::PatternUnsupported { .. }
4552
));
4653
}
4754
}
55+
assert!(matches!(
56+
try_parse("^*/*", Operation::Fetch).unwrap_err(),
57+
Error::PatternUnsupported { .. }
58+
));
4859
}
4960

5061
#[test]

git-refspec/tests/parse/push.rs

-7
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
use crate::parse::{assert_parse, b};
22
use git_refspec::{Instruction, Mode, Push};
33

4-
#[test]
5-
fn exclude() {
6-
assert_parse("^a", Instruction::Push(Push::Exclude { src: b("a") }));
7-
assert_parse("^a*", Instruction::Push(Push::Exclude { src: b("a*") }));
8-
}
9-
104
#[test]
115
fn ampersand_is_resolved_to_head() {
126
assert_parse(
@@ -26,7 +20,6 @@ fn ampersand_is_resolved_to_head() {
2620
allow_non_fast_forward: true,
2721
}),
2822
);
29-
assert_parse("^@", Instruction::Push(Push::Exclude { src: b("HEAD") }));
3023
}
3124

3225
#[test]

0 commit comments

Comments
 (0)