##// END OF EJS Templates
merge: stable into default
Raphaël Gomès -
r49869:12adf8c6 merge default
parent child Browse files
Show More
@@ -269,7 +269,7 b' def _rundispatch(req):'
269 269 ferr.write(inst.format())
270 270 return -1
271 271
272 msg = _formatargs(req.args)
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 msg,
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.5.3"
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.119"
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 let map = self.get_map_mut();
726 self.with_dmap_mut(|map| {
727 727 map.root = Default::default();
728 728 map.nodes_with_entry_count = 0;
729 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 let map = self.get_map_mut();
738 self.with_dmap_mut(|map| {
738 739 map.get_or_insert(&filename)?.data = NodeData::Entry(entry);
739 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 let map = self.get_map_mut();
750 self.with_dmap_mut(|map| {
749 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 let map = self.get_map_mut();
783 self.with_dmap_mut(|map| {
781 784 let entry = DirstateEntry::new_removed(size);
782 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 -= 1;
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 -= 1;
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 had_entry = node.data.has_entry();
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: node
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: node.copy_source.take().is_some(),
874 had_copy_source,
861 875 };
862 876 }
863 877 // After recursion, for both leaf (rest_of_path is None) nodes and
@@ -876,6 +890,7 b' impl OwningDirstateMap {'
876 890 Ok(Some((dropped, remove)))
877 891 }
878 892
893 self.with_dmap_mut(|map| {
879 894 if let Some((dropped, _removed)) = recur(
880 895 map.on_disk,
881 896 &mut map.unreachable_bytes,
@@ -883,22 +898,29 b' impl OwningDirstateMap {'
883 898 filename,
884 899 )? {
885 900 if dropped.had_entry {
886 map.nodes_with_entry_count -= 1
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");
887 905 }
888 906 if dropped.had_copy_source {
889 map.nodes_with_copy_source_count -= 1
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");
890 911 }
891 912 } else {
892 913 debug_assert!(!was_tracked);
893 914 }
894 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 let map = self.get_map_mut();
923 self.with_dmap_mut(|map| {
902 924 if let Some(node) = map.get_node(directory)? {
903 925 // A node without a `DirstateEntry` was created to hold child
904 926 // nodes, and is therefore a directory.
@@ -907,13 +929,14 b' impl OwningDirstateMap {'
907 929 } else {
908 930 Ok(false)
909 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 let map = self.get_map_mut();
939 self.with_dmap_mut(|map| {
917 940 if let Some(node) = map.get_node(directory)? {
918 941 // A node without a `DirstateEntry` was created to hold child
919 942 // nodes, and is therefore a directory.
@@ -922,6 +945,7 b' impl OwningDirstateMap {'
922 945 } else {
923 946 Ok(false)
924 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,7 +1067,7 b' impl OwningDirstateMap {'
1030 1067 &mut self,
1031 1068 key: &HgPath,
1032 1069 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1033 let map = self.get_map_mut();
1070 self.with_dmap_mut(|map| {
1034 1071 let count = &mut map.nodes_with_copy_source_count;
1035 1072 let unreachable_bytes = &mut map.unreachable_bytes;
1036 1073 Ok(DirstateMap::get_node_mut(
@@ -1046,6 +1083,7 b' impl OwningDirstateMap {'
1046 1083 }
1047 1084 node.copy_source.take().map(Cow::into_owned)
1048 1085 }))
1086 })
1049 1087 }
1050 1088
1051 1089 pub fn copy_map_insert(
@@ -1053,7 +1091,7 b' impl OwningDirstateMap {'
1053 1091 key: HgPathBuf,
1054 1092 value: HgPathBuf,
1055 1093 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1056 let map = self.get_map_mut();
1094 self.with_dmap_mut(|map| {
1057 1095 let node = DirstateMap::get_or_insert_node(
1058 1096 map.on_disk,
1059 1097 &mut map.unreachable_bytes,
@@ -1066,6 +1104,7 b' impl OwningDirstateMap {'
1066 1104 map.nodes_with_copy_source_count += 1
1067 1105 }
1068 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_mut();
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]> + StableDeref + Send + 'static,
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 })
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;
40
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 },
61 50 }
62
63 pub fn get_map_mut<'a>(&'a mut self) -> &'a mut DirstateMap<'a> {
64 self.get_pair_mut().1
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 71 }
72
73 pub fn on_disk<'a>(&'a self) -> &'a [u8] {
74 &self.on_disk
75 }
72 .try_build()
76 73 }
77 74
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)) }
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()
91 88 }
92 89 }
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<'tree, 'on_disk: 'tree>(
44 dmap: &'tree mut DirstateMap<'on_disk>,
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<'on_disk>, Vec<PatternFileWarning>), StatusError> {
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 let mut map = if let Some(data_mmap) = self
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_empty(data_mmap)
323 OwningDirstateMap::new_v2(data_mmap, data_size, metadata)
325 324 } else {
326 OwningDirstateMap::new_empty(Vec::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 mut map = OwningDirstateMap::new_empty(on_disk);
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 let p1 = PyBytes::new(py, p.p1.as_bytes());
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 mut map = OwningDirstateMap::new_empty(on_disk);
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,11 +127,16 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 .status(
139 dmap.with_status(
135 140 &matcher,
136 141 root_dir.to_path_buf(),
137 142 ignore_files,
@@ -143,9 +148,8 b' pub fn status_wrapper('
143 148 list_copies,
144 149 collect_traversed_dirs,
145 150 },
151 after_status,
146 152 )
147 .map_err(|e| handle_fallback(py, e))?;
148 build_response(py, status_res, warnings)
149 153 }
150 154 "exactmatcher" => {
151 155 let files = matcher.call_method(
@@ -167,8 +171,7 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 .status(
174 dmap.with_status(
172 175 &matcher,
173 176 root_dir.to_path_buf(),
174 177 ignore_files,
@@ -180,9 +183,8 b' pub fn status_wrapper('
180 183 list_copies,
181 184 collect_traversed_dirs,
182 185 },
186 after_status,
183 187 )
184 .map_err(|e| handle_fallback(py, e))?;
185 build_response(py, status_res, warnings)
186 188 }
187 189 "includematcher" => {
188 190 // Get the patterns from Python even though most of them are
@@ -219,8 +221,7 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 .status(
224 dmap.with_status(
224 225 &matcher,
225 226 root_dir.to_path_buf(),
226 227 ignore_files,
@@ -232,10 +233,8 b' pub fn status_wrapper('
232 233 list_copies,
233 234 collect_traversed_dirs,
234 235 },
236 after_status,
235 237 )
236 .map_err(|e| handle_fallback(py, e))?;
237
238 build_response(py, status_res, warnings)
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,12 +233,12 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 )?;
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?;
239 242 for warning in pattern_warnings {
240 243 match warning {
241 244 hg::PatternFileWarning::InvalidSyntax(path, syntax) => ui
@@ -338,10 +341,25 b' pub fn run(invocation: &crate::CliInvoca'
338 341 output.display(b"C ", "status.clean", ds_status.clean)?;
339 342 }
340 343
341 let mut dirstate_write_needed = ds_status.dirty;
344 let dirstate_write_needed = ds_status.dirty;
342 345 let filesystem_time_at_status_start =
343 346 ds_status.filesystem_time_at_status_start;
344 347
348 Ok((
349 fixup,
350 dirstate_write_needed,
351 filesystem_time_at_status_start,
352 ))
353 };
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 )?;
362
345 363 if (fixup.is_empty() || filesystem_time_at_status_start.is_none())
346 364 && !dirstate_write_needed
347 365 {
General Comments 0
You need to be logged in to leave comments. Login now