##// END OF EJS Templates
rhg: fallback to slow path on invalid patterns in hgignore
Arseniy Alekseyev -
r50434:8076298b stable
parent child Browse files
Show More
@@ -1,290 +1,295 b''
1 use crate::ui::utf8_to_local;
1 use crate::ui::utf8_to_local;
2 use crate::ui::UiError;
2 use crate::ui::UiError;
3 use crate::NoRepoInCwdError;
3 use crate::NoRepoInCwdError;
4 use format_bytes::format_bytes;
4 use format_bytes::format_bytes;
5 use hg::config::{ConfigError, ConfigParseError, ConfigValueParseError};
5 use hg::config::{ConfigError, ConfigParseError, ConfigValueParseError};
6 use hg::dirstate_tree::on_disk::DirstateV2ParseError;
6 use hg::dirstate_tree::on_disk::DirstateV2ParseError;
7 use hg::errors::HgError;
7 use hg::errors::HgError;
8 use hg::exit_codes;
8 use hg::exit_codes;
9 use hg::repo::RepoError;
9 use hg::repo::RepoError;
10 use hg::revlog::revlog::RevlogError;
10 use hg::revlog::revlog::RevlogError;
11 use hg::sparse::SparseConfigError;
11 use hg::sparse::SparseConfigError;
12 use hg::utils::files::get_bytes_from_path;
12 use hg::utils::files::get_bytes_from_path;
13 use hg::{DirstateError, DirstateMapError, StatusError};
13 use hg::{DirstateError, DirstateMapError, StatusError};
14 use std::convert::From;
14 use std::convert::From;
15
15
16 /// The kind of command error
16 /// The kind of command error
17 #[derive(Debug)]
17 #[derive(Debug)]
18 pub enum CommandError {
18 pub enum CommandError {
19 /// Exit with an error message and "standard" failure exit code.
19 /// Exit with an error message and "standard" failure exit code.
20 Abort {
20 Abort {
21 message: Vec<u8>,
21 message: Vec<u8>,
22 detailed_exit_code: exit_codes::ExitCode,
22 detailed_exit_code: exit_codes::ExitCode,
23 hint: Option<Vec<u8>>,
23 hint: Option<Vec<u8>>,
24 },
24 },
25
25
26 /// Exit with a failure exit code but no message.
26 /// Exit with a failure exit code but no message.
27 Unsuccessful,
27 Unsuccessful,
28
28
29 /// Encountered something (such as a CLI argument, repository layout, …)
29 /// Encountered something (such as a CLI argument, repository layout, …)
30 /// not supported by this version of `rhg`. Depending on configuration
30 /// not supported by this version of `rhg`. Depending on configuration
31 /// `rhg` may attempt to silently fall back to Python-based `hg`, which
31 /// `rhg` may attempt to silently fall back to Python-based `hg`, which
32 /// may or may not support this feature.
32 /// may or may not support this feature.
33 UnsupportedFeature { message: Vec<u8> },
33 UnsupportedFeature { message: Vec<u8> },
34 /// The fallback executable does not exist (or has some other problem if
34 /// The fallback executable does not exist (or has some other problem if
35 /// we end up being more precise about broken fallbacks).
35 /// we end up being more precise about broken fallbacks).
36 InvalidFallback { path: Vec<u8>, err: String },
36 InvalidFallback { path: Vec<u8>, err: String },
37 }
37 }
38
38
39 impl CommandError {
39 impl CommandError {
40 pub fn abort(message: impl AsRef<str>) -> Self {
40 pub fn abort(message: impl AsRef<str>) -> Self {
41 CommandError::abort_with_exit_code(message, exit_codes::ABORT)
41 CommandError::abort_with_exit_code(message, exit_codes::ABORT)
42 }
42 }
43
43
44 pub fn abort_with_exit_code(
44 pub fn abort_with_exit_code(
45 message: impl AsRef<str>,
45 message: impl AsRef<str>,
46 detailed_exit_code: exit_codes::ExitCode,
46 detailed_exit_code: exit_codes::ExitCode,
47 ) -> Self {
47 ) -> Self {
48 CommandError::Abort {
48 CommandError::Abort {
49 // TODO: bytes-based (instead of Unicode-based) formatting
49 // TODO: bytes-based (instead of Unicode-based) formatting
50 // of error messages to handle non-UTF-8 filenames etc:
50 // of error messages to handle non-UTF-8 filenames etc:
51 // https://www.mercurial-scm.org/wiki/EncodingStrategy#Mixing_output
51 // https://www.mercurial-scm.org/wiki/EncodingStrategy#Mixing_output
52 message: utf8_to_local(message.as_ref()).into(),
52 message: utf8_to_local(message.as_ref()).into(),
53 detailed_exit_code: detailed_exit_code,
53 detailed_exit_code: detailed_exit_code,
54 hint: None,
54 hint: None,
55 }
55 }
56 }
56 }
57
57
58 pub fn abort_with_exit_code_and_hint(
58 pub fn abort_with_exit_code_and_hint(
59 message: impl AsRef<str>,
59 message: impl AsRef<str>,
60 detailed_exit_code: exit_codes::ExitCode,
60 detailed_exit_code: exit_codes::ExitCode,
61 hint: Option<impl AsRef<str>>,
61 hint: Option<impl AsRef<str>>,
62 ) -> Self {
62 ) -> Self {
63 CommandError::Abort {
63 CommandError::Abort {
64 message: utf8_to_local(message.as_ref()).into(),
64 message: utf8_to_local(message.as_ref()).into(),
65 detailed_exit_code,
65 detailed_exit_code,
66 hint: hint.map(|h| utf8_to_local(h.as_ref()).into()),
66 hint: hint.map(|h| utf8_to_local(h.as_ref()).into()),
67 }
67 }
68 }
68 }
69
69
70 pub fn abort_with_exit_code_bytes(
70 pub fn abort_with_exit_code_bytes(
71 message: impl AsRef<[u8]>,
71 message: impl AsRef<[u8]>,
72 detailed_exit_code: exit_codes::ExitCode,
72 detailed_exit_code: exit_codes::ExitCode,
73 ) -> Self {
73 ) -> Self {
74 // TODO: use this everywhere it makes sense instead of the string
74 // TODO: use this everywhere it makes sense instead of the string
75 // version.
75 // version.
76 CommandError::Abort {
76 CommandError::Abort {
77 message: message.as_ref().into(),
77 message: message.as_ref().into(),
78 detailed_exit_code,
78 detailed_exit_code,
79 hint: None,
79 hint: None,
80 }
80 }
81 }
81 }
82
82
83 pub fn unsupported(message: impl AsRef<str>) -> Self {
83 pub fn unsupported(message: impl AsRef<str>) -> Self {
84 CommandError::UnsupportedFeature {
84 CommandError::UnsupportedFeature {
85 message: utf8_to_local(message.as_ref()).into(),
85 message: utf8_to_local(message.as_ref()).into(),
86 }
86 }
87 }
87 }
88 }
88 }
89
89
90 /// For now we don’t differenciate between invalid CLI args and valid for `hg`
90 /// For now we don’t differenciate between invalid CLI args and valid for `hg`
91 /// but not supported yet by `rhg`.
91 /// but not supported yet by `rhg`.
92 impl From<clap::Error> for CommandError {
92 impl From<clap::Error> for CommandError {
93 fn from(error: clap::Error) -> Self {
93 fn from(error: clap::Error) -> Self {
94 CommandError::unsupported(error.to_string())
94 CommandError::unsupported(error.to_string())
95 }
95 }
96 }
96 }
97
97
98 impl From<HgError> for CommandError {
98 impl From<HgError> for CommandError {
99 fn from(error: HgError) -> Self {
99 fn from(error: HgError) -> Self {
100 match error {
100 match error {
101 HgError::UnsupportedFeature(message) => {
101 HgError::UnsupportedFeature(message) => {
102 CommandError::unsupported(message)
102 CommandError::unsupported(message)
103 }
103 }
104 HgError::CensoredNodeError => {
104 HgError::CensoredNodeError => {
105 CommandError::unsupported("Encountered a censored node")
105 CommandError::unsupported("Encountered a censored node")
106 }
106 }
107 HgError::Abort {
107 HgError::Abort {
108 message,
108 message,
109 detailed_exit_code,
109 detailed_exit_code,
110 hint,
110 hint,
111 } => CommandError::abort_with_exit_code_and_hint(
111 } => CommandError::abort_with_exit_code_and_hint(
112 message,
112 message,
113 detailed_exit_code,
113 detailed_exit_code,
114 hint,
114 hint,
115 ),
115 ),
116 _ => CommandError::abort(error.to_string()),
116 _ => CommandError::abort(error.to_string()),
117 }
117 }
118 }
118 }
119 }
119 }
120
120
121 impl From<ConfigValueParseError> for CommandError {
121 impl From<ConfigValueParseError> for CommandError {
122 fn from(error: ConfigValueParseError) -> Self {
122 fn from(error: ConfigValueParseError) -> Self {
123 CommandError::abort_with_exit_code(
123 CommandError::abort_with_exit_code(
124 error.to_string(),
124 error.to_string(),
125 exit_codes::CONFIG_ERROR_ABORT,
125 exit_codes::CONFIG_ERROR_ABORT,
126 )
126 )
127 }
127 }
128 }
128 }
129
129
130 impl From<UiError> for CommandError {
130 impl From<UiError> for CommandError {
131 fn from(_error: UiError) -> Self {
131 fn from(_error: UiError) -> Self {
132 // If we already failed writing to stdout or stderr,
132 // If we already failed writing to stdout or stderr,
133 // writing an error message to stderr about it would be likely to fail
133 // writing an error message to stderr about it would be likely to fail
134 // too.
134 // too.
135 CommandError::abort("")
135 CommandError::abort("")
136 }
136 }
137 }
137 }
138
138
139 impl From<RepoError> for CommandError {
139 impl From<RepoError> for CommandError {
140 fn from(error: RepoError) -> Self {
140 fn from(error: RepoError) -> Self {
141 match error {
141 match error {
142 RepoError::NotFound { at } => {
142 RepoError::NotFound { at } => {
143 CommandError::abort_with_exit_code_bytes(
143 CommandError::abort_with_exit_code_bytes(
144 format_bytes!(
144 format_bytes!(
145 b"abort: repository {} not found",
145 b"abort: repository {} not found",
146 get_bytes_from_path(at)
146 get_bytes_from_path(at)
147 ),
147 ),
148 exit_codes::ABORT,
148 exit_codes::ABORT,
149 )
149 )
150 }
150 }
151 RepoError::ConfigParseError(error) => error.into(),
151 RepoError::ConfigParseError(error) => error.into(),
152 RepoError::Other(error) => error.into(),
152 RepoError::Other(error) => error.into(),
153 }
153 }
154 }
154 }
155 }
155 }
156
156
157 impl<'a> From<&'a NoRepoInCwdError> for CommandError {
157 impl<'a> From<&'a NoRepoInCwdError> for CommandError {
158 fn from(error: &'a NoRepoInCwdError) -> Self {
158 fn from(error: &'a NoRepoInCwdError) -> Self {
159 let NoRepoInCwdError { cwd } = error;
159 let NoRepoInCwdError { cwd } = error;
160 CommandError::abort_with_exit_code_bytes(
160 CommandError::abort_with_exit_code_bytes(
161 format_bytes!(
161 format_bytes!(
162 b"abort: no repository found in '{}' (.hg not found)!",
162 b"abort: no repository found in '{}' (.hg not found)!",
163 get_bytes_from_path(cwd)
163 get_bytes_from_path(cwd)
164 ),
164 ),
165 exit_codes::ABORT,
165 exit_codes::ABORT,
166 )
166 )
167 }
167 }
168 }
168 }
169
169
170 impl From<ConfigError> for CommandError {
170 impl From<ConfigError> for CommandError {
171 fn from(error: ConfigError) -> Self {
171 fn from(error: ConfigError) -> Self {
172 match error {
172 match error {
173 ConfigError::Parse(error) => error.into(),
173 ConfigError::Parse(error) => error.into(),
174 ConfigError::Other(error) => error.into(),
174 ConfigError::Other(error) => error.into(),
175 }
175 }
176 }
176 }
177 }
177 }
178
178
179 impl From<ConfigParseError> for CommandError {
179 impl From<ConfigParseError> for CommandError {
180 fn from(error: ConfigParseError) -> Self {
180 fn from(error: ConfigParseError) -> Self {
181 let ConfigParseError {
181 let ConfigParseError {
182 origin,
182 origin,
183 line,
183 line,
184 message,
184 message,
185 } = error;
185 } = error;
186 let line_message = if let Some(line_number) = line {
186 let line_message = if let Some(line_number) = line {
187 format_bytes!(b":{}", line_number.to_string().into_bytes())
187 format_bytes!(b":{}", line_number.to_string().into_bytes())
188 } else {
188 } else {
189 Vec::new()
189 Vec::new()
190 };
190 };
191 CommandError::abort_with_exit_code_bytes(
191 CommandError::abort_with_exit_code_bytes(
192 format_bytes!(
192 format_bytes!(
193 b"config error at {}{}: {}",
193 b"config error at {}{}: {}",
194 origin,
194 origin,
195 line_message,
195 line_message,
196 message
196 message
197 ),
197 ),
198 exit_codes::CONFIG_ERROR_ABORT,
198 exit_codes::CONFIG_ERROR_ABORT,
199 )
199 )
200 }
200 }
201 }
201 }
202
202
203 impl From<(RevlogError, &str)> for CommandError {
203 impl From<(RevlogError, &str)> for CommandError {
204 fn from((err, rev): (RevlogError, &str)) -> CommandError {
204 fn from((err, rev): (RevlogError, &str)) -> CommandError {
205 match err {
205 match err {
206 RevlogError::WDirUnsupported => CommandError::abort(
206 RevlogError::WDirUnsupported => CommandError::abort(
207 "abort: working directory revision cannot be specified",
207 "abort: working directory revision cannot be specified",
208 ),
208 ),
209 RevlogError::InvalidRevision => CommandError::abort(format!(
209 RevlogError::InvalidRevision => CommandError::abort(format!(
210 "abort: invalid revision identifier: {}",
210 "abort: invalid revision identifier: {}",
211 rev
211 rev
212 )),
212 )),
213 RevlogError::AmbiguousPrefix => CommandError::abort(format!(
213 RevlogError::AmbiguousPrefix => CommandError::abort(format!(
214 "abort: ambiguous revision identifier: {}",
214 "abort: ambiguous revision identifier: {}",
215 rev
215 rev
216 )),
216 )),
217 RevlogError::Other(error) => error.into(),
217 RevlogError::Other(error) => error.into(),
218 }
218 }
219 }
219 }
220 }
220 }
221
221
222 impl From<StatusError> for CommandError {
222 impl From<StatusError> for CommandError {
223 fn from(error: StatusError) -> Self {
223 fn from(error: StatusError) -> Self {
224 CommandError::abort(format!("{}", error))
224 match error {
225 StatusError::Pattern(_) => {
226 CommandError::unsupported(format!("{}", error))
227 }
228 _ => CommandError::abort(format!("{}", error)),
229 }
225 }
230 }
226 }
231 }
227
232
228 impl From<DirstateMapError> for CommandError {
233 impl From<DirstateMapError> for CommandError {
229 fn from(error: DirstateMapError) -> Self {
234 fn from(error: DirstateMapError) -> Self {
230 CommandError::abort(format!("{}", error))
235 CommandError::abort(format!("{}", error))
231 }
236 }
232 }
237 }
233
238
234 impl From<DirstateError> for CommandError {
239 impl From<DirstateError> for CommandError {
235 fn from(error: DirstateError) -> Self {
240 fn from(error: DirstateError) -> Self {
236 match error {
241 match error {
237 DirstateError::Common(error) => error.into(),
242 DirstateError::Common(error) => error.into(),
238 DirstateError::Map(error) => error.into(),
243 DirstateError::Map(error) => error.into(),
239 }
244 }
240 }
245 }
241 }
246 }
242
247
243 impl From<DirstateV2ParseError> for CommandError {
248 impl From<DirstateV2ParseError> for CommandError {
244 fn from(error: DirstateV2ParseError) -> Self {
249 fn from(error: DirstateV2ParseError) -> Self {
245 HgError::from(error).into()
250 HgError::from(error).into()
246 }
251 }
247 }
252 }
248
253
249 impl From<SparseConfigError> for CommandError {
254 impl From<SparseConfigError> for CommandError {
250 fn from(e: SparseConfigError) -> Self {
255 fn from(e: SparseConfigError) -> Self {
251 match e {
256 match e {
252 SparseConfigError::IncludesAfterExcludes { context } => {
257 SparseConfigError::IncludesAfterExcludes { context } => {
253 Self::abort_with_exit_code_bytes(
258 Self::abort_with_exit_code_bytes(
254 format_bytes!(
259 format_bytes!(
255 b"{} config cannot have includes after excludes",
260 b"{} config cannot have includes after excludes",
256 context
261 context
257 ),
262 ),
258 exit_codes::CONFIG_PARSE_ERROR_ABORT,
263 exit_codes::CONFIG_PARSE_ERROR_ABORT,
259 )
264 )
260 }
265 }
261 SparseConfigError::EntryOutsideSection { context, line } => {
266 SparseConfigError::EntryOutsideSection { context, line } => {
262 Self::abort_with_exit_code_bytes(
267 Self::abort_with_exit_code_bytes(
263 format_bytes!(
268 format_bytes!(
264 b"{} config entry outside of section: {}",
269 b"{} config entry outside of section: {}",
265 context,
270 context,
266 &line,
271 &line,
267 ),
272 ),
268 exit_codes::CONFIG_PARSE_ERROR_ABORT,
273 exit_codes::CONFIG_PARSE_ERROR_ABORT,
269 )
274 )
270 }
275 }
271 SparseConfigError::InvalidNarrowPrefix(prefix) => {
276 SparseConfigError::InvalidNarrowPrefix(prefix) => {
272 Self::abort_with_exit_code_bytes(
277 Self::abort_with_exit_code_bytes(
273 format_bytes!(
278 format_bytes!(
274 b"invalid prefix on narrow pattern: {}",
279 b"invalid prefix on narrow pattern: {}",
275 &prefix
280 &prefix
276 ),
281 ),
277 exit_codes::ABORT,
282 exit_codes::ABORT,
278 )
283 )
279 }
284 }
280 SparseConfigError::IncludesInNarrow => Self::abort(
285 SparseConfigError::IncludesInNarrow => Self::abort(
281 "including other spec files using '%include' \
286 "including other spec files using '%include' \
282 is not supported in narrowspec",
287 is not supported in narrowspec",
283 ),
288 ),
284 SparseConfigError::HgError(e) => Self::from(e),
289 SparseConfigError::HgError(e) => Self::from(e),
285 SparseConfigError::PatternError(e) => {
290 SparseConfigError::PatternError(e) => {
286 Self::unsupported(format!("{}", e))
291 Self::unsupported(format!("{}", e))
287 }
292 }
288 }
293 }
289 }
294 }
290 }
295 }
@@ -1,449 +1,437 b''
1 #testcases dirstate-v1 dirstate-v2
1 #testcases dirstate-v1 dirstate-v2
2
2
3 #if dirstate-v2
3 #if dirstate-v2
4 $ cat >> $HGRCPATH << EOF
4 $ cat >> $HGRCPATH << EOF
5 > [format]
5 > [format]
6 > use-dirstate-v2=1
6 > use-dirstate-v2=1
7 > [storage]
7 > [storage]
8 > dirstate-v2.slow-path=allow
8 > dirstate-v2.slow-path=allow
9 > EOF
9 > EOF
10 #endif
10 #endif
11
11
12 $ hg init ignorerepo
12 $ hg init ignorerepo
13 $ cd ignorerepo
13 $ cd ignorerepo
14
14
15 debugignore with no hgignore should be deterministic:
15 debugignore with no hgignore should be deterministic:
16 $ hg debugignore
16 $ hg debugignore
17 <nevermatcher>
17 <nevermatcher>
18
18
19 Issue562: .hgignore requires newline at end:
19 Issue562: .hgignore requires newline at end:
20
20
21 $ touch foo
21 $ touch foo
22 $ touch bar
22 $ touch bar
23 $ touch baz
23 $ touch baz
24 $ cat > makeignore.py <<EOF
24 $ cat > makeignore.py <<EOF
25 > f = open(".hgignore", "w")
25 > f = open(".hgignore", "w")
26 > f.write("ignore\n")
26 > f.write("ignore\n")
27 > f.write("foo\n")
27 > f.write("foo\n")
28 > # No EOL here
28 > # No EOL here
29 > f.write("bar")
29 > f.write("bar")
30 > f.close()
30 > f.close()
31 > EOF
31 > EOF
32
32
33 $ "$PYTHON" makeignore.py
33 $ "$PYTHON" makeignore.py
34
34
35 Should display baz only:
35 Should display baz only:
36
36
37 $ hg status
37 $ hg status
38 ? baz
38 ? baz
39
39
40 $ rm foo bar baz .hgignore makeignore.py
40 $ rm foo bar baz .hgignore makeignore.py
41
41
42 $ touch a.o
42 $ touch a.o
43 $ touch a.c
43 $ touch a.c
44 $ touch syntax
44 $ touch syntax
45 $ mkdir dir
45 $ mkdir dir
46 $ touch dir/a.o
46 $ touch dir/a.o
47 $ touch dir/b.o
47 $ touch dir/b.o
48 $ touch dir/c.o
48 $ touch dir/c.o
49
49
50 $ hg add dir/a.o
50 $ hg add dir/a.o
51 $ hg commit -m 0
51 $ hg commit -m 0
52 $ hg add dir/b.o
52 $ hg add dir/b.o
53
53
54 $ hg status
54 $ hg status
55 A dir/b.o
55 A dir/b.o
56 ? a.c
56 ? a.c
57 ? a.o
57 ? a.o
58 ? dir/c.o
58 ? dir/c.o
59 ? syntax
59 ? syntax
60
60
61 $ echo "*.o" > .hgignore
61 $ echo "*.o" > .hgignore
62 #if no-rhg
63 $ hg status
62 $ hg status
64 abort: $TESTTMP/ignorerepo/.hgignore: invalid pattern (relre): *.o (glob)
63 abort: $TESTTMP/ignorerepo/.hgignore: invalid pattern (relre): *.o (glob)
65 [255]
64 [255]
66 #endif
67 #if rhg
68 $ hg status
69 Unsupported syntax regex parse error:
70 ^(?:*.o)
71 ^
72 error: repetition operator missing expression
73 [255]
74 #endif
75
65
76 $ echo 're:^(?!a).*\.o$' > .hgignore
66 $ echo 're:^(?!a).*\.o$' > .hgignore
77 #if no-rhg
78 $ hg status
67 $ hg status
79 A dir/b.o
68 A dir/b.o
80 ? .hgignore
69 ? .hgignore
81 ? a.c
70 ? a.c
82 ? a.o
71 ? a.o
83 ? syntax
72 ? syntax
84 #endif
85 #if rhg
73 #if rhg
86 $ hg status
74 $ hg status --config rhg.on-unsupported=abort
87 Unsupported syntax regex parse error:
75 unsupported feature: Unsupported syntax regex parse error:
88 ^(?:^(?!a).*\.o$)
76 ^(?:^(?!a).*\.o$)
89 ^^^
77 ^^^
90 error: look-around, including look-ahead and look-behind, is not supported
78 error: look-around, including look-ahead and look-behind, is not supported
91 [255]
79 [252]
92 #endif
80 #endif
93
81
94 Ensure given files are relative to cwd
82 Ensure given files are relative to cwd
95
83
96 $ echo "dir/.*\.o" > .hgignore
84 $ echo "dir/.*\.o" > .hgignore
97 $ hg status -i
85 $ hg status -i
98 I dir/c.o
86 I dir/c.o
99
87
100 $ hg debugignore dir/c.o dir/missing.o
88 $ hg debugignore dir/c.o dir/missing.o
101 dir/c.o is ignored
89 dir/c.o is ignored
102 (ignore rule in $TESTTMP/ignorerepo/.hgignore, line 1: 'dir/.*\.o') (glob)
90 (ignore rule in $TESTTMP/ignorerepo/.hgignore, line 1: 'dir/.*\.o') (glob)
103 dir/missing.o is ignored
91 dir/missing.o is ignored
104 (ignore rule in $TESTTMP/ignorerepo/.hgignore, line 1: 'dir/.*\.o') (glob)
92 (ignore rule in $TESTTMP/ignorerepo/.hgignore, line 1: 'dir/.*\.o') (glob)
105 $ cd dir
93 $ cd dir
106 $ hg debugignore c.o missing.o
94 $ hg debugignore c.o missing.o
107 c.o is ignored
95 c.o is ignored
108 (ignore rule in $TESTTMP/ignorerepo/.hgignore, line 1: 'dir/.*\.o') (glob)
96 (ignore rule in $TESTTMP/ignorerepo/.hgignore, line 1: 'dir/.*\.o') (glob)
109 missing.o is ignored
97 missing.o is ignored
110 (ignore rule in $TESTTMP/ignorerepo/.hgignore, line 1: 'dir/.*\.o') (glob)
98 (ignore rule in $TESTTMP/ignorerepo/.hgignore, line 1: 'dir/.*\.o') (glob)
111
99
112 For icasefs, inexact matches also work, except for missing files
100 For icasefs, inexact matches also work, except for missing files
113
101
114 #if icasefs
102 #if icasefs
115 $ hg debugignore c.O missing.O
103 $ hg debugignore c.O missing.O
116 c.o is ignored
104 c.o is ignored
117 (ignore rule in $TESTTMP/ignorerepo/.hgignore, line 1: 'dir/.*\.o') (glob)
105 (ignore rule in $TESTTMP/ignorerepo/.hgignore, line 1: 'dir/.*\.o') (glob)
118 missing.O is not ignored
106 missing.O is not ignored
119 #endif
107 #endif
120
108
121 $ cd ..
109 $ cd ..
122
110
123 $ echo ".*\.o" > .hgignore
111 $ echo ".*\.o" > .hgignore
124 $ hg status
112 $ hg status
125 A dir/b.o
113 A dir/b.o
126 ? .hgignore
114 ? .hgignore
127 ? a.c
115 ? a.c
128 ? syntax
116 ? syntax
129
117
130 Ensure that comments work:
118 Ensure that comments work:
131
119
132 $ touch 'foo#bar' 'quux#' 'quu0#'
120 $ touch 'foo#bar' 'quux#' 'quu0#'
133 #if no-windows
121 #if no-windows
134 $ touch 'baz\' 'baz\wat' 'ba0\#wat' 'ba1\\' 'ba1\\wat' 'quu0\'
122 $ touch 'baz\' 'baz\wat' 'ba0\#wat' 'ba1\\' 'ba1\\wat' 'quu0\'
135 #endif
123 #endif
136
124
137 $ cat <<'EOF' >> .hgignore
125 $ cat <<'EOF' >> .hgignore
138 > # full-line comment
126 > # full-line comment
139 > # whitespace-only comment line
127 > # whitespace-only comment line
140 > syntax# pattern, no whitespace, then comment
128 > syntax# pattern, no whitespace, then comment
141 > a.c # pattern, then whitespace, then comment
129 > a.c # pattern, then whitespace, then comment
142 > baz\\# # (escaped) backslash, then comment
130 > baz\\# # (escaped) backslash, then comment
143 > ba0\\\#w # (escaped) backslash, escaped comment character, then comment
131 > ba0\\\#w # (escaped) backslash, escaped comment character, then comment
144 > ba1\\\\# # (escaped) backslashes, then comment
132 > ba1\\\\# # (escaped) backslashes, then comment
145 > foo\#b # escaped comment character
133 > foo\#b # escaped comment character
146 > quux\## escaped comment character at end of name
134 > quux\## escaped comment character at end of name
147 > EOF
135 > EOF
148 $ hg status
136 $ hg status
149 A dir/b.o
137 A dir/b.o
150 ? .hgignore
138 ? .hgignore
151 ? quu0#
139 ? quu0#
152 ? quu0\ (no-windows !)
140 ? quu0\ (no-windows !)
153
141
154 $ cat <<'EOF' > .hgignore
142 $ cat <<'EOF' > .hgignore
155 > .*\.o
143 > .*\.o
156 > syntax: glob
144 > syntax: glob
157 > syntax# pattern, no whitespace, then comment
145 > syntax# pattern, no whitespace, then comment
158 > a.c # pattern, then whitespace, then comment
146 > a.c # pattern, then whitespace, then comment
159 > baz\\#* # (escaped) backslash, then comment
147 > baz\\#* # (escaped) backslash, then comment
160 > ba0\\\#w* # (escaped) backslash, escaped comment character, then comment
148 > ba0\\\#w* # (escaped) backslash, escaped comment character, then comment
161 > ba1\\\\#* # (escaped) backslashes, then comment
149 > ba1\\\\#* # (escaped) backslashes, then comment
162 > foo\#b* # escaped comment character
150 > foo\#b* # escaped comment character
163 > quux\## escaped comment character at end of name
151 > quux\## escaped comment character at end of name
164 > quu0[\#]# escaped comment character inside [...]
152 > quu0[\#]# escaped comment character inside [...]
165 > EOF
153 > EOF
166 $ hg status
154 $ hg status
167 A dir/b.o
155 A dir/b.o
168 ? .hgignore
156 ? .hgignore
169 ? ba1\\wat (no-windows !)
157 ? ba1\\wat (no-windows !)
170 ? baz\wat (no-windows !)
158 ? baz\wat (no-windows !)
171 ? quu0\ (no-windows !)
159 ? quu0\ (no-windows !)
172
160
173 $ rm 'foo#bar' 'quux#' 'quu0#'
161 $ rm 'foo#bar' 'quux#' 'quu0#'
174 #if no-windows
162 #if no-windows
175 $ rm 'baz\' 'baz\wat' 'ba0\#wat' 'ba1\\' 'ba1\\wat' 'quu0\'
163 $ rm 'baz\' 'baz\wat' 'ba0\#wat' 'ba1\\' 'ba1\\wat' 'quu0\'
176 #endif
164 #endif
177
165
178 Check that '^\.' does not ignore the root directory:
166 Check that '^\.' does not ignore the root directory:
179
167
180 $ echo "^\." > .hgignore
168 $ echo "^\." > .hgignore
181 $ hg status
169 $ hg status
182 A dir/b.o
170 A dir/b.o
183 ? a.c
171 ? a.c
184 ? a.o
172 ? a.o
185 ? dir/c.o
173 ? dir/c.o
186 ? syntax
174 ? syntax
187
175
188 Test that patterns from ui.ignore options are read:
176 Test that patterns from ui.ignore options are read:
189
177
190 $ echo > .hgignore
178 $ echo > .hgignore
191 $ cat >> $HGRCPATH << EOF
179 $ cat >> $HGRCPATH << EOF
192 > [ui]
180 > [ui]
193 > ignore.other = $TESTTMP/ignorerepo/.hg/testhgignore
181 > ignore.other = $TESTTMP/ignorerepo/.hg/testhgignore
194 > EOF
182 > EOF
195 $ echo "glob:**.o" > .hg/testhgignore
183 $ echo "glob:**.o" > .hg/testhgignore
196 $ hg status
184 $ hg status
197 A dir/b.o
185 A dir/b.o
198 ? .hgignore
186 ? .hgignore
199 ? a.c
187 ? a.c
200 ? syntax
188 ? syntax
201
189
202 empty out testhgignore
190 empty out testhgignore
203 $ echo > .hg/testhgignore
191 $ echo > .hg/testhgignore
204
192
205 Test relative ignore path (issue4473):
193 Test relative ignore path (issue4473):
206
194
207 $ cat >> $HGRCPATH << EOF
195 $ cat >> $HGRCPATH << EOF
208 > [ui]
196 > [ui]
209 > ignore.relative = .hg/testhgignorerel
197 > ignore.relative = .hg/testhgignorerel
210 > EOF
198 > EOF
211 $ echo "glob:*.o" > .hg/testhgignorerel
199 $ echo "glob:*.o" > .hg/testhgignorerel
212 $ cd dir
200 $ cd dir
213 $ hg status
201 $ hg status
214 A dir/b.o
202 A dir/b.o
215 ? .hgignore
203 ? .hgignore
216 ? a.c
204 ? a.c
217 ? syntax
205 ? syntax
218 $ hg debugignore
206 $ hg debugignore
219 <includematcher includes='.*\\.o(?:/|$)'>
207 <includematcher includes='.*\\.o(?:/|$)'>
220
208
221 $ cd ..
209 $ cd ..
222 $ echo > .hg/testhgignorerel
210 $ echo > .hg/testhgignorerel
223 $ echo "syntax: glob" > .hgignore
211 $ echo "syntax: glob" > .hgignore
224 $ echo "re:.*\.o" >> .hgignore
212 $ echo "re:.*\.o" >> .hgignore
225 $ hg status
213 $ hg status
226 A dir/b.o
214 A dir/b.o
227 ? .hgignore
215 ? .hgignore
228 ? a.c
216 ? a.c
229 ? syntax
217 ? syntax
230
218
231 $ echo "syntax: invalid" > .hgignore
219 $ echo "syntax: invalid" > .hgignore
232 $ hg status
220 $ hg status
233 $TESTTMP/ignorerepo/.hgignore: ignoring invalid syntax 'invalid'
221 $TESTTMP/ignorerepo/.hgignore: ignoring invalid syntax 'invalid'
234 A dir/b.o
222 A dir/b.o
235 ? .hgignore
223 ? .hgignore
236 ? a.c
224 ? a.c
237 ? a.o
225 ? a.o
238 ? dir/c.o
226 ? dir/c.o
239 ? syntax
227 ? syntax
240
228
241 $ echo "syntax: glob" > .hgignore
229 $ echo "syntax: glob" > .hgignore
242 $ echo "*.o" >> .hgignore
230 $ echo "*.o" >> .hgignore
243 $ hg status
231 $ hg status
244 A dir/b.o
232 A dir/b.o
245 ? .hgignore
233 ? .hgignore
246 ? a.c
234 ? a.c
247 ? syntax
235 ? syntax
248
236
249 $ echo "relglob:syntax*" > .hgignore
237 $ echo "relglob:syntax*" > .hgignore
250 $ hg status
238 $ hg status
251 A dir/b.o
239 A dir/b.o
252 ? .hgignore
240 ? .hgignore
253 ? a.c
241 ? a.c
254 ? a.o
242 ? a.o
255 ? dir/c.o
243 ? dir/c.o
256
244
257 $ echo "relglob:*" > .hgignore
245 $ echo "relglob:*" > .hgignore
258 $ hg status
246 $ hg status
259 A dir/b.o
247 A dir/b.o
260
248
261 $ cd dir
249 $ cd dir
262 $ hg status .
250 $ hg status .
263 A b.o
251 A b.o
264
252
265 $ hg debugignore
253 $ hg debugignore
266 <includematcher includes='.*(?:/|$)'>
254 <includematcher includes='.*(?:/|$)'>
267
255
268 $ hg debugignore b.o
256 $ hg debugignore b.o
269 b.o is ignored
257 b.o is ignored
270 (ignore rule in $TESTTMP/ignorerepo/.hgignore, line 1: '*') (glob)
258 (ignore rule in $TESTTMP/ignorerepo/.hgignore, line 1: '*') (glob)
271
259
272 $ cd ..
260 $ cd ..
273
261
274 Check patterns that match only the directory
262 Check patterns that match only the directory
275
263
276 "(fsmonitor !)" below assumes that fsmonitor is enabled with
264 "(fsmonitor !)" below assumes that fsmonitor is enabled with
277 "walk_on_invalidate = false" (default), which doesn't involve
265 "walk_on_invalidate = false" (default), which doesn't involve
278 re-walking whole repository at detection of .hgignore change.
266 re-walking whole repository at detection of .hgignore change.
279
267
280 $ echo "^dir\$" > .hgignore
268 $ echo "^dir\$" > .hgignore
281 $ hg status
269 $ hg status
282 A dir/b.o
270 A dir/b.o
283 ? .hgignore
271 ? .hgignore
284 ? a.c
272 ? a.c
285 ? a.o
273 ? a.o
286 ? dir/c.o (fsmonitor !)
274 ? dir/c.o (fsmonitor !)
287 ? syntax
275 ? syntax
288
276
289 Check recursive glob pattern matches no directories (dir/**/c.o matches dir/c.o)
277 Check recursive glob pattern matches no directories (dir/**/c.o matches dir/c.o)
290
278
291 $ echo "syntax: glob" > .hgignore
279 $ echo "syntax: glob" > .hgignore
292 $ echo "dir/**/c.o" >> .hgignore
280 $ echo "dir/**/c.o" >> .hgignore
293 $ touch dir/c.o
281 $ touch dir/c.o
294 $ mkdir dir/subdir
282 $ mkdir dir/subdir
295 $ touch dir/subdir/c.o
283 $ touch dir/subdir/c.o
296 $ hg status
284 $ hg status
297 A dir/b.o
285 A dir/b.o
298 ? .hgignore
286 ? .hgignore
299 ? a.c
287 ? a.c
300 ? a.o
288 ? a.o
301 ? syntax
289 ? syntax
302 $ hg debugignore a.c
290 $ hg debugignore a.c
303 a.c is not ignored
291 a.c is not ignored
304 $ hg debugignore dir/c.o
292 $ hg debugignore dir/c.o
305 dir/c.o is ignored
293 dir/c.o is ignored
306 (ignore rule in $TESTTMP/ignorerepo/.hgignore, line 2: 'dir/**/c.o') (glob)
294 (ignore rule in $TESTTMP/ignorerepo/.hgignore, line 2: 'dir/**/c.o') (glob)
307
295
308 Check rooted globs
296 Check rooted globs
309
297
310 $ hg purge --all --config extensions.purge=
298 $ hg purge --all --config extensions.purge=
311 $ echo "syntax: rootglob" > .hgignore
299 $ echo "syntax: rootglob" > .hgignore
312 $ echo "a/*.ext" >> .hgignore
300 $ echo "a/*.ext" >> .hgignore
313 $ for p in a b/a aa; do mkdir -p $p; touch $p/b.ext; done
301 $ for p in a b/a aa; do mkdir -p $p; touch $p/b.ext; done
314 $ hg status -A 'set:**.ext'
302 $ hg status -A 'set:**.ext'
315 ? aa/b.ext
303 ? aa/b.ext
316 ? b/a/b.ext
304 ? b/a/b.ext
317 I a/b.ext
305 I a/b.ext
318
306
319 Check using 'include:' in ignore file
307 Check using 'include:' in ignore file
320
308
321 $ hg purge --all --config extensions.purge=
309 $ hg purge --all --config extensions.purge=
322 $ touch foo.included
310 $ touch foo.included
323
311
324 $ echo ".*.included" > otherignore
312 $ echo ".*.included" > otherignore
325 $ hg status -I "include:otherignore"
313 $ hg status -I "include:otherignore"
326 ? foo.included
314 ? foo.included
327
315
328 $ echo "include:otherignore" >> .hgignore
316 $ echo "include:otherignore" >> .hgignore
329 $ hg status
317 $ hg status
330 A dir/b.o
318 A dir/b.o
331 ? .hgignore
319 ? .hgignore
332 ? otherignore
320 ? otherignore
333
321
334 Check recursive uses of 'include:'
322 Check recursive uses of 'include:'
335
323
336 $ echo "include:nested/ignore" >> otherignore
324 $ echo "include:nested/ignore" >> otherignore
337 $ mkdir nested nested/more
325 $ mkdir nested nested/more
338 $ echo "glob:*ignore" > nested/ignore
326 $ echo "glob:*ignore" > nested/ignore
339 $ echo "rootglob:a" >> nested/ignore
327 $ echo "rootglob:a" >> nested/ignore
340 $ touch a nested/a nested/more/a
328 $ touch a nested/a nested/more/a
341 $ hg status
329 $ hg status
342 A dir/b.o
330 A dir/b.o
343 ? nested/a
331 ? nested/a
344 ? nested/more/a
332 ? nested/more/a
345 $ rm a nested/a nested/more/a
333 $ rm a nested/a nested/more/a
346
334
347 $ cp otherignore goodignore
335 $ cp otherignore goodignore
348 $ echo "include:badignore" >> otherignore
336 $ echo "include:badignore" >> otherignore
349 $ hg status
337 $ hg status
350 skipping unreadable pattern file 'badignore': $ENOENT$
338 skipping unreadable pattern file 'badignore': $ENOENT$
351 A dir/b.o
339 A dir/b.o
352
340
353 $ mv goodignore otherignore
341 $ mv goodignore otherignore
354
342
355 Check using 'include:' while in a non-root directory
343 Check using 'include:' while in a non-root directory
356
344
357 $ cd ..
345 $ cd ..
358 $ hg -R ignorerepo status
346 $ hg -R ignorerepo status
359 A dir/b.o
347 A dir/b.o
360 $ cd ignorerepo
348 $ cd ignorerepo
361
349
362 Check including subincludes
350 Check including subincludes
363
351
364 $ hg revert -q --all
352 $ hg revert -q --all
365 $ hg purge --all --config extensions.purge=
353 $ hg purge --all --config extensions.purge=
366 $ echo ".hgignore" > .hgignore
354 $ echo ".hgignore" > .hgignore
367 $ mkdir dir1 dir2
355 $ mkdir dir1 dir2
368 $ touch dir1/file1 dir1/file2 dir2/file1 dir2/file2
356 $ touch dir1/file1 dir1/file2 dir2/file1 dir2/file2
369 $ echo "subinclude:dir2/.hgignore" >> .hgignore
357 $ echo "subinclude:dir2/.hgignore" >> .hgignore
370 $ echo "glob:file*2" > dir2/.hgignore
358 $ echo "glob:file*2" > dir2/.hgignore
371 $ hg status
359 $ hg status
372 ? dir1/file1
360 ? dir1/file1
373 ? dir1/file2
361 ? dir1/file2
374 ? dir2/file1
362 ? dir2/file1
375
363
376 Check including subincludes with other patterns
364 Check including subincludes with other patterns
377
365
378 $ echo "subinclude:dir1/.hgignore" >> .hgignore
366 $ echo "subinclude:dir1/.hgignore" >> .hgignore
379
367
380 $ mkdir dir1/subdir
368 $ mkdir dir1/subdir
381 $ touch dir1/subdir/file1
369 $ touch dir1/subdir/file1
382 $ echo "rootglob:f?le1" > dir1/.hgignore
370 $ echo "rootglob:f?le1" > dir1/.hgignore
383 $ hg status
371 $ hg status
384 ? dir1/file2
372 ? dir1/file2
385 ? dir1/subdir/file1
373 ? dir1/subdir/file1
386 ? dir2/file1
374 ? dir2/file1
387 $ rm dir1/subdir/file1
375 $ rm dir1/subdir/file1
388
376
389 $ echo "regexp:f.le1" > dir1/.hgignore
377 $ echo "regexp:f.le1" > dir1/.hgignore
390 $ hg status
378 $ hg status
391 ? dir1/file2
379 ? dir1/file2
392 ? dir2/file1
380 ? dir2/file1
393
381
394 Check multiple levels of sub-ignores
382 Check multiple levels of sub-ignores
395
383
396 $ touch dir1/subdir/subfile1 dir1/subdir/subfile3 dir1/subdir/subfile4
384 $ touch dir1/subdir/subfile1 dir1/subdir/subfile3 dir1/subdir/subfile4
397 $ echo "subinclude:subdir/.hgignore" >> dir1/.hgignore
385 $ echo "subinclude:subdir/.hgignore" >> dir1/.hgignore
398 $ echo "glob:subfil*3" >> dir1/subdir/.hgignore
386 $ echo "glob:subfil*3" >> dir1/subdir/.hgignore
399
387
400 $ hg status
388 $ hg status
401 ? dir1/file2
389 ? dir1/file2
402 ? dir1/subdir/subfile4
390 ? dir1/subdir/subfile4
403 ? dir2/file1
391 ? dir2/file1
404
392
405 Check include subignore at the same level
393 Check include subignore at the same level
406
394
407 $ mv dir1/subdir/.hgignore dir1/.hgignoretwo
395 $ mv dir1/subdir/.hgignore dir1/.hgignoretwo
408 $ echo "regexp:f.le1" > dir1/.hgignore
396 $ echo "regexp:f.le1" > dir1/.hgignore
409 $ echo "subinclude:.hgignoretwo" >> dir1/.hgignore
397 $ echo "subinclude:.hgignoretwo" >> dir1/.hgignore
410 $ echo "glob:file*2" > dir1/.hgignoretwo
398 $ echo "glob:file*2" > dir1/.hgignoretwo
411
399
412 $ hg status | grep file2
400 $ hg status | grep file2
413 [1]
401 [1]
414 $ hg debugignore dir1/file2
402 $ hg debugignore dir1/file2
415 dir1/file2 is ignored
403 dir1/file2 is ignored
416 (ignore rule in dir2/.hgignore, line 1: 'file*2')
404 (ignore rule in dir2/.hgignore, line 1: 'file*2')
417
405
418 #if windows
406 #if windows
419
407
420 Windows paths are accepted on input
408 Windows paths are accepted on input
421
409
422 $ rm dir1/.hgignore
410 $ rm dir1/.hgignore
423 $ echo "dir1/file*" >> .hgignore
411 $ echo "dir1/file*" >> .hgignore
424 $ hg debugignore "dir1\file2"
412 $ hg debugignore "dir1\file2"
425 dir1/file2 is ignored
413 dir1/file2 is ignored
426 (ignore rule in $TESTTMP\ignorerepo\.hgignore, line 4: 'dir1/file*')
414 (ignore rule in $TESTTMP\ignorerepo\.hgignore, line 4: 'dir1/file*')
427 $ hg up -qC .
415 $ hg up -qC .
428
416
429 #endif
417 #endif
430
418
431 #if dirstate-v2 rust
419 #if dirstate-v2 rust
432
420
433 Check the hash of ignore patterns written in the dirstate
421 Check the hash of ignore patterns written in the dirstate
434 This is an optimization that is only relevant when using the Rust extensions
422 This is an optimization that is only relevant when using the Rust extensions
435
423
436 $ hg status > /dev/null
424 $ hg status > /dev/null
437 $ cat .hg/testhgignore .hg/testhgignorerel .hgignore dir2/.hgignore dir1/.hgignore dir1/.hgignoretwo | $TESTDIR/f --sha1
425 $ cat .hg/testhgignore .hg/testhgignorerel .hgignore dir2/.hgignore dir1/.hgignore dir1/.hgignoretwo | $TESTDIR/f --sha1
438 sha1=6e315b60f15fb5dfa02be00f3e2c8f923051f5ff
426 sha1=6e315b60f15fb5dfa02be00f3e2c8f923051f5ff
439 $ hg debugstate --docket | grep ignore
427 $ hg debugstate --docket | grep ignore
440 ignore pattern hash: 6e315b60f15fb5dfa02be00f3e2c8f923051f5ff
428 ignore pattern hash: 6e315b60f15fb5dfa02be00f3e2c8f923051f5ff
441
429
442 $ echo rel > .hg/testhgignorerel
430 $ echo rel > .hg/testhgignorerel
443 $ hg status > /dev/null
431 $ hg status > /dev/null
444 $ cat .hg/testhgignore .hg/testhgignorerel .hgignore dir2/.hgignore dir1/.hgignore dir1/.hgignoretwo | $TESTDIR/f --sha1
432 $ cat .hg/testhgignore .hg/testhgignorerel .hgignore dir2/.hgignore dir1/.hgignore dir1/.hgignoretwo | $TESTDIR/f --sha1
445 sha1=dea19cc7119213f24b6b582a4bae7b0cb063e34e
433 sha1=dea19cc7119213f24b6b582a4bae7b0cb063e34e
446 $ hg debugstate --docket | grep ignore
434 $ hg debugstate --docket | grep ignore
447 ignore pattern hash: dea19cc7119213f24b6b582a4bae7b0cb063e34e
435 ignore pattern hash: dea19cc7119213f24b6b582a4bae7b0cb063e34e
448
436
449 #endif
437 #endif
General Comments 0
You need to be logged in to leave comments. Login now