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 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
91 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
92 |
|
92 | |||
93 | [[package]] |
|
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 | name = "clap" |
|
106 | name = "clap" | |
95 | version = "2.33.3" |
|
107 | version = "2.33.3" | |
96 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
108 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
@@ -467,6 +479,15 b' dependencies = [' | |||||
467 | ] |
|
479 | ] | |
468 |
|
480 | |||
469 | [[package]] |
|
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 | name = "num-traits" |
|
491 | name = "num-traits" | |
471 | version = "0.2.14" |
|
492 | version = "0.2.14" | |
472 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
493 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
@@ -715,6 +736,7 b' dependencies = [' | |||||
715 | name = "rhg" |
|
736 | name = "rhg" | |
716 | version = "0.1.0" |
|
737 | version = "0.1.0" | |
717 | dependencies = [ |
|
738 | dependencies = [ | |
|
739 | "chrono 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)", | |||
718 | "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", |
|
740 | "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", | |
719 | "derive_more 0.99.11 (registry+https://github.com/rust-lang/crates.io-index)", |
|
741 | "derive_more 0.99.11 (registry+https://github.com/rust-lang/crates.io-index)", | |
720 | "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", |
|
742 | "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", | |
@@ -722,6 +744,7 b' dependencies = [' | |||||
722 | "hg-core 0.1.0", |
|
744 | "hg-core 0.1.0", | |
723 | "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", |
|
745 | "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", | |
724 | "micro-timer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", |
|
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 | [[package]] |
|
750 | [[package]] | |
@@ -856,6 +879,15 b' version = "0.2.1"' | |||||
856 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
879 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
857 |
|
880 | |||
858 | [[package]] |
|
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 | name = "vcpkg" |
|
891 | name = "vcpkg" | |
860 | version = "0.2.11" |
|
892 | version = "0.2.11" | |
861 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
893 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
@@ -949,6 +981,7 b' dependencies = [' | |||||
949 | "checksum cc 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)" = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" |
|
981 | "checksum cc 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)" = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" | |
950 | "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" |
|
982 | "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" | |
951 | "checksum cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" |
|
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 | "checksum clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)" = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" |
|
985 | "checksum clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)" = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" | |
953 | "checksum const_fn 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd51eab21ab4fd6a3bf889e2d0958c0a6e3a61ad04260325e919e652a2a62826" |
|
986 | "checksum const_fn 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd51eab21ab4fd6a3bf889e2d0958c0a6e3a61ad04260325e919e652a2a62826" | |
954 | "checksum cpython 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bfaf3847ab963e40c4f6dd8d6be279bdf74007ae2413786a0dcbb28c52139a95" |
|
987 | "checksum cpython 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bfaf3847ab963e40c4f6dd8d6be279bdf74007ae2413786a0dcbb28c52139a95" | |
@@ -988,6 +1021,7 b' dependencies = [' | |||||
988 | "checksum micro-timer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2620153e1d903d26b72b89f0e9c48d8c4756cba941c185461dddc234980c298c" |
|
1021 | "checksum micro-timer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2620153e1d903d26b72b89f0e9c48d8c4756cba941c185461dddc234980c298c" | |
989 | "checksum micro-timer-macros 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e28a3473e6abd6e9aab36aaeef32ad22ae0bd34e79f376643594c2b152ec1c5d" |
|
1022 | "checksum micro-timer-macros 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e28a3473e6abd6e9aab36aaeef32ad22ae0bd34e79f376643594c2b152ec1c5d" | |
990 | "checksum miniz_oxide 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" |
|
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 | "checksum num-traits 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" |
|
1025 | "checksum num-traits 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" | |
992 | "checksum num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" |
|
1026 | "checksum num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" | |
993 | "checksum output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" |
|
1027 | "checksum output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" | |
@@ -1035,6 +1069,7 b' dependencies = [' | |||||
1035 | "checksum typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" |
|
1069 | "checksum typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" | |
1036 | "checksum unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" |
|
1070 | "checksum unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" | |
1037 | "checksum unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" |
|
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 | "checksum vcpkg 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb" |
|
1073 | "checksum vcpkg 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb" | |
1039 | "checksum vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" |
|
1074 | "checksum vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" | |
1040 | "checksum version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" |
|
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 | Vec<(&'a HgPath, &'a HgPath)>, |
|
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 | #[timed] |
|
37 | #[timed] | |
30 | pub fn parse_dirstate(mut contents: &[u8]) -> Result<ParseResult, HgError> { |
|
38 | pub fn parse_dirstate(mut contents: &[u8]) -> Result<ParseResult, HgError> { | |
31 | let mut copies = Vec::new(); |
|
39 | let mut copies = Vec::new(); |
@@ -214,6 +214,15 b' impl Repo {' | |||||
214 | base: &self.working_directory, |
|
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 | impl Vfs<'_> { |
|
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 | pub fn current_dir() -> Result<std::path::PathBuf, HgError> { |
|
220 | pub fn current_dir() -> Result<std::path::PathBuf, HgError> { | |
192 | std::env::current_dir().map_err(|error| HgError::IoError { |
|
221 | std::env::current_dir().map_err(|error| HgError::IoError { | |
193 | error, |
|
222 | error, |
@@ -9,9 +9,11 b' edition = "2018"' | |||||
9 |
|
9 | |||
10 | [dependencies] |
|
10 | [dependencies] | |
11 | hg-core = { path = "../hg-core"} |
|
11 | hg-core = { path = "../hg-core"} | |
|
12 | chrono = "0.4.19" | |||
12 | clap = "2.33.1" |
|
13 | clap = "2.33.1" | |
13 | derive_more = "0.99" |
|
14 | derive_more = "0.99" | |
14 | log = "0.4.11" |
|
15 | log = "0.4.11" | |
15 | micro-timer = "0.3.1" |
|
16 | micro-timer = "0.3.1" | |
16 | env_logger = "0.7.1" |
|
17 | env_logger = "0.7.1" | |
17 | format-bytes = "0.2.0" |
|
18 | format-bytes = "0.2.0" | |
|
19 | users = "0.11.0" |
@@ -9,6 +9,7 b' use hg::config::Config;' | |||||
9 | use hg::repo::{Repo, RepoError}; |
|
9 | use hg::repo::{Repo, RepoError}; | |
10 | use std::path::{Path, PathBuf}; |
|
10 | use std::path::{Path, PathBuf}; | |
11 |
|
11 | |||
|
12 | mod blackbox; | |||
12 | mod error; |
|
13 | mod error; | |
13 | mod exitcode; |
|
14 | mod exitcode; | |
14 | mod ui; |
|
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 | env_logger::init(); |
|
44 | env_logger::init(); | |
41 | let app = App::new("rhg") |
|
45 | let app = App::new("rhg") | |
42 | .setting(AppSettings::AllowInvalidUtf8) |
|
46 | .setting(AppSettings::AllowInvalidUtf8) | |
@@ -83,35 +87,47 b' fn main_with_result(ui: &ui::Ui) -> Resu' | |||||
83 | Err(error) => return Err(error.into()), |
|
87 | Err(error) => return Err(error.into()), | |
84 | }; |
|
88 | }; | |
85 |
|
89 | |||
86 |
|
|
90 | let invocation = CliInvocation { | |
87 | ui, |
|
91 | ui, | |
88 | subcommand_args, |
|
92 | subcommand_args, | |
89 | non_repo_config, |
|
93 | non_repo_config, | |
90 | repo: repo.as_ref(), |
|
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 | fn main() { |
|
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 | let ui = ui::Ui::new(); |
|
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 | Ok(()) => exitcode::OK, |
|
124 | Ok(()) => exitcode::OK, | |
|
125 | Err(CommandError::Abort { .. }) => exitcode::ABORT, | |||
99 |
|
126 | |||
100 | // Exit with a specific code and no error message to let a potential |
|
127 | // Exit with a specific code and no error message to let a potential | |
101 | // wrapper script fallback to Python-based Mercurial. |
|
128 | // wrapper script fallback to Python-based Mercurial. | |
102 | Err(CommandError::Unimplemented) => exitcode::UNIMPLEMENTED, |
|
129 | Err(CommandError::Unimplemented) => exitcode::UNIMPLEMENTED, | |
103 |
|
130 | } | ||
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) |
|
|||
115 | } |
|
131 | } | |
116 |
|
132 | |||
117 | macro_rules! subcommands { |
|
133 | macro_rules! subcommands { |
@@ -261,3 +261,15 b' And check that basic rhg commands work w' | |||||
261 | $ rhg cat -r 0 a |
|
261 | $ rhg cat -r 0 a | |
262 | a |
|
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