Show More
@@ -269,7 +269,7 b' def _rundispatch(req):' | |||||
269 | ferr.write(inst.format()) |
|
269 | ferr.write(inst.format()) | |
270 | return -1 |
|
270 | return -1 | |
271 |
|
271 | |||
272 |
|
|
272 | formattedargs = _formatargs(req.args) | |
273 | starttime = util.timer() |
|
273 | starttime = util.timer() | |
274 | ret = 1 # default of Python exit code on unhandled exception |
|
274 | ret = 1 # default of Python exit code on unhandled exception | |
275 | try: |
|
275 | try: | |
@@ -308,7 +308,7 b' def _rundispatch(req):' | |||||
308 | req.ui.log( |
|
308 | req.ui.log( | |
309 | b"commandfinish", |
|
309 | b"commandfinish", | |
310 | b"%s exited %d after %0.2f seconds\n", |
|
310 | b"%s exited %d after %0.2f seconds\n", | |
311 |
|
|
311 | formattedargs, | |
312 | return_code, |
|
312 | return_code, | |
313 | duration, |
|
313 | duration, | |
314 | return_code=return_code, |
|
314 | return_code=return_code, |
@@ -3,6 +3,12 b'' | |||||
3 | version = 3 |
|
3 | version = 3 | |
4 |
|
4 | |||
5 | [[package]] |
|
5 | [[package]] | |
|
6 | name = "Inflector" | |||
|
7 | version = "0.11.4" | |||
|
8 | source = "registry+https://github.com/rust-lang/crates.io-index" | |||
|
9 | checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" | |||
|
10 | ||||
|
11 | [[package]] | |||
6 | name = "adler" |
|
12 | name = "adler" | |
7 | version = "0.2.3" |
|
13 | version = "0.2.3" | |
8 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
14 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
@@ -24,6 +30,12 b' dependencies = [' | |||||
24 | ] |
|
30 | ] | |
25 |
|
31 | |||
26 | [[package]] |
|
32 | [[package]] | |
|
33 | name = "aliasable" | |||
|
34 | version = "0.1.3" | |||
|
35 | source = "registry+https://github.com/rust-lang/crates.io-index" | |||
|
36 | checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" | |||
|
37 | ||||
|
38 | [[package]] | |||
27 | name = "ansi_term" |
|
39 | name = "ansi_term" | |
28 | version = "0.12.1" |
|
40 | version = "0.12.1" | |
29 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
41 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
@@ -210,12 +222,22 b' dependencies = [' | |||||
210 |
|
222 | |||
211 | [[package]] |
|
223 | [[package]] | |
212 | name = "crossbeam-channel" |
|
224 | name = "crossbeam-channel" | |
|
225 | version = "0.4.4" | |||
|
226 | source = "registry+https://github.com/rust-lang/crates.io-index" | |||
|
227 | checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87" | |||
|
228 | dependencies = [ | |||
|
229 | "crossbeam-utils 0.7.2", | |||
|
230 | "maybe-uninit", | |||
|
231 | ] | |||
|
232 | ||||
|
233 | [[package]] | |||
|
234 | name = "crossbeam-channel" | |||
213 | version = "0.5.2" |
|
235 | version = "0.5.2" | |
214 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
236 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
215 | checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" |
|
237 | checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" | |
216 | dependencies = [ |
|
238 | dependencies = [ | |
217 | "cfg-if 1.0.0", |
|
239 | "cfg-if 1.0.0", | |
218 | "crossbeam-utils", |
|
240 | "crossbeam-utils 0.8.1", | |
219 | ] |
|
241 | ] | |
220 |
|
242 | |||
221 | [[package]] |
|
243 | [[package]] | |
@@ -226,7 +248,7 b' checksum = "94af6efb46fef72616855b036a62' | |||||
226 | dependencies = [ |
|
248 | dependencies = [ | |
227 | "cfg-if 1.0.0", |
|
249 | "cfg-if 1.0.0", | |
228 | "crossbeam-epoch", |
|
250 | "crossbeam-epoch", | |
229 | "crossbeam-utils", |
|
251 | "crossbeam-utils 0.8.1", | |
230 | ] |
|
252 | ] | |
231 |
|
253 | |||
232 | [[package]] |
|
254 | [[package]] | |
@@ -237,7 +259,7 b' checksum = "a1aaa739f95311c2c7887a76863f' | |||||
237 | dependencies = [ |
|
259 | dependencies = [ | |
238 | "cfg-if 1.0.0", |
|
260 | "cfg-if 1.0.0", | |
239 | "const_fn", |
|
261 | "const_fn", | |
240 | "crossbeam-utils", |
|
262 | "crossbeam-utils 0.8.1", | |
241 | "lazy_static", |
|
263 | "lazy_static", | |
242 | "memoffset", |
|
264 | "memoffset", | |
243 | "scopeguard", |
|
265 | "scopeguard", | |
@@ -245,6 +267,17 b' dependencies = [' | |||||
245 |
|
267 | |||
246 | [[package]] |
|
268 | [[package]] | |
247 | name = "crossbeam-utils" |
|
269 | name = "crossbeam-utils" | |
|
270 | version = "0.7.2" | |||
|
271 | source = "registry+https://github.com/rust-lang/crates.io-index" | |||
|
272 | checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" | |||
|
273 | dependencies = [ | |||
|
274 | "autocfg", | |||
|
275 | "cfg-if 0.1.10", | |||
|
276 | "lazy_static", | |||
|
277 | ] | |||
|
278 | ||||
|
279 | [[package]] | |||
|
280 | name = "crossbeam-utils" | |||
248 | version = "0.8.1" |
|
281 | version = "0.8.1" | |
249 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
282 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
250 | checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d" |
|
283 | checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d" | |
@@ -443,7 +476,7 b' dependencies = [' | |||||
443 | "byteorder", |
|
476 | "byteorder", | |
444 | "bytes-cast", |
|
477 | "bytes-cast", | |
445 | "clap", |
|
478 | "clap", | |
446 | "crossbeam-channel", |
|
479 | "crossbeam-channel 0.4.4", | |
447 | "derive_more", |
|
480 | "derive_more", | |
448 | "flate2", |
|
481 | "flate2", | |
449 | "format-bytes", |
|
482 | "format-bytes", | |
@@ -455,7 +488,8 b' dependencies = [' | |||||
455 | "libc", |
|
488 | "libc", | |
456 | "log", |
|
489 | "log", | |
457 | "memmap2", |
|
490 | "memmap2", | |
458 | "micro-timer", |
|
491 | "micro-timer 0.3.1", | |
|
492 | "ouroboros", | |||
459 | "pretty_assertions", |
|
493 | "pretty_assertions", | |
460 | "rand 0.8.5", |
|
494 | "rand 0.8.5", | |
461 | "rand_distr", |
|
495 | "rand_distr", | |
@@ -464,7 +498,6 b' dependencies = [' | |||||
464 | "regex", |
|
498 | "regex", | |
465 | "same-file", |
|
499 | "same-file", | |
466 | "sha-1 0.10.0", |
|
500 | "sha-1 0.10.0", | |
467 | "stable_deref_trait", |
|
|||
468 | "tempfile", |
|
501 | "tempfile", | |
469 | "twox-hash", |
|
502 | "twox-hash", | |
470 | "zstd", |
|
503 | "zstd", | |
@@ -475,7 +508,7 b' name = "hg-cpython"' | |||||
475 | version = "0.1.0" |
|
508 | version = "0.1.0" | |
476 | dependencies = [ |
|
509 | dependencies = [ | |
477 | "cpython", |
|
510 | "cpython", | |
478 | "crossbeam-channel", |
|
511 | "crossbeam-channel 0.5.2", | |
479 | "env_logger", |
|
512 | "env_logger", | |
480 | "hg-core", |
|
513 | "hg-core", | |
481 | "libc", |
|
514 | "libc", | |
@@ -588,6 +621,12 b' dependencies = [' | |||||
588 | ] |
|
621 | ] | |
589 |
|
622 | |||
590 | [[package]] |
|
623 | [[package]] | |
|
624 | name = "maybe-uninit" | |||
|
625 | version = "2.0.0" | |||
|
626 | source = "registry+https://github.com/rust-lang/crates.io-index" | |||
|
627 | checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" | |||
|
628 | ||||
|
629 | [[package]] | |||
591 | name = "memchr" |
|
630 | name = "memchr" | |
592 | version = "2.4.1" |
|
631 | version = "2.4.1" | |
593 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
632 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
@@ -595,9 +634,9 b' checksum = "308cc39be01b73d0d18f82a0e7b2' | |||||
595 |
|
634 | |||
596 | [[package]] |
|
635 | [[package]] | |
597 | name = "memmap2" |
|
636 | name = "memmap2" | |
598 |
version = "0. |
|
637 | version = "0.4.0" | |
599 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
638 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
600 | checksum = "057a3db23999c867821a7a59feb06a578fcb03685e983dff90daf9e7d24ac08f" |
|
639 | checksum = "de5d3112c080d58ce560081baeaab7e1e864ca21795ddbf533d5b1842bb1ecf8" | |
601 | dependencies = [ |
|
640 | dependencies = [ | |
602 | "libc", |
|
641 | "libc", | |
603 | "stable_deref_trait", |
|
642 | "stable_deref_trait", | |
@@ -614,16 +653,38 b' dependencies = [' | |||||
614 |
|
653 | |||
615 | [[package]] |
|
654 | [[package]] | |
616 | name = "micro-timer" |
|
655 | name = "micro-timer" | |
|
656 | version = "0.3.1" | |||
|
657 | source = "registry+https://github.com/rust-lang/crates.io-index" | |||
|
658 | checksum = "2620153e1d903d26b72b89f0e9c48d8c4756cba941c185461dddc234980c298c" | |||
|
659 | dependencies = [ | |||
|
660 | "micro-timer-macros 0.3.1", | |||
|
661 | "scopeguard", | |||
|
662 | ] | |||
|
663 | ||||
|
664 | [[package]] | |||
|
665 | name = "micro-timer" | |||
617 | version = "0.4.0" |
|
666 | version = "0.4.0" | |
618 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
667 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
619 | checksum = "5de32cb59a062672560d6f0842c4aa7714727457b9fe2daf8987d995a176a405" |
|
668 | checksum = "5de32cb59a062672560d6f0842c4aa7714727457b9fe2daf8987d995a176a405" | |
620 | dependencies = [ |
|
669 | dependencies = [ | |
621 | "micro-timer-macros", |
|
670 | "micro-timer-macros 0.4.0", | |
622 | "scopeguard", |
|
671 | "scopeguard", | |
623 | ] |
|
672 | ] | |
624 |
|
673 | |||
625 | [[package]] |
|
674 | [[package]] | |
626 | name = "micro-timer-macros" |
|
675 | name = "micro-timer-macros" | |
|
676 | version = "0.3.1" | |||
|
677 | source = "registry+https://github.com/rust-lang/crates.io-index" | |||
|
678 | checksum = "e28a3473e6abd6e9aab36aaeef32ad22ae0bd34e79f376643594c2b152ec1c5d" | |||
|
679 | dependencies = [ | |||
|
680 | "proc-macro2", | |||
|
681 | "quote", | |||
|
682 | "scopeguard", | |||
|
683 | "syn", | |||
|
684 | ] | |||
|
685 | ||||
|
686 | [[package]] | |||
|
687 | name = "micro-timer-macros" | |||
627 | version = "0.4.0" |
|
688 | version = "0.4.0" | |
628 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
689 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
629 | checksum = "cee948b94700125b52dfb68dd17c19f6326696c1df57f92c05ee857463c93ba1" |
|
690 | checksum = "cee948b94700125b52dfb68dd17c19f6326696c1df57f92c05ee857463c93ba1" | |
@@ -681,6 +742,30 b' source = "registry+https://github.com/ru' | |||||
681 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" |
|
742 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" | |
682 |
|
743 | |||
683 | [[package]] |
|
744 | [[package]] | |
|
745 | name = "ouroboros" | |||
|
746 | version = "0.15.0" | |||
|
747 | source = "registry+https://github.com/rust-lang/crates.io-index" | |||
|
748 | checksum = "9f31a3b678685b150cba82b702dcdc5e155893f63610cf388d30cd988d4ca2bf" | |||
|
749 | dependencies = [ | |||
|
750 | "aliasable", | |||
|
751 | "ouroboros_macro", | |||
|
752 | "stable_deref_trait", | |||
|
753 | ] | |||
|
754 | ||||
|
755 | [[package]] | |||
|
756 | name = "ouroboros_macro" | |||
|
757 | version = "0.15.0" | |||
|
758 | source = "registry+https://github.com/rust-lang/crates.io-index" | |||
|
759 | checksum = "084fd65d5dd8b3772edccb5ffd1e4b7eba43897ecd0f9401e330e8c542959408" | |||
|
760 | dependencies = [ | |||
|
761 | "Inflector", | |||
|
762 | "proc-macro-error", | |||
|
763 | "proc-macro2", | |||
|
764 | "quote", | |||
|
765 | "syn", | |||
|
766 | ] | |||
|
767 | ||||
|
768 | [[package]] | |||
684 | name = "output_vt100" |
|
769 | name = "output_vt100" | |
685 | version = "0.1.2" |
|
770 | version = "0.1.2" | |
686 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
771 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
@@ -720,6 +805,30 b' dependencies = [' | |||||
720 | ] |
|
805 | ] | |
721 |
|
806 | |||
722 | [[package]] |
|
807 | [[package]] | |
|
808 | name = "proc-macro-error" | |||
|
809 | version = "1.0.4" | |||
|
810 | source = "registry+https://github.com/rust-lang/crates.io-index" | |||
|
811 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" | |||
|
812 | dependencies = [ | |||
|
813 | "proc-macro-error-attr", | |||
|
814 | "proc-macro2", | |||
|
815 | "quote", | |||
|
816 | "syn", | |||
|
817 | "version_check", | |||
|
818 | ] | |||
|
819 | ||||
|
820 | [[package]] | |||
|
821 | name = "proc-macro-error-attr" | |||
|
822 | version = "1.0.4" | |||
|
823 | source = "registry+https://github.com/rust-lang/crates.io-index" | |||
|
824 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" | |||
|
825 | dependencies = [ | |||
|
826 | "proc-macro2", | |||
|
827 | "quote", | |||
|
828 | "version_check", | |||
|
829 | ] | |||
|
830 | ||||
|
831 | [[package]] | |||
723 | name = "proc-macro2" |
|
832 | name = "proc-macro2" | |
724 | version = "1.0.24" |
|
833 | version = "1.0.24" | |
725 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
834 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
@@ -864,9 +973,9 b' version = "1.9.1"' | |||||
864 | source = "registry+https://github.com/rust-lang/crates.io-index" |
|
973 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
865 | checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" |
|
974 | checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" | |
866 | dependencies = [ |
|
975 | dependencies = [ | |
867 | "crossbeam-channel", |
|
976 | "crossbeam-channel 0.5.2", | |
868 | "crossbeam-deque", |
|
977 | "crossbeam-deque", | |
869 | "crossbeam-utils", |
|
978 | "crossbeam-utils 0.8.1", | |
870 | "lazy_static", |
|
979 | "lazy_static", | |
871 | "num_cpus", |
|
980 | "num_cpus", | |
872 | ] |
|
981 | ] | |
@@ -920,7 +1029,7 b' dependencies = [' | |||||
920 | "home", |
|
1029 | "home", | |
921 | "lazy_static", |
|
1030 | "lazy_static", | |
922 | "log", |
|
1031 | "log", | |
923 | "micro-timer", |
|
1032 | "micro-timer 0.4.0", | |
924 | "regex", |
|
1033 | "regex", | |
925 | "users", |
|
1034 | "users", | |
926 | ] |
|
1035 | ] |
@@ -18,8 +18,9 b' home = "0.5.3"' | |||||
18 | im-rc = "15.0.0" |
|
18 | im-rc = "15.0.0" | |
19 | itertools = "0.10.3" |
|
19 | itertools = "0.10.3" | |
20 | lazy_static = "1.4.0" |
|
20 | lazy_static = "1.4.0" | |
21 |
libc = "0.2 |
|
21 | libc = "0.2" | |
22 | rand = "0.8.5" |
|
22 | ouroboros = "0.15.0" | |
|
23 | rand = "0.8.4" | |||
23 | rand_pcg = "0.3.1" |
|
24 | rand_pcg = "0.3.1" | |
24 | rand_distr = "0.4.3" |
|
25 | rand_distr = "0.4.3" | |
25 | rayon = "1.5.1" |
|
26 | rayon = "1.5.1" | |
@@ -27,12 +28,11 b' regex = "1.5.5"' | |||||
27 | sha-1 = "0.10.0" |
|
28 | sha-1 = "0.10.0" | |
28 | twox-hash = "1.6.2" |
|
29 | twox-hash = "1.6.2" | |
29 | same-file = "1.0.6" |
|
30 | same-file = "1.0.6" | |
30 | stable_deref_trait = "1.2.0" |
|
31 | tempfile = "3.1.0" | |
31 | tempfile = "3.3.0" |
|
32 | crossbeam-channel = "0.4" | |
32 | crossbeam-channel = "0.5.2" |
|
33 | micro-timer = "0.3.0" | |
33 | micro-timer = "0.4.0" |
|
34 | log = "0.4.8" | |
34 | log = "0.4.14" |
|
35 | memmap2 = {version = "0.4", features = ["stable_deref_trait"]} | |
35 | memmap2 = { version = "0.5.3", features = ["stable_deref_trait"] } |
|
|||
36 | zstd = "0.5.3" |
|
36 | zstd = "0.5.3" | |
37 | format-bytes = "0.3.0" |
|
37 | format-bytes = "0.3.0" | |
38 |
|
38 |
@@ -136,8 +136,6 b' pub enum StatusError {' | |||||
136 | DirstateV2ParseError(DirstateV2ParseError), |
|
136 | DirstateV2ParseError(DirstateV2ParseError), | |
137 | } |
|
137 | } | |
138 |
|
138 | |||
139 | pub type StatusResult<T> = Result<T, StatusError>; |
|
|||
140 |
|
||||
141 | impl fmt::Display for StatusError { |
|
139 | impl fmt::Display for StatusError { | |
142 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
|
140 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
143 | match self { |
|
141 | match self { |
@@ -723,10 +723,11 b' where' | |||||
723 |
|
723 | |||
724 | impl OwningDirstateMap { |
|
724 | impl OwningDirstateMap { | |
725 | pub fn clear(&mut self) { |
|
725 | pub fn clear(&mut self) { | |
726 |
|
|
726 | self.with_dmap_mut(|map| { | |
727 | map.root = Default::default(); |
|
727 | map.root = Default::default(); | |
728 | map.nodes_with_entry_count = 0; |
|
728 | map.nodes_with_entry_count = 0; | |
729 | map.nodes_with_copy_source_count = 0; |
|
729 | map.nodes_with_copy_source_count = 0; | |
|
730 | }); | |||
730 | } |
|
731 | } | |
731 |
|
732 | |||
732 | pub fn set_entry( |
|
733 | pub fn set_entry( | |
@@ -734,9 +735,10 b' impl OwningDirstateMap {' | |||||
734 | filename: &HgPath, |
|
735 | filename: &HgPath, | |
735 | entry: DirstateEntry, |
|
736 | entry: DirstateEntry, | |
736 | ) -> Result<(), DirstateV2ParseError> { |
|
737 | ) -> Result<(), DirstateV2ParseError> { | |
737 |
|
|
738 | self.with_dmap_mut(|map| { | |
738 | map.get_or_insert(&filename)?.data = NodeData::Entry(entry); |
|
739 | map.get_or_insert(&filename)?.data = NodeData::Entry(entry); | |
739 | Ok(()) |
|
740 | Ok(()) | |
|
741 | }) | |||
740 | } |
|
742 | } | |
741 |
|
743 | |||
742 | pub fn add_file( |
|
744 | pub fn add_file( | |
@@ -745,8 +747,9 b' impl OwningDirstateMap {' | |||||
745 | entry: DirstateEntry, |
|
747 | entry: DirstateEntry, | |
746 | ) -> Result<(), DirstateError> { |
|
748 | ) -> Result<(), DirstateError> { | |
747 | let old_state = self.get(filename)?.map(|e| e.state()); |
|
749 | let old_state = self.get(filename)?.map(|e| e.state()); | |
748 |
|
|
750 | self.with_dmap_mut(|map| { | |
749 | Ok(map.add_or_remove_file(filename, old_state, entry)?) |
|
751 | Ok(map.add_or_remove_file(filename, old_state, entry)?) | |
|
752 | }) | |||
750 | } |
|
753 | } | |
751 |
|
754 | |||
752 | pub fn remove_file( |
|
755 | pub fn remove_file( | |
@@ -777,9 +780,10 b' impl OwningDirstateMap {' | |||||
777 | if size == 0 { |
|
780 | if size == 0 { | |
778 | self.copy_map_remove(filename)?; |
|
781 | self.copy_map_remove(filename)?; | |
779 | } |
|
782 | } | |
780 |
|
|
783 | self.with_dmap_mut(|map| { | |
781 | let entry = DirstateEntry::new_removed(size); |
|
784 | let entry = DirstateEntry::new_removed(size); | |
782 | Ok(map.add_or_remove_file(filename, old_state, entry)?) |
|
785 | Ok(map.add_or_remove_file(filename, old_state, entry)?) | |
|
786 | }) | |||
783 | } |
|
787 | } | |
784 |
|
788 | |||
785 | pub fn drop_entry_and_copy_source( |
|
789 | pub fn drop_entry_and_copy_source( | |
@@ -789,7 +793,6 b' impl OwningDirstateMap {' | |||||
789 | let was_tracked = self |
|
793 | let was_tracked = self | |
790 | .get(filename)? |
|
794 | .get(filename)? | |
791 | .map_or(false, |e| e.state().is_tracked()); |
|
795 | .map_or(false, |e| e.state().is_tracked()); | |
792 | let map = self.get_map_mut(); |
|
|||
793 | struct Dropped { |
|
796 | struct Dropped { | |
794 | was_tracked: bool, |
|
797 | was_tracked: bool, | |
795 | had_entry: bool, |
|
798 | had_entry: bool, | |
@@ -826,10 +829,20 b' impl OwningDirstateMap {' | |||||
826 | )? { |
|
829 | )? { | |
827 | dropped = d; |
|
830 | dropped = d; | |
828 | if dropped.had_entry { |
|
831 | if dropped.had_entry { | |
829 |
node.descendants_with_entry_count |
|
832 | node.descendants_with_entry_count = node | |
|
833 | .descendants_with_entry_count | |||
|
834 | .checked_sub(1) | |||
|
835 | .expect( | |||
|
836 | "descendants_with_entry_count should be >= 0", | |||
|
837 | ); | |||
830 | } |
|
838 | } | |
831 | if dropped.was_tracked { |
|
839 | if dropped.was_tracked { | |
832 |
node.tracked_descendants_count |
|
840 | node.tracked_descendants_count = node | |
|
841 | .tracked_descendants_count | |||
|
842 | .checked_sub(1) | |||
|
843 | .expect( | |||
|
844 | "tracked_descendants_count should be >= 0", | |||
|
845 | ); | |||
833 | } |
|
846 | } | |
834 |
|
847 | |||
835 | // Directory caches must be invalidated when removing a |
|
848 | // Directory caches must be invalidated when removing a | |
@@ -843,21 +856,22 b' impl OwningDirstateMap {' | |||||
843 | return Ok(None); |
|
856 | return Ok(None); | |
844 | } |
|
857 | } | |
845 | } else { |
|
858 | } else { | |
846 |
let |
|
859 | let entry = node.data.as_entry(); | |
|
860 | let was_tracked = entry.map_or(false, |entry| entry.tracked()); | |||
|
861 | let had_entry = entry.is_some(); | |||
847 | if had_entry { |
|
862 | if had_entry { | |
848 | node.data = NodeData::None |
|
863 | node.data = NodeData::None | |
849 | } |
|
864 | } | |
|
865 | let mut had_copy_source = false; | |||
850 | if let Some(source) = &node.copy_source { |
|
866 | if let Some(source) = &node.copy_source { | |
851 | DirstateMap::count_dropped_path(unreachable_bytes, source); |
|
867 | DirstateMap::count_dropped_path(unreachable_bytes, source); | |
|
868 | had_copy_source = true; | |||
852 | node.copy_source = None |
|
869 | node.copy_source = None | |
853 | } |
|
870 | } | |
854 | dropped = Dropped { |
|
871 | dropped = Dropped { | |
855 |
was_tracked |
|
872 | was_tracked, | |
856 | .data |
|
|||
857 | .as_entry() |
|
|||
858 | .map_or(false, |entry| entry.state().is_tracked()), |
|
|||
859 | had_entry, |
|
873 | had_entry, | |
860 |
had_copy_source |
|
874 | had_copy_source, | |
861 | }; |
|
875 | }; | |
862 | } |
|
876 | } | |
863 | // After recursion, for both leaf (rest_of_path is None) nodes and |
|
877 | // After recursion, for both leaf (rest_of_path is None) nodes and | |
@@ -876,52 +890,62 b' impl OwningDirstateMap {' | |||||
876 | Ok(Some((dropped, remove))) |
|
890 | Ok(Some((dropped, remove))) | |
877 | } |
|
891 | } | |
878 |
|
892 | |||
879 | if let Some((dropped, _removed)) = recur( |
|
893 | self.with_dmap_mut(|map| { | |
880 | map.on_disk, |
|
894 | if let Some((dropped, _removed)) = recur( | |
881 |
|
|
895 | map.on_disk, | |
882 |
&mut map. |
|
896 | &mut map.unreachable_bytes, | |
883 | filename, |
|
897 | &mut map.root, | |
884 | )? { |
|
898 | filename, | |
885 | if dropped.had_entry { |
|
899 | )? { | |
886 | map.nodes_with_entry_count -= 1 |
|
900 | if dropped.had_entry { | |
|
901 | map.nodes_with_entry_count = map | |||
|
902 | .nodes_with_entry_count | |||
|
903 | .checked_sub(1) | |||
|
904 | .expect("nodes_with_entry_count should be >= 0"); | |||
|
905 | } | |||
|
906 | if dropped.had_copy_source { | |||
|
907 | map.nodes_with_copy_source_count = map | |||
|
908 | .nodes_with_copy_source_count | |||
|
909 | .checked_sub(1) | |||
|
910 | .expect("nodes_with_copy_source_count should be >= 0"); | |||
|
911 | } | |||
|
912 | } else { | |||
|
913 | debug_assert!(!was_tracked); | |||
887 | } |
|
914 | } | |
888 | if dropped.had_copy_source { |
|
915 | Ok(()) | |
889 | map.nodes_with_copy_source_count -= 1 |
|
916 | }) | |
890 | } |
|
|||
891 | } else { |
|
|||
892 | debug_assert!(!was_tracked); |
|
|||
893 | } |
|
|||
894 | Ok(()) |
|
|||
895 | } |
|
917 | } | |
896 |
|
918 | |||
897 | pub fn has_tracked_dir( |
|
919 | pub fn has_tracked_dir( | |
898 | &mut self, |
|
920 | &mut self, | |
899 | directory: &HgPath, |
|
921 | directory: &HgPath, | |
900 | ) -> Result<bool, DirstateError> { |
|
922 | ) -> Result<bool, DirstateError> { | |
901 |
|
|
923 | self.with_dmap_mut(|map| { | |
902 | if let Some(node) = map.get_node(directory)? { |
|
924 | if let Some(node) = map.get_node(directory)? { | |
903 | // A node without a `DirstateEntry` was created to hold child |
|
925 | // A node without a `DirstateEntry` was created to hold child | |
904 | // nodes, and is therefore a directory. |
|
926 | // nodes, and is therefore a directory. | |
905 | let state = node.state()?; |
|
927 | let state = node.state()?; | |
906 | Ok(state.is_none() && node.tracked_descendants_count() > 0) |
|
928 | Ok(state.is_none() && node.tracked_descendants_count() > 0) | |
907 | } else { |
|
929 | } else { | |
908 | Ok(false) |
|
930 | Ok(false) | |
909 | } |
|
931 | } | |
|
932 | }) | |||
910 | } |
|
933 | } | |
911 |
|
934 | |||
912 | pub fn has_dir( |
|
935 | pub fn has_dir( | |
913 | &mut self, |
|
936 | &mut self, | |
914 | directory: &HgPath, |
|
937 | directory: &HgPath, | |
915 | ) -> Result<bool, DirstateError> { |
|
938 | ) -> Result<bool, DirstateError> { | |
916 |
|
|
939 | self.with_dmap_mut(|map| { | |
917 | if let Some(node) = map.get_node(directory)? { |
|
940 | if let Some(node) = map.get_node(directory)? { | |
918 | // A node without a `DirstateEntry` was created to hold child |
|
941 | // A node without a `DirstateEntry` was created to hold child | |
919 | // nodes, and is therefore a directory. |
|
942 | // nodes, and is therefore a directory. | |
920 | let state = node.state()?; |
|
943 | let state = node.state()?; | |
921 | Ok(state.is_none() && node.descendants_with_entry_count() > 0) |
|
944 | Ok(state.is_none() && node.descendants_with_entry_count() > 0) | |
922 | } else { |
|
945 | } else { | |
923 | Ok(false) |
|
946 | Ok(false) | |
924 | } |
|
947 | } | |
|
948 | }) | |||
925 | } |
|
949 | } | |
926 |
|
950 | |||
927 | #[timed] |
|
951 | #[timed] | |
@@ -973,16 +997,29 b' impl OwningDirstateMap {' | |||||
973 | on_disk::write(map, can_append) |
|
997 | on_disk::write(map, can_append) | |
974 | } |
|
998 | } | |
975 |
|
999 | |||
976 | pub fn status<'a>( |
|
1000 | /// `callback` allows the caller to process and do something with the | |
977 | &'a mut self, |
|
1001 | /// results of the status. This is needed to do so efficiently (i.e. | |
978 | matcher: &'a (dyn Matcher + Sync), |
|
1002 | /// without cloning the `DirstateStatus` object with its paths) because | |
|
1003 | /// we need to borrow from `Self`. | |||
|
1004 | pub fn with_status<R>( | |||
|
1005 | &mut self, | |||
|
1006 | matcher: &(dyn Matcher + Sync), | |||
979 | root_dir: PathBuf, |
|
1007 | root_dir: PathBuf, | |
980 | ignore_files: Vec<PathBuf>, |
|
1008 | ignore_files: Vec<PathBuf>, | |
981 | options: StatusOptions, |
|
1009 | options: StatusOptions, | |
982 | ) -> Result<(DirstateStatus<'a>, Vec<PatternFileWarning>), StatusError> |
|
1010 | callback: impl for<'r> FnOnce( | |
983 | { |
|
1011 | Result<(DirstateStatus<'r>, Vec<PatternFileWarning>), StatusError>, | |
984 | let map = self.get_map_mut(); |
|
1012 | ) -> R, | |
985 | super::status::status(map, matcher, root_dir, ignore_files, options) |
|
1013 | ) -> R { | |
|
1014 | self.with_dmap_mut(|map| { | |||
|
1015 | callback(super::status::status( | |||
|
1016 | map, | |||
|
1017 | matcher, | |||
|
1018 | root_dir, | |||
|
1019 | ignore_files, | |||
|
1020 | options, | |||
|
1021 | )) | |||
|
1022 | }) | |||
986 | } |
|
1023 | } | |
987 |
|
1024 | |||
988 | pub fn copy_map_len(&self) -> usize { |
|
1025 | pub fn copy_map_len(&self) -> usize { | |
@@ -1030,22 +1067,23 b' impl OwningDirstateMap {' | |||||
1030 | &mut self, |
|
1067 | &mut self, | |
1031 | key: &HgPath, |
|
1068 | key: &HgPath, | |
1032 | ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> { |
|
1069 | ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> { | |
1033 |
|
|
1070 | self.with_dmap_mut(|map| { | |
1034 | let count = &mut map.nodes_with_copy_source_count; |
|
1071 | let count = &mut map.nodes_with_copy_source_count; | |
1035 | let unreachable_bytes = &mut map.unreachable_bytes; |
|
1072 | let unreachable_bytes = &mut map.unreachable_bytes; | |
1036 | Ok(DirstateMap::get_node_mut( |
|
1073 | Ok(DirstateMap::get_node_mut( | |
1037 | map.on_disk, |
|
1074 | map.on_disk, | |
1038 | unreachable_bytes, |
|
1075 | unreachable_bytes, | |
1039 | &mut map.root, |
|
1076 | &mut map.root, | |
1040 | key, |
|
1077 | key, | |
1041 | )? |
|
1078 | )? | |
1042 | .and_then(|node| { |
|
1079 | .and_then(|node| { | |
1043 | if let Some(source) = &node.copy_source { |
|
1080 | if let Some(source) = &node.copy_source { | |
1044 | *count -= 1; |
|
1081 | *count -= 1; | |
1045 | DirstateMap::count_dropped_path(unreachable_bytes, source); |
|
1082 | DirstateMap::count_dropped_path(unreachable_bytes, source); | |
1046 | } |
|
1083 | } | |
1047 | node.copy_source.take().map(Cow::into_owned) |
|
1084 | node.copy_source.take().map(Cow::into_owned) | |
1048 | })) |
|
1085 | })) | |
|
1086 | }) | |||
1049 | } |
|
1087 | } | |
1050 |
|
1088 | |||
1051 | pub fn copy_map_insert( |
|
1089 | pub fn copy_map_insert( | |
@@ -1053,19 +1091,20 b' impl OwningDirstateMap {' | |||||
1053 | key: HgPathBuf, |
|
1091 | key: HgPathBuf, | |
1054 | value: HgPathBuf, |
|
1092 | value: HgPathBuf, | |
1055 | ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> { |
|
1093 | ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> { | |
1056 |
|
|
1094 | self.with_dmap_mut(|map| { | |
1057 | let node = DirstateMap::get_or_insert_node( |
|
1095 | let node = DirstateMap::get_or_insert_node( | |
1058 | map.on_disk, |
|
1096 | map.on_disk, | |
1059 | &mut map.unreachable_bytes, |
|
1097 | &mut map.unreachable_bytes, | |
1060 | &mut map.root, |
|
1098 | &mut map.root, | |
1061 | &key, |
|
1099 | &key, | |
1062 | WithBasename::to_cow_owned, |
|
1100 | WithBasename::to_cow_owned, | |
1063 | |_ancestor| {}, |
|
1101 | |_ancestor| {}, | |
1064 | )?; |
|
1102 | )?; | |
1065 | if node.copy_source.is_none() { |
|
1103 | if node.copy_source.is_none() { | |
1066 | map.nodes_with_copy_source_count += 1 |
|
1104 | map.nodes_with_copy_source_count += 1 | |
1067 | } |
|
1105 | } | |
1068 | Ok(node.copy_source.replace(value.into()).map(Cow::into_owned)) |
|
1106 | Ok(node.copy_source.replace(value.into()).map(Cow::into_owned)) | |
|
1107 | }) | |||
1069 | } |
|
1108 | } | |
1070 |
|
1109 | |||
1071 | pub fn len(&self) -> usize { |
|
1110 | pub fn len(&self) -> usize { | |
@@ -1113,7 +1152,7 b' impl OwningDirstateMap {' | |||||
1113 | >, |
|
1152 | >, | |
1114 | DirstateError, |
|
1153 | DirstateError, | |
1115 | > { |
|
1154 | > { | |
1116 |
let map = self.get_map |
|
1155 | let map = self.get_map(); | |
1117 | let on_disk = map.on_disk; |
|
1156 | let on_disk = map.on_disk; | |
1118 | Ok(Box::new(filter_map_results( |
|
1157 | Ok(Box::new(filter_map_results( | |
1119 | map.iter_nodes(), |
|
1158 | map.iter_nodes(), |
@@ -1,105 +1,89 b'' | |||||
|
1 | use crate::{DirstateError, DirstateParents}; | |||
|
2 | ||||
1 | use super::dirstate_map::DirstateMap; |
|
3 | use super::dirstate_map::DirstateMap; | |
2 | use stable_deref_trait::StableDeref; |
|
|||
3 | use std::ops::Deref; |
|
4 | use std::ops::Deref; | |
4 |
|
5 | |||
|
6 | use ouroboros::self_referencing; | |||
|
7 | ||||
5 | /// Keep a `DirstateMap<'on_disk>` next to the `on_disk` buffer that it |
|
8 | /// Keep a `DirstateMap<'on_disk>` next to the `on_disk` buffer that it | |
6 | /// borrows. |
|
9 | /// borrows. | |
7 | /// |
|
10 | #[self_referencing] | |
8 | /// This is similar to [`OwningRef`] which is more limited because it |
|
|||
9 | /// represents exactly one `&T` reference next to the value it borrows, as |
|
|||
10 | /// opposed to a struct that may contain an arbitrary number of references in |
|
|||
11 | /// arbitrarily-nested data structures. |
|
|||
12 | /// |
|
|||
13 | /// [`OwningRef`]: https://docs.rs/owning_ref/0.4.1/owning_ref/struct.OwningRef.html |
|
|||
14 | pub struct OwningDirstateMap { |
|
11 | pub struct OwningDirstateMap { | |
15 | /// Owned handle to a bytes buffer with a stable address. |
|
|||
16 | /// |
|
|||
17 | /// See <https://docs.rs/owning_ref/0.4.1/owning_ref/trait.StableAddress.html>. |
|
|||
18 | on_disk: Box<dyn Deref<Target = [u8]> + Send>, |
|
12 | on_disk: Box<dyn Deref<Target = [u8]> + Send>, | |
19 |
|
13 | #[borrows(on_disk)] | ||
20 | /// Pointer for `Box<DirstateMap<'on_disk>>`, typed-erased because the |
|
14 | #[covariant] | |
21 | /// language cannot represent a lifetime referencing a sibling field. |
|
15 | map: DirstateMap<'this>, | |
22 | /// This is not quite a self-referencial struct (moving this struct is not |
|
|||
23 | /// a problem as it doesn’t change the address of the bytes buffer owned |
|
|||
24 | /// by `on_disk`) but touches similar borrow-checker limitations. |
|
|||
25 | ptr: *mut (), |
|
|||
26 | } |
|
16 | } | |
27 |
|
17 | |||
28 | impl OwningDirstateMap { |
|
18 | impl OwningDirstateMap { | |
29 | pub fn new_empty<OnDisk>(on_disk: OnDisk) -> Self |
|
19 | pub fn new_empty<OnDisk>(on_disk: OnDisk) -> Self | |
30 | where |
|
20 | where | |
31 |
OnDisk: Deref<Target = [u8]> + |
|
21 | OnDisk: Deref<Target = [u8]> + Send + 'static, | |
32 | { |
|
22 | { | |
33 | let on_disk = Box::new(on_disk); |
|
23 | let on_disk = Box::new(on_disk); | |
34 | let bytes: &'_ [u8] = &on_disk; |
|
|||
35 | let map = DirstateMap::empty(bytes); |
|
|||
36 |
|
24 | |||
37 | // Like in `bytes` above, this `'_` lifetime parameter borrows from |
|
25 | OwningDirstateMapBuilder { | |
38 | // the bytes buffer owned by `on_disk`. |
|
26 | on_disk, | |
39 | let ptr: *mut DirstateMap<'_> = Box::into_raw(Box::new(map)); |
|
27 | map_builder: |bytes| DirstateMap::empty(&bytes), | |
40 |
|
28 | } | ||
41 | // Erase the pointed type entirely in order to erase the lifetime. |
|
29 | .build() | |
42 | let ptr: *mut () = ptr.cast(); |
|
|||
43 |
|
||||
44 | Self { on_disk, ptr } |
|
|||
45 | } |
|
30 | } | |
46 |
|
31 | |||
47 | pub fn get_pair_mut<'a>( |
|
32 | pub fn new_v1<OnDisk>( | |
48 | &'a mut self, |
|
33 | on_disk: OnDisk, | |
49 | ) -> (&'a [u8], &'a mut DirstateMap<'a>) { |
|
34 | ) -> Result<(Self, DirstateParents), DirstateError> | |
50 | // SAFETY: We cast the type-erased pointer back to the same type it had |
|
35 | where | |
51 | // in `new`, except with a different lifetime parameter. This time we |
|
36 | OnDisk: Deref<Target = [u8]> + Send + 'static, | |
52 | // connect the lifetime to that of `self`. This cast is valid because |
|
37 | { | |
53 | // `self` owns the same `on_disk` whose buffer `DirstateMap` |
|
38 | let on_disk = Box::new(on_disk); | |
54 | // references. That buffer has a stable memory address because our |
|
39 | let mut parents = DirstateParents::NULL; | |
55 | // `Self::new_empty` counstructor requires `StableDeref`. |
|
|||
56 | let ptr: *mut DirstateMap<'a> = self.ptr.cast(); |
|
|||
57 | // SAFETY: we dereference that pointer, connecting the lifetime of the |
|
|||
58 | // new `&mut` to that of `self`. This is valid because the |
|
|||
59 | // raw pointer is to a boxed value, and `self` owns that box. |
|
|||
60 | (&self.on_disk, unsafe { &mut *ptr }) |
|
|||
61 | } |
|
|||
62 |
|
40 | |||
63 | pub fn get_map_mut<'a>(&'a mut self) -> &'a mut DirstateMap<'a> { |
|
41 | Ok(( | |
64 | self.get_pair_mut().1 |
|
42 | OwningDirstateMapTryBuilder { | |
|
43 | on_disk, | |||
|
44 | map_builder: |bytes| { | |||
|
45 | DirstateMap::new_v1(&bytes).map(|(dmap, p)| { | |||
|
46 | parents = p.unwrap_or(DirstateParents::NULL); | |||
|
47 | dmap | |||
|
48 | }) | |||
|
49 | }, | |||
|
50 | } | |||
|
51 | .try_build()?, | |||
|
52 | parents, | |||
|
53 | )) | |||
65 | } |
|
54 | } | |
66 |
|
55 | |||
67 | pub fn get_map<'a>(&'a self) -> &'a DirstateMap<'a> { |
|
56 | pub fn new_v2<OnDisk>( | |
68 | // SAFETY: same reasoning as in `get_pair_mut` above. |
|
57 | on_disk: OnDisk, | |
69 | let ptr: *mut DirstateMap<'a> = self.ptr.cast(); |
|
58 | data_size: usize, | |
70 | unsafe { &*ptr } |
|
59 | metadata: &[u8], | |
|
60 | ) -> Result<Self, DirstateError> | |||
|
61 | where | |||
|
62 | OnDisk: Deref<Target = [u8]> + Send + 'static, | |||
|
63 | { | |||
|
64 | let on_disk = Box::new(on_disk); | |||
|
65 | ||||
|
66 | OwningDirstateMapTryBuilder { | |||
|
67 | on_disk, | |||
|
68 | map_builder: |bytes| { | |||
|
69 | DirstateMap::new_v2(&bytes, data_size, metadata) | |||
|
70 | }, | |||
|
71 | } | |||
|
72 | .try_build() | |||
71 | } |
|
73 | } | |
72 |
|
74 | |||
73 | pub fn on_disk<'a>(&'a self) -> &'a [u8] { |
|
75 | pub fn with_dmap_mut<R>( | |
74 |
&self |
|
76 | &mut self, | |
|
77 | f: impl FnOnce(&mut DirstateMap) -> R, | |||
|
78 | ) -> R { | |||
|
79 | self.with_map_mut(f) | |||
|
80 | } | |||
|
81 | ||||
|
82 | pub fn get_map(&self) -> &DirstateMap { | |||
|
83 | self.borrow_map() | |||
|
84 | } | |||
|
85 | ||||
|
86 | pub fn on_disk(&self) -> &[u8] { | |||
|
87 | self.borrow_on_disk() | |||
75 | } |
|
88 | } | |
76 | } |
|
89 | } | |
77 |
|
||||
78 | impl Drop for OwningDirstateMap { |
|
|||
79 | fn drop(&mut self) { |
|
|||
80 | // Silence a "field is never read" warning, and demonstrate that this |
|
|||
81 | // value is still alive. |
|
|||
82 | let _: &Box<dyn Deref<Target = [u8]> + Send> = &self.on_disk; |
|
|||
83 | // SAFETY: this cast is the same as in `get_mut`, and is valid for the |
|
|||
84 | // same reason. `self.on_disk` still exists at this point, drop glue |
|
|||
85 | // will drop it implicitly after this `drop` method returns. |
|
|||
86 | let ptr: *mut DirstateMap<'_> = self.ptr.cast(); |
|
|||
87 | // SAFETY: `Box::from_raw` takes ownership of the box away from `self`. |
|
|||
88 | // This is fine because drop glue does nothing for `*mut ()` and we’re |
|
|||
89 | // in `drop`, so `get` and `get_mut` cannot be called again. |
|
|||
90 | unsafe { drop(Box::from_raw(ptr)) } |
|
|||
91 | } |
|
|||
92 | } |
|
|||
93 |
|
||||
94 | fn _static_assert_is_send<T: Send>() {} |
|
|||
95 |
|
||||
96 | fn _static_assert_fields_are_send() { |
|
|||
97 | _static_assert_is_send::<Box<DirstateMap<'_>>>(); |
|
|||
98 | } |
|
|||
99 |
|
||||
100 | // SAFETY: we don’t get this impl implicitly because `*mut (): !Send` because |
|
|||
101 | // thread-safety of raw pointers is unknown in the general case. However this |
|
|||
102 | // particular raw pointer represents a `Box<DirstateMap<'on_disk>>` that we |
|
|||
103 | // own. Since that `Box` is `Send` as shown in above, it is sound to mark |
|
|||
104 | // this struct as `Send` too. |
|
|||
105 | unsafe impl Send for OwningDirstateMap {} |
|
@@ -40,13 +40,14 b' use std::time::SystemTime;' | |||||
40 | /// exists in one of the two trees, depending on information requested by |
|
40 | /// exists in one of the two trees, depending on information requested by | |
41 | /// `options` we may need to traverse the remaining subtree. |
|
41 | /// `options` we may need to traverse the remaining subtree. | |
42 | #[timed] |
|
42 | #[timed] | |
43 |
pub fn status<' |
|
43 | pub fn status<'dirstate>( | |
44 |
dmap: &' |
|
44 | dmap: &'dirstate mut DirstateMap, | |
45 | matcher: &(dyn Matcher + Sync), |
|
45 | matcher: &(dyn Matcher + Sync), | |
46 | root_dir: PathBuf, |
|
46 | root_dir: PathBuf, | |
47 | ignore_files: Vec<PathBuf>, |
|
47 | ignore_files: Vec<PathBuf>, | |
48 | options: StatusOptions, |
|
48 | options: StatusOptions, | |
49 |
) -> Result<(DirstateStatus<' |
|
49 | ) -> Result<(DirstateStatus<'dirstate>, Vec<PatternFileWarning>), StatusError> | |
|
50 | { | |||
50 | // Force the global rayon threadpool to not exceed 16 concurrent threads. |
|
51 | // Force the global rayon threadpool to not exceed 16 concurrent threads. | |
51 | // This is a stop-gap measure until we figure out why using more than 16 |
|
52 | // This is a stop-gap measure until we figure out why using more than 16 | |
52 | // threads makes `status` slower for each additional thread. |
|
53 | // threads makes `status` slower for each additional thread. |
@@ -1,7 +1,6 b'' | |||||
1 | use crate::changelog::Changelog; |
|
1 | use crate::changelog::Changelog; | |
2 | use crate::config::{Config, ConfigError, ConfigParseError}; |
|
2 | use crate::config::{Config, ConfigError, ConfigParseError}; | |
3 | use crate::dirstate::DirstateParents; |
|
3 | use crate::dirstate::DirstateParents; | |
4 | use crate::dirstate_tree::dirstate_map::DirstateMap; |
|
|||
5 | use crate::dirstate_tree::on_disk::Docket as DirstateDocket; |
|
4 | use crate::dirstate_tree::on_disk::Docket as DirstateDocket; | |
6 | use crate::dirstate_tree::owning::OwningDirstateMap; |
|
5 | use crate::dirstate_tree::owning::OwningDirstateMap; | |
7 | use crate::errors::HgResultExt; |
|
6 | use crate::errors::HgResultExt; | |
@@ -316,25 +315,19 b' impl Repo {' | |||||
316 | .set(Some(docket.uuid.to_owned())); |
|
315 | .set(Some(docket.uuid.to_owned())); | |
317 | let data_size = docket.data_size(); |
|
316 | let data_size = docket.data_size(); | |
318 | let metadata = docket.tree_metadata(); |
|
317 | let metadata = docket.tree_metadata(); | |
319 |
|
|
318 | if let Some(data_mmap) = self | |
320 | .hg_vfs() |
|
319 | .hg_vfs() | |
321 | .mmap_open(docket.data_filename()) |
|
320 | .mmap_open(docket.data_filename()) | |
322 | .io_not_found_as_none()? |
|
321 | .io_not_found_as_none()? | |
323 | { |
|
322 | { | |
324 |
OwningDirstateMap::new_ |
|
323 | OwningDirstateMap::new_v2(data_mmap, data_size, metadata) | |
325 | } else { |
|
324 | } else { | |
326 |
OwningDirstateMap::new_ |
|
325 | OwningDirstateMap::new_v2(Vec::new(), data_size, metadata) | |
327 |
} |
|
326 | } | |
328 | let (on_disk, placeholder) = map.get_pair_mut(); |
|
|||
329 | *placeholder = DirstateMap::new_v2(on_disk, data_size, metadata)?; |
|
|||
330 | Ok(map) |
|
|||
331 | } else { |
|
327 | } else { | |
332 | let mut map = OwningDirstateMap::new_empty(dirstate_file_contents); |
|
328 | let (map, parents) = | |
333 | let (on_disk, placeholder) = map.get_pair_mut(); |
|
329 | OwningDirstateMap::new_v1(dirstate_file_contents)?; | |
334 | let (inner, parents) = DirstateMap::new_v1(on_disk)?; |
|
330 | self.dirstate_parents.set(parents); | |
335 | self.dirstate_parents |
|
|||
336 | .set(parents.unwrap_or(DirstateParents::NULL)); |
|
|||
337 | *placeholder = inner; |
|
|||
338 | Ok(map) |
|
331 | Ok(map) | |
339 | } |
|
332 | } | |
340 | } |
|
333 | } |
@@ -144,15 +144,8 b' impl From<HgPathError> for std::io::Erro' | |||||
144 | /// On Unix, it's just byte-to-byte conversion. On Windows, it has to be |
|
144 | /// On Unix, it's just byte-to-byte conversion. On Windows, it has to be | |
145 | /// decoded from MBCS to WTF-8. If WindowsUTF8Plan is implemented, the source |
|
145 | /// decoded from MBCS to WTF-8. If WindowsUTF8Plan is implemented, the source | |
146 | /// character encoding will be determined on a per-repository basis. |
|
146 | /// character encoding will be determined on a per-repository basis. | |
147 | // |
|
|||
148 | // FIXME: (adapted from a comment in the stdlib) |
|
|||
149 | // `HgPath::new()` current implementation relies on `Slice` being |
|
|||
150 | // layout-compatible with `[u8]`. |
|
|||
151 | // When attribute privacy is implemented, `Slice` should be annotated as |
|
|||
152 | // `#[repr(transparent)]`. |
|
|||
153 | // Anyway, `Slice` representation and layout are considered implementation |
|
|||
154 | // detail, are not documented and must not be relied upon. |
|
|||
155 | #[derive(Eq, Ord, PartialEq, PartialOrd, Hash)] |
|
147 | #[derive(Eq, Ord, PartialEq, PartialOrd, Hash)] | |
|
148 | #[repr(transparent)] | |||
156 | pub struct HgPath { |
|
149 | pub struct HgPath { | |
157 | inner: [u8], |
|
150 | inner: [u8], | |
158 | } |
|
151 | } |
@@ -23,7 +23,6 b' use crate::{' | |||||
23 | }; |
|
23 | }; | |
24 | use hg::{ |
|
24 | use hg::{ | |
25 | dirstate::StateMapIter, |
|
25 | dirstate::StateMapIter, | |
26 | dirstate_tree::dirstate_map::DirstateMap as TreeDirstateMap, |
|
|||
27 | dirstate_tree::on_disk::DirstateV2ParseError, |
|
26 | dirstate_tree::on_disk::DirstateV2ParseError, | |
28 | dirstate_tree::owning::OwningDirstateMap, |
|
27 | dirstate_tree::owning::OwningDirstateMap, | |
29 | revlog::Node, |
|
28 | revlog::Node, | |
@@ -53,18 +52,12 b' py_class!(pub class DirstateMap |py| {' | |||||
53 | on_disk: PyBytes, |
|
52 | on_disk: PyBytes, | |
54 | ) -> PyResult<PyObject> { |
|
53 | ) -> PyResult<PyObject> { | |
55 | let on_disk = PyBytesDeref::new(py, on_disk); |
|
54 | let on_disk = PyBytesDeref::new(py, on_disk); | |
56 |
let |
|
55 | let (map, parents) = OwningDirstateMap::new_v1(on_disk) | |
57 | let (on_disk, map_placeholder) = map.get_pair_mut(); |
|
|||
58 |
|
||||
59 | let (actual_map, parents) = TreeDirstateMap::new_v1(on_disk) |
|
|||
60 | .map_err(|e| dirstate_error(py, e))?; |
|
56 | .map_err(|e| dirstate_error(py, e))?; | |
61 | *map_placeholder = actual_map; |
|
|||
62 | let map = Self::create_instance(py, map)?; |
|
57 | let map = Self::create_instance(py, map)?; | |
63 | let parents = parents.map(|p| { |
|
58 | let p1 = PyBytes::new(py, parents.p1.as_bytes()); | |
64 |
|
|
59 | let p2 = PyBytes::new(py, parents.p2.as_bytes()); | |
65 | let p2 = PyBytes::new(py, p.p2.as_bytes()); |
|
60 | let parents = (p1, p2); | |
66 | (p1, p2) |
|
|||
67 | }); |
|
|||
68 | Ok((map, parents).to_py_object(py).into_object()) |
|
61 | Ok((map, parents).to_py_object(py).into_object()) | |
69 | } |
|
62 | } | |
70 |
|
63 | |||
@@ -79,9 +72,7 b' py_class!(pub class DirstateMap |py| {' | |||||
79 | PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e)) |
|
72 | PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e)) | |
80 | }; |
|
73 | }; | |
81 | let on_disk = PyBytesDeref::new(py, on_disk); |
|
74 | let on_disk = PyBytesDeref::new(py, on_disk); | |
82 |
let |
|
75 | let map = OwningDirstateMap::new_v2( | |
83 | let (on_disk, map_placeholder) = map.get_pair_mut(); |
|
|||
84 | *map_placeholder = TreeDirstateMap::new_v2( |
|
|||
85 | on_disk, data_size, tree_metadata.data(py), |
|
76 | on_disk, data_size, tree_metadata.data(py), | |
86 | ).map_err(dirstate_error)?; |
|
77 | ).map_err(dirstate_error)?; | |
87 | let map = Self::create_instance(py, map)?; |
|
78 | let map = Self::create_instance(py, map)?; |
@@ -127,25 +127,29 b' pub fn status_wrapper(' | |||||
127 | // The caller may call `copymap.items()` separately |
|
127 | // The caller may call `copymap.items()` separately | |
128 | let list_copies = false; |
|
128 | let list_copies = false; | |
129 |
|
129 | |||
|
130 | let after_status = |res: Result<(DirstateStatus<'_>, _), StatusError>| { | |||
|
131 | let (status_res, warnings) = | |||
|
132 | res.map_err(|e| handle_fallback(py, e))?; | |||
|
133 | build_response(py, status_res, warnings) | |||
|
134 | }; | |||
|
135 | ||||
130 | match matcher.get_type(py).name(py).borrow() { |
|
136 | match matcher.get_type(py).name(py).borrow() { | |
131 | "alwaysmatcher" => { |
|
137 | "alwaysmatcher" => { | |
132 | let matcher = AlwaysMatcher; |
|
138 | let matcher = AlwaysMatcher; | |
133 | let (status_res, warnings) = dmap |
|
139 | dmap.with_status( | |
134 |
|
|
140 | &matcher, | |
135 | &matcher, |
|
141 | root_dir.to_path_buf(), | |
136 | root_dir.to_path_buf(), |
|
142 | ignore_files, | |
137 | ignore_files, |
|
143 | StatusOptions { | |
138 |
|
|
144 | check_exec, | |
139 |
|
|
145 | list_clean, | |
140 |
|
|
146 | list_ignored, | |
141 |
|
|
147 | list_unknown, | |
142 |
|
|
148 | list_copies, | |
143 |
|
|
149 | collect_traversed_dirs, | |
144 | collect_traversed_dirs, |
|
150 | }, | |
145 |
|
|
151 | after_status, | |
146 |
|
|
152 | ) | |
147 | .map_err(|e| handle_fallback(py, e))?; |
|
|||
148 | build_response(py, status_res, warnings) |
|
|||
149 | } |
|
153 | } | |
150 | "exactmatcher" => { |
|
154 | "exactmatcher" => { | |
151 | let files = matcher.call_method( |
|
155 | let files = matcher.call_method( | |
@@ -167,22 +171,20 b' pub fn status_wrapper(' | |||||
167 | let files = files?; |
|
171 | let files = files?; | |
168 | let matcher = FileMatcher::new(files.as_ref()) |
|
172 | let matcher = FileMatcher::new(files.as_ref()) | |
169 | .map_err(|e| PyErr::new::<ValueError, _>(py, e.to_string()))?; |
|
173 | .map_err(|e| PyErr::new::<ValueError, _>(py, e.to_string()))?; | |
170 | let (status_res, warnings) = dmap |
|
174 | dmap.with_status( | |
171 |
|
|
175 | &matcher, | |
172 | &matcher, |
|
176 | root_dir.to_path_buf(), | |
173 | root_dir.to_path_buf(), |
|
177 | ignore_files, | |
174 | ignore_files, |
|
178 | StatusOptions { | |
175 |
|
|
179 | check_exec, | |
176 |
|
|
180 | list_clean, | |
177 |
|
|
181 | list_ignored, | |
178 |
|
|
182 | list_unknown, | |
179 |
|
|
183 | list_copies, | |
180 |
|
|
184 | collect_traversed_dirs, | |
181 | collect_traversed_dirs, |
|
185 | }, | |
182 |
|
|
186 | after_status, | |
183 |
|
|
187 | ) | |
184 | .map_err(|e| handle_fallback(py, e))?; |
|
|||
185 | build_response(py, status_res, warnings) |
|
|||
186 | } |
|
188 | } | |
187 | "includematcher" => { |
|
189 | "includematcher" => { | |
188 | // Get the patterns from Python even though most of them are |
|
190 | // Get the patterns from Python even though most of them are | |
@@ -219,23 +221,20 b' pub fn status_wrapper(' | |||||
219 | let matcher = IncludeMatcher::new(ignore_patterns) |
|
221 | let matcher = IncludeMatcher::new(ignore_patterns) | |
220 | .map_err(|e| handle_fallback(py, e.into()))?; |
|
222 | .map_err(|e| handle_fallback(py, e.into()))?; | |
221 |
|
223 | |||
222 | let (status_res, warnings) = dmap |
|
224 | dmap.with_status( | |
223 |
|
|
225 | &matcher, | |
224 | &matcher, |
|
226 | root_dir.to_path_buf(), | |
225 | root_dir.to_path_buf(), |
|
227 | ignore_files, | |
226 | ignore_files, |
|
228 | StatusOptions { | |
227 |
|
|
229 | check_exec, | |
228 |
|
|
230 | list_clean, | |
229 |
|
|
231 | list_ignored, | |
230 |
|
|
232 | list_unknown, | |
231 |
|
|
233 | list_copies, | |
232 |
|
|
234 | collect_traversed_dirs, | |
233 | collect_traversed_dirs, |
|
235 | }, | |
234 |
|
|
236 | after_status, | |
235 |
|
|
237 | ) | |
236 | .map_err(|e| handle_fallback(py, e))?; |
|
|||
237 |
|
||||
238 | build_response(py, status_res, warnings) |
|
|||
239 | } |
|
238 | } | |
240 | e => Err(PyErr::new::<ValueError, _>( |
|
239 | e => Err(PyErr::new::<ValueError, _>( | |
241 | py, |
|
240 | py, |
@@ -25,6 +25,9 b' use hg::utils::files::get_bytes_from_os_' | |||||
25 | use hg::utils::files::get_bytes_from_path; |
|
25 | use hg::utils::files::get_bytes_from_path; | |
26 | use hg::utils::files::get_path_from_bytes; |
|
26 | use hg::utils::files::get_path_from_bytes; | |
27 | use hg::utils::hg_path::{hg_path_to_path_buf, HgPath}; |
|
27 | use hg::utils::hg_path::{hg_path_to_path_buf, HgPath}; | |
|
28 | use hg::DirstateStatus; | |||
|
29 | use hg::PatternFileWarning; | |||
|
30 | use hg::StatusError; | |||
28 | use hg::StatusOptions; |
|
31 | use hg::StatusOptions; | |
29 | use log::info; |
|
32 | use log::info; | |
30 | use std::io; |
|
33 | use std::io; | |
@@ -230,117 +233,132 b' pub fn run(invocation: &crate::CliInvoca' | |||||
230 | list_copies, |
|
233 | list_copies, | |
231 | collect_traversed_dirs: false, |
|
234 | collect_traversed_dirs: false, | |
232 | }; |
|
235 | }; | |
233 | let (mut ds_status, pattern_warnings) = dmap.status( |
|
236 | ||
234 | &AlwaysMatcher, |
|
237 | type StatusResult<'a> = | |
235 | repo.working_directory_path().to_owned(), |
|
238 | Result<(DirstateStatus<'a>, Vec<PatternFileWarning>), StatusError>; | |
236 | ignore_files(repo, config), |
|
239 | ||
237 | options, |
|
240 | let after_status = |res: StatusResult| -> Result<_, CommandError> { | |
238 | )?; |
|
241 | let (mut ds_status, pattern_warnings) = res?; | |
239 | for warning in pattern_warnings { |
|
242 | for warning in pattern_warnings { | |
240 | match warning { |
|
243 | match warning { | |
241 | hg::PatternFileWarning::InvalidSyntax(path, syntax) => ui |
|
244 | hg::PatternFileWarning::InvalidSyntax(path, syntax) => ui | |
242 | .write_stderr(&format_bytes!( |
|
245 | .write_stderr(&format_bytes!( | |
243 | b"{}: ignoring invalid syntax '{}'\n", |
|
246 | b"{}: ignoring invalid syntax '{}'\n", | |
244 | get_bytes_from_path(path), |
|
247 | get_bytes_from_path(path), | |
245 | &*syntax |
|
248 | &*syntax | |
246 | ))?, |
|
249 | ))?, | |
247 | hg::PatternFileWarning::NoSuchFile(path) => { |
|
250 | hg::PatternFileWarning::NoSuchFile(path) => { | |
248 | let path = if let Ok(relative) = |
|
251 | let path = if let Ok(relative) = | |
249 | path.strip_prefix(repo.working_directory_path()) |
|
252 | path.strip_prefix(repo.working_directory_path()) | |
250 | { |
|
253 | { | |
251 | relative |
|
254 | relative | |
252 | } else { |
|
255 | } else { | |
253 | &*path |
|
256 | &*path | |
254 | }; |
|
257 | }; | |
255 | ui.write_stderr(&format_bytes!( |
|
258 | ui.write_stderr(&format_bytes!( | |
256 | b"skipping unreadable pattern file '{}': \ |
|
259 | b"skipping unreadable pattern file '{}': \ | |
257 | No such file or directory\n", |
|
260 | No such file or directory\n", | |
258 | get_bytes_from_path(path), |
|
261 | get_bytes_from_path(path), | |
259 | ))? |
|
262 | ))? | |
|
263 | } | |||
260 | } |
|
264 | } | |
261 | } |
|
265 | } | |
262 | } |
|
|||
263 |
|
266 | |||
264 | for (path, error) in ds_status.bad { |
|
267 | for (path, error) in ds_status.bad { | |
265 | let error = match error { |
|
268 | let error = match error { | |
266 | hg::BadMatch::OsError(code) => { |
|
269 | hg::BadMatch::OsError(code) => { | |
267 | std::io::Error::from_raw_os_error(code).to_string() |
|
270 | std::io::Error::from_raw_os_error(code).to_string() | |
268 | } |
|
271 | } | |
269 | hg::BadMatch::BadType(ty) => { |
|
272 | hg::BadMatch::BadType(ty) => { | |
270 | format!("unsupported file type (type is {})", ty) |
|
273 | format!("unsupported file type (type is {})", ty) | |
271 | } |
|
|||
272 | }; |
|
|||
273 | ui.write_stderr(&format_bytes!( |
|
|||
274 | b"{}: {}\n", |
|
|||
275 | path.as_bytes(), |
|
|||
276 | error.as_bytes() |
|
|||
277 | ))? |
|
|||
278 | } |
|
|||
279 | if !ds_status.unsure.is_empty() { |
|
|||
280 | info!( |
|
|||
281 | "Files to be rechecked by retrieval from filelog: {:?}", |
|
|||
282 | ds_status.unsure.iter().map(|s| &s.path).collect::<Vec<_>>() |
|
|||
283 | ); |
|
|||
284 | } |
|
|||
285 | let mut fixup = Vec::new(); |
|
|||
286 | if !ds_status.unsure.is_empty() |
|
|||
287 | && (display_states.modified || display_states.clean) |
|
|||
288 | { |
|
|||
289 | let p1 = repo.dirstate_parents()?.p1; |
|
|||
290 | let manifest = repo.manifest_for_node(p1).map_err(|e| { |
|
|||
291 | CommandError::from((e, &*format!("{:x}", p1.short()))) |
|
|||
292 | })?; |
|
|||
293 | for to_check in ds_status.unsure { |
|
|||
294 | if unsure_is_modified(repo, &manifest, &to_check.path)? { |
|
|||
295 | if display_states.modified { |
|
|||
296 | ds_status.modified.push(to_check); |
|
|||
297 | } |
|
274 | } | |
298 |
} |
|
275 | }; | |
299 | if display_states.clean { |
|
276 | ui.write_stderr(&format_bytes!( | |
300 | ds_status.clean.push(to_check.clone()); |
|
277 | b"{}: {}\n", | |
|
278 | path.as_bytes(), | |||
|
279 | error.as_bytes() | |||
|
280 | ))? | |||
|
281 | } | |||
|
282 | if !ds_status.unsure.is_empty() { | |||
|
283 | info!( | |||
|
284 | "Files to be rechecked by retrieval from filelog: {:?}", | |||
|
285 | ds_status.unsure.iter().map(|s| &s.path).collect::<Vec<_>>() | |||
|
286 | ); | |||
|
287 | } | |||
|
288 | let mut fixup = Vec::new(); | |||
|
289 | if !ds_status.unsure.is_empty() | |||
|
290 | && (display_states.modified || display_states.clean) | |||
|
291 | { | |||
|
292 | let p1 = repo.dirstate_parents()?.p1; | |||
|
293 | let manifest = repo.manifest_for_node(p1).map_err(|e| { | |||
|
294 | CommandError::from((e, &*format!("{:x}", p1.short()))) | |||
|
295 | })?; | |||
|
296 | for to_check in ds_status.unsure { | |||
|
297 | if unsure_is_modified(repo, &manifest, &to_check.path)? { | |||
|
298 | if display_states.modified { | |||
|
299 | ds_status.modified.push(to_check); | |||
|
300 | } | |||
|
301 | } else { | |||
|
302 | if display_states.clean { | |||
|
303 | ds_status.clean.push(to_check.clone()); | |||
|
304 | } | |||
|
305 | fixup.push(to_check.path.into_owned()) | |||
301 | } |
|
306 | } | |
302 | fixup.push(to_check.path.into_owned()) |
|
|||
303 | } |
|
307 | } | |
304 | } |
|
308 | } | |
305 | } |
|
309 | let relative_paths = (!ui.plain(None)) | |
306 | let relative_paths = (!ui.plain(None)) |
|
310 | && config | |
307 | && config |
|
311 | .get_option(b"commands", b"status.relative")? | |
308 | .get_option(b"commands", b"status.relative")? |
|
312 | .unwrap_or(config.get_bool(b"ui", b"relative-paths")?); | |
309 | .unwrap_or(config.get_bool(b"ui", b"relative-paths")?); |
|
313 | let output = DisplayStatusPaths { | |
310 | let output = DisplayStatusPaths { |
|
314 | ui, | |
311 |
|
|
315 | no_status, | |
312 | no_status, |
|
316 | relativize: if relative_paths { | |
313 | relativize: if relative_paths { |
|
317 | Some(RelativizePaths::new(repo)?) | |
314 | Some(RelativizePaths::new(repo)?) |
|
318 | } else { | |
315 | } else { |
|
319 | None | |
316 |
|
|
320 | }, | |
317 |
} |
|
321 | }; | |
|
322 | if display_states.modified { | |||
|
323 | output.display(b"M ", "status.modified", ds_status.modified)?; | |||
|
324 | } | |||
|
325 | if display_states.added { | |||
|
326 | output.display(b"A ", "status.added", ds_status.added)?; | |||
|
327 | } | |||
|
328 | if display_states.removed { | |||
|
329 | output.display(b"R ", "status.removed", ds_status.removed)?; | |||
|
330 | } | |||
|
331 | if display_states.deleted { | |||
|
332 | output.display(b"! ", "status.deleted", ds_status.deleted)?; | |||
|
333 | } | |||
|
334 | if display_states.unknown { | |||
|
335 | output.display(b"? ", "status.unknown", ds_status.unknown)?; | |||
|
336 | } | |||
|
337 | if display_states.ignored { | |||
|
338 | output.display(b"I ", "status.ignored", ds_status.ignored)?; | |||
|
339 | } | |||
|
340 | if display_states.clean { | |||
|
341 | output.display(b"C ", "status.clean", ds_status.clean)?; | |||
|
342 | } | |||
|
343 | ||||
|
344 | let dirstate_write_needed = ds_status.dirty; | |||
|
345 | let filesystem_time_at_status_start = | |||
|
346 | ds_status.filesystem_time_at_status_start; | |||
|
347 | ||||
|
348 | Ok(( | |||
|
349 | fixup, | |||
|
350 | dirstate_write_needed, | |||
|
351 | filesystem_time_at_status_start, | |||
|
352 | )) | |||
318 | }; |
|
353 | }; | |
319 | if display_states.modified { |
|
354 | let (fixup, mut dirstate_write_needed, filesystem_time_at_status_start) = | |
320 | output.display(b"M ", "status.modified", ds_status.modified)?; |
|
355 | dmap.with_status( | |
321 | } |
|
356 | &AlwaysMatcher, | |
322 | if display_states.added { |
|
357 | repo.working_directory_path().to_owned(), | |
323 | output.display(b"A ", "status.added", ds_status.added)?; |
|
358 | ignore_files(repo, config), | |
324 | } |
|
359 | options, | |
325 | if display_states.removed { |
|
360 | after_status, | |
326 | output.display(b"R ", "status.removed", ds_status.removed)?; |
|
361 | )?; | |
327 | } |
|
|||
328 | if display_states.deleted { |
|
|||
329 | output.display(b"! ", "status.deleted", ds_status.deleted)?; |
|
|||
330 | } |
|
|||
331 | if display_states.unknown { |
|
|||
332 | output.display(b"? ", "status.unknown", ds_status.unknown)?; |
|
|||
333 | } |
|
|||
334 | if display_states.ignored { |
|
|||
335 | output.display(b"I ", "status.ignored", ds_status.ignored)?; |
|
|||
336 | } |
|
|||
337 | if display_states.clean { |
|
|||
338 | output.display(b"C ", "status.clean", ds_status.clean)?; |
|
|||
339 | } |
|
|||
340 |
|
||||
341 | let mut dirstate_write_needed = ds_status.dirty; |
|
|||
342 | let filesystem_time_at_status_start = |
|
|||
343 | ds_status.filesystem_time_at_status_start; |
|
|||
344 |
|
362 | |||
345 | if (fixup.is_empty() || filesystem_time_at_status_start.is_none()) |
|
363 | if (fixup.is_empty() || filesystem_time_at_status_start.is_none()) | |
346 | && !dirstate_write_needed |
|
364 | && !dirstate_write_needed |
General Comments 0
You need to be logged in to leave comments.
Login now