Show More
@@ -1,110 +1,112 b'' | |||
|
1 | 1 | use crate::error::CommandError; |
|
2 | use crate::ui::{print_narrow_sparse_warnings, Ui}; | |
|
2 | use crate::ui::{print_narrow_sparse_warnings, Ui, RelativePaths, relative_paths}; | |
|
3 | 3 | use crate::utils::path_utils::RelativizePaths; |
|
4 | 4 | use clap::Arg; |
|
5 | 5 | use hg::narrow; |
|
6 | 6 | use hg::operations::list_rev_tracked_files; |
|
7 | 7 | use hg::repo::Repo; |
|
8 | 8 | use hg::utils::filter_map_results; |
|
9 | 9 | use hg::utils::hg_path::HgPath; |
|
10 | 10 | use rayon::prelude::*; |
|
11 | 11 | |
|
12 | 12 | pub const HELP_TEXT: &str = " |
|
13 | 13 | List tracked files. |
|
14 | 14 | |
|
15 | 15 | Returns 0 on success. |
|
16 | 16 | "; |
|
17 | 17 | |
|
18 | 18 | pub fn args() -> clap::Command { |
|
19 | 19 | clap::command!("files") |
|
20 | 20 | .arg( |
|
21 | 21 | Arg::new("rev") |
|
22 | 22 | .help("search the repository as it is in REV") |
|
23 | 23 | .short('r') |
|
24 | 24 | .long("revision") |
|
25 | 25 | .value_name("REV"), |
|
26 | 26 | ) |
|
27 | 27 | .about(HELP_TEXT) |
|
28 | 28 | } |
|
29 | 29 | |
|
30 | 30 | pub fn run(invocation: &crate::CliInvocation) -> Result<(), CommandError> { |
|
31 | let relative = invocation.config.get(b"ui", b"relative-paths"); | |
|
32 | if relative.is_some() { | |
|
31 | match relative_paths(invocation.config)? { | |
|
32 | RelativePaths::Legacy | RelativePaths::Bool(true) => (), | |
|
33 | RelativePaths::Bool(false) => { | |
|
33 | 34 | return Err(CommandError::unsupported( |
|
34 | 35 | "non-default ui.relative-paths", |
|
35 | 36 | )); |
|
36 | 37 | } |
|
38 | } | |
|
37 | 39 | |
|
38 | 40 | let rev = invocation.subcommand_args.get_one::<String>("rev"); |
|
39 | 41 | |
|
40 | 42 | let repo = invocation.repo?; |
|
41 | 43 | |
|
42 | 44 | // It seems better if this check is removed: this would correspond to |
|
43 | 45 | // automatically enabling the extension if the repo requires it. |
|
44 | 46 | // However we need this check to be in sync with vanilla hg so hg tests |
|
45 | 47 | // pass. |
|
46 | 48 | if repo.has_sparse() |
|
47 | 49 | && invocation.config.get(b"extensions", b"sparse").is_none() |
|
48 | 50 | { |
|
49 | 51 | return Err(CommandError::unsupported( |
|
50 | 52 | "repo is using sparse, but sparse extension is not enabled", |
|
51 | 53 | )); |
|
52 | 54 | } |
|
53 | 55 | |
|
54 | 56 | let (narrow_matcher, narrow_warnings) = narrow::matcher(repo)?; |
|
55 | 57 | print_narrow_sparse_warnings(&narrow_warnings, &[], invocation.ui, repo)?; |
|
56 | 58 | |
|
57 | 59 | if let Some(rev) = rev { |
|
58 | 60 | let files = list_rev_tracked_files(repo, rev, narrow_matcher) |
|
59 | 61 | .map_err(|e| (e, rev.as_ref()))?; |
|
60 | 62 | display_files(invocation.ui, repo, files.iter()) |
|
61 | 63 | } else { |
|
62 | 64 | // The dirstate always reflects the sparse narrowspec. |
|
63 | 65 | let dirstate = repo.dirstate_map()?; |
|
64 | 66 | let files_res: Result<Vec<_>, _> = |
|
65 | 67 | filter_map_results(dirstate.iter(), |(path, entry)| { |
|
66 | 68 | Ok(if entry.tracked() && narrow_matcher.matches(path) { |
|
67 | 69 | Some(path) |
|
68 | 70 | } else { |
|
69 | 71 | None |
|
70 | 72 | }) |
|
71 | 73 | }) |
|
72 | 74 | .collect(); |
|
73 | 75 | |
|
74 | 76 | let mut files = files_res?; |
|
75 | 77 | files.par_sort_unstable(); |
|
76 | 78 | |
|
77 | 79 | display_files( |
|
78 | 80 | invocation.ui, |
|
79 | 81 | repo, |
|
80 | 82 | files.into_iter().map::<Result<_, CommandError>, _>(Ok), |
|
81 | 83 | ) |
|
82 | 84 | } |
|
83 | 85 | } |
|
84 | 86 | |
|
85 | 87 | fn display_files<'a, E>( |
|
86 | 88 | ui: &Ui, |
|
87 | 89 | repo: &Repo, |
|
88 | 90 | files: impl IntoIterator<Item = Result<&'a HgPath, E>>, |
|
89 | 91 | ) -> Result<(), CommandError> |
|
90 | 92 | where |
|
91 | 93 | CommandError: From<E>, |
|
92 | 94 | { |
|
93 | 95 | let mut stdout = ui.stdout_buffer(); |
|
94 | 96 | let mut any = false; |
|
95 | 97 | |
|
96 | 98 | let relativize = RelativizePaths::new(repo)?; |
|
97 | 99 | for result in files { |
|
98 | 100 | let path = result?; |
|
99 | 101 | stdout.write_all(&relativize.relativize(path))?; |
|
100 | 102 | stdout.write_all(b"\n")?; |
|
101 | 103 | any = true; |
|
102 | 104 | } |
|
103 | 105 | |
|
104 | 106 | stdout.flush()?; |
|
105 | 107 | if any { |
|
106 | 108 | Ok(()) |
|
107 | 109 | } else { |
|
108 | 110 | Err(CommandError::Unsuccessful) |
|
109 | 111 | } |
|
110 | 112 | } |
@@ -1,295 +1,307 b'' | |||
|
1 | 1 | use crate::color::ColorConfig; |
|
2 | 2 | use crate::color::Effect; |
|
3 | 3 | use crate::error::CommandError; |
|
4 | 4 | use format_bytes::format_bytes; |
|
5 | 5 | use format_bytes::write_bytes; |
|
6 | 6 | use hg::config::Config; |
|
7 | 7 | use hg::config::PlainInfo; |
|
8 | 8 | use hg::errors::HgError; |
|
9 | 9 | use hg::repo::Repo; |
|
10 | 10 | use hg::sparse; |
|
11 | 11 | use hg::utils::files::get_bytes_from_path; |
|
12 | 12 | use hg::PatternFileWarning; |
|
13 | 13 | use std::borrow::Cow; |
|
14 | 14 | use std::io; |
|
15 | 15 | use std::io::{ErrorKind, Write}; |
|
16 | 16 | |
|
17 | 17 | pub struct Ui { |
|
18 | 18 | stdout: std::io::Stdout, |
|
19 | 19 | stderr: std::io::Stderr, |
|
20 | 20 | colors: Option<ColorConfig>, |
|
21 | 21 | } |
|
22 | 22 | |
|
23 | 23 | /// The kind of user interface error |
|
24 | 24 | pub enum UiError { |
|
25 | 25 | /// The standard output stream cannot be written to |
|
26 | 26 | StdoutError(io::Error), |
|
27 | 27 | /// The standard error stream cannot be written to |
|
28 | 28 | StderrError(io::Error), |
|
29 | 29 | } |
|
30 | 30 | |
|
31 | 31 | /// The commandline user interface |
|
32 | 32 | impl Ui { |
|
33 | 33 | pub fn new(config: &Config) -> Result<Self, HgError> { |
|
34 | 34 | Ok(Ui { |
|
35 | 35 | // If using something else, also adapt `isatty()` below. |
|
36 | 36 | stdout: std::io::stdout(), |
|
37 | 37 | |
|
38 | 38 | stderr: std::io::stderr(), |
|
39 | 39 | colors: ColorConfig::new(config)?, |
|
40 | 40 | }) |
|
41 | 41 | } |
|
42 | 42 | |
|
43 | 43 | /// Default to no color if color configuration errors. |
|
44 | 44 | /// |
|
45 | 45 | /// Useful when weβre already handling another error. |
|
46 | 46 | pub fn new_infallible(config: &Config) -> Self { |
|
47 | 47 | Ui { |
|
48 | 48 | // If using something else, also adapt `isatty()` below. |
|
49 | 49 | stdout: std::io::stdout(), |
|
50 | 50 | |
|
51 | 51 | stderr: std::io::stderr(), |
|
52 | 52 | colors: ColorConfig::new(config).unwrap_or(None), |
|
53 | 53 | } |
|
54 | 54 | } |
|
55 | 55 | |
|
56 | 56 | /// Returns a buffered handle on stdout for faster batch printing |
|
57 | 57 | /// operations. |
|
58 | 58 | pub fn stdout_buffer(&self) -> StdoutBuffer<std::io::StdoutLock> { |
|
59 | 59 | StdoutBuffer::new(self.stdout.lock()) |
|
60 | 60 | } |
|
61 | 61 | |
|
62 | 62 | /// Write bytes to stdout |
|
63 | 63 | pub fn write_stdout(&self, bytes: &[u8]) -> Result<(), UiError> { |
|
64 | 64 | let mut stdout = self.stdout.lock(); |
|
65 | 65 | |
|
66 | 66 | stdout.write_all(bytes).or_else(handle_stdout_error)?; |
|
67 | 67 | |
|
68 | 68 | stdout.flush().or_else(handle_stdout_error) |
|
69 | 69 | } |
|
70 | 70 | |
|
71 | 71 | /// Write bytes to stderr |
|
72 | 72 | pub fn write_stderr(&self, bytes: &[u8]) -> Result<(), UiError> { |
|
73 | 73 | let mut stderr = self.stderr.lock(); |
|
74 | 74 | |
|
75 | 75 | stderr.write_all(bytes).or_else(handle_stderr_error)?; |
|
76 | 76 | |
|
77 | 77 | stderr.flush().or_else(handle_stderr_error) |
|
78 | 78 | } |
|
79 | 79 | |
|
80 | 80 | /// Write bytes to stdout with the given label |
|
81 | 81 | /// |
|
82 | 82 | /// Like the optional `label` parameter in `mercurial/ui.py`, |
|
83 | 83 | /// this label influences the color used for this output. |
|
84 | 84 | pub fn write_stdout_labelled( |
|
85 | 85 | &self, |
|
86 | 86 | bytes: &[u8], |
|
87 | 87 | label: &str, |
|
88 | 88 | ) -> Result<(), UiError> { |
|
89 | 89 | if let Some(colors) = &self.colors { |
|
90 | 90 | if let Some(effects) = colors.styles.get(label.as_bytes()) { |
|
91 | 91 | if !effects.is_empty() { |
|
92 | 92 | return self |
|
93 | 93 | .write_stdout_with_effects(bytes, effects) |
|
94 | 94 | .or_else(handle_stdout_error); |
|
95 | 95 | } |
|
96 | 96 | } |
|
97 | 97 | } |
|
98 | 98 | self.write_stdout(bytes) |
|
99 | 99 | } |
|
100 | 100 | |
|
101 | 101 | fn write_stdout_with_effects( |
|
102 | 102 | &self, |
|
103 | 103 | bytes: &[u8], |
|
104 | 104 | effects: &[Effect], |
|
105 | 105 | ) -> io::Result<()> { |
|
106 | 106 | let stdout = &mut self.stdout.lock(); |
|
107 | 107 | let mut write_line = |line: &[u8], first: bool| { |
|
108 | 108 | // `line` does not include the newline delimiter |
|
109 | 109 | if !first { |
|
110 | 110 | stdout.write_all(b"\n")?; |
|
111 | 111 | } |
|
112 | 112 | if line.is_empty() { |
|
113 | 113 | return Ok(()); |
|
114 | 114 | } |
|
115 | 115 | /// 0x1B == 27 == 0o33 |
|
116 | 116 | const ASCII_ESCAPE: &[u8] = b"\x1b"; |
|
117 | 117 | write_bytes!(stdout, b"{}[0", ASCII_ESCAPE)?; |
|
118 | 118 | for effect in effects { |
|
119 | 119 | write_bytes!(stdout, b";{}", effect)?; |
|
120 | 120 | } |
|
121 | 121 | write_bytes!(stdout, b"m")?; |
|
122 | 122 | stdout.write_all(line)?; |
|
123 | 123 | write_bytes!(stdout, b"{}[0m", ASCII_ESCAPE) |
|
124 | 124 | }; |
|
125 | 125 | let mut lines = bytes.split(|&byte| byte == b'\n'); |
|
126 | 126 | if let Some(first) = lines.next() { |
|
127 | 127 | write_line(first, true)?; |
|
128 | 128 | for line in lines { |
|
129 | 129 | write_line(line, false)? |
|
130 | 130 | } |
|
131 | 131 | } |
|
132 | 132 | stdout.flush() |
|
133 | 133 | } |
|
134 | 134 | } |
|
135 | 135 | |
|
136 | 136 | // TODO: pass the PlainInfo to call sites directly and |
|
137 | 137 | // delete this function |
|
138 | 138 | pub fn plain(opt_feature: Option<&str>) -> bool { |
|
139 | 139 | let plain_info = PlainInfo::from_env(); |
|
140 | 140 | match opt_feature { |
|
141 | 141 | None => plain_info.is_plain(), |
|
142 | 142 | Some(feature) => plain_info.is_feature_plain(feature), |
|
143 | 143 | } |
|
144 | 144 | } |
|
145 | 145 | |
|
146 | 146 | /// A buffered stdout writer for faster batch printing operations. |
|
147 | 147 | pub struct StdoutBuffer<W: Write> { |
|
148 | 148 | buf: io::BufWriter<W>, |
|
149 | 149 | } |
|
150 | 150 | |
|
151 | 151 | impl<W: Write> StdoutBuffer<W> { |
|
152 | 152 | pub fn new(writer: W) -> Self { |
|
153 | 153 | let buf = io::BufWriter::new(writer); |
|
154 | 154 | Self { buf } |
|
155 | 155 | } |
|
156 | 156 | |
|
157 | 157 | /// Write bytes to stdout buffer |
|
158 | 158 | pub fn write_all(&mut self, bytes: &[u8]) -> Result<(), UiError> { |
|
159 | 159 | self.buf.write_all(bytes).or_else(handle_stdout_error) |
|
160 | 160 | } |
|
161 | 161 | |
|
162 | 162 | /// Flush bytes to stdout |
|
163 | 163 | pub fn flush(&mut self) -> Result<(), UiError> { |
|
164 | 164 | self.buf.flush().or_else(handle_stdout_error) |
|
165 | 165 | } |
|
166 | 166 | } |
|
167 | 167 | |
|
168 | 168 | /// Sometimes writing to stdout is not possible, try writing to stderr to |
|
169 | 169 | /// signal that failure, otherwise just bail. |
|
170 | 170 | fn handle_stdout_error(error: io::Error) -> Result<(), UiError> { |
|
171 | 171 | if let ErrorKind::BrokenPipe = error.kind() { |
|
172 | 172 | // This makes `| head` work for example |
|
173 | 173 | return Ok(()); |
|
174 | 174 | } |
|
175 | 175 | let mut stderr = io::stderr(); |
|
176 | 176 | |
|
177 | 177 | stderr |
|
178 | 178 | .write_all(&format_bytes!( |
|
179 | 179 | b"abort: {}\n", |
|
180 | 180 | error.to_string().as_bytes() |
|
181 | 181 | )) |
|
182 | 182 | .map_err(UiError::StderrError)?; |
|
183 | 183 | |
|
184 | 184 | stderr.flush().map_err(UiError::StderrError)?; |
|
185 | 185 | |
|
186 | 186 | Err(UiError::StdoutError(error)) |
|
187 | 187 | } |
|
188 | 188 | |
|
189 | 189 | /// Sometimes writing to stderr is not possible. |
|
190 | 190 | fn handle_stderr_error(error: io::Error) -> Result<(), UiError> { |
|
191 | 191 | // A broken pipe should not result in a error |
|
192 | 192 | // like with `| head` for example |
|
193 | 193 | if let ErrorKind::BrokenPipe = error.kind() { |
|
194 | 194 | return Ok(()); |
|
195 | 195 | } |
|
196 | 196 | Err(UiError::StdoutError(error)) |
|
197 | 197 | } |
|
198 | 198 | |
|
199 | 199 | /// Encode rust strings according to the user system. |
|
200 | 200 | pub fn utf8_to_local(s: &str) -> Cow<[u8]> { |
|
201 | 201 | // TODO encode for the user's system // |
|
202 | 202 | let bytes = s.as_bytes(); |
|
203 | 203 | Cow::Borrowed(bytes) |
|
204 | 204 | } |
|
205 | 205 | |
|
206 | 206 | /// Decode user system bytes to Rust string. |
|
207 | 207 | pub fn local_to_utf8(s: &[u8]) -> Cow<str> { |
|
208 | 208 | // TODO decode from the user's system |
|
209 | 209 | String::from_utf8_lossy(s) |
|
210 | 210 | } |
|
211 | 211 | |
|
212 | 212 | /// Should formatted output be used? |
|
213 | 213 | /// |
|
214 | 214 | /// Note: rhg does not have the formatter mechanism yet, |
|
215 | 215 | /// but this is also used when deciding whether to use color. |
|
216 | 216 | pub fn formatted(config: &Config) -> Result<bool, HgError> { |
|
217 | 217 | if let Some(formatted) = config.get_option(b"ui", b"formatted")? { |
|
218 | 218 | Ok(formatted) |
|
219 | 219 | } else { |
|
220 | 220 | isatty(config) |
|
221 | 221 | } |
|
222 | 222 | } |
|
223 | 223 | |
|
224 | pub enum RelativePaths { | |
|
225 | Legacy, | |
|
226 | Bool(bool), | |
|
227 | } | |
|
228 | ||
|
229 | pub fn relative_paths(config: &Config) -> Result<RelativePaths, HgError> { | |
|
230 | Ok(match config.get(b"ui", b"relative-paths") { | |
|
231 | None | Some(b"legacy") => RelativePaths::Legacy, | |
|
232 | _ => RelativePaths::Bool(config.get_bool(b"ui", b"relative-paths")?), | |
|
233 | }) | |
|
234 | } | |
|
235 | ||
|
224 | 236 | fn isatty(config: &Config) -> Result<bool, HgError> { |
|
225 | 237 | Ok(if config.get_bool(b"ui", b"nontty")? { |
|
226 | 238 | false |
|
227 | 239 | } else { |
|
228 | 240 | atty::is(atty::Stream::Stdout) |
|
229 | 241 | }) |
|
230 | 242 | } |
|
231 | 243 | |
|
232 | 244 | /// Return the formatted bytestring corresponding to a pattern file warning, |
|
233 | 245 | /// as expected by the CLI. |
|
234 | 246 | pub(crate) fn format_pattern_file_warning( |
|
235 | 247 | warning: &PatternFileWarning, |
|
236 | 248 | repo: &Repo, |
|
237 | 249 | ) -> Vec<u8> { |
|
238 | 250 | match warning { |
|
239 | 251 | PatternFileWarning::InvalidSyntax(path, syntax) => format_bytes!( |
|
240 | 252 | b"{}: ignoring invalid syntax '{}'\n", |
|
241 | 253 | get_bytes_from_path(path), |
|
242 | 254 | &*syntax |
|
243 | 255 | ), |
|
244 | 256 | PatternFileWarning::NoSuchFile(path) => { |
|
245 | 257 | let path = if let Ok(relative) = |
|
246 | 258 | path.strip_prefix(repo.working_directory_path()) |
|
247 | 259 | { |
|
248 | 260 | relative |
|
249 | 261 | } else { |
|
250 | 262 | &*path |
|
251 | 263 | }; |
|
252 | 264 | format_bytes!( |
|
253 | 265 | b"skipping unreadable pattern file '{}': \ |
|
254 | 266 | No such file or directory\n", |
|
255 | 267 | get_bytes_from_path(path), |
|
256 | 268 | ) |
|
257 | 269 | } |
|
258 | 270 | } |
|
259 | 271 | } |
|
260 | 272 | |
|
261 | 273 | /// Print with `Ui` the formatted bytestring corresponding to a |
|
262 | 274 | /// sparse/narrow warning, as expected by the CLI. |
|
263 | 275 | pub(crate) fn print_narrow_sparse_warnings( |
|
264 | 276 | narrow_warnings: &[sparse::SparseWarning], |
|
265 | 277 | sparse_warnings: &[sparse::SparseWarning], |
|
266 | 278 | ui: &Ui, |
|
267 | 279 | repo: &Repo, |
|
268 | 280 | ) -> Result<(), CommandError> { |
|
269 | 281 | for warning in narrow_warnings.iter().chain(sparse_warnings) { |
|
270 | 282 | match &warning { |
|
271 | 283 | sparse::SparseWarning::RootWarning { context, line } => { |
|
272 | 284 | let msg = format_bytes!( |
|
273 | 285 | b"warning: {} profile cannot use paths \" |
|
274 | 286 | starting with /, ignoring {}\n", |
|
275 | 287 | context, |
|
276 | 288 | line |
|
277 | 289 | ); |
|
278 | 290 | ui.write_stderr(&msg)?; |
|
279 | 291 | } |
|
280 | 292 | sparse::SparseWarning::ProfileNotFound { profile, rev } => { |
|
281 | 293 | let msg = format_bytes!( |
|
282 | 294 | b"warning: sparse profile '{}' not found \" |
|
283 | 295 | in rev {} - ignoring it\n", |
|
284 | 296 | profile, |
|
285 | 297 | rev |
|
286 | 298 | ); |
|
287 | 299 | ui.write_stderr(&msg)?; |
|
288 | 300 | } |
|
289 | 301 | sparse::SparseWarning::Pattern(e) => { |
|
290 | 302 | ui.write_stderr(&format_pattern_file_warning(e, repo))?; |
|
291 | 303 | } |
|
292 | 304 | } |
|
293 | 305 | } |
|
294 | 306 | Ok(()) |
|
295 | 307 | } |
@@ -1,417 +1,419 b'' | |||
|
1 | 1 | #require rhg |
|
2 | 2 | |
|
3 | 3 | $ NO_FALLBACK="env RHG_ON_UNSUPPORTED=abort" |
|
4 | 4 | |
|
5 | 5 | Unimplemented command |
|
6 | 6 | $ $NO_FALLBACK rhg unimplemented-command |
|
7 | 7 | unsupported feature: error: The subcommand 'unimplemented-command' wasn't recognized |
|
8 | 8 | |
|
9 | 9 | Usage: rhg [OPTIONS] <COMMAND> |
|
10 | 10 | |
|
11 | 11 | For more information try '--help' |
|
12 | 12 | |
|
13 | 13 | [252] |
|
14 | 14 | $ rhg unimplemented-command --config rhg.on-unsupported=abort-silent |
|
15 | 15 | [252] |
|
16 | 16 | |
|
17 | 17 | Finding root |
|
18 | 18 | $ $NO_FALLBACK rhg root |
|
19 | 19 | abort: no repository found in '$TESTTMP' (.hg not found)! |
|
20 | 20 | [255] |
|
21 | 21 | |
|
22 | 22 | $ hg init repository |
|
23 | 23 | $ cd repository |
|
24 | 24 | $ $NO_FALLBACK rhg root |
|
25 | 25 | $TESTTMP/repository |
|
26 | 26 | |
|
27 | 27 | Reading and setting configuration |
|
28 | 28 | $ echo "[ui]" >> $HGRCPATH |
|
29 | 29 | $ echo "username = user1" >> $HGRCPATH |
|
30 | 30 | $ $NO_FALLBACK rhg config ui.username |
|
31 | 31 | user1 |
|
32 | 32 | $ echo "[ui]" >> .hg/hgrc |
|
33 | 33 | $ echo "username = user2" >> .hg/hgrc |
|
34 | 34 | $ $NO_FALLBACK rhg config ui.username |
|
35 | 35 | user2 |
|
36 | 36 | $ $NO_FALLBACK rhg --config ui.username=user3 config ui.username |
|
37 | 37 | user3 |
|
38 | 38 | |
|
39 | 39 | Unwritable file descriptor |
|
40 | 40 | $ $NO_FALLBACK rhg root > /dev/full |
|
41 | 41 | abort: No space left on device (os error 28) |
|
42 | 42 | [255] |
|
43 | 43 | |
|
44 | 44 | Deleted repository |
|
45 | 45 | $ rm -rf `pwd` |
|
46 | 46 | $ $NO_FALLBACK rhg root |
|
47 | 47 | abort: error getting current working directory: $ENOENT$ |
|
48 | 48 | [255] |
|
49 | 49 | |
|
50 | 50 | Listing tracked files |
|
51 | 51 | $ cd $TESTTMP |
|
52 | 52 | $ hg init repository |
|
53 | 53 | $ cd repository |
|
54 | 54 | $ for i in 1 2 3; do |
|
55 | 55 | > echo $i >> file$i |
|
56 | 56 | > hg add file$i |
|
57 | 57 | > done |
|
58 | 58 | > hg commit -m "commit $i" -q |
|
59 | 59 | |
|
60 | 60 | Listing tracked files from root |
|
61 | 61 | $ $NO_FALLBACK rhg files |
|
62 | 62 | file1 |
|
63 | 63 | file2 |
|
64 | 64 | file3 |
|
65 | 65 | |
|
66 | 66 | Listing tracked files from subdirectory |
|
67 | 67 | $ mkdir -p path/to/directory |
|
68 | 68 | $ cd path/to/directory |
|
69 | 69 | $ $NO_FALLBACK rhg files |
|
70 | 70 | ../../../file1 |
|
71 | 71 | ../../../file2 |
|
72 | 72 | ../../../file3 |
|
73 | 73 | |
|
74 | 74 | $ $NO_FALLBACK rhg files --config ui.relative-paths=legacy |
|
75 | unsupported feature: non-default ui.relative-paths | |
|
76 | [252] | |
|
75 | ../../../file1 | |
|
76 | ../../../file2 | |
|
77 | ../../../file3 | |
|
77 | 78 | |
|
78 | 79 | $ $NO_FALLBACK rhg files --config ui.relative-paths=false |
|
79 | 80 | unsupported feature: non-default ui.relative-paths |
|
80 | 81 | [252] |
|
81 | 82 | |
|
82 | 83 | $ $NO_FALLBACK rhg files --config ui.relative-paths=true |
|
83 | unsupported feature: non-default ui.relative-paths | |
|
84 | [252] | |
|
84 | ../../../file1 | |
|
85 | ../../../file2 | |
|
86 | ../../../file3 | |
|
85 | 87 | |
|
86 | 88 | Listing tracked files through broken pipe |
|
87 | 89 | $ $NO_FALLBACK rhg files | head -n 1 |
|
88 | 90 | ../../../file1 |
|
89 | 91 | |
|
90 | 92 | Debuging data in inline index |
|
91 | 93 | $ cd $TESTTMP |
|
92 | 94 | $ rm -rf repository |
|
93 | 95 | $ hg init repository |
|
94 | 96 | $ cd repository |
|
95 | 97 | $ for i in 1 2 3 4 5 6; do |
|
96 | 98 | > echo $i >> file-$i |
|
97 | 99 | > hg add file-$i |
|
98 | 100 | > hg commit -m "Commit $i" -q |
|
99 | 101 | > done |
|
100 | 102 | $ $NO_FALLBACK rhg debugdata -c 2 |
|
101 | 103 | 8d0267cb034247ebfa5ee58ce59e22e57a492297 |
|
102 | 104 | test |
|
103 | 105 | 0 0 |
|
104 | 106 | file-3 |
|
105 | 107 | |
|
106 | 108 | Commit 3 (no-eol) |
|
107 | 109 | $ $NO_FALLBACK rhg debugdata -m 2 |
|
108 | 110 | file-1\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc) |
|
109 | 111 | file-2\x005d9299349fc01ddd25d0070d149b124d8f10411e (esc) |
|
110 | 112 | file-3\x002661d26c649684b482d10f91960cc3db683c38b4 (esc) |
|
111 | 113 | |
|
112 | 114 | Debuging with full node id |
|
113 | 115 | $ $NO_FALLBACK rhg debugdata -c `hg log -r 0 -T '{node}'` |
|
114 | 116 | d1d1c679d3053e8926061b6f45ca52009f011e3f |
|
115 | 117 | test |
|
116 | 118 | 0 0 |
|
117 | 119 | file-1 |
|
118 | 120 | |
|
119 | 121 | Commit 1 (no-eol) |
|
120 | 122 | |
|
121 | 123 | Specifying revisions by changeset ID |
|
122 | 124 | $ hg log -T '{node}\n' |
|
123 | 125 | c6ad58c44207b6ff8a4fbbca7045a5edaa7e908b |
|
124 | 126 | d654274993d0149eecc3cc03214f598320211900 |
|
125 | 127 | f646af7e96481d3a5470b695cf30ad8e3ab6c575 |
|
126 | 128 | cf8b83f14ead62b374b6e91a0e9303b85dfd9ed7 |
|
127 | 129 | 91c6f6e73e39318534dc415ea4e8a09c99cd74d6 |
|
128 | 130 | 6ae9681c6d30389694d8701faf24b583cf3ccafe |
|
129 | 131 | $ $NO_FALLBACK rhg files -r cf8b83 |
|
130 | 132 | file-1 |
|
131 | 133 | file-2 |
|
132 | 134 | file-3 |
|
133 | 135 | $ $NO_FALLBACK rhg cat -r cf8b83 file-2 |
|
134 | 136 | 2 |
|
135 | 137 | $ $NO_FALLBACK rhg cat --rev cf8b83 file-2 |
|
136 | 138 | 2 |
|
137 | 139 | $ $NO_FALLBACK rhg cat -r c file-2 |
|
138 | 140 | abort: ambiguous revision identifier: c |
|
139 | 141 | [255] |
|
140 | 142 | $ $NO_FALLBACK rhg cat -r d file-2 |
|
141 | 143 | 2 |
|
142 | 144 | $ $NO_FALLBACK rhg cat -r 0000 file-2 |
|
143 | 145 | file-2: no such file in rev 000000000000 |
|
144 | 146 | [1] |
|
145 | 147 | |
|
146 | 148 | Cat files |
|
147 | 149 | $ cd $TESTTMP |
|
148 | 150 | $ rm -rf repository |
|
149 | 151 | $ hg init repository |
|
150 | 152 | $ cd repository |
|
151 | 153 | $ echo "original content" > original |
|
152 | 154 | $ hg add original |
|
153 | 155 | $ hg commit -m "add original" original |
|
154 | 156 | Without `--rev` |
|
155 | 157 | $ $NO_FALLBACK rhg cat original |
|
156 | 158 | original content |
|
157 | 159 | With `--rev` |
|
158 | 160 | $ $NO_FALLBACK rhg cat -r 0 original |
|
159 | 161 | original content |
|
160 | 162 | Cat copied file should not display copy metadata |
|
161 | 163 | $ hg copy original copy_of_original |
|
162 | 164 | $ hg commit -m "add copy of original" |
|
163 | 165 | $ $NO_FALLBACK rhg cat original |
|
164 | 166 | original content |
|
165 | 167 | $ $NO_FALLBACK rhg cat -r 1 copy_of_original |
|
166 | 168 | original content |
|
167 | 169 | |
|
168 | 170 | |
|
169 | 171 | Fallback to Python |
|
170 | 172 | $ $NO_FALLBACK rhg cat original --exclude="*.rs" |
|
171 | 173 | unsupported feature: error: Found argument '--exclude' which wasn't expected, or isn't valid in this context |
|
172 | 174 | |
|
173 | 175 | If you tried to supply '--exclude' as a value rather than a flag, use '-- --exclude' |
|
174 | 176 | |
|
175 | 177 | Usage: rhg cat <FILE>... |
|
176 | 178 | |
|
177 | 179 | For more information try '--help' |
|
178 | 180 | |
|
179 | 181 | [252] |
|
180 | 182 | $ rhg cat original --exclude="*.rs" |
|
181 | 183 | original content |
|
182 | 184 | |
|
183 | 185 | Check that `fallback-immediately` overrides `$NO_FALLBACK` |
|
184 | 186 | $ $NO_FALLBACK rhg cat original --exclude="*.rs" --config rhg.fallback-immediately=1 |
|
185 | 187 | original content |
|
186 | 188 | |
|
187 | 189 | $ (unset RHG_FALLBACK_EXECUTABLE; rhg cat original --exclude="*.rs") |
|
188 | 190 | abort: 'rhg.on-unsupported=fallback' without 'rhg.fallback-executable' set. |
|
189 | 191 | [255] |
|
190 | 192 | |
|
191 | 193 | $ (unset RHG_FALLBACK_EXECUTABLE; rhg cat original) |
|
192 | 194 | original content |
|
193 | 195 | |
|
194 | 196 | $ rhg cat original --exclude="*.rs" --config rhg.fallback-executable=false |
|
195 | 197 | [1] |
|
196 | 198 | |
|
197 | 199 | $ rhg cat original --exclude="*.rs" --config rhg.fallback-executable=hg-non-existent |
|
198 | 200 | abort: invalid fallback 'hg-non-existent': cannot find binary path |
|
199 | 201 | [253] |
|
200 | 202 | |
|
201 | 203 | $ rhg cat original --exclude="*.rs" --config rhg.fallback-executable=rhg |
|
202 | 204 | Blocking recursive fallback. The 'rhg.fallback-executable = rhg' config points to `rhg` itself. |
|
203 | 205 | unsupported feature: error: Found argument '--exclude' which wasn't expected, or isn't valid in this context |
|
204 | 206 | |
|
205 | 207 | If you tried to supply '--exclude' as a value rather than a flag, use '-- --exclude' |
|
206 | 208 | |
|
207 | 209 | Usage: rhg cat <FILE>... |
|
208 | 210 | |
|
209 | 211 | For more information try '--help' |
|
210 | 212 | |
|
211 | 213 | [252] |
|
212 | 214 | |
|
213 | 215 | Fallback with shell path segments |
|
214 | 216 | $ $NO_FALLBACK rhg cat . |
|
215 | 217 | unsupported feature: `..` or `.` path segment |
|
216 | 218 | [252] |
|
217 | 219 | $ $NO_FALLBACK rhg cat .. |
|
218 | 220 | unsupported feature: `..` or `.` path segment |
|
219 | 221 | [252] |
|
220 | 222 | $ $NO_FALLBACK rhg cat ../.. |
|
221 | 223 | unsupported feature: `..` or `.` path segment |
|
222 | 224 | [252] |
|
223 | 225 | |
|
224 | 226 | Fallback with filesets |
|
225 | 227 | $ $NO_FALLBACK rhg cat "set:c or b" |
|
226 | 228 | unsupported feature: fileset |
|
227 | 229 | [252] |
|
228 | 230 | |
|
229 | 231 | Fallback with generic hooks |
|
230 | 232 | $ $NO_FALLBACK rhg cat original --config hooks.pre-cat=something |
|
231 | 233 | unsupported feature: pre-cat hook defined |
|
232 | 234 | [252] |
|
233 | 235 | |
|
234 | 236 | $ $NO_FALLBACK rhg cat original --config hooks.post-cat=something |
|
235 | 237 | unsupported feature: post-cat hook defined |
|
236 | 238 | [252] |
|
237 | 239 | |
|
238 | 240 | $ $NO_FALLBACK rhg cat original --config hooks.fail-cat=something |
|
239 | 241 | unsupported feature: fail-cat hook defined |
|
240 | 242 | [252] |
|
241 | 243 | |
|
242 | 244 | Fallback with [defaults] |
|
243 | 245 | $ $NO_FALLBACK rhg cat original --config "defaults.cat=-r null" |
|
244 | 246 | unsupported feature: `defaults` config set |
|
245 | 247 | [252] |
|
246 | 248 | |
|
247 | 249 | |
|
248 | 250 | Requirements |
|
249 | 251 | $ $NO_FALLBACK rhg debugrequirements |
|
250 | 252 | dotencode |
|
251 | 253 | fncache |
|
252 | 254 | generaldelta |
|
253 | 255 | persistent-nodemap |
|
254 | 256 | revlog-compression-zstd (zstd !) |
|
255 | 257 | revlogv1 |
|
256 | 258 | share-safe |
|
257 | 259 | sparserevlog |
|
258 | 260 | store |
|
259 | 261 | |
|
260 | 262 | $ echo indoor-pool >> .hg/requires |
|
261 | 263 | $ $NO_FALLBACK rhg files |
|
262 | 264 | unsupported feature: repository requires feature unknown to this Mercurial: indoor-pool |
|
263 | 265 | [252] |
|
264 | 266 | |
|
265 | 267 | $ $NO_FALLBACK rhg cat -r 1 copy_of_original |
|
266 | 268 | unsupported feature: repository requires feature unknown to this Mercurial: indoor-pool |
|
267 | 269 | [252] |
|
268 | 270 | |
|
269 | 271 | $ $NO_FALLBACK rhg debugrequirements |
|
270 | 272 | unsupported feature: repository requires feature unknown to this Mercurial: indoor-pool |
|
271 | 273 | [252] |
|
272 | 274 | |
|
273 | 275 | $ echo -e '\xFF' >> .hg/requires |
|
274 | 276 | $ $NO_FALLBACK rhg debugrequirements |
|
275 | 277 | abort: parse error in 'requires' file |
|
276 | 278 | [255] |
|
277 | 279 | |
|
278 | 280 | Persistent nodemap |
|
279 | 281 | $ cd $TESTTMP |
|
280 | 282 | $ rm -rf repository |
|
281 | 283 | $ hg --config format.use-persistent-nodemap=no init repository |
|
282 | 284 | $ cd repository |
|
283 | 285 | $ $NO_FALLBACK rhg debugrequirements | grep nodemap |
|
284 | 286 | [1] |
|
285 | 287 | $ hg debugbuilddag .+5000 --overwritten-file --config "storage.revlog.nodemap.mode=warn" |
|
286 | 288 | $ hg id -r tip |
|
287 | 289 | c3ae8dec9fad tip |
|
288 | 290 | $ ls .hg/store/00changelog* |
|
289 | 291 | .hg/store/00changelog.d |
|
290 | 292 | .hg/store/00changelog.i |
|
291 | 293 | $ $NO_FALLBACK rhg files -r c3ae8dec9fad |
|
292 | 294 | of |
|
293 | 295 | |
|
294 | 296 | $ cd $TESTTMP |
|
295 | 297 | $ rm -rf repository |
|
296 | 298 | $ hg --config format.use-persistent-nodemap=True init repository |
|
297 | 299 | $ cd repository |
|
298 | 300 | $ $NO_FALLBACK rhg debugrequirements | grep nodemap |
|
299 | 301 | persistent-nodemap |
|
300 | 302 | $ hg debugbuilddag .+5000 --overwritten-file --config "storage.revlog.nodemap.mode=warn" |
|
301 | 303 | $ hg id -r tip |
|
302 | 304 | c3ae8dec9fad tip |
|
303 | 305 | $ ls .hg/store/00changelog* |
|
304 | 306 | .hg/store/00changelog-*.nd (glob) |
|
305 | 307 | .hg/store/00changelog.d |
|
306 | 308 | .hg/store/00changelog.i |
|
307 | 309 | .hg/store/00changelog.n |
|
308 | 310 | |
|
309 | 311 | Specifying revisions by changeset ID |
|
310 | 312 | $ $NO_FALLBACK rhg files -r c3ae8dec9fad |
|
311 | 313 | of |
|
312 | 314 | $ $NO_FALLBACK rhg cat -r c3ae8dec9fad of |
|
313 | 315 | r5000 |
|
314 | 316 | |
|
315 | 317 | Crate a shared repository |
|
316 | 318 | |
|
317 | 319 | $ echo "[extensions]" >> $HGRCPATH |
|
318 | 320 | $ echo "share = " >> $HGRCPATH |
|
319 | 321 | |
|
320 | 322 | $ cd $TESTTMP |
|
321 | 323 | $ hg init repo1 |
|
322 | 324 | $ echo a > repo1/a |
|
323 | 325 | $ hg -R repo1 commit -A -m'init' |
|
324 | 326 | adding a |
|
325 | 327 | |
|
326 | 328 | $ hg share repo1 repo2 |
|
327 | 329 | updating working directory |
|
328 | 330 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
329 | 331 | |
|
330 | 332 | And check that basic rhg commands work with sharing |
|
331 | 333 | |
|
332 | 334 | $ $NO_FALLBACK rhg files -R repo2 |
|
333 | 335 | repo2/a |
|
334 | 336 | $ $NO_FALLBACK rhg -R repo2 cat -r 0 repo2/a |
|
335 | 337 | a |
|
336 | 338 | |
|
337 | 339 | Same with relative sharing |
|
338 | 340 | |
|
339 | 341 | $ hg share repo2 repo3 --relative |
|
340 | 342 | updating working directory |
|
341 | 343 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
342 | 344 | |
|
343 | 345 | $ $NO_FALLBACK rhg files -R repo3 |
|
344 | 346 | repo3/a |
|
345 | 347 | $ $NO_FALLBACK rhg -R repo3 cat -r 0 repo3/a |
|
346 | 348 | a |
|
347 | 349 | |
|
348 | 350 | Same with share-safe |
|
349 | 351 | |
|
350 | 352 | $ echo "[format]" >> $HGRCPATH |
|
351 | 353 | $ echo "use-share-safe = True" >> $HGRCPATH |
|
352 | 354 | |
|
353 | 355 | $ cd $TESTTMP |
|
354 | 356 | $ hg init repo4 |
|
355 | 357 | $ cd repo4 |
|
356 | 358 | $ echo a > a |
|
357 | 359 | $ hg commit -A -m'init' |
|
358 | 360 | adding a |
|
359 | 361 | |
|
360 | 362 | $ cd .. |
|
361 | 363 | $ hg share repo4 repo5 |
|
362 | 364 | updating working directory |
|
363 | 365 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
364 | 366 | |
|
365 | 367 | And check that basic rhg commands work with sharing |
|
366 | 368 | |
|
367 | 369 | $ cd repo5 |
|
368 | 370 | $ $NO_FALLBACK rhg files |
|
369 | 371 | a |
|
370 | 372 | $ $NO_FALLBACK rhg cat -r 0 a |
|
371 | 373 | a |
|
372 | 374 | |
|
373 | 375 | The blackbox extension is supported |
|
374 | 376 | |
|
375 | 377 | $ echo "[extensions]" >> $HGRCPATH |
|
376 | 378 | $ echo "blackbox =" >> $HGRCPATH |
|
377 | 379 | $ echo "[blackbox]" >> $HGRCPATH |
|
378 | 380 | $ echo "maxsize = 1" >> $HGRCPATH |
|
379 | 381 | $ $NO_FALLBACK rhg files > /dev/null |
|
380 | 382 | $ cat .hg/blackbox.log |
|
381 | 383 | ????-??-?? ??:??:??.??? * @d3873e73d99ef67873dac33fbcc66268d5d2b6f4 (*)> (rust) files exited 0 after * seconds (glob) |
|
382 | 384 | $ cat .hg/blackbox.log.1 |
|
383 | 385 | ????-??-?? ??:??:??.??? * @d3873e73d99ef67873dac33fbcc66268d5d2b6f4 (*)> (rust) files (glob) |
|
384 | 386 | |
|
385 | 387 | Subrepos are not supported |
|
386 | 388 | |
|
387 | 389 | $ touch .hgsub |
|
388 | 390 | $ $NO_FALLBACK rhg files |
|
389 | 391 | unsupported feature: subrepos (.hgsub is present) |
|
390 | 392 | [252] |
|
391 | 393 | $ rhg files |
|
392 | 394 | a |
|
393 | 395 | $ rm .hgsub |
|
394 | 396 | |
|
395 | 397 | The `:required` extension suboptions are correctly ignored |
|
396 | 398 | |
|
397 | 399 | $ echo "[extensions]" >> $HGRCPATH |
|
398 | 400 | $ echo "blackbox:required = yes" >> $HGRCPATH |
|
399 | 401 | $ rhg files |
|
400 | 402 | a |
|
401 | 403 | $ echo "*:required = yes" >> $HGRCPATH |
|
402 | 404 | $ rhg files |
|
403 | 405 | a |
|
404 | 406 | |
|
405 | 407 | We can ignore all extensions at once |
|
406 | 408 | |
|
407 | 409 | $ echo "[extensions]" >> $HGRCPATH |
|
408 | 410 | $ echo "thisextensionbetternotexist=" >> $HGRCPATH |
|
409 | 411 | $ echo "thisextensionbetternotexisteither=" >> $HGRCPATH |
|
410 | 412 | $ $NO_FALLBACK rhg files |
|
411 | 413 | unsupported feature: extensions: thisextensionbetternotexist, thisextensionbetternotexisteither (consider adding them to 'rhg.ignored-extensions' config) |
|
412 | 414 | [252] |
|
413 | 415 | |
|
414 | 416 | $ echo "[rhg]" >> $HGRCPATH |
|
415 | 417 | $ echo "ignored-extensions=*" >> $HGRCPATH |
|
416 | 418 | $ $NO_FALLBACK rhg files |
|
417 | 419 | a |
General Comments 0
You need to be logged in to leave comments.
Login now