Show More
@@ -0,0 +1,161 b'' | |||
|
1 | //! Logging for repository events, including commands run in the repository. | |
|
2 | ||
|
3 | use crate::CliInvocation; | |
|
4 | use format_bytes::format_bytes; | |
|
5 | use hg::errors::HgError; | |
|
6 | use hg::repo::Repo; | |
|
7 | use hg::utils::{files::get_bytes_from_os_str, shell_quote}; | |
|
8 | ||
|
9 | const ONE_MEBIBYTE: u64 = 1 << 20; | |
|
10 | ||
|
11 | // TODO: somehow keep defaults in sync with `configitem` in `hgext/blackbox.py` | |
|
12 | const DEFAULT_MAX_SIZE: u64 = ONE_MEBIBYTE; | |
|
13 | const DEFAULT_MAX_FILES: u32 = 7; | |
|
14 | ||
|
15 | // Python does not support %.3f, only %f | |
|
16 | const DEFAULT_DATE_FORMAT: &str = "%Y/%m/%d %H:%M:%S%.3f"; | |
|
17 | ||
|
18 | type DateTime = chrono::DateTime<chrono::Local>; | |
|
19 | ||
|
20 | pub struct ProcessStartTime { | |
|
21 | /// For measuring duration | |
|
22 | monotonic_clock: std::time::Instant, | |
|
23 | /// For formatting with year, month, day, etc. | |
|
24 | calendar_based: DateTime, | |
|
25 | } | |
|
26 | ||
|
27 | impl ProcessStartTime { | |
|
28 | pub fn now() -> Self { | |
|
29 | Self { | |
|
30 | monotonic_clock: std::time::Instant::now(), | |
|
31 | calendar_based: chrono::Local::now(), | |
|
32 | } | |
|
33 | } | |
|
34 | } | |
|
35 | ||
|
36 | pub struct Blackbox<'a> { | |
|
37 | process_start_time: &'a ProcessStartTime, | |
|
38 | /// Do nothing if this is `None` | |
|
39 | configured: Option<ConfiguredBlackbox<'a>>, | |
|
40 | } | |
|
41 | ||
|
42 | struct ConfiguredBlackbox<'a> { | |
|
43 | repo: &'a Repo, | |
|
44 | max_size: u64, | |
|
45 | max_files: u32, | |
|
46 | date_format: &'a str, | |
|
47 | } | |
|
48 | ||
|
49 | impl<'a> Blackbox<'a> { | |
|
50 | pub fn new( | |
|
51 | invocation: &'a CliInvocation<'a>, | |
|
52 | process_start_time: &'a ProcessStartTime, | |
|
53 | ) -> Result<Self, HgError> { | |
|
54 | let configured = if let Ok(repo) = invocation.repo { | |
|
55 | let config = invocation.config(); | |
|
56 | if config.get(b"extensions", b"blackbox").is_none() { | |
|
57 | // The extension is not enabled | |
|
58 | None | |
|
59 | } else { | |
|
60 | Some(ConfiguredBlackbox { | |
|
61 | repo, | |
|
62 | max_size: config | |
|
63 | .get_byte_size(b"blackbox", b"maxsize")? | |
|
64 | .unwrap_or(DEFAULT_MAX_SIZE), | |
|
65 | max_files: config | |
|
66 | .get_u32(b"blackbox", b"maxfiles")? | |
|
67 | .unwrap_or(DEFAULT_MAX_FILES), | |
|
68 | date_format: config | |
|
69 | .get_str(b"blackbox", b"date-format")? | |
|
70 | .unwrap_or(DEFAULT_DATE_FORMAT), | |
|
71 | }) | |
|
72 | } | |
|
73 | } else { | |
|
74 | // Without a local repository there’s no `.hg/blackbox.log` to | |
|
75 | // write to. | |
|
76 | None | |
|
77 | }; | |
|
78 | Ok(Self { | |
|
79 | process_start_time, | |
|
80 | configured, | |
|
81 | }) | |
|
82 | } | |
|
83 | ||
|
84 | pub fn log_command_start(&self) { | |
|
85 | if let Some(configured) = &self.configured { | |
|
86 | let message = format_bytes!(b"(rust) {}", format_cli_args()); | |
|
87 | configured.log(&self.process_start_time.calendar_based, &message); | |
|
88 | } | |
|
89 | } | |
|
90 | ||
|
91 | pub fn log_command_end(&self, exit_code: i32) { | |
|
92 | if let Some(configured) = &self.configured { | |
|
93 | let now = chrono::Local::now(); | |
|
94 | let duration = self | |
|
95 | .process_start_time | |
|
96 | .monotonic_clock | |
|
97 | .elapsed() | |
|
98 | .as_secs_f64(); | |
|
99 | let message = format_bytes!( | |
|
100 | b"(rust) {} exited {} after {} seconds", | |
|
101 | format_cli_args(), | |
|
102 | exit_code, | |
|
103 | format_bytes::Utf8(format_args!("{:.03}", duration)) | |
|
104 | ); | |
|
105 | configured.log(&now, &message); | |
|
106 | } | |
|
107 | } | |
|
108 | } | |
|
109 | ||
|
110 | impl ConfiguredBlackbox<'_> { | |
|
111 | fn log(&self, date_time: &DateTime, message: &[u8]) { | |
|
112 | let date = format_bytes::Utf8(date_time.format(self.date_format)); | |
|
113 | let user = users::get_current_username().map(get_bytes_from_os_str); | |
|
114 | let user = user.as_deref().unwrap_or(b"???"); | |
|
115 | let rev = format_bytes::Utf8(match self.repo.dirstate_parents() { | |
|
116 | Ok(parents) if parents.p2 == hg::revlog::node::NULL_NODE => { | |
|
117 | format!("{:x}", parents.p1) | |
|
118 | } | |
|
119 | Ok(parents) => format!("{:x}+{:x}", parents.p1, parents.p2), | |
|
120 | Err(_dirstate_corruption_error) => { | |
|
121 | // TODO: log a non-fatal warning to stderr | |
|
122 | "???".to_owned() | |
|
123 | } | |
|
124 | }); | |
|
125 | let pid = std::process::id(); | |
|
126 | let line = format_bytes!( | |
|
127 | b"{} {} @{} ({})> {}\n", | |
|
128 | date, | |
|
129 | user, | |
|
130 | rev, | |
|
131 | pid, | |
|
132 | message | |
|
133 | ); | |
|
134 | let result = | |
|
135 | hg::logging::LogFile::new(self.repo.hg_vfs(), "blackbox.log") | |
|
136 | .max_size(Some(self.max_size)) | |
|
137 | .max_files(self.max_files) | |
|
138 | .write(&line); | |
|
139 | match result { | |
|
140 | Ok(()) => {} | |
|
141 | Err(_io_error) => { | |
|
142 | // TODO: log a non-fatal warning to stderr | |
|
143 | } | |
|
144 | } | |
|
145 | } | |
|
146 | } | |
|
147 | ||
|
148 | fn format_cli_args() -> Vec<u8> { | |
|
149 | let mut args = std::env::args_os(); | |
|
150 | let _ = args.next(); // Skip the first (or zeroth) arg, the name of the `rhg` executable | |
|
151 | let mut args = args.map(|arg| shell_quote(&get_bytes_from_os_str(arg))); | |
|
152 | let mut formatted = Vec::new(); | |
|
153 | if let Some(arg) = args.next() { | |
|
154 | formatted.extend(arg) | |
|
155 | } | |
|
156 | for arg in args { | |
|
157 | formatted.push(b' '); | |
|
158 | formatted.extend(arg) | |
|
159 | } | |
|
160 | formatted | |
|
161 | } |
@@ -91,6 +91,18 b' version = "1.0.0"' | |||
|
91 | 91 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
92 | 92 | |
|
93 | 93 | [[package]] |
|
94 | name = "chrono" | |
|
95 | version = "0.4.19" | |
|
96 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
|
97 | dependencies = [ | |
|
98 | "libc 0.2.81 (registry+https://github.com/rust-lang/crates.io-index)", | |
|
99 | "num-integer 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", | |
|
100 | "num-traits 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", | |
|
101 | "time 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", | |
|
102 | "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", | |
|
103 | ] | |
|
104 | ||
|
105 | [[package]] | |
|
94 | 106 | name = "clap" |
|
95 | 107 | version = "2.33.3" |
|
96 | 108 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -467,6 +479,15 b' dependencies = [' | |||
|
467 | 479 | ] |
|
468 | 480 | |
|
469 | 481 | [[package]] |
|
482 | name = "num-integer" | |
|
483 | version = "0.1.44" | |
|
484 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
|
485 | dependencies = [ | |
|
486 | "autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", | |
|
487 | "num-traits 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", | |
|
488 | ] | |
|
489 | ||
|
490 | [[package]] | |
|
470 | 491 | name = "num-traits" |
|
471 | 492 | version = "0.2.14" |
|
472 | 493 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -715,6 +736,7 b' dependencies = [' | |||
|
715 | 736 | name = "rhg" |
|
716 | 737 | version = "0.1.0" |
|
717 | 738 | dependencies = [ |
|
739 | "chrono 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)", | |
|
718 | 740 | "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", |
|
719 | 741 | "derive_more 0.99.11 (registry+https://github.com/rust-lang/crates.io-index)", |
|
720 | 742 | "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", |
@@ -722,6 +744,7 b' dependencies = [' | |||
|
722 | 744 | "hg-core 0.1.0", |
|
723 | 745 | "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", |
|
724 | 746 | "micro-timer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", |
|
747 | "users 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", | |
|
725 | 748 | ] |
|
726 | 749 | |
|
727 | 750 | [[package]] |
@@ -856,6 +879,15 b' version = "0.2.1"' | |||
|
856 | 879 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
857 | 880 | |
|
858 | 881 | [[package]] |
|
882 | name = "users" | |
|
883 | version = "0.11.0" | |
|
884 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
|
885 | dependencies = [ | |
|
886 | "libc 0.2.81 (registry+https://github.com/rust-lang/crates.io-index)", | |
|
887 | "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", | |
|
888 | ] | |
|
889 | ||
|
890 | [[package]] | |
|
859 | 891 | name = "vcpkg" |
|
860 | 892 | version = "0.2.11" |
|
861 | 893 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -949,6 +981,7 b' dependencies = [' | |||
|
949 | 981 | "checksum cc 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)" = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" |
|
950 | 982 | "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" |
|
951 | 983 | "checksum cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" |
|
984 | "checksum chrono 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)" = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" | |
|
952 | 985 | "checksum clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)" = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" |
|
953 | 986 | "checksum const_fn 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd51eab21ab4fd6a3bf889e2d0958c0a6e3a61ad04260325e919e652a2a62826" |
|
954 | 987 | "checksum cpython 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bfaf3847ab963e40c4f6dd8d6be279bdf74007ae2413786a0dcbb28c52139a95" |
@@ -988,6 +1021,7 b' dependencies = [' | |||
|
988 | 1021 | "checksum micro-timer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2620153e1d903d26b72b89f0e9c48d8c4756cba941c185461dddc234980c298c" |
|
989 | 1022 | "checksum micro-timer-macros 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e28a3473e6abd6e9aab36aaeef32ad22ae0bd34e79f376643594c2b152ec1c5d" |
|
990 | 1023 | "checksum miniz_oxide 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" |
|
1024 | "checksum num-integer 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)" = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" | |
|
991 | 1025 | "checksum num-traits 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" |
|
992 | 1026 | "checksum num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" |
|
993 | 1027 | "checksum output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" |
@@ -1035,6 +1069,7 b' dependencies = [' | |||
|
1035 | 1069 | "checksum typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" |
|
1036 | 1070 | "checksum unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" |
|
1037 | 1071 | "checksum unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" |
|
1072 | "checksum users 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032" | |
|
1038 | 1073 | "checksum vcpkg 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb" |
|
1039 | 1074 | "checksum vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" |
|
1040 | 1075 | "checksum version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" |
@@ -26,6 +26,14 b" type ParseResult<'a> = (" | |||
|
26 | 26 | Vec<(&'a HgPath, &'a HgPath)>, |
|
27 | 27 | ); |
|
28 | 28 | |
|
29 | pub fn parse_dirstate_parents( | |
|
30 | contents: &[u8], | |
|
31 | ) -> Result<&DirstateParents, HgError> { | |
|
32 | let (parents, _rest) = DirstateParents::from_bytes(contents) | |
|
33 | .map_err(|_| HgError::corrupted("Too little data for dirstate."))?; | |
|
34 | Ok(parents) | |
|
35 | } | |
|
36 | ||
|
29 | 37 | #[timed] |
|
30 | 38 | pub fn parse_dirstate(mut contents: &[u8]) -> Result<ParseResult, HgError> { |
|
31 | 39 | let mut copies = Vec::new(); |
@@ -214,6 +214,15 b' impl Repo {' | |||
|
214 | 214 | base: &self.working_directory, |
|
215 | 215 | } |
|
216 | 216 | } |
|
217 | ||
|
218 | pub fn dirstate_parents( | |
|
219 | &self, | |
|
220 | ) -> Result<crate::dirstate::DirstateParents, HgError> { | |
|
221 | let dirstate = self.hg_vfs().mmap_open("dirstate")?; | |
|
222 | let parents = | |
|
223 | crate::dirstate::parsers::parse_dirstate_parents(&dirstate)?; | |
|
224 | Ok(parents.clone()) | |
|
225 | } | |
|
217 | 226 | } |
|
218 | 227 | |
|
219 | 228 | impl Vfs<'_> { |
@@ -188,6 +188,35 b" pub(crate) fn strip_suffix<'a>(s: &'a st" | |||
|
188 | 188 | } |
|
189 | 189 | } |
|
190 | 190 | |
|
191 | #[cfg(unix)] | |
|
192 | pub fn shell_quote(value: &[u8]) -> Vec<u8> { | |
|
193 | // TODO: Use the `matches!` macro when we require Rust 1.42+ | |
|
194 | if value.iter().all(|&byte| match byte { | |
|
195 | b'a'..=b'z' | |
|
196 | | b'A'..=b'Z' | |
|
197 | | b'0'..=b'9' | |
|
198 | | b'.' | |
|
199 | | b'_' | |
|
200 | | b'/' | |
|
201 | | b'+' | |
|
202 | | b'-' => true, | |
|
203 | _ => false, | |
|
204 | }) { | |
|
205 | value.to_owned() | |
|
206 | } else { | |
|
207 | let mut quoted = Vec::with_capacity(value.len() + 2); | |
|
208 | quoted.push(b'\''); | |
|
209 | for &byte in value { | |
|
210 | if byte == b'\'' { | |
|
211 | quoted.push(b'\\'); | |
|
212 | } | |
|
213 | quoted.push(byte); | |
|
214 | } | |
|
215 | quoted.push(b'\''); | |
|
216 | quoted | |
|
217 | } | |
|
218 | } | |
|
219 | ||
|
191 | 220 | pub fn current_dir() -> Result<std::path::PathBuf, HgError> { |
|
192 | 221 | std::env::current_dir().map_err(|error| HgError::IoError { |
|
193 | 222 | error, |
@@ -9,9 +9,11 b' edition = "2018"' | |||
|
9 | 9 | |
|
10 | 10 | [dependencies] |
|
11 | 11 | hg-core = { path = "../hg-core"} |
|
12 | chrono = "0.4.19" | |
|
12 | 13 | clap = "2.33.1" |
|
13 | 14 | derive_more = "0.99" |
|
14 | 15 | log = "0.4.11" |
|
15 | 16 | micro-timer = "0.3.1" |
|
16 | 17 | env_logger = "0.7.1" |
|
17 | 18 | format-bytes = "0.2.0" |
|
19 | users = "0.11.0" |
@@ -9,6 +9,7 b' use hg::config::Config;' | |||
|
9 | 9 | use hg::repo::{Repo, RepoError}; |
|
10 | 10 | use std::path::{Path, PathBuf}; |
|
11 | 11 | |
|
12 | mod blackbox; | |
|
12 | 13 | mod error; |
|
13 | 14 | mod exitcode; |
|
14 | 15 | mod ui; |
@@ -36,7 +37,10 b" fn add_global_args<'a, 'b>(app: App<'a, " | |||
|
36 | 37 | ) |
|
37 | 38 | } |
|
38 | 39 | |
|
39 | fn main_with_result(ui: &ui::Ui) -> Result<(), CommandError> { | |
|
40 | fn main_with_result( | |
|
41 | ui: &ui::Ui, | |
|
42 | process_start_time: &blackbox::ProcessStartTime, | |
|
43 | ) -> Result<(), CommandError> { | |
|
40 | 44 | env_logger::init(); |
|
41 | 45 | let app = App::new("rhg") |
|
42 | 46 | .setting(AppSettings::AllowInvalidUtf8) |
@@ -83,35 +87,47 b' fn main_with_result(ui: &ui::Ui) -> Resu' | |||
|
83 | 87 | Err(error) => return Err(error.into()), |
|
84 | 88 | }; |
|
85 | 89 | |
|
86 |
|
|
|
90 | let invocation = CliInvocation { | |
|
87 | 91 | ui, |
|
88 | 92 | subcommand_args, |
|
89 | 93 | non_repo_config, |
|
90 | 94 | repo: repo.as_ref(), |
|
91 |
} |
|
|
95 | }; | |
|
96 | let blackbox = blackbox::Blackbox::new(&invocation, process_start_time)?; | |
|
97 | blackbox.log_command_start(); | |
|
98 | let result = run(&invocation); | |
|
99 | blackbox.log_command_end(exit_code(&result)); | |
|
100 | result | |
|
92 | 101 | } |
|
93 | 102 | |
|
94 | 103 | fn main() { |
|
104 | // Run this first, before we find out if the blackbox extension is even | |
|
105 | // enabled, in order to include everything in-between in the duration | |
|
106 | // measurements. Reading config files can be slow if they’re on NFS. | |
|
107 | let process_start_time = blackbox::ProcessStartTime::now(); | |
|
108 | ||
|
95 | 109 | let ui = ui::Ui::new(); |
|
96 | 110 | |
|
97 |
let |
|
|
111 | let result = main_with_result(&ui, &process_start_time); | |
|
112 | if let Err(CommandError::Abort { message }) = &result { | |
|
113 | if !message.is_empty() { | |
|
114 | // Ignore errors when writing to stderr, we’re already exiting | |
|
115 | // with failure code so there’s not much more we can do. | |
|
116 | let _ = ui.write_stderr(&format_bytes!(b"abort: {}\n", message)); | |
|
117 | } | |
|
118 | } | |
|
119 | std::process::exit(exit_code(&result)) | |
|
120 | } | |
|
121 | ||
|
122 | fn exit_code(result: &Result<(), CommandError>) -> i32 { | |
|
123 | match result { | |
|
98 | 124 | Ok(()) => exitcode::OK, |
|
125 | Err(CommandError::Abort { .. }) => exitcode::ABORT, | |
|
99 | 126 | |
|
100 | 127 | // Exit with a specific code and no error message to let a potential |
|
101 | 128 | // wrapper script fallback to Python-based Mercurial. |
|
102 | 129 | Err(CommandError::Unimplemented) => exitcode::UNIMPLEMENTED, |
|
103 | ||
|
104 | Err(CommandError::Abort { message }) => { | |
|
105 | if !message.is_empty() { | |
|
106 | // Ignore errors when writing to stderr, we’re already exiting | |
|
107 | // with failure code so there’s not much more we can do. | |
|
108 | let _ = | |
|
109 | ui.write_stderr(&format_bytes!(b"abort: {}\n", message)); | |
|
110 | } | |
|
111 | exitcode::ABORT | |
|
112 | } | |
|
113 | }; | |
|
114 | std::process::exit(exit_code) | |
|
130 | } | |
|
115 | 131 | } |
|
116 | 132 | |
|
117 | 133 | macro_rules! subcommands { |
@@ -261,3 +261,15 b' And check that basic rhg commands work w' | |||
|
261 | 261 | $ rhg cat -r 0 a |
|
262 | 262 | a |
|
263 | 263 | |
|
264 | The blackbox extension is supported | |
|
265 | ||
|
266 | $ echo "[extensions]" >> $HGRCPATH | |
|
267 | $ echo "blackbox =" >> $HGRCPATH | |
|
268 | $ echo "[blackbox]" >> $HGRCPATH | |
|
269 | $ echo "maxsize = 1" >> $HGRCPATH | |
|
270 | $ rhg files > /dev/null | |
|
271 | $ cat .hg/blackbox.log | |
|
272 | ????/??/?? ??:??:??.??? * @d3873e73d99ef67873dac33fbcc66268d5d2b6f4 (*)> (rust) files exited 0 after 0.??? seconds (glob) | |
|
273 | $ cat .hg/blackbox.log.1 | |
|
274 | ????/??/?? ??:??:??.??? * @d3873e73d99ef67873dac33fbcc66268d5d2b6f4 (*)> (rust) files (glob) | |
|
275 |
General Comments 0
You need to be logged in to leave comments.
Login now