##// END OF EJS Templates
rhg: Add support for `rhg status -n`...
Simon Sapin -
r49171:c12ed335 default
parent child Browse files
Show More
@@ -1,343 +1,400
1 // status.rs
1 // status.rs
2 //
2 //
3 // Copyright 2020, Georges Racinet <georges.racinets@octobus.net>
3 // Copyright 2020, Georges Racinet <georges.racinets@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 use crate::error::CommandError;
8 use crate::error::CommandError;
9 use crate::ui::{Ui, UiError};
9 use crate::ui::Ui;
10 use crate::utils::path_utils::relativize_paths;
10 use crate::utils::path_utils::relativize_paths;
11 use clap::{Arg, SubCommand};
11 use clap::{Arg, SubCommand};
12 use format_bytes::format_bytes;
12 use hg;
13 use hg;
13 use hg::config::Config;
14 use hg::config::Config;
14 use hg::dirstate::{has_exec_bit, TruncatedTimestamp};
15 use hg::dirstate::{has_exec_bit, TruncatedTimestamp};
15 use hg::errors::HgError;
16 use hg::errors::HgError;
16 use hg::manifest::Manifest;
17 use hg::manifest::Manifest;
17 use hg::matchers::AlwaysMatcher;
18 use hg::matchers::AlwaysMatcher;
18 use hg::repo::Repo;
19 use hg::repo::Repo;
19 use hg::utils::files::get_bytes_from_os_string;
20 use hg::utils::files::get_bytes_from_os_string;
20 use hg::utils::hg_path::{hg_path_to_os_string, HgPath};
21 use hg::utils::hg_path::{hg_path_to_os_string, HgPath};
21 use hg::{HgPathCow, StatusOptions};
22 use hg::{HgPathCow, StatusOptions};
22 use log::{info, warn};
23 use log::{info, warn};
23 use std::borrow::Cow;
24
24
25 pub const HELP_TEXT: &str = "
25 pub const HELP_TEXT: &str = "
26 Show changed files in the working directory
26 Show changed files in the working directory
27
27
28 This is a pure Rust version of `hg status`.
28 This is a pure Rust version of `hg status`.
29
29
30 Some options might be missing, check the list below.
30 Some options might be missing, check the list below.
31 ";
31 ";
32
32
33 pub fn args() -> clap::App<'static, 'static> {
33 pub fn args() -> clap::App<'static, 'static> {
34 SubCommand::with_name("status")
34 SubCommand::with_name("status")
35 .alias("st")
35 .alias("st")
36 .about(HELP_TEXT)
36 .about(HELP_TEXT)
37 .arg(
37 .arg(
38 Arg::with_name("all")
38 Arg::with_name("all")
39 .help("show status of all files")
39 .help("show status of all files")
40 .short("-A")
40 .short("-A")
41 .long("--all"),
41 .long("--all"),
42 )
42 )
43 .arg(
43 .arg(
44 Arg::with_name("modified")
44 Arg::with_name("modified")
45 .help("show only modified files")
45 .help("show only modified files")
46 .short("-m")
46 .short("-m")
47 .long("--modified"),
47 .long("--modified"),
48 )
48 )
49 .arg(
49 .arg(
50 Arg::with_name("added")
50 Arg::with_name("added")
51 .help("show only added files")
51 .help("show only added files")
52 .short("-a")
52 .short("-a")
53 .long("--added"),
53 .long("--added"),
54 )
54 )
55 .arg(
55 .arg(
56 Arg::with_name("removed")
56 Arg::with_name("removed")
57 .help("show only removed files")
57 .help("show only removed files")
58 .short("-r")
58 .short("-r")
59 .long("--removed"),
59 .long("--removed"),
60 )
60 )
61 .arg(
61 .arg(
62 Arg::with_name("clean")
62 Arg::with_name("clean")
63 .help("show only clean files")
63 .help("show only clean files")
64 .short("-c")
64 .short("-c")
65 .long("--clean"),
65 .long("--clean"),
66 )
66 )
67 .arg(
67 .arg(
68 Arg::with_name("deleted")
68 Arg::with_name("deleted")
69 .help("show only deleted files")
69 .help("show only deleted files")
70 .short("-d")
70 .short("-d")
71 .long("--deleted"),
71 .long("--deleted"),
72 )
72 )
73 .arg(
73 .arg(
74 Arg::with_name("unknown")
74 Arg::with_name("unknown")
75 .help("show only unknown (not tracked) files")
75 .help("show only unknown (not tracked) files")
76 .short("-u")
76 .short("-u")
77 .long("--unknown"),
77 .long("--unknown"),
78 )
78 )
79 .arg(
79 .arg(
80 Arg::with_name("ignored")
80 Arg::with_name("ignored")
81 .help("show only ignored files")
81 .help("show only ignored files")
82 .short("-i")
82 .short("-i")
83 .long("--ignored"),
83 .long("--ignored"),
84 )
84 )
85 .arg(
86 Arg::with_name("no-status")
87 .help("hide status prefix")
88 .short("-n")
89 .long("--no-status"),
90 )
85 }
91 }
86
92
87 /// Pure data type allowing the caller to specify file states to display
93 /// Pure data type allowing the caller to specify file states to display
88 #[derive(Copy, Clone, Debug)]
94 #[derive(Copy, Clone, Debug)]
89 pub struct DisplayStates {
95 pub struct DisplayStates {
90 pub modified: bool,
96 pub modified: bool,
91 pub added: bool,
97 pub added: bool,
92 pub removed: bool,
98 pub removed: bool,
93 pub clean: bool,
99 pub clean: bool,
94 pub deleted: bool,
100 pub deleted: bool,
95 pub unknown: bool,
101 pub unknown: bool,
96 pub ignored: bool,
102 pub ignored: bool,
97 }
103 }
98
104
99 pub const DEFAULT_DISPLAY_STATES: DisplayStates = DisplayStates {
105 pub const DEFAULT_DISPLAY_STATES: DisplayStates = DisplayStates {
100 modified: true,
106 modified: true,
101 added: true,
107 added: true,
102 removed: true,
108 removed: true,
103 clean: false,
109 clean: false,
104 deleted: true,
110 deleted: true,
105 unknown: true,
111 unknown: true,
106 ignored: false,
112 ignored: false,
107 };
113 };
108
114
109 pub const ALL_DISPLAY_STATES: DisplayStates = DisplayStates {
115 pub const ALL_DISPLAY_STATES: DisplayStates = DisplayStates {
110 modified: true,
116 modified: true,
111 added: true,
117 added: true,
112 removed: true,
118 removed: true,
113 clean: true,
119 clean: true,
114 deleted: true,
120 deleted: true,
115 unknown: true,
121 unknown: true,
116 ignored: true,
122 ignored: true,
117 };
123 };
118
124
119 impl DisplayStates {
125 impl DisplayStates {
120 pub fn is_empty(&self) -> bool {
126 pub fn is_empty(&self) -> bool {
121 !(self.modified
127 !(self.modified
122 || self.added
128 || self.added
123 || self.removed
129 || self.removed
124 || self.clean
130 || self.clean
125 || self.deleted
131 || self.deleted
126 || self.unknown
132 || self.unknown
127 || self.ignored)
133 || self.ignored)
128 }
134 }
129 }
135 }
130
136
131 pub fn run(invocation: &crate::CliInvocation) -> Result<(), CommandError> {
137 pub fn run(invocation: &crate::CliInvocation) -> Result<(), CommandError> {
132 let status_enabled_default = false;
138 let status_enabled_default = false;
133 let status_enabled = invocation.config.get_option(b"rhg", b"status")?;
139 let status_enabled = invocation.config.get_option(b"rhg", b"status")?;
134 if !status_enabled.unwrap_or(status_enabled_default) {
140 if !status_enabled.unwrap_or(status_enabled_default) {
135 return Err(CommandError::unsupported(
141 return Err(CommandError::unsupported(
136 "status is experimental in rhg (enable it with 'rhg.status = true' \
142 "status is experimental in rhg (enable it with 'rhg.status = true' \
137 or enable fallback with 'rhg.on-unsupported = fallback')"
143 or enable fallback with 'rhg.on-unsupported = fallback')"
138 ));
144 ));
139 }
145 }
140
146
141 // TODO: lift these limitations
147 // TODO: lift these limitations
142 if invocation.config.get_bool(b"ui", b"tweakdefaults")? {
148 if invocation.config.get_bool(b"ui", b"tweakdefaults")? {
143 return Err(CommandError::unsupported(
149 return Err(CommandError::unsupported(
144 "ui.tweakdefaults is not yet supported with rhg status",
150 "ui.tweakdefaults is not yet supported with rhg status",
145 ));
151 ));
146 }
152 }
147 if invocation.config.get_bool(b"ui", b"statuscopies")? {
153 if invocation.config.get_bool(b"ui", b"statuscopies")? {
148 return Err(CommandError::unsupported(
154 return Err(CommandError::unsupported(
149 "ui.statuscopies is not yet supported with rhg status",
155 "ui.statuscopies is not yet supported with rhg status",
150 ));
156 ));
151 }
157 }
152 if invocation
158 if invocation
153 .config
159 .config
154 .get(b"commands", b"status.terse")
160 .get(b"commands", b"status.terse")
155 .is_some()
161 .is_some()
156 {
162 {
157 return Err(CommandError::unsupported(
163 return Err(CommandError::unsupported(
158 "status.terse is not yet supported with rhg status",
164 "status.terse is not yet supported with rhg status",
159 ));
165 ));
160 }
166 }
161
167
162 let ui = invocation.ui;
168 let ui = invocation.ui;
163 let config = invocation.config;
169 let config = invocation.config;
164 let args = invocation.subcommand_args;
170 let args = invocation.subcommand_args;
165 let display_states = if args.is_present("all") {
171 let display_states = if args.is_present("all") {
166 // TODO when implementing `--quiet`: it excludes clean files
172 // TODO when implementing `--quiet`: it excludes clean files
167 // from `--all`
173 // from `--all`
168 ALL_DISPLAY_STATES
174 ALL_DISPLAY_STATES
169 } else {
175 } else {
170 let requested = DisplayStates {
176 let requested = DisplayStates {
171 modified: args.is_present("modified"),
177 modified: args.is_present("modified"),
172 added: args.is_present("added"),
178 added: args.is_present("added"),
173 removed: args.is_present("removed"),
179 removed: args.is_present("removed"),
174 clean: args.is_present("clean"),
180 clean: args.is_present("clean"),
175 deleted: args.is_present("deleted"),
181 deleted: args.is_present("deleted"),
176 unknown: args.is_present("unknown"),
182 unknown: args.is_present("unknown"),
177 ignored: args.is_present("ignored"),
183 ignored: args.is_present("ignored"),
178 };
184 };
179 if requested.is_empty() {
185 if requested.is_empty() {
180 DEFAULT_DISPLAY_STATES
186 DEFAULT_DISPLAY_STATES
181 } else {
187 } else {
182 requested
188 requested
183 }
189 }
184 };
190 };
191 let no_status = args.is_present("no-status");
185
192
186 let repo = invocation.repo?;
193 let repo = invocation.repo?;
187 let mut dmap = repo.dirstate_map_mut()?;
194 let mut dmap = repo.dirstate_map_mut()?;
188
195
189 let options = StatusOptions {
196 let options = StatusOptions {
190 // TODO should be provided by the dirstate parsing and
197 // TODO should be provided by the dirstate parsing and
191 // hence be stored on dmap. Using a value that assumes we aren't
198 // hence be stored on dmap. Using a value that assumes we aren't
192 // below the time resolution granularity of the FS and the
199 // below the time resolution granularity of the FS and the
193 // dirstate.
200 // dirstate.
194 last_normal_time: TruncatedTimestamp::new_truncate(0, 0),
201 last_normal_time: TruncatedTimestamp::new_truncate(0, 0),
195 // we're currently supporting file systems with exec flags only
202 // we're currently supporting file systems with exec flags only
196 // anyway
203 // anyway
197 check_exec: true,
204 check_exec: true,
198 list_clean: display_states.clean,
205 list_clean: display_states.clean,
199 list_unknown: display_states.unknown,
206 list_unknown: display_states.unknown,
200 list_ignored: display_states.ignored,
207 list_ignored: display_states.ignored,
201 collect_traversed_dirs: false,
208 collect_traversed_dirs: false,
202 };
209 };
203 let ignore_file = repo.working_directory_vfs().join(".hgignore"); // TODO hardcoded
210 let ignore_file = repo.working_directory_vfs().join(".hgignore"); // TODO hardcoded
204 let (mut ds_status, pattern_warnings) = dmap.status(
211 let (mut ds_status, pattern_warnings) = dmap.status(
205 &AlwaysMatcher,
212 &AlwaysMatcher,
206 repo.working_directory_path().to_owned(),
213 repo.working_directory_path().to_owned(),
207 vec![ignore_file],
214 vec![ignore_file],
208 options,
215 options,
209 )?;
216 )?;
210 if !pattern_warnings.is_empty() {
217 if !pattern_warnings.is_empty() {
211 warn!("Pattern warnings: {:?}", &pattern_warnings);
218 warn!("Pattern warnings: {:?}", &pattern_warnings);
212 }
219 }
213
220
214 if !ds_status.bad.is_empty() {
221 if !ds_status.bad.is_empty() {
215 warn!("Bad matches {:?}", &(ds_status.bad))
222 warn!("Bad matches {:?}", &(ds_status.bad))
216 }
223 }
217 if !ds_status.unsure.is_empty() {
224 if !ds_status.unsure.is_empty() {
218 info!(
225 info!(
219 "Files to be rechecked by retrieval from filelog: {:?}",
226 "Files to be rechecked by retrieval from filelog: {:?}",
220 &ds_status.unsure
227 &ds_status.unsure
221 );
228 );
222 }
229 }
223 if !ds_status.unsure.is_empty()
230 if !ds_status.unsure.is_empty()
224 && (display_states.modified || display_states.clean)
231 && (display_states.modified || display_states.clean)
225 {
232 {
226 let p1 = repo.dirstate_parents()?.p1;
233 let p1 = repo.dirstate_parents()?.p1;
227 let manifest = repo.manifest_for_node(p1).map_err(|e| {
234 let manifest = repo.manifest_for_node(p1).map_err(|e| {
228 CommandError::from((e, &*format!("{:x}", p1.short())))
235 CommandError::from((e, &*format!("{:x}", p1.short())))
229 })?;
236 })?;
230 for to_check in ds_status.unsure {
237 for to_check in ds_status.unsure {
231 if unsure_is_modified(repo, &manifest, &to_check)? {
238 if unsure_is_modified(repo, &manifest, &to_check)? {
232 if display_states.modified {
239 if display_states.modified {
233 ds_status.modified.push(to_check);
240 ds_status.modified.push(to_check);
234 }
241 }
235 } else {
242 } else {
236 if display_states.clean {
243 if display_states.clean {
237 ds_status.clean.push(to_check);
244 ds_status.clean.push(to_check);
238 }
245 }
239 }
246 }
240 }
247 }
241 }
248 }
242 if display_states.modified {
249 if display_states.modified {
243 display_status_paths(ui, repo, config, &mut ds_status.modified, b"M")?;
250 display_status_paths(
251 ui,
252 repo,
253 config,
254 no_status,
255 &mut ds_status.modified,
256 b"M",
257 )?;
244 }
258 }
245 if display_states.added {
259 if display_states.added {
246 display_status_paths(ui, repo, config, &mut ds_status.added, b"A")?;
260 display_status_paths(
261 ui,
262 repo,
263 config,
264 no_status,
265 &mut ds_status.added,
266 b"A",
267 )?;
247 }
268 }
248 if display_states.removed {
269 if display_states.removed {
249 display_status_paths(ui, repo, config, &mut ds_status.removed, b"R")?;
270 display_status_paths(
271 ui,
272 repo,
273 config,
274 no_status,
275 &mut ds_status.removed,
276 b"R",
277 )?;
250 }
278 }
251 if display_states.deleted {
279 if display_states.deleted {
252 display_status_paths(ui, repo, config, &mut ds_status.deleted, b"!")?;
280 display_status_paths(
281 ui,
282 repo,
283 config,
284 no_status,
285 &mut ds_status.deleted,
286 b"!",
287 )?;
253 }
288 }
254 if display_states.unknown {
289 if display_states.unknown {
255 display_status_paths(ui, repo, config, &mut ds_status.unknown, b"?")?;
290 display_status_paths(
291 ui,
292 repo,
293 config,
294 no_status,
295 &mut ds_status.unknown,
296 b"?",
297 )?;
256 }
298 }
257 if display_states.ignored {
299 if display_states.ignored {
258 display_status_paths(ui, repo, config, &mut ds_status.ignored, b"I")?;
300 display_status_paths(
301 ui,
302 repo,
303 config,
304 no_status,
305 &mut ds_status.ignored,
306 b"I",
307 )?;
259 }
308 }
260 if display_states.clean {
309 if display_states.clean {
261 display_status_paths(ui, repo, config, &mut ds_status.clean, b"C")?;
310 display_status_paths(
311 ui,
312 repo,
313 config,
314 no_status,
315 &mut ds_status.clean,
316 b"C",
317 )?;
262 }
318 }
263 Ok(())
319 Ok(())
264 }
320 }
265
321
266 // Probably more elegant to use a Deref or Borrow trait rather than
322 // Probably more elegant to use a Deref or Borrow trait rather than
267 // harcode HgPathBuf, but probably not really useful at this point
323 // harcode HgPathBuf, but probably not really useful at this point
268 fn display_status_paths(
324 fn display_status_paths(
269 ui: &Ui,
325 ui: &Ui,
270 repo: &Repo,
326 repo: &Repo,
271 config: &Config,
327 config: &Config,
328 no_status: bool,
272 paths: &mut [HgPathCow],
329 paths: &mut [HgPathCow],
273 status_prefix: &[u8],
330 status_prefix: &[u8],
274 ) -> Result<(), CommandError> {
331 ) -> Result<(), CommandError> {
275 paths.sort_unstable();
332 paths.sort_unstable();
276 let mut relative: bool = config.get_bool(b"ui", b"relative-paths")?;
333 let mut relative: bool = config.get_bool(b"ui", b"relative-paths")?;
277 relative = config
334 relative = config
278 .get_option(b"commands", b"status.relative")?
335 .get_option(b"commands", b"status.relative")?
279 .unwrap_or(relative);
336 .unwrap_or(relative);
337 let print_path = |path: &[u8]| {
338 // TODO optim, probably lots of unneeded copies here, especially
339 // if out stream is buffered
340 if no_status {
341 ui.write_stdout(&format_bytes!(b"{}\n", path))
342 } else {
343 ui.write_stdout(&format_bytes!(b"{} {}\n", status_prefix, path))
344 }
345 };
346
280 if relative && !ui.plain() {
347 if relative && !ui.plain() {
281 relativize_paths(
348 relativize_paths(repo, paths.iter().map(Ok), |path| {
282 repo,
349 print_path(&path)
283 paths.iter().map(Ok),
350 })?;
284 |path: Cow<[u8]>| -> Result<(), UiError> {
285 ui.write_stdout(
286 &[status_prefix, b" ", path.as_ref(), b"\n"].concat(),
287 )
288 },
289 )?;
290 } else {
351 } else {
291 for path in paths {
352 for path in paths {
292 // Same TODO as in commands::root
353 print_path(path.as_bytes())?
293 let bytes: &[u8] = path.as_bytes();
294 // TODO optim, probably lots of unneeded copies here, especially
295 // if out stream is buffered
296 ui.write_stdout(&[status_prefix, b" ", bytes, b"\n"].concat())?;
297 }
354 }
298 }
355 }
299 Ok(())
356 Ok(())
300 }
357 }
301
358
302 /// Check if a file is modified by comparing actual repo store and file system.
359 /// Check if a file is modified by comparing actual repo store and file system.
303 ///
360 ///
304 /// This meant to be used for those that the dirstate cannot resolve, due
361 /// This meant to be used for those that the dirstate cannot resolve, due
305 /// to time resolution limits.
362 /// to time resolution limits.
306 fn unsure_is_modified(
363 fn unsure_is_modified(
307 repo: &Repo,
364 repo: &Repo,
308 manifest: &Manifest,
365 manifest: &Manifest,
309 hg_path: &HgPath,
366 hg_path: &HgPath,
310 ) -> Result<bool, HgError> {
367 ) -> Result<bool, HgError> {
311 let vfs = repo.working_directory_vfs();
368 let vfs = repo.working_directory_vfs();
312 let fs_path = hg_path_to_os_string(hg_path).expect("HgPath conversion");
369 let fs_path = hg_path_to_os_string(hg_path).expect("HgPath conversion");
313 let fs_metadata = vfs.symlink_metadata(&fs_path)?;
370 let fs_metadata = vfs.symlink_metadata(&fs_path)?;
314 let is_symlink = fs_metadata.file_type().is_symlink();
371 let is_symlink = fs_metadata.file_type().is_symlink();
315 // TODO: Also account for `FALLBACK_SYMLINK` and `FALLBACK_EXEC` from the dirstate
372 // TODO: Also account for `FALLBACK_SYMLINK` and `FALLBACK_EXEC` from the dirstate
316 let fs_flags = if is_symlink {
373 let fs_flags = if is_symlink {
317 Some(b'l')
374 Some(b'l')
318 } else if has_exec_bit(&fs_metadata) {
375 } else if has_exec_bit(&fs_metadata) {
319 Some(b'x')
376 Some(b'x')
320 } else {
377 } else {
321 None
378 None
322 };
379 };
323
380
324 let entry = manifest
381 let entry = manifest
325 .find_file(hg_path)?
382 .find_file(hg_path)?
326 .expect("ambgious file not in p1");
383 .expect("ambgious file not in p1");
327 if entry.flags != fs_flags {
384 if entry.flags != fs_flags {
328 return Ok(true);
385 return Ok(true);
329 }
386 }
330 let filelog = repo.filelog(hg_path)?;
387 let filelog = repo.filelog(hg_path)?;
331 let filelog_entry =
388 let filelog_entry =
332 filelog.data_for_node(entry.node_id()?).map_err(|_| {
389 filelog.data_for_node(entry.node_id()?).map_err(|_| {
333 HgError::corrupted("filelog missing node from manifest")
390 HgError::corrupted("filelog missing node from manifest")
334 })?;
391 })?;
335 let contents_in_p1 = filelog_entry.data()?;
392 let contents_in_p1 = filelog_entry.data()?;
336
393
337 let fs_contents = if is_symlink {
394 let fs_contents = if is_symlink {
338 get_bytes_from_os_string(vfs.read_link(fs_path)?.into_os_string())
395 get_bytes_from_os_string(vfs.read_link(fs_path)?.into_os_string())
339 } else {
396 } else {
340 vfs.read(fs_path)?
397 vfs.read(fs_path)?
341 };
398 };
342 return Ok(contents_in_p1 != &*fs_contents);
399 return Ok(contents_in_p1 != &*fs_contents);
343 }
400 }
@@ -1,969 +1,976
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 > exp-rc-dirstate-v2=1
6 > exp-rc-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 TODO: fix rhg bugs that make this test fail when status is enabled
12 TODO: fix rhg bugs that make this test fail when status is enabled
13 $ unset RHG_STATUS
13 $ unset RHG_STATUS
14
14
15
15
16 $ hg init repo1
16 $ hg init repo1
17 $ cd repo1
17 $ cd repo1
18 $ mkdir a b a/1 b/1 b/2
18 $ mkdir a b a/1 b/1 b/2
19 $ touch in_root a/in_a b/in_b a/1/in_a_1 b/1/in_b_1 b/2/in_b_2
19 $ touch in_root a/in_a b/in_b a/1/in_a_1 b/1/in_b_1 b/2/in_b_2
20
20
21 hg status in repo root:
21 hg status in repo root:
22
22
23 $ hg status
23 $ hg status
24 ? a/1/in_a_1
24 ? a/1/in_a_1
25 ? a/in_a
25 ? a/in_a
26 ? b/1/in_b_1
26 ? b/1/in_b_1
27 ? b/2/in_b_2
27 ? b/2/in_b_2
28 ? b/in_b
28 ? b/in_b
29 ? in_root
29 ? in_root
30
30
31 hg status . in repo root:
31 hg status . in repo root:
32
32
33 $ hg status .
33 $ hg status .
34 ? a/1/in_a_1
34 ? a/1/in_a_1
35 ? a/in_a
35 ? a/in_a
36 ? b/1/in_b_1
36 ? b/1/in_b_1
37 ? b/2/in_b_2
37 ? b/2/in_b_2
38 ? b/in_b
38 ? b/in_b
39 ? in_root
39 ? in_root
40
40
41 $ hg status --cwd a
41 $ hg status --cwd a
42 ? a/1/in_a_1
42 ? a/1/in_a_1
43 ? a/in_a
43 ? a/in_a
44 ? b/1/in_b_1
44 ? b/1/in_b_1
45 ? b/2/in_b_2
45 ? b/2/in_b_2
46 ? b/in_b
46 ? b/in_b
47 ? in_root
47 ? in_root
48 $ hg status --cwd a .
48 $ hg status --cwd a .
49 ? 1/in_a_1
49 ? 1/in_a_1
50 ? in_a
50 ? in_a
51 $ hg status --cwd a ..
51 $ hg status --cwd a ..
52 ? 1/in_a_1
52 ? 1/in_a_1
53 ? in_a
53 ? in_a
54 ? ../b/1/in_b_1
54 ? ../b/1/in_b_1
55 ? ../b/2/in_b_2
55 ? ../b/2/in_b_2
56 ? ../b/in_b
56 ? ../b/in_b
57 ? ../in_root
57 ? ../in_root
58
58
59 $ hg status --cwd b
59 $ hg status --cwd b
60 ? a/1/in_a_1
60 ? a/1/in_a_1
61 ? a/in_a
61 ? a/in_a
62 ? b/1/in_b_1
62 ? b/1/in_b_1
63 ? b/2/in_b_2
63 ? b/2/in_b_2
64 ? b/in_b
64 ? b/in_b
65 ? in_root
65 ? in_root
66 $ hg status --cwd b .
66 $ hg status --cwd b .
67 ? 1/in_b_1
67 ? 1/in_b_1
68 ? 2/in_b_2
68 ? 2/in_b_2
69 ? in_b
69 ? in_b
70 $ hg status --cwd b ..
70 $ hg status --cwd b ..
71 ? ../a/1/in_a_1
71 ? ../a/1/in_a_1
72 ? ../a/in_a
72 ? ../a/in_a
73 ? 1/in_b_1
73 ? 1/in_b_1
74 ? 2/in_b_2
74 ? 2/in_b_2
75 ? in_b
75 ? in_b
76 ? ../in_root
76 ? ../in_root
77
77
78 $ hg status --cwd a/1
78 $ hg status --cwd a/1
79 ? a/1/in_a_1
79 ? a/1/in_a_1
80 ? a/in_a
80 ? a/in_a
81 ? b/1/in_b_1
81 ? b/1/in_b_1
82 ? b/2/in_b_2
82 ? b/2/in_b_2
83 ? b/in_b
83 ? b/in_b
84 ? in_root
84 ? in_root
85 $ hg status --cwd a/1 .
85 $ hg status --cwd a/1 .
86 ? in_a_1
86 ? in_a_1
87 $ hg status --cwd a/1 ..
87 $ hg status --cwd a/1 ..
88 ? in_a_1
88 ? in_a_1
89 ? ../in_a
89 ? ../in_a
90
90
91 $ hg status --cwd b/1
91 $ hg status --cwd b/1
92 ? a/1/in_a_1
92 ? a/1/in_a_1
93 ? a/in_a
93 ? a/in_a
94 ? b/1/in_b_1
94 ? b/1/in_b_1
95 ? b/2/in_b_2
95 ? b/2/in_b_2
96 ? b/in_b
96 ? b/in_b
97 ? in_root
97 ? in_root
98 $ hg status --cwd b/1 .
98 $ hg status --cwd b/1 .
99 ? in_b_1
99 ? in_b_1
100 $ hg status --cwd b/1 ..
100 $ hg status --cwd b/1 ..
101 ? in_b_1
101 ? in_b_1
102 ? ../2/in_b_2
102 ? ../2/in_b_2
103 ? ../in_b
103 ? ../in_b
104
104
105 $ hg status --cwd b/2
105 $ hg status --cwd b/2
106 ? a/1/in_a_1
106 ? a/1/in_a_1
107 ? a/in_a
107 ? a/in_a
108 ? b/1/in_b_1
108 ? b/1/in_b_1
109 ? b/2/in_b_2
109 ? b/2/in_b_2
110 ? b/in_b
110 ? b/in_b
111 ? in_root
111 ? in_root
112 $ hg status --cwd b/2 .
112 $ hg status --cwd b/2 .
113 ? in_b_2
113 ? in_b_2
114 $ hg status --cwd b/2 ..
114 $ hg status --cwd b/2 ..
115 ? ../1/in_b_1
115 ? ../1/in_b_1
116 ? in_b_2
116 ? in_b_2
117 ? ../in_b
117 ? ../in_b
118
118
119 combining patterns with root and patterns without a root works
119 combining patterns with root and patterns without a root works
120
120
121 $ hg st a/in_a re:.*b$
121 $ hg st a/in_a re:.*b$
122 ? a/in_a
122 ? a/in_a
123 ? b/in_b
123 ? b/in_b
124
124
125 tweaking defaults works
125 tweaking defaults works
126 $ hg status --cwd a --config ui.tweakdefaults=yes
126 $ hg status --cwd a --config ui.tweakdefaults=yes
127 ? 1/in_a_1
127 ? 1/in_a_1
128 ? in_a
128 ? in_a
129 ? ../b/1/in_b_1
129 ? ../b/1/in_b_1
130 ? ../b/2/in_b_2
130 ? ../b/2/in_b_2
131 ? ../b/in_b
131 ? ../b/in_b
132 ? ../in_root
132 ? ../in_root
133 $ HGPLAIN=1 hg status --cwd a --config ui.tweakdefaults=yes
133 $ HGPLAIN=1 hg status --cwd a --config ui.tweakdefaults=yes
134 ? a/1/in_a_1 (glob)
134 ? a/1/in_a_1 (glob)
135 ? a/in_a (glob)
135 ? a/in_a (glob)
136 ? b/1/in_b_1 (glob)
136 ? b/1/in_b_1 (glob)
137 ? b/2/in_b_2 (glob)
137 ? b/2/in_b_2 (glob)
138 ? b/in_b (glob)
138 ? b/in_b (glob)
139 ? in_root
139 ? in_root
140 $ HGPLAINEXCEPT=tweakdefaults hg status --cwd a --config ui.tweakdefaults=yes
140 $ HGPLAINEXCEPT=tweakdefaults hg status --cwd a --config ui.tweakdefaults=yes
141 ? 1/in_a_1
141 ? 1/in_a_1
142 ? in_a
142 ? in_a
143 ? ../b/1/in_b_1
143 ? ../b/1/in_b_1
144 ? ../b/2/in_b_2
144 ? ../b/2/in_b_2
145 ? ../b/in_b
145 ? ../b/in_b
146 ? ../in_root (glob)
146 ? ../in_root (glob)
147
147
148 relative paths can be requested
148 relative paths can be requested
149
149
150 $ hg status --cwd a --config ui.relative-paths=yes
150 $ hg status --cwd a --config ui.relative-paths=yes
151 ? 1/in_a_1
151 ? 1/in_a_1
152 ? in_a
152 ? in_a
153 ? ../b/1/in_b_1
153 ? ../b/1/in_b_1
154 ? ../b/2/in_b_2
154 ? ../b/2/in_b_2
155 ? ../b/in_b
155 ? ../b/in_b
156 ? ../in_root
156 ? ../in_root
157
157
158 $ hg status --cwd a . --config ui.relative-paths=legacy
158 $ hg status --cwd a . --config ui.relative-paths=legacy
159 ? 1/in_a_1
159 ? 1/in_a_1
160 ? in_a
160 ? in_a
161 $ hg status --cwd a . --config ui.relative-paths=no
161 $ hg status --cwd a . --config ui.relative-paths=no
162 ? a/1/in_a_1
162 ? a/1/in_a_1
163 ? a/in_a
163 ? a/in_a
164
164
165 commands.status.relative overrides ui.relative-paths
165 commands.status.relative overrides ui.relative-paths
166
166
167 $ cat >> $HGRCPATH <<EOF
167 $ cat >> $HGRCPATH <<EOF
168 > [ui]
168 > [ui]
169 > relative-paths = False
169 > relative-paths = False
170 > [commands]
170 > [commands]
171 > status.relative = True
171 > status.relative = True
172 > EOF
172 > EOF
173 $ hg status --cwd a
173 $ hg status --cwd a
174 ? 1/in_a_1
174 ? 1/in_a_1
175 ? in_a
175 ? in_a
176 ? ../b/1/in_b_1
176 ? ../b/1/in_b_1
177 ? ../b/2/in_b_2
177 ? ../b/2/in_b_2
178 ? ../b/in_b
178 ? ../b/in_b
179 ? ../in_root
179 ? ../in_root
180 $ HGPLAIN=1 hg status --cwd a
180 $ HGPLAIN=1 hg status --cwd a
181 ? a/1/in_a_1 (glob)
181 ? a/1/in_a_1 (glob)
182 ? a/in_a (glob)
182 ? a/in_a (glob)
183 ? b/1/in_b_1 (glob)
183 ? b/1/in_b_1 (glob)
184 ? b/2/in_b_2 (glob)
184 ? b/2/in_b_2 (glob)
185 ? b/in_b (glob)
185 ? b/in_b (glob)
186 ? in_root
186 ? in_root
187
187
188 if relative paths are explicitly off, tweakdefaults doesn't change it
188 if relative paths are explicitly off, tweakdefaults doesn't change it
189 $ cat >> $HGRCPATH <<EOF
189 $ cat >> $HGRCPATH <<EOF
190 > [commands]
190 > [commands]
191 > status.relative = False
191 > status.relative = False
192 > EOF
192 > EOF
193 $ hg status --cwd a --config ui.tweakdefaults=yes
193 $ hg status --cwd a --config ui.tweakdefaults=yes
194 ? a/1/in_a_1
194 ? a/1/in_a_1
195 ? a/in_a
195 ? a/in_a
196 ? b/1/in_b_1
196 ? b/1/in_b_1
197 ? b/2/in_b_2
197 ? b/2/in_b_2
198 ? b/in_b
198 ? b/in_b
199 ? in_root
199 ? in_root
200
200
201 $ cd ..
201 $ cd ..
202
202
203 $ hg init repo2
203 $ hg init repo2
204 $ cd repo2
204 $ cd repo2
205 $ touch modified removed deleted ignored
205 $ touch modified removed deleted ignored
206 $ echo "^ignored$" > .hgignore
206 $ echo "^ignored$" > .hgignore
207 $ hg ci -A -m 'initial checkin'
207 $ hg ci -A -m 'initial checkin'
208 adding .hgignore
208 adding .hgignore
209 adding deleted
209 adding deleted
210 adding modified
210 adding modified
211 adding removed
211 adding removed
212 $ touch modified added unknown ignored
212 $ touch modified added unknown ignored
213 $ hg add added
213 $ hg add added
214 $ hg remove removed
214 $ hg remove removed
215 $ rm deleted
215 $ rm deleted
216
216
217 hg status:
217 hg status:
218
218
219 $ hg status
219 $ hg status
220 A added
220 A added
221 R removed
221 R removed
222 ! deleted
222 ! deleted
223 ? unknown
223 ? unknown
224
224
225 hg status -n:
226 $ env RHG_STATUS=1 RHG_ON_UNSUPPORTED=abort hg status -n
227 added
228 removed
229 deleted
230 unknown
231
225 hg status modified added removed deleted unknown never-existed ignored:
232 hg status modified added removed deleted unknown never-existed ignored:
226
233
227 $ hg status modified added removed deleted unknown never-existed ignored
234 $ hg status modified added removed deleted unknown never-existed ignored
228 never-existed: * (glob)
235 never-existed: * (glob)
229 A added
236 A added
230 R removed
237 R removed
231 ! deleted
238 ! deleted
232 ? unknown
239 ? unknown
233
240
234 $ hg copy modified copied
241 $ hg copy modified copied
235
242
236 hg status -C:
243 hg status -C:
237
244
238 $ hg status -C
245 $ hg status -C
239 A added
246 A added
240 A copied
247 A copied
241 modified
248 modified
242 R removed
249 R removed
243 ! deleted
250 ! deleted
244 ? unknown
251 ? unknown
245
252
246 hg status -A:
253 hg status -A:
247
254
248 $ hg status -A
255 $ hg status -A
249 A added
256 A added
250 A copied
257 A copied
251 modified
258 modified
252 R removed
259 R removed
253 ! deleted
260 ! deleted
254 ? unknown
261 ? unknown
255 I ignored
262 I ignored
256 C .hgignore
263 C .hgignore
257 C modified
264 C modified
258
265
259 $ hg status -A -T '{status} {path} {node|shortest}\n'
266 $ hg status -A -T '{status} {path} {node|shortest}\n'
260 A added ffff
267 A added ffff
261 A copied ffff
268 A copied ffff
262 R removed ffff
269 R removed ffff
263 ! deleted ffff
270 ! deleted ffff
264 ? unknown ffff
271 ? unknown ffff
265 I ignored ffff
272 I ignored ffff
266 C .hgignore ffff
273 C .hgignore ffff
267 C modified ffff
274 C modified ffff
268
275
269 $ hg status -A -Tjson
276 $ hg status -A -Tjson
270 [
277 [
271 {
278 {
272 "itemtype": "file",
279 "itemtype": "file",
273 "path": "added",
280 "path": "added",
274 "status": "A"
281 "status": "A"
275 },
282 },
276 {
283 {
277 "itemtype": "file",
284 "itemtype": "file",
278 "path": "copied",
285 "path": "copied",
279 "source": "modified",
286 "source": "modified",
280 "status": "A"
287 "status": "A"
281 },
288 },
282 {
289 {
283 "itemtype": "file",
290 "itemtype": "file",
284 "path": "removed",
291 "path": "removed",
285 "status": "R"
292 "status": "R"
286 },
293 },
287 {
294 {
288 "itemtype": "file",
295 "itemtype": "file",
289 "path": "deleted",
296 "path": "deleted",
290 "status": "!"
297 "status": "!"
291 },
298 },
292 {
299 {
293 "itemtype": "file",
300 "itemtype": "file",
294 "path": "unknown",
301 "path": "unknown",
295 "status": "?"
302 "status": "?"
296 },
303 },
297 {
304 {
298 "itemtype": "file",
305 "itemtype": "file",
299 "path": "ignored",
306 "path": "ignored",
300 "status": "I"
307 "status": "I"
301 },
308 },
302 {
309 {
303 "itemtype": "file",
310 "itemtype": "file",
304 "path": ".hgignore",
311 "path": ".hgignore",
305 "status": "C"
312 "status": "C"
306 },
313 },
307 {
314 {
308 "itemtype": "file",
315 "itemtype": "file",
309 "path": "modified",
316 "path": "modified",
310 "status": "C"
317 "status": "C"
311 }
318 }
312 ]
319 ]
313
320
314 $ hg status -A -Tpickle > pickle
321 $ hg status -A -Tpickle > pickle
315 >>> from __future__ import print_function
322 >>> from __future__ import print_function
316 >>> from mercurial import util
323 >>> from mercurial import util
317 >>> pickle = util.pickle
324 >>> pickle = util.pickle
318 >>> data = sorted((x[b'status'].decode(), x[b'path'].decode()) for x in pickle.load(open("pickle", r"rb")))
325 >>> data = sorted((x[b'status'].decode(), x[b'path'].decode()) for x in pickle.load(open("pickle", r"rb")))
319 >>> for s, p in data: print("%s %s" % (s, p))
326 >>> for s, p in data: print("%s %s" % (s, p))
320 ! deleted
327 ! deleted
321 ? pickle
328 ? pickle
322 ? unknown
329 ? unknown
323 A added
330 A added
324 A copied
331 A copied
325 C .hgignore
332 C .hgignore
326 C modified
333 C modified
327 I ignored
334 I ignored
328 R removed
335 R removed
329 $ rm pickle
336 $ rm pickle
330
337
331 $ echo "^ignoreddir$" > .hgignore
338 $ echo "^ignoreddir$" > .hgignore
332 $ mkdir ignoreddir
339 $ mkdir ignoreddir
333 $ touch ignoreddir/file
340 $ touch ignoreddir/file
334
341
335 Test templater support:
342 Test templater support:
336
343
337 $ hg status -AT "[{status}]\t{if(source, '{source} -> ')}{path}\n"
344 $ hg status -AT "[{status}]\t{if(source, '{source} -> ')}{path}\n"
338 [M] .hgignore
345 [M] .hgignore
339 [A] added
346 [A] added
340 [A] modified -> copied
347 [A] modified -> copied
341 [R] removed
348 [R] removed
342 [!] deleted
349 [!] deleted
343 [?] ignored
350 [?] ignored
344 [?] unknown
351 [?] unknown
345 [I] ignoreddir/file
352 [I] ignoreddir/file
346 [C] modified
353 [C] modified
347 $ hg status -AT default
354 $ hg status -AT default
348 M .hgignore
355 M .hgignore
349 A added
356 A added
350 A copied
357 A copied
351 modified
358 modified
352 R removed
359 R removed
353 ! deleted
360 ! deleted
354 ? ignored
361 ? ignored
355 ? unknown
362 ? unknown
356 I ignoreddir/file
363 I ignoreddir/file
357 C modified
364 C modified
358 $ hg status -T compact
365 $ hg status -T compact
359 abort: "status" not in template map
366 abort: "status" not in template map
360 [255]
367 [255]
361
368
362 hg status ignoreddir/file:
369 hg status ignoreddir/file:
363
370
364 $ hg status ignoreddir/file
371 $ hg status ignoreddir/file
365
372
366 hg status -i ignoreddir/file:
373 hg status -i ignoreddir/file:
367
374
368 $ hg status -i ignoreddir/file
375 $ hg status -i ignoreddir/file
369 I ignoreddir/file
376 I ignoreddir/file
370 $ cd ..
377 $ cd ..
371
378
372 Check 'status -q' and some combinations
379 Check 'status -q' and some combinations
373
380
374 $ hg init repo3
381 $ hg init repo3
375 $ cd repo3
382 $ cd repo3
376 $ touch modified removed deleted ignored
383 $ touch modified removed deleted ignored
377 $ echo "^ignored$" > .hgignore
384 $ echo "^ignored$" > .hgignore
378 $ hg commit -A -m 'initial checkin'
385 $ hg commit -A -m 'initial checkin'
379 adding .hgignore
386 adding .hgignore
380 adding deleted
387 adding deleted
381 adding modified
388 adding modified
382 adding removed
389 adding removed
383 $ touch added unknown ignored
390 $ touch added unknown ignored
384 $ hg add added
391 $ hg add added
385 $ echo "test" >> modified
392 $ echo "test" >> modified
386 $ hg remove removed
393 $ hg remove removed
387 $ rm deleted
394 $ rm deleted
388 $ hg copy modified copied
395 $ hg copy modified copied
389
396
390 Specify working directory revision explicitly, that should be the same as
397 Specify working directory revision explicitly, that should be the same as
391 "hg status"
398 "hg status"
392
399
393 $ hg status --change "wdir()"
400 $ hg status --change "wdir()"
394 M modified
401 M modified
395 A added
402 A added
396 A copied
403 A copied
397 R removed
404 R removed
398 ! deleted
405 ! deleted
399 ? unknown
406 ? unknown
400
407
401 Run status with 2 different flags.
408 Run status with 2 different flags.
402 Check if result is the same or different.
409 Check if result is the same or different.
403 If result is not as expected, raise error
410 If result is not as expected, raise error
404
411
405 $ assert() {
412 $ assert() {
406 > hg status $1 > ../a
413 > hg status $1 > ../a
407 > hg status $2 > ../b
414 > hg status $2 > ../b
408 > if diff ../a ../b > /dev/null; then
415 > if diff ../a ../b > /dev/null; then
409 > out=0
416 > out=0
410 > else
417 > else
411 > out=1
418 > out=1
412 > fi
419 > fi
413 > if [ $3 -eq 0 ]; then
420 > if [ $3 -eq 0 ]; then
414 > df="same"
421 > df="same"
415 > else
422 > else
416 > df="different"
423 > df="different"
417 > fi
424 > fi
418 > if [ $out -ne $3 ]; then
425 > if [ $out -ne $3 ]; then
419 > echo "Error on $1 and $2, should be $df."
426 > echo "Error on $1 and $2, should be $df."
420 > fi
427 > fi
421 > }
428 > }
422
429
423 Assert flag1 flag2 [0-same | 1-different]
430 Assert flag1 flag2 [0-same | 1-different]
424
431
425 $ assert "-q" "-mard" 0
432 $ assert "-q" "-mard" 0
426 $ assert "-A" "-marduicC" 0
433 $ assert "-A" "-marduicC" 0
427 $ assert "-qA" "-mardcC" 0
434 $ assert "-qA" "-mardcC" 0
428 $ assert "-qAui" "-A" 0
435 $ assert "-qAui" "-A" 0
429 $ assert "-qAu" "-marducC" 0
436 $ assert "-qAu" "-marducC" 0
430 $ assert "-qAi" "-mardicC" 0
437 $ assert "-qAi" "-mardicC" 0
431 $ assert "-qu" "-u" 0
438 $ assert "-qu" "-u" 0
432 $ assert "-q" "-u" 1
439 $ assert "-q" "-u" 1
433 $ assert "-m" "-a" 1
440 $ assert "-m" "-a" 1
434 $ assert "-r" "-d" 1
441 $ assert "-r" "-d" 1
435 $ cd ..
442 $ cd ..
436
443
437 $ hg init repo4
444 $ hg init repo4
438 $ cd repo4
445 $ cd repo4
439 $ touch modified removed deleted
446 $ touch modified removed deleted
440 $ hg ci -q -A -m 'initial checkin'
447 $ hg ci -q -A -m 'initial checkin'
441 $ touch added unknown
448 $ touch added unknown
442 $ hg add added
449 $ hg add added
443 $ hg remove removed
450 $ hg remove removed
444 $ rm deleted
451 $ rm deleted
445 $ echo x > modified
452 $ echo x > modified
446 $ hg copy modified copied
453 $ hg copy modified copied
447 $ hg ci -m 'test checkin' -d "1000001 0"
454 $ hg ci -m 'test checkin' -d "1000001 0"
448 $ rm *
455 $ rm *
449 $ touch unrelated
456 $ touch unrelated
450 $ hg ci -q -A -m 'unrelated checkin' -d "1000002 0"
457 $ hg ci -q -A -m 'unrelated checkin' -d "1000002 0"
451
458
452 hg status --change 1:
459 hg status --change 1:
453
460
454 $ hg status --change 1
461 $ hg status --change 1
455 M modified
462 M modified
456 A added
463 A added
457 A copied
464 A copied
458 R removed
465 R removed
459
466
460 hg status --change 1 unrelated:
467 hg status --change 1 unrelated:
461
468
462 $ hg status --change 1 unrelated
469 $ hg status --change 1 unrelated
463
470
464 hg status -C --change 1 added modified copied removed deleted:
471 hg status -C --change 1 added modified copied removed deleted:
465
472
466 $ hg status -C --change 1 added modified copied removed deleted
473 $ hg status -C --change 1 added modified copied removed deleted
467 M modified
474 M modified
468 A added
475 A added
469 A copied
476 A copied
470 modified
477 modified
471 R removed
478 R removed
472
479
473 hg status -A --change 1 and revset:
480 hg status -A --change 1 and revset:
474
481
475 $ hg status -A --change '1|1'
482 $ hg status -A --change '1|1'
476 M modified
483 M modified
477 A added
484 A added
478 A copied
485 A copied
479 modified
486 modified
480 R removed
487 R removed
481 C deleted
488 C deleted
482
489
483 $ cd ..
490 $ cd ..
484
491
485 hg status with --rev and reverted changes:
492 hg status with --rev and reverted changes:
486
493
487 $ hg init reverted-changes-repo
494 $ hg init reverted-changes-repo
488 $ cd reverted-changes-repo
495 $ cd reverted-changes-repo
489 $ echo a > file
496 $ echo a > file
490 $ hg add file
497 $ hg add file
491 $ hg ci -m a
498 $ hg ci -m a
492 $ echo b > file
499 $ echo b > file
493 $ hg ci -m b
500 $ hg ci -m b
494
501
495 reverted file should appear clean
502 reverted file should appear clean
496
503
497 $ hg revert -r 0 .
504 $ hg revert -r 0 .
498 reverting file
505 reverting file
499 $ hg status -A --rev 0
506 $ hg status -A --rev 0
500 C file
507 C file
501
508
502 #if execbit
509 #if execbit
503 reverted file with changed flag should appear modified
510 reverted file with changed flag should appear modified
504
511
505 $ chmod +x file
512 $ chmod +x file
506 $ hg status -A --rev 0
513 $ hg status -A --rev 0
507 M file
514 M file
508
515
509 $ hg revert -r 0 .
516 $ hg revert -r 0 .
510 reverting file
517 reverting file
511
518
512 reverted and committed file with changed flag should appear modified
519 reverted and committed file with changed flag should appear modified
513
520
514 $ hg co -C .
521 $ hg co -C .
515 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
522 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
516 $ chmod +x file
523 $ chmod +x file
517 $ hg ci -m 'change flag'
524 $ hg ci -m 'change flag'
518 $ hg status -A --rev 1 --rev 2
525 $ hg status -A --rev 1 --rev 2
519 M file
526 M file
520 $ hg diff -r 1 -r 2
527 $ hg diff -r 1 -r 2
521
528
522 #endif
529 #endif
523
530
524 $ cd ..
531 $ cd ..
525
532
526 hg status of binary file starting with '\1\n', a separator for metadata:
533 hg status of binary file starting with '\1\n', a separator for metadata:
527
534
528 $ hg init repo5
535 $ hg init repo5
529 $ cd repo5
536 $ cd repo5
530 >>> open("010a", r"wb").write(b"\1\nfoo") and None
537 >>> open("010a", r"wb").write(b"\1\nfoo") and None
531 $ hg ci -q -A -m 'initial checkin'
538 $ hg ci -q -A -m 'initial checkin'
532 $ hg status -A
539 $ hg status -A
533 C 010a
540 C 010a
534
541
535 >>> open("010a", r"wb").write(b"\1\nbar") and None
542 >>> open("010a", r"wb").write(b"\1\nbar") and None
536 $ hg status -A
543 $ hg status -A
537 M 010a
544 M 010a
538 $ hg ci -q -m 'modify 010a'
545 $ hg ci -q -m 'modify 010a'
539 $ hg status -A --rev 0:1
546 $ hg status -A --rev 0:1
540 M 010a
547 M 010a
541
548
542 $ touch empty
549 $ touch empty
543 $ hg ci -q -A -m 'add another file'
550 $ hg ci -q -A -m 'add another file'
544 $ hg status -A --rev 1:2 010a
551 $ hg status -A --rev 1:2 010a
545 C 010a
552 C 010a
546
553
547 $ cd ..
554 $ cd ..
548
555
549 test "hg status" with "directory pattern" which matches against files
556 test "hg status" with "directory pattern" which matches against files
550 only known on target revision.
557 only known on target revision.
551
558
552 $ hg init repo6
559 $ hg init repo6
553 $ cd repo6
560 $ cd repo6
554
561
555 $ echo a > a.txt
562 $ echo a > a.txt
556 $ hg add a.txt
563 $ hg add a.txt
557 $ hg commit -m '#0'
564 $ hg commit -m '#0'
558 $ mkdir -p 1/2/3/4/5
565 $ mkdir -p 1/2/3/4/5
559 $ echo b > 1/2/3/4/5/b.txt
566 $ echo b > 1/2/3/4/5/b.txt
560 $ hg add 1/2/3/4/5/b.txt
567 $ hg add 1/2/3/4/5/b.txt
561 $ hg commit -m '#1'
568 $ hg commit -m '#1'
562
569
563 $ hg update -C 0 > /dev/null
570 $ hg update -C 0 > /dev/null
564 $ hg status -A
571 $ hg status -A
565 C a.txt
572 C a.txt
566
573
567 the directory matching against specified pattern should be removed,
574 the directory matching against specified pattern should be removed,
568 because directory existence prevents 'dirstate.walk()' from showing
575 because directory existence prevents 'dirstate.walk()' from showing
569 warning message about such pattern.
576 warning message about such pattern.
570
577
571 $ test ! -d 1
578 $ test ! -d 1
572 $ hg status -A --rev 1 1/2/3/4/5/b.txt
579 $ hg status -A --rev 1 1/2/3/4/5/b.txt
573 R 1/2/3/4/5/b.txt
580 R 1/2/3/4/5/b.txt
574 $ hg status -A --rev 1 1/2/3/4/5
581 $ hg status -A --rev 1 1/2/3/4/5
575 R 1/2/3/4/5/b.txt
582 R 1/2/3/4/5/b.txt
576 $ hg status -A --rev 1 1/2/3
583 $ hg status -A --rev 1 1/2/3
577 R 1/2/3/4/5/b.txt
584 R 1/2/3/4/5/b.txt
578 $ hg status -A --rev 1 1
585 $ hg status -A --rev 1 1
579 R 1/2/3/4/5/b.txt
586 R 1/2/3/4/5/b.txt
580
587
581 $ hg status --config ui.formatdebug=True --rev 1 1
588 $ hg status --config ui.formatdebug=True --rev 1 1
582 status = [
589 status = [
583 {
590 {
584 'itemtype': 'file',
591 'itemtype': 'file',
585 'path': '1/2/3/4/5/b.txt',
592 'path': '1/2/3/4/5/b.txt',
586 'status': 'R'
593 'status': 'R'
587 },
594 },
588 ]
595 ]
589
596
590 #if windows
597 #if windows
591 $ hg --config ui.slash=false status -A --rev 1 1
598 $ hg --config ui.slash=false status -A --rev 1 1
592 R 1\2\3\4\5\b.txt
599 R 1\2\3\4\5\b.txt
593 #endif
600 #endif
594
601
595 $ cd ..
602 $ cd ..
596
603
597 Status after move overwriting a file (issue4458)
604 Status after move overwriting a file (issue4458)
598 =================================================
605 =================================================
599
606
600
607
601 $ hg init issue4458
608 $ hg init issue4458
602 $ cd issue4458
609 $ cd issue4458
603 $ echo a > a
610 $ echo a > a
604 $ echo b > b
611 $ echo b > b
605 $ hg commit -Am base
612 $ hg commit -Am base
606 adding a
613 adding a
607 adding b
614 adding b
608
615
609
616
610 with --force
617 with --force
611
618
612 $ hg mv b --force a
619 $ hg mv b --force a
613 $ hg st --copies
620 $ hg st --copies
614 M a
621 M a
615 b
622 b
616 R b
623 R b
617 $ hg revert --all
624 $ hg revert --all
618 reverting a
625 reverting a
619 undeleting b
626 undeleting b
620 $ rm *.orig
627 $ rm *.orig
621
628
622 without force
629 without force
623
630
624 $ hg rm a
631 $ hg rm a
625 $ hg st --copies
632 $ hg st --copies
626 R a
633 R a
627 $ hg mv b a
634 $ hg mv b a
628 $ hg st --copies
635 $ hg st --copies
629 M a
636 M a
630 b
637 b
631 R b
638 R b
632
639
633 using ui.statuscopies setting
640 using ui.statuscopies setting
634 $ hg st --config ui.statuscopies=true
641 $ hg st --config ui.statuscopies=true
635 M a
642 M a
636 b
643 b
637 R b
644 R b
638 $ hg st --config ui.statuscopies=false
645 $ hg st --config ui.statuscopies=false
639 M a
646 M a
640 R b
647 R b
641 $ hg st --config ui.tweakdefaults=yes
648 $ hg st --config ui.tweakdefaults=yes
642 M a
649 M a
643 b
650 b
644 R b
651 R b
645
652
646 using log status template (issue5155)
653 using log status template (issue5155)
647 $ hg log -Tstatus -r 'wdir()' -C
654 $ hg log -Tstatus -r 'wdir()' -C
648 changeset: 2147483647:ffffffffffff
655 changeset: 2147483647:ffffffffffff
649 parent: 0:8c55c58b4c0e
656 parent: 0:8c55c58b4c0e
650 user: test
657 user: test
651 date: * (glob)
658 date: * (glob)
652 files:
659 files:
653 M a
660 M a
654 b
661 b
655 R b
662 R b
656
663
657 $ hg log -GTstatus -r 'wdir()' -C
664 $ hg log -GTstatus -r 'wdir()' -C
658 o changeset: 2147483647:ffffffffffff
665 o changeset: 2147483647:ffffffffffff
659 | parent: 0:8c55c58b4c0e
666 | parent: 0:8c55c58b4c0e
660 ~ user: test
667 ~ user: test
661 date: * (glob)
668 date: * (glob)
662 files:
669 files:
663 M a
670 M a
664 b
671 b
665 R b
672 R b
666
673
667
674
668 Other "bug" highlight, the revision status does not report the copy information.
675 Other "bug" highlight, the revision status does not report the copy information.
669 This is buggy behavior.
676 This is buggy behavior.
670
677
671 $ hg commit -m 'blah'
678 $ hg commit -m 'blah'
672 $ hg st --copies --change .
679 $ hg st --copies --change .
673 M a
680 M a
674 R b
681 R b
675
682
676 using log status template, the copy information is displayed correctly.
683 using log status template, the copy information is displayed correctly.
677 $ hg log -Tstatus -r. -C
684 $ hg log -Tstatus -r. -C
678 changeset: 1:6685fde43d21
685 changeset: 1:6685fde43d21
679 tag: tip
686 tag: tip
680 user: test
687 user: test
681 date: * (glob)
688 date: * (glob)
682 summary: blah
689 summary: blah
683 files:
690 files:
684 M a
691 M a
685 b
692 b
686 R b
693 R b
687
694
688
695
689 $ cd ..
696 $ cd ..
690
697
691 Make sure .hg doesn't show up even as a symlink
698 Make sure .hg doesn't show up even as a symlink
692
699
693 $ hg init repo0
700 $ hg init repo0
694 $ mkdir symlink-repo0
701 $ mkdir symlink-repo0
695 $ cd symlink-repo0
702 $ cd symlink-repo0
696 $ ln -s ../repo0/.hg
703 $ ln -s ../repo0/.hg
697 $ hg status
704 $ hg status
698
705
699 If the size hasnt changed but mtime has, status needs to read the contents
706 If the size hasnt changed but mtime has, status needs to read the contents
700 of the file to check whether it has changed
707 of the file to check whether it has changed
701
708
702 $ echo 1 > a
709 $ echo 1 > a
703 $ echo 1 > b
710 $ echo 1 > b
704 $ touch -t 200102030000 a b
711 $ touch -t 200102030000 a b
705 $ hg commit -Aqm '#0'
712 $ hg commit -Aqm '#0'
706 $ echo 2 > a
713 $ echo 2 > a
707 $ touch -t 200102040000 a b
714 $ touch -t 200102040000 a b
708 $ hg status
715 $ hg status
709 M a
716 M a
710
717
711 Asking specifically for the status of a deleted/removed file
718 Asking specifically for the status of a deleted/removed file
712
719
713 $ rm a
720 $ rm a
714 $ rm b
721 $ rm b
715 $ hg status a
722 $ hg status a
716 ! a
723 ! a
717 $ hg rm a
724 $ hg rm a
718 $ hg rm b
725 $ hg rm b
719 $ hg status a
726 $ hg status a
720 R a
727 R a
721 $ hg commit -qm '#1'
728 $ hg commit -qm '#1'
722 $ hg status a
729 $ hg status a
723 a: $ENOENT$
730 a: $ENOENT$
724
731
725 Check using include flag with pattern when status does not need to traverse
732 Check using include flag with pattern when status does not need to traverse
726 the working directory (issue6483)
733 the working directory (issue6483)
727
734
728 $ cd ..
735 $ cd ..
729 $ hg init issue6483
736 $ hg init issue6483
730 $ cd issue6483
737 $ cd issue6483
731 $ touch a.py b.rs
738 $ touch a.py b.rs
732 $ hg add a.py b.rs
739 $ hg add a.py b.rs
733 $ hg st -aI "*.py"
740 $ hg st -aI "*.py"
734 A a.py
741 A a.py
735
742
736 Also check exclude pattern
743 Also check exclude pattern
737
744
738 $ hg st -aX "*.rs"
745 $ hg st -aX "*.rs"
739 A a.py
746 A a.py
740
747
741 issue6335
748 issue6335
742 When a directory containing a tracked file gets symlinked, as of 5.8
749 When a directory containing a tracked file gets symlinked, as of 5.8
743 `hg st` only gives the correct answer about clean (or deleted) files
750 `hg st` only gives the correct answer about clean (or deleted) files
744 if also listing unknowns.
751 if also listing unknowns.
745 The tree-based dirstate and status algorithm fix this:
752 The tree-based dirstate and status algorithm fix this:
746
753
747 #if symlink no-dirstate-v1 rust
754 #if symlink no-dirstate-v1 rust
748
755
749 $ cd ..
756 $ cd ..
750 $ hg init issue6335
757 $ hg init issue6335
751 $ cd issue6335
758 $ cd issue6335
752 $ mkdir foo
759 $ mkdir foo
753 $ touch foo/a
760 $ touch foo/a
754 $ hg ci -Ama
761 $ hg ci -Ama
755 adding foo/a
762 adding foo/a
756 $ mv foo bar
763 $ mv foo bar
757 $ ln -s bar foo
764 $ ln -s bar foo
758 $ hg status
765 $ hg status
759 ! foo/a
766 ! foo/a
760 ? bar/a
767 ? bar/a
761 ? foo
768 ? foo
762
769
763 $ hg status -c # incorrect output without the Rust implementation
770 $ hg status -c # incorrect output without the Rust implementation
764 $ hg status -cu
771 $ hg status -cu
765 ? bar/a
772 ? bar/a
766 ? foo
773 ? foo
767 $ hg status -d # incorrect output without the Rust implementation
774 $ hg status -d # incorrect output without the Rust implementation
768 ! foo/a
775 ! foo/a
769 $ hg status -du
776 $ hg status -du
770 ! foo/a
777 ! foo/a
771 ? bar/a
778 ? bar/a
772 ? foo
779 ? foo
773
780
774 #endif
781 #endif
775
782
776
783
777 Create a repo with files in each possible status
784 Create a repo with files in each possible status
778
785
779 $ cd ..
786 $ cd ..
780 $ hg init repo7
787 $ hg init repo7
781 $ cd repo7
788 $ cd repo7
782 $ mkdir subdir
789 $ mkdir subdir
783 $ touch clean modified deleted removed
790 $ touch clean modified deleted removed
784 $ touch subdir/clean subdir/modified subdir/deleted subdir/removed
791 $ touch subdir/clean subdir/modified subdir/deleted subdir/removed
785 $ echo ignored > .hgignore
792 $ echo ignored > .hgignore
786 $ hg ci -Aqm '#0'
793 $ hg ci -Aqm '#0'
787 $ echo 1 > modified
794 $ echo 1 > modified
788 $ echo 1 > subdir/modified
795 $ echo 1 > subdir/modified
789 $ rm deleted
796 $ rm deleted
790 $ rm subdir/deleted
797 $ rm subdir/deleted
791 $ hg rm removed
798 $ hg rm removed
792 $ hg rm subdir/removed
799 $ hg rm subdir/removed
793 $ touch unknown ignored
800 $ touch unknown ignored
794 $ touch subdir/unknown subdir/ignored
801 $ touch subdir/unknown subdir/ignored
795
802
796 Check the output
803 Check the output
797
804
798 $ hg status
805 $ hg status
799 M modified
806 M modified
800 M subdir/modified
807 M subdir/modified
801 R removed
808 R removed
802 R subdir/removed
809 R subdir/removed
803 ! deleted
810 ! deleted
804 ! subdir/deleted
811 ! subdir/deleted
805 ? subdir/unknown
812 ? subdir/unknown
806 ? unknown
813 ? unknown
807
814
808 $ hg status -mard
815 $ hg status -mard
809 M modified
816 M modified
810 M subdir/modified
817 M subdir/modified
811 R removed
818 R removed
812 R subdir/removed
819 R subdir/removed
813 ! deleted
820 ! deleted
814 ! subdir/deleted
821 ! subdir/deleted
815
822
816 $ hg status -A
823 $ hg status -A
817 M modified
824 M modified
818 M subdir/modified
825 M subdir/modified
819 R removed
826 R removed
820 R subdir/removed
827 R subdir/removed
821 ! deleted
828 ! deleted
822 ! subdir/deleted
829 ! subdir/deleted
823 ? subdir/unknown
830 ? subdir/unknown
824 ? unknown
831 ? unknown
825 I ignored
832 I ignored
826 I subdir/ignored
833 I subdir/ignored
827 C .hgignore
834 C .hgignore
828 C clean
835 C clean
829 C subdir/clean
836 C subdir/clean
830
837
831 Note: `hg status some-name` creates a patternmatcher which is not supported
838 Note: `hg status some-name` creates a patternmatcher which is not supported
832 yet by the Rust implementation of status, but includematcher is supported.
839 yet by the Rust implementation of status, but includematcher is supported.
833 --include is used below for that reason
840 --include is used below for that reason
834
841
835 #if unix-permissions
842 #if unix-permissions
836
843
837 Not having permission to read a directory that contains tracked files makes
844 Not having permission to read a directory that contains tracked files makes
838 status emit a warning then behave as if the directory was empty or removed
845 status emit a warning then behave as if the directory was empty or removed
839 entirely:
846 entirely:
840
847
841 $ chmod 0 subdir
848 $ chmod 0 subdir
842 $ hg status --include subdir
849 $ hg status --include subdir
843 subdir: Permission denied
850 subdir: Permission denied
844 R subdir/removed
851 R subdir/removed
845 ! subdir/clean
852 ! subdir/clean
846 ! subdir/deleted
853 ! subdir/deleted
847 ! subdir/modified
854 ! subdir/modified
848 $ chmod 755 subdir
855 $ chmod 755 subdir
849
856
850 #endif
857 #endif
851
858
852 Remove a directory that contains tracked files
859 Remove a directory that contains tracked files
853
860
854 $ rm -r subdir
861 $ rm -r subdir
855 $ hg status --include subdir
862 $ hg status --include subdir
856 R subdir/removed
863 R subdir/removed
857 ! subdir/clean
864 ! subdir/clean
858 ! subdir/deleted
865 ! subdir/deleted
859 ! subdir/modified
866 ! subdir/modified
860
867
861 and replace it by a file
868 and replace it by a file
862
869
863 $ touch subdir
870 $ touch subdir
864 $ hg status --include subdir
871 $ hg status --include subdir
865 R subdir/removed
872 R subdir/removed
866 ! subdir/clean
873 ! subdir/clean
867 ! subdir/deleted
874 ! subdir/deleted
868 ! subdir/modified
875 ! subdir/modified
869 ? subdir
876 ? subdir
870
877
871 Replaced a deleted or removed file with a directory
878 Replaced a deleted or removed file with a directory
872
879
873 $ mkdir deleted removed
880 $ mkdir deleted removed
874 $ touch deleted/1 removed/1
881 $ touch deleted/1 removed/1
875 $ hg status --include deleted --include removed
882 $ hg status --include deleted --include removed
876 R removed
883 R removed
877 ! deleted
884 ! deleted
878 ? deleted/1
885 ? deleted/1
879 ? removed/1
886 ? removed/1
880 $ hg add removed/1
887 $ hg add removed/1
881 $ hg status --include deleted --include removed
888 $ hg status --include deleted --include removed
882 A removed/1
889 A removed/1
883 R removed
890 R removed
884 ! deleted
891 ! deleted
885 ? deleted/1
892 ? deleted/1
886
893
887 Deeply nested files in an ignored directory are still listed on request
894 Deeply nested files in an ignored directory are still listed on request
888
895
889 $ echo ignored-dir >> .hgignore
896 $ echo ignored-dir >> .hgignore
890 $ mkdir ignored-dir
897 $ mkdir ignored-dir
891 $ mkdir ignored-dir/subdir
898 $ mkdir ignored-dir/subdir
892 $ touch ignored-dir/subdir/1
899 $ touch ignored-dir/subdir/1
893 $ hg status --ignored
900 $ hg status --ignored
894 I ignored
901 I ignored
895 I ignored-dir/subdir/1
902 I ignored-dir/subdir/1
896
903
897 Check using include flag while listing ignored composes correctly (issue6514)
904 Check using include flag while listing ignored composes correctly (issue6514)
898
905
899 $ cd ..
906 $ cd ..
900 $ hg init issue6514
907 $ hg init issue6514
901 $ cd issue6514
908 $ cd issue6514
902 $ mkdir ignored-folder
909 $ mkdir ignored-folder
903 $ touch A.hs B.hs C.hs ignored-folder/other.txt ignored-folder/ctest.hs
910 $ touch A.hs B.hs C.hs ignored-folder/other.txt ignored-folder/ctest.hs
904 $ cat >.hgignore <<EOF
911 $ cat >.hgignore <<EOF
905 > A.hs
912 > A.hs
906 > B.hs
913 > B.hs
907 > ignored-folder/
914 > ignored-folder/
908 > EOF
915 > EOF
909 $ hg st -i -I 're:.*\.hs$'
916 $ hg st -i -I 're:.*\.hs$'
910 I A.hs
917 I A.hs
911 I B.hs
918 I B.hs
912 I ignored-folder/ctest.hs
919 I ignored-folder/ctest.hs
913
920
914 #if rust dirstate-v2
921 #if rust dirstate-v2
915
922
916 Check read_dir caching
923 Check read_dir caching
917
924
918 $ cd ..
925 $ cd ..
919 $ hg init repo8
926 $ hg init repo8
920 $ cd repo8
927 $ cd repo8
921 $ mkdir subdir
928 $ mkdir subdir
922 $ touch subdir/a subdir/b
929 $ touch subdir/a subdir/b
923 $ hg ci -Aqm '#0'
930 $ hg ci -Aqm '#0'
924
931
925 The cached mtime is initially unset
932 The cached mtime is initially unset
926
933
927 $ hg debugdirstate --all --no-dates | grep '^ '
934 $ hg debugdirstate --all --no-dates | grep '^ '
928 0 -1 unset subdir
935 0 -1 unset subdir
929
936
930 It is still not set when there are unknown files
937 It is still not set when there are unknown files
931
938
932 $ touch subdir/unknown
939 $ touch subdir/unknown
933 $ hg status
940 $ hg status
934 ? subdir/unknown
941 ? subdir/unknown
935 $ hg debugdirstate --all --no-dates | grep '^ '
942 $ hg debugdirstate --all --no-dates | grep '^ '
936 0 -1 unset subdir
943 0 -1 unset subdir
937
944
938 Now the directory is eligible for caching, so its mtime is save in the dirstate
945 Now the directory is eligible for caching, so its mtime is save in the dirstate
939
946
940 $ rm subdir/unknown
947 $ rm subdir/unknown
941 $ hg status
948 $ hg status
942 $ hg debugdirstate --all --no-dates | grep '^ '
949 $ hg debugdirstate --all --no-dates | grep '^ '
943 0 -1 set subdir
950 0 -1 set subdir
944
951
945 This time the command should be ever so slightly faster since it does not need `read_dir("subdir")`
952 This time the command should be ever so slightly faster since it does not need `read_dir("subdir")`
946
953
947 $ hg status
954 $ hg status
948
955
949 Creating a new file changes the directorys mtime, invalidating the cache
956 Creating a new file changes the directorys mtime, invalidating the cache
950
957
951 $ touch subdir/unknown
958 $ touch subdir/unknown
952 $ hg status
959 $ hg status
953 ? subdir/unknown
960 ? subdir/unknown
954
961
955 $ rm subdir/unknown
962 $ rm subdir/unknown
956 $ hg status
963 $ hg status
957
964
958 Removing a node from the dirstate resets the cache for its parent directory
965 Removing a node from the dirstate resets the cache for its parent directory
959
966
960 $ hg forget subdir/a
967 $ hg forget subdir/a
961 $ hg debugdirstate --all --no-dates | grep '^ '
968 $ hg debugdirstate --all --no-dates | grep '^ '
962 0 -1 set subdir
969 0 -1 set subdir
963 $ hg ci -qm '#1'
970 $ hg ci -qm '#1'
964 $ hg debugdirstate --all --no-dates | grep '^ '
971 $ hg debugdirstate --all --no-dates | grep '^ '
965 0 -1 unset subdir
972 0 -1 unset subdir
966 $ hg status
973 $ hg status
967 ? subdir/a
974 ? subdir/a
968
975
969 #endif
976 #endif
General Comments 0
You need to be logged in to leave comments. Login now