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