##// END OF EJS Templates
rhg: Align with Python on some revset parsing corner cases...
Simon Sapin -
r48776:8c29af0f default
parent child Browse files
Show More
@@ -1,396 +1,401 b''
1 use std::borrow::Cow;
1 use std::borrow::Cow;
2 use std::io::Read;
2 use std::io::Read;
3 use std::ops::Deref;
3 use std::ops::Deref;
4 use std::path::Path;
4 use std::path::Path;
5
5
6 use byteorder::{BigEndian, ByteOrder};
6 use byteorder::{BigEndian, ByteOrder};
7 use flate2::read::ZlibDecoder;
7 use flate2::read::ZlibDecoder;
8 use micro_timer::timed;
8 use micro_timer::timed;
9 use sha1::{Digest, Sha1};
9 use sha1::{Digest, Sha1};
10 use zstd;
10 use zstd;
11
11
12 use super::index::Index;
12 use super::index::Index;
13 use super::node::{NodePrefix, NODE_BYTES_LENGTH, NULL_NODE};
13 use super::node::{NodePrefix, NODE_BYTES_LENGTH, NULL_NODE};
14 use super::nodemap;
14 use super::nodemap;
15 use super::nodemap::{NodeMap, NodeMapError};
15 use super::nodemap::{NodeMap, NodeMapError};
16 use super::nodemap_docket::NodeMapDocket;
16 use super::nodemap_docket::NodeMapDocket;
17 use super::patch;
17 use super::patch;
18 use crate::errors::HgError;
18 use crate::errors::HgError;
19 use crate::repo::Repo;
19 use crate::repo::Repo;
20 use crate::revlog::Revision;
20 use crate::revlog::Revision;
21 use crate::NULL_REVISION;
21
22
22 #[derive(derive_more::From)]
23 #[derive(derive_more::From)]
23 pub enum RevlogError {
24 pub enum RevlogError {
24 InvalidRevision,
25 InvalidRevision,
25 /// Working directory is not supported
26 /// Working directory is not supported
26 WDirUnsupported,
27 WDirUnsupported,
27 /// Found more than one entry whose ID match the requested prefix
28 /// Found more than one entry whose ID match the requested prefix
28 AmbiguousPrefix,
29 AmbiguousPrefix,
29 #[from]
30 #[from]
30 Other(HgError),
31 Other(HgError),
31 }
32 }
32
33
33 impl From<NodeMapError> for RevlogError {
34 impl From<NodeMapError> for RevlogError {
34 fn from(error: NodeMapError) -> Self {
35 fn from(error: NodeMapError) -> Self {
35 match error {
36 match error {
36 NodeMapError::MultipleResults => RevlogError::AmbiguousPrefix,
37 NodeMapError::MultipleResults => RevlogError::AmbiguousPrefix,
37 NodeMapError::RevisionNotInIndex(_) => RevlogError::corrupted(),
38 NodeMapError::RevisionNotInIndex(_) => RevlogError::corrupted(),
38 }
39 }
39 }
40 }
40 }
41 }
41
42
42 impl RevlogError {
43 impl RevlogError {
43 fn corrupted() -> Self {
44 fn corrupted() -> Self {
44 RevlogError::Other(HgError::corrupted("corrupted revlog"))
45 RevlogError::Other(HgError::corrupted("corrupted revlog"))
45 }
46 }
46 }
47 }
47
48
48 /// Read only implementation of revlog.
49 /// Read only implementation of revlog.
49 pub struct Revlog {
50 pub struct Revlog {
50 /// When index and data are not interleaved: bytes of the revlog index.
51 /// When index and data are not interleaved: bytes of the revlog index.
51 /// When index and data are interleaved: bytes of the revlog index and
52 /// When index and data are interleaved: bytes of the revlog index and
52 /// data.
53 /// data.
53 pub(crate) index: Index,
54 pub(crate) index: Index,
54 /// When index and data are not interleaved: bytes of the revlog data
55 /// When index and data are not interleaved: bytes of the revlog data
55 data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>>,
56 data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>>,
56 /// When present on disk: the persistent nodemap for this revlog
57 /// When present on disk: the persistent nodemap for this revlog
57 nodemap: Option<nodemap::NodeTree>,
58 nodemap: Option<nodemap::NodeTree>,
58 }
59 }
59
60
60 impl Revlog {
61 impl Revlog {
61 /// Open a revlog index file.
62 /// Open a revlog index file.
62 ///
63 ///
63 /// It will also open the associated data file if index and data are not
64 /// It will also open the associated data file if index and data are not
64 /// interleaved.
65 /// interleaved.
65 #[timed]
66 #[timed]
66 pub fn open(
67 pub fn open(
67 repo: &Repo,
68 repo: &Repo,
68 index_path: impl AsRef<Path>,
69 index_path: impl AsRef<Path>,
69 data_path: Option<&Path>,
70 data_path: Option<&Path>,
70 ) -> Result<Self, RevlogError> {
71 ) -> Result<Self, RevlogError> {
71 let index_path = index_path.as_ref();
72 let index_path = index_path.as_ref();
72 let index_mmap = repo.store_vfs().mmap_open(&index_path)?;
73 let index_mmap = repo.store_vfs().mmap_open(&index_path)?;
73
74
74 let version = get_version(&index_mmap);
75 let version = get_version(&index_mmap);
75 if version != 1 {
76 if version != 1 {
76 // A proper new version should have had a repo/store requirement.
77 // A proper new version should have had a repo/store requirement.
77 return Err(RevlogError::corrupted());
78 return Err(RevlogError::corrupted());
78 }
79 }
79
80
80 let index = Index::new(Box::new(index_mmap))?;
81 let index = Index::new(Box::new(index_mmap))?;
81
82
82 let default_data_path = index_path.with_extension("d");
83 let default_data_path = index_path.with_extension("d");
83
84
84 // type annotation required
85 // type annotation required
85 // won't recognize Mmap as Deref<Target = [u8]>
86 // won't recognize Mmap as Deref<Target = [u8]>
86 let data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>> =
87 let data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>> =
87 if index.is_inline() {
88 if index.is_inline() {
88 None
89 None
89 } else {
90 } else {
90 let data_path = data_path.unwrap_or(&default_data_path);
91 let data_path = data_path.unwrap_or(&default_data_path);
91 let data_mmap = repo.store_vfs().mmap_open(data_path)?;
92 let data_mmap = repo.store_vfs().mmap_open(data_path)?;
92 Some(Box::new(data_mmap))
93 Some(Box::new(data_mmap))
93 };
94 };
94
95
95 let nodemap = NodeMapDocket::read_from_file(repo, index_path)?.map(
96 let nodemap = NodeMapDocket::read_from_file(repo, index_path)?.map(
96 |(docket, data)| {
97 |(docket, data)| {
97 nodemap::NodeTree::load_bytes(
98 nodemap::NodeTree::load_bytes(
98 Box::new(data),
99 Box::new(data),
99 docket.data_length,
100 docket.data_length,
100 )
101 )
101 },
102 },
102 );
103 );
103
104
104 Ok(Revlog {
105 Ok(Revlog {
105 index,
106 index,
106 data_bytes,
107 data_bytes,
107 nodemap,
108 nodemap,
108 })
109 })
109 }
110 }
110
111
111 /// Return number of entries of the `Revlog`.
112 /// Return number of entries of the `Revlog`.
112 pub fn len(&self) -> usize {
113 pub fn len(&self) -> usize {
113 self.index.len()
114 self.index.len()
114 }
115 }
115
116
116 /// Returns `true` if the `Revlog` has zero `entries`.
117 /// Returns `true` if the `Revlog` has zero `entries`.
117 pub fn is_empty(&self) -> bool {
118 pub fn is_empty(&self) -> bool {
118 self.index.is_empty()
119 self.index.is_empty()
119 }
120 }
120
121
121 /// Return the full data associated to a node.
122 /// Return the full data associated to a node.
122 #[timed]
123 #[timed]
123 pub fn get_node_rev(
124 pub fn get_node_rev(
124 &self,
125 &self,
125 node: NodePrefix,
126 node: NodePrefix,
126 ) -> Result<Revision, RevlogError> {
127 ) -> Result<Revision, RevlogError> {
128 if node.is_prefix_of(&NULL_NODE) {
129 return Ok(NULL_REVISION);
130 }
131
127 if let Some(nodemap) = &self.nodemap {
132 if let Some(nodemap) = &self.nodemap {
128 return nodemap
133 return nodemap
129 .find_bin(&self.index, node)?
134 .find_bin(&self.index, node)?
130 .ok_or(RevlogError::InvalidRevision);
135 .ok_or(RevlogError::InvalidRevision);
131 }
136 }
132
137
133 // Fallback to linear scan when a persistent nodemap is not present.
138 // Fallback to linear scan when a persistent nodemap is not present.
134 // This happens when the persistent-nodemap experimental feature is not
139 // This happens when the persistent-nodemap experimental feature is not
135 // enabled, or for small revlogs.
140 // enabled, or for small revlogs.
136 //
141 //
137 // TODO: consider building a non-persistent nodemap in memory to
142 // TODO: consider building a non-persistent nodemap in memory to
138 // optimize these cases.
143 // optimize these cases.
139 let mut found_by_prefix = None;
144 let mut found_by_prefix = None;
140 for rev in (0..self.len() as Revision).rev() {
145 for rev in (0..self.len() as Revision).rev() {
141 let index_entry =
146 let index_entry =
142 self.index.get_entry(rev).ok_or(HgError::corrupted(
147 self.index.get_entry(rev).ok_or(HgError::corrupted(
143 "revlog references a revision not in the index",
148 "revlog references a revision not in the index",
144 ))?;
149 ))?;
145 if node == *index_entry.hash() {
150 if node == *index_entry.hash() {
146 return Ok(rev);
151 return Ok(rev);
147 }
152 }
148 if node.is_prefix_of(index_entry.hash()) {
153 if node.is_prefix_of(index_entry.hash()) {
149 if found_by_prefix.is_some() {
154 if found_by_prefix.is_some() {
150 return Err(RevlogError::AmbiguousPrefix);
155 return Err(RevlogError::AmbiguousPrefix);
151 }
156 }
152 found_by_prefix = Some(rev)
157 found_by_prefix = Some(rev)
153 }
158 }
154 }
159 }
155 found_by_prefix.ok_or(RevlogError::InvalidRevision)
160 found_by_prefix.ok_or(RevlogError::InvalidRevision)
156 }
161 }
157
162
158 /// Returns whether the given revision exists in this revlog.
163 /// Returns whether the given revision exists in this revlog.
159 pub fn has_rev(&self, rev: Revision) -> bool {
164 pub fn has_rev(&self, rev: Revision) -> bool {
160 self.index.get_entry(rev).is_some()
165 self.index.get_entry(rev).is_some()
161 }
166 }
162
167
163 /// Return the full data associated to a revision.
168 /// Return the full data associated to a revision.
164 ///
169 ///
165 /// All entries required to build the final data out of deltas will be
170 /// All entries required to build the final data out of deltas will be
166 /// retrieved as needed, and the deltas will be applied to the inital
171 /// retrieved as needed, and the deltas will be applied to the inital
167 /// snapshot to rebuild the final data.
172 /// snapshot to rebuild the final data.
168 #[timed]
173 #[timed]
169 pub fn get_rev_data(&self, rev: Revision) -> Result<Vec<u8>, RevlogError> {
174 pub fn get_rev_data(&self, rev: Revision) -> Result<Vec<u8>, RevlogError> {
170 // Todo return -> Cow
175 // Todo return -> Cow
171 let mut entry = self.get_entry(rev)?;
176 let mut entry = self.get_entry(rev)?;
172 let mut delta_chain = vec![];
177 let mut delta_chain = vec![];
173 while let Some(base_rev) = entry.base_rev {
178 while let Some(base_rev) = entry.base_rev {
174 delta_chain.push(entry);
179 delta_chain.push(entry);
175 entry = self
180 entry = self
176 .get_entry(base_rev)
181 .get_entry(base_rev)
177 .map_err(|_| RevlogError::corrupted())?;
182 .map_err(|_| RevlogError::corrupted())?;
178 }
183 }
179
184
180 // TODO do not look twice in the index
185 // TODO do not look twice in the index
181 let index_entry = self
186 let index_entry = self
182 .index
187 .index
183 .get_entry(rev)
188 .get_entry(rev)
184 .ok_or(RevlogError::InvalidRevision)?;
189 .ok_or(RevlogError::InvalidRevision)?;
185
190
186 let data: Vec<u8> = if delta_chain.is_empty() {
191 let data: Vec<u8> = if delta_chain.is_empty() {
187 entry.data()?.into()
192 entry.data()?.into()
188 } else {
193 } else {
189 Revlog::build_data_from_deltas(entry, &delta_chain)?
194 Revlog::build_data_from_deltas(entry, &delta_chain)?
190 };
195 };
191
196
192 if self.check_hash(
197 if self.check_hash(
193 index_entry.p1(),
198 index_entry.p1(),
194 index_entry.p2(),
199 index_entry.p2(),
195 index_entry.hash().as_bytes(),
200 index_entry.hash().as_bytes(),
196 &data,
201 &data,
197 ) {
202 ) {
198 Ok(data)
203 Ok(data)
199 } else {
204 } else {
200 Err(RevlogError::corrupted())
205 Err(RevlogError::corrupted())
201 }
206 }
202 }
207 }
203
208
204 /// Check the hash of some given data against the recorded hash.
209 /// Check the hash of some given data against the recorded hash.
205 pub fn check_hash(
210 pub fn check_hash(
206 &self,
211 &self,
207 p1: Revision,
212 p1: Revision,
208 p2: Revision,
213 p2: Revision,
209 expected: &[u8],
214 expected: &[u8],
210 data: &[u8],
215 data: &[u8],
211 ) -> bool {
216 ) -> bool {
212 let e1 = self.index.get_entry(p1);
217 let e1 = self.index.get_entry(p1);
213 let h1 = match e1 {
218 let h1 = match e1 {
214 Some(ref entry) => entry.hash(),
219 Some(ref entry) => entry.hash(),
215 None => &NULL_NODE,
220 None => &NULL_NODE,
216 };
221 };
217 let e2 = self.index.get_entry(p2);
222 let e2 = self.index.get_entry(p2);
218 let h2 = match e2 {
223 let h2 = match e2 {
219 Some(ref entry) => entry.hash(),
224 Some(ref entry) => entry.hash(),
220 None => &NULL_NODE,
225 None => &NULL_NODE,
221 };
226 };
222
227
223 &hash(data, h1.as_bytes(), h2.as_bytes()) == expected
228 &hash(data, h1.as_bytes(), h2.as_bytes()) == expected
224 }
229 }
225
230
226 /// Build the full data of a revision out its snapshot
231 /// Build the full data of a revision out its snapshot
227 /// and its deltas.
232 /// and its deltas.
228 #[timed]
233 #[timed]
229 fn build_data_from_deltas(
234 fn build_data_from_deltas(
230 snapshot: RevlogEntry,
235 snapshot: RevlogEntry,
231 deltas: &[RevlogEntry],
236 deltas: &[RevlogEntry],
232 ) -> Result<Vec<u8>, RevlogError> {
237 ) -> Result<Vec<u8>, RevlogError> {
233 let snapshot = snapshot.data()?;
238 let snapshot = snapshot.data()?;
234 let deltas = deltas
239 let deltas = deltas
235 .iter()
240 .iter()
236 .rev()
241 .rev()
237 .map(RevlogEntry::data)
242 .map(RevlogEntry::data)
238 .collect::<Result<Vec<Cow<'_, [u8]>>, RevlogError>>()?;
243 .collect::<Result<Vec<Cow<'_, [u8]>>, RevlogError>>()?;
239 let patches: Vec<_> =
244 let patches: Vec<_> =
240 deltas.iter().map(|d| patch::PatchList::new(d)).collect();
245 deltas.iter().map(|d| patch::PatchList::new(d)).collect();
241 let patch = patch::fold_patch_lists(&patches);
246 let patch = patch::fold_patch_lists(&patches);
242 Ok(patch.apply(&snapshot))
247 Ok(patch.apply(&snapshot))
243 }
248 }
244
249
245 /// Return the revlog data.
250 /// Return the revlog data.
246 fn data(&self) -> &[u8] {
251 fn data(&self) -> &[u8] {
247 match self.data_bytes {
252 match self.data_bytes {
248 Some(ref data_bytes) => &data_bytes,
253 Some(ref data_bytes) => &data_bytes,
249 None => panic!(
254 None => panic!(
250 "forgot to load the data or trying to access inline data"
255 "forgot to load the data or trying to access inline data"
251 ),
256 ),
252 }
257 }
253 }
258 }
254
259
255 /// Get an entry of the revlog.
260 /// Get an entry of the revlog.
256 fn get_entry(&self, rev: Revision) -> Result<RevlogEntry, RevlogError> {
261 fn get_entry(&self, rev: Revision) -> Result<RevlogEntry, RevlogError> {
257 let index_entry = self
262 let index_entry = self
258 .index
263 .index
259 .get_entry(rev)
264 .get_entry(rev)
260 .ok_or(RevlogError::InvalidRevision)?;
265 .ok_or(RevlogError::InvalidRevision)?;
261 let start = index_entry.offset();
266 let start = index_entry.offset();
262 let end = start + index_entry.compressed_len();
267 let end = start + index_entry.compressed_len();
263 let data = if self.index.is_inline() {
268 let data = if self.index.is_inline() {
264 self.index.data(start, end)
269 self.index.data(start, end)
265 } else {
270 } else {
266 &self.data()[start..end]
271 &self.data()[start..end]
267 };
272 };
268 let entry = RevlogEntry {
273 let entry = RevlogEntry {
269 rev,
274 rev,
270 bytes: data,
275 bytes: data,
271 compressed_len: index_entry.compressed_len(),
276 compressed_len: index_entry.compressed_len(),
272 uncompressed_len: index_entry.uncompressed_len(),
277 uncompressed_len: index_entry.uncompressed_len(),
273 base_rev: if index_entry.base_revision() == rev {
278 base_rev: if index_entry.base_revision() == rev {
274 None
279 None
275 } else {
280 } else {
276 Some(index_entry.base_revision())
281 Some(index_entry.base_revision())
277 },
282 },
278 };
283 };
279 Ok(entry)
284 Ok(entry)
280 }
285 }
281 }
286 }
282
287
283 /// The revlog entry's bytes and the necessary informations to extract
288 /// The revlog entry's bytes and the necessary informations to extract
284 /// the entry's data.
289 /// the entry's data.
285 #[derive(Debug)]
290 #[derive(Debug)]
286 pub struct RevlogEntry<'a> {
291 pub struct RevlogEntry<'a> {
287 rev: Revision,
292 rev: Revision,
288 bytes: &'a [u8],
293 bytes: &'a [u8],
289 compressed_len: usize,
294 compressed_len: usize,
290 uncompressed_len: usize,
295 uncompressed_len: usize,
291 base_rev: Option<Revision>,
296 base_rev: Option<Revision>,
292 }
297 }
293
298
294 impl<'a> RevlogEntry<'a> {
299 impl<'a> RevlogEntry<'a> {
295 /// Extract the data contained in the entry.
300 /// Extract the data contained in the entry.
296 pub fn data(&self) -> Result<Cow<'_, [u8]>, RevlogError> {
301 pub fn data(&self) -> Result<Cow<'_, [u8]>, RevlogError> {
297 if self.bytes.is_empty() {
302 if self.bytes.is_empty() {
298 return Ok(Cow::Borrowed(&[]));
303 return Ok(Cow::Borrowed(&[]));
299 }
304 }
300 match self.bytes[0] {
305 match self.bytes[0] {
301 // Revision data is the entirety of the entry, including this
306 // Revision data is the entirety of the entry, including this
302 // header.
307 // header.
303 b'\0' => Ok(Cow::Borrowed(self.bytes)),
308 b'\0' => Ok(Cow::Borrowed(self.bytes)),
304 // Raw revision data follows.
309 // Raw revision data follows.
305 b'u' => Ok(Cow::Borrowed(&self.bytes[1..])),
310 b'u' => Ok(Cow::Borrowed(&self.bytes[1..])),
306 // zlib (RFC 1950) data.
311 // zlib (RFC 1950) data.
307 b'x' => Ok(Cow::Owned(self.uncompressed_zlib_data()?)),
312 b'x' => Ok(Cow::Owned(self.uncompressed_zlib_data()?)),
308 // zstd data.
313 // zstd data.
309 b'\x28' => Ok(Cow::Owned(self.uncompressed_zstd_data()?)),
314 b'\x28' => Ok(Cow::Owned(self.uncompressed_zstd_data()?)),
310 // A proper new format should have had a repo/store requirement.
315 // A proper new format should have had a repo/store requirement.
311 _format_type => Err(RevlogError::corrupted()),
316 _format_type => Err(RevlogError::corrupted()),
312 }
317 }
313 }
318 }
314
319
315 fn uncompressed_zlib_data(&self) -> Result<Vec<u8>, RevlogError> {
320 fn uncompressed_zlib_data(&self) -> Result<Vec<u8>, RevlogError> {
316 let mut decoder = ZlibDecoder::new(self.bytes);
321 let mut decoder = ZlibDecoder::new(self.bytes);
317 if self.is_delta() {
322 if self.is_delta() {
318 let mut buf = Vec::with_capacity(self.compressed_len);
323 let mut buf = Vec::with_capacity(self.compressed_len);
319 decoder
324 decoder
320 .read_to_end(&mut buf)
325 .read_to_end(&mut buf)
321 .map_err(|_| RevlogError::corrupted())?;
326 .map_err(|_| RevlogError::corrupted())?;
322 Ok(buf)
327 Ok(buf)
323 } else {
328 } else {
324 let mut buf = vec![0; self.uncompressed_len];
329 let mut buf = vec![0; self.uncompressed_len];
325 decoder
330 decoder
326 .read_exact(&mut buf)
331 .read_exact(&mut buf)
327 .map_err(|_| RevlogError::corrupted())?;
332 .map_err(|_| RevlogError::corrupted())?;
328 Ok(buf)
333 Ok(buf)
329 }
334 }
330 }
335 }
331
336
332 fn uncompressed_zstd_data(&self) -> Result<Vec<u8>, RevlogError> {
337 fn uncompressed_zstd_data(&self) -> Result<Vec<u8>, RevlogError> {
333 if self.is_delta() {
338 if self.is_delta() {
334 let mut buf = Vec::with_capacity(self.compressed_len);
339 let mut buf = Vec::with_capacity(self.compressed_len);
335 zstd::stream::copy_decode(self.bytes, &mut buf)
340 zstd::stream::copy_decode(self.bytes, &mut buf)
336 .map_err(|_| RevlogError::corrupted())?;
341 .map_err(|_| RevlogError::corrupted())?;
337 Ok(buf)
342 Ok(buf)
338 } else {
343 } else {
339 let mut buf = vec![0; self.uncompressed_len];
344 let mut buf = vec![0; self.uncompressed_len];
340 let len = zstd::block::decompress_to_buffer(self.bytes, &mut buf)
345 let len = zstd::block::decompress_to_buffer(self.bytes, &mut buf)
341 .map_err(|_| RevlogError::corrupted())?;
346 .map_err(|_| RevlogError::corrupted())?;
342 if len != self.uncompressed_len {
347 if len != self.uncompressed_len {
343 Err(RevlogError::corrupted())
348 Err(RevlogError::corrupted())
344 } else {
349 } else {
345 Ok(buf)
350 Ok(buf)
346 }
351 }
347 }
352 }
348 }
353 }
349
354
350 /// Tell if the entry is a snapshot or a delta
355 /// Tell if the entry is a snapshot or a delta
351 /// (influences on decompression).
356 /// (influences on decompression).
352 fn is_delta(&self) -> bool {
357 fn is_delta(&self) -> bool {
353 self.base_rev.is_some()
358 self.base_rev.is_some()
354 }
359 }
355 }
360 }
356
361
357 /// Format version of the revlog.
362 /// Format version of the revlog.
358 pub fn get_version(index_bytes: &[u8]) -> u16 {
363 pub fn get_version(index_bytes: &[u8]) -> u16 {
359 BigEndian::read_u16(&index_bytes[2..=3])
364 BigEndian::read_u16(&index_bytes[2..=3])
360 }
365 }
361
366
362 /// Calculate the hash of a revision given its data and its parents.
367 /// Calculate the hash of a revision given its data and its parents.
363 fn hash(
368 fn hash(
364 data: &[u8],
369 data: &[u8],
365 p1_hash: &[u8],
370 p1_hash: &[u8],
366 p2_hash: &[u8],
371 p2_hash: &[u8],
367 ) -> [u8; NODE_BYTES_LENGTH] {
372 ) -> [u8; NODE_BYTES_LENGTH] {
368 let mut hasher = Sha1::new();
373 let mut hasher = Sha1::new();
369 let (a, b) = (p1_hash, p2_hash);
374 let (a, b) = (p1_hash, p2_hash);
370 if a > b {
375 if a > b {
371 hasher.update(b);
376 hasher.update(b);
372 hasher.update(a);
377 hasher.update(a);
373 } else {
378 } else {
374 hasher.update(a);
379 hasher.update(a);
375 hasher.update(b);
380 hasher.update(b);
376 }
381 }
377 hasher.update(data);
382 hasher.update(data);
378 *hasher.finalize().as_ref()
383 *hasher.finalize().as_ref()
379 }
384 }
380
385
381 #[cfg(test)]
386 #[cfg(test)]
382 mod tests {
387 mod tests {
383 use super::*;
388 use super::*;
384
389
385 use super::super::index::IndexEntryBuilder;
390 use super::super::index::IndexEntryBuilder;
386
391
387 #[test]
392 #[test]
388 fn version_test() {
393 fn version_test() {
389 let bytes = IndexEntryBuilder::new()
394 let bytes = IndexEntryBuilder::new()
390 .is_first(true)
395 .is_first(true)
391 .with_version(1)
396 .with_version(1)
392 .build();
397 .build();
393
398
394 assert_eq!(get_version(&bytes), 1)
399 assert_eq!(get_version(&bytes), 1)
395 }
400 }
396 }
401 }
@@ -1,61 +1,67 b''
1 //! The revset query language
1 //! The revset query language
2 //!
2 //!
3 //! <https://www.mercurial-scm.org/repo/hg/help/revsets>
3 //! <https://www.mercurial-scm.org/repo/hg/help/revsets>
4
4
5 use crate::errors::HgError;
5 use crate::errors::HgError;
6 use crate::repo::Repo;
6 use crate::repo::Repo;
7 use crate::revlog::revlog::{Revlog, RevlogError};
7 use crate::revlog::revlog::{Revlog, RevlogError};
8 use crate::revlog::NodePrefix;
8 use crate::revlog::NodePrefix;
9 use crate::revlog::{Revision, NULL_REVISION, WORKING_DIRECTORY_HEX};
9 use crate::revlog::{Revision, NULL_REVISION, WORKING_DIRECTORY_HEX};
10 use crate::Node;
10 use crate::Node;
11
11
12 /// Resolve a query string into a single revision.
12 /// Resolve a query string into a single revision.
13 ///
13 ///
14 /// Only some of the revset language is implemented yet.
14 /// Only some of the revset language is implemented yet.
15 pub fn resolve_single(
15 pub fn resolve_single(
16 input: &str,
16 input: &str,
17 repo: &Repo,
17 repo: &Repo,
18 ) -> Result<Revision, RevlogError> {
18 ) -> Result<Revision, RevlogError> {
19 let changelog = repo.changelog()?;
19 let changelog = repo.changelog()?;
20
20
21 match resolve_rev_number_or_hex_prefix(input, &changelog.revlog) {
21 match resolve_rev_number_or_hex_prefix(input, &changelog.revlog) {
22 Err(RevlogError::InvalidRevision) => {} // Try other syntax
22 Err(RevlogError::InvalidRevision) => {} // Try other syntax
23 result => return result,
23 result => return result,
24 }
24 }
25
25
26 if input == "null" {
26 if input == "null" {
27 return Ok(NULL_REVISION);
27 return Ok(NULL_REVISION);
28 }
28 }
29
29
30 // TODO: support for the rest of the language here.
30 // TODO: support for the rest of the language here.
31
31
32 Err(
32 Err(
33 HgError::unsupported(format!("cannot parse revset '{}'", input))
33 HgError::unsupported(format!("cannot parse revset '{}'", input))
34 .into(),
34 .into(),
35 )
35 )
36 }
36 }
37
37
38 /// Resolve the small subset of the language suitable for revlogs other than
38 /// Resolve the small subset of the language suitable for revlogs other than
39 /// the changelog, such as in `hg debugdata --manifest` CLI argument.
39 /// the changelog, such as in `hg debugdata --manifest` CLI argument.
40 ///
40 ///
41 /// * A non-negative decimal integer for a revision number, or
41 /// * A non-negative decimal integer for a revision number, or
42 /// * An hexadecimal string, for the unique node ID that starts with this
42 /// * An hexadecimal string, for the unique node ID that starts with this
43 /// prefix
43 /// prefix
44 pub fn resolve_rev_number_or_hex_prefix(
44 pub fn resolve_rev_number_or_hex_prefix(
45 input: &str,
45 input: &str,
46 revlog: &Revlog,
46 revlog: &Revlog,
47 ) -> Result<Revision, RevlogError> {
47 ) -> Result<Revision, RevlogError> {
48 // The Python equivalent of this is part of `revsymbol` in
49 // `mercurial/scmutil.py`
50
48 if let Ok(integer) = input.parse::<i32>() {
51 if let Ok(integer) = input.parse::<i32>() {
49 if integer >= 0 && revlog.has_rev(integer) {
52 if integer.to_string() == input
53 && integer >= 0
54 && revlog.has_rev(integer)
55 {
50 return Ok(integer);
56 return Ok(integer);
51 }
57 }
52 }
58 }
53 if let Ok(prefix) = NodePrefix::from_hex(input) {
59 if let Ok(prefix) = NodePrefix::from_hex(input) {
54 if prefix.is_prefix_of(&Node::from_hex(WORKING_DIRECTORY_HEX).unwrap())
60 if prefix.is_prefix_of(&Node::from_hex(WORKING_DIRECTORY_HEX).unwrap())
55 {
61 {
56 return Err(RevlogError::WDirUnsupported);
62 return Err(RevlogError::WDirUnsupported);
57 }
63 }
58 return revlog.get_node_rev(prefix);
64 return revlog.get_node_rev(prefix);
59 }
65 }
60 Err(RevlogError::InvalidRevision)
66 Err(RevlogError::InvalidRevision)
61 }
67 }
@@ -1,309 +1,312 b''
1 #require rhg
1 #require rhg
2
2
3 $ NO_FALLBACK="env RHG_ON_UNSUPPORTED=abort"
3 $ NO_FALLBACK="env RHG_ON_UNSUPPORTED=abort"
4
4
5 Unimplemented command
5 Unimplemented command
6 $ $NO_FALLBACK rhg unimplemented-command
6 $ $NO_FALLBACK rhg unimplemented-command
7 unsupported feature: error: Found argument 'unimplemented-command' which wasn't expected, or isn't valid in this context
7 unsupported feature: error: Found argument 'unimplemented-command' which wasn't expected, or isn't valid in this context
8
8
9 USAGE:
9 USAGE:
10 rhg [OPTIONS] <SUBCOMMAND>
10 rhg [OPTIONS] <SUBCOMMAND>
11
11
12 For more information try --help
12 For more information try --help
13
13
14 [252]
14 [252]
15 $ rhg unimplemented-command --config rhg.on-unsupported=abort-silent
15 $ rhg unimplemented-command --config rhg.on-unsupported=abort-silent
16 [252]
16 [252]
17
17
18 Finding root
18 Finding root
19 $ $NO_FALLBACK rhg root
19 $ $NO_FALLBACK rhg root
20 abort: no repository found in '$TESTTMP' (.hg not found)!
20 abort: no repository found in '$TESTTMP' (.hg not found)!
21 [255]
21 [255]
22
22
23 $ hg init repository
23 $ hg init repository
24 $ cd repository
24 $ cd repository
25 $ $NO_FALLBACK rhg root
25 $ $NO_FALLBACK rhg root
26 $TESTTMP/repository
26 $TESTTMP/repository
27
27
28 Reading and setting configuration
28 Reading and setting configuration
29 $ echo "[ui]" >> $HGRCPATH
29 $ echo "[ui]" >> $HGRCPATH
30 $ echo "username = user1" >> $HGRCPATH
30 $ echo "username = user1" >> $HGRCPATH
31 $ $NO_FALLBACK rhg config ui.username
31 $ $NO_FALLBACK rhg config ui.username
32 user1
32 user1
33 $ echo "[ui]" >> .hg/hgrc
33 $ echo "[ui]" >> .hg/hgrc
34 $ echo "username = user2" >> .hg/hgrc
34 $ echo "username = user2" >> .hg/hgrc
35 $ $NO_FALLBACK rhg config ui.username
35 $ $NO_FALLBACK rhg config ui.username
36 user2
36 user2
37 $ $NO_FALLBACK rhg --config ui.username=user3 config ui.username
37 $ $NO_FALLBACK rhg --config ui.username=user3 config ui.username
38 user3
38 user3
39
39
40 Unwritable file descriptor
40 Unwritable file descriptor
41 $ $NO_FALLBACK rhg root > /dev/full
41 $ $NO_FALLBACK rhg root > /dev/full
42 abort: No space left on device (os error 28)
42 abort: No space left on device (os error 28)
43 [255]
43 [255]
44
44
45 Deleted repository
45 Deleted repository
46 $ rm -rf `pwd`
46 $ rm -rf `pwd`
47 $ $NO_FALLBACK rhg root
47 $ $NO_FALLBACK rhg root
48 abort: error getting current working directory: $ENOENT$
48 abort: error getting current working directory: $ENOENT$
49 [255]
49 [255]
50
50
51 Listing tracked files
51 Listing tracked files
52 $ cd $TESTTMP
52 $ cd $TESTTMP
53 $ hg init repository
53 $ hg init repository
54 $ cd repository
54 $ cd repository
55 $ for i in 1 2 3; do
55 $ for i in 1 2 3; do
56 > echo $i >> file$i
56 > echo $i >> file$i
57 > hg add file$i
57 > hg add file$i
58 > done
58 > done
59 > hg commit -m "commit $i" -q
59 > hg commit -m "commit $i" -q
60
60
61 Listing tracked files from root
61 Listing tracked files from root
62 $ $NO_FALLBACK rhg files
62 $ $NO_FALLBACK rhg files
63 file1
63 file1
64 file2
64 file2
65 file3
65 file3
66
66
67 Listing tracked files from subdirectory
67 Listing tracked files from subdirectory
68 $ mkdir -p path/to/directory
68 $ mkdir -p path/to/directory
69 $ cd path/to/directory
69 $ cd path/to/directory
70 $ $NO_FALLBACK rhg files
70 $ $NO_FALLBACK rhg files
71 ../../../file1
71 ../../../file1
72 ../../../file2
72 ../../../file2
73 ../../../file3
73 ../../../file3
74
74
75 Listing tracked files through broken pipe
75 Listing tracked files through broken pipe
76 $ $NO_FALLBACK rhg files | head -n 1
76 $ $NO_FALLBACK rhg files | head -n 1
77 ../../../file1
77 ../../../file1
78
78
79 Debuging data in inline index
79 Debuging data in inline index
80 $ cd $TESTTMP
80 $ cd $TESTTMP
81 $ rm -rf repository
81 $ rm -rf repository
82 $ hg init repository
82 $ hg init repository
83 $ cd repository
83 $ cd repository
84 $ for i in 1 2 3 4 5 6; do
84 $ for i in 1 2 3 4 5 6; do
85 > echo $i >> file-$i
85 > echo $i >> file-$i
86 > hg add file-$i
86 > hg add file-$i
87 > hg commit -m "Commit $i" -q
87 > hg commit -m "Commit $i" -q
88 > done
88 > done
89 $ $NO_FALLBACK rhg debugdata -c 2
89 $ $NO_FALLBACK rhg debugdata -c 2
90 8d0267cb034247ebfa5ee58ce59e22e57a492297
90 8d0267cb034247ebfa5ee58ce59e22e57a492297
91 test
91 test
92 0 0
92 0 0
93 file-3
93 file-3
94
94
95 Commit 3 (no-eol)
95 Commit 3 (no-eol)
96 $ $NO_FALLBACK rhg debugdata -m 2
96 $ $NO_FALLBACK rhg debugdata -m 2
97 file-1\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
97 file-1\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
98 file-2\x005d9299349fc01ddd25d0070d149b124d8f10411e (esc)
98 file-2\x005d9299349fc01ddd25d0070d149b124d8f10411e (esc)
99 file-3\x002661d26c649684b482d10f91960cc3db683c38b4 (esc)
99 file-3\x002661d26c649684b482d10f91960cc3db683c38b4 (esc)
100
100
101 Debuging with full node id
101 Debuging with full node id
102 $ $NO_FALLBACK rhg debugdata -c `hg log -r 0 -T '{node}'`
102 $ $NO_FALLBACK rhg debugdata -c `hg log -r 0 -T '{node}'`
103 d1d1c679d3053e8926061b6f45ca52009f011e3f
103 d1d1c679d3053e8926061b6f45ca52009f011e3f
104 test
104 test
105 0 0
105 0 0
106 file-1
106 file-1
107
107
108 Commit 1 (no-eol)
108 Commit 1 (no-eol)
109
109
110 Specifying revisions by changeset ID
110 Specifying revisions by changeset ID
111 $ hg log -T '{node}\n'
111 $ hg log -T '{node}\n'
112 c6ad58c44207b6ff8a4fbbca7045a5edaa7e908b
112 c6ad58c44207b6ff8a4fbbca7045a5edaa7e908b
113 d654274993d0149eecc3cc03214f598320211900
113 d654274993d0149eecc3cc03214f598320211900
114 f646af7e96481d3a5470b695cf30ad8e3ab6c575
114 f646af7e96481d3a5470b695cf30ad8e3ab6c575
115 cf8b83f14ead62b374b6e91a0e9303b85dfd9ed7
115 cf8b83f14ead62b374b6e91a0e9303b85dfd9ed7
116 91c6f6e73e39318534dc415ea4e8a09c99cd74d6
116 91c6f6e73e39318534dc415ea4e8a09c99cd74d6
117 6ae9681c6d30389694d8701faf24b583cf3ccafe
117 6ae9681c6d30389694d8701faf24b583cf3ccafe
118 $ $NO_FALLBACK rhg files -r cf8b83
118 $ $NO_FALLBACK rhg files -r cf8b83
119 file-1
119 file-1
120 file-2
120 file-2
121 file-3
121 file-3
122 $ $NO_FALLBACK rhg cat -r cf8b83 file-2
122 $ $NO_FALLBACK rhg cat -r cf8b83 file-2
123 2
123 2
124 $ $NO_FALLBACK rhg cat -r c file-2
124 $ $NO_FALLBACK rhg cat -r c file-2
125 abort: ambiguous revision identifier: c
125 abort: ambiguous revision identifier: c
126 [255]
126 [255]
127 $ $NO_FALLBACK rhg cat -r d file-2
127 $ $NO_FALLBACK rhg cat -r d file-2
128 2
128 2
129 $ $NO_FALLBACK rhg cat -r 0000 file-2
130 abort: invalid revision identifier: 0000
131 [255]
129
132
130 Cat files
133 Cat files
131 $ cd $TESTTMP
134 $ cd $TESTTMP
132 $ rm -rf repository
135 $ rm -rf repository
133 $ hg init repository
136 $ hg init repository
134 $ cd repository
137 $ cd repository
135 $ echo "original content" > original
138 $ echo "original content" > original
136 $ hg add original
139 $ hg add original
137 $ hg commit -m "add original" original
140 $ hg commit -m "add original" original
138 $ $NO_FALLBACK rhg cat -r 0 original
141 $ $NO_FALLBACK rhg cat -r 0 original
139 original content
142 original content
140 Cat copied file should not display copy metadata
143 Cat copied file should not display copy metadata
141 $ hg copy original copy_of_original
144 $ hg copy original copy_of_original
142 $ hg commit -m "add copy of original"
145 $ hg commit -m "add copy of original"
143 $ $NO_FALLBACK rhg cat -r 1 copy_of_original
146 $ $NO_FALLBACK rhg cat -r 1 copy_of_original
144 original content
147 original content
145
148
146 Fallback to Python
149 Fallback to Python
147 $ $NO_FALLBACK rhg cat original
150 $ $NO_FALLBACK rhg cat original
148 unsupported feature: `rhg cat` without `--rev` / `-r`
151 unsupported feature: `rhg cat` without `--rev` / `-r`
149 [252]
152 [252]
150 $ rhg cat original
153 $ rhg cat original
151 original content
154 original content
152
155
153 $ FALLBACK_EXE="$RHG_FALLBACK_EXECUTABLE"
156 $ FALLBACK_EXE="$RHG_FALLBACK_EXECUTABLE"
154 $ unset RHG_FALLBACK_EXECUTABLE
157 $ unset RHG_FALLBACK_EXECUTABLE
155 $ rhg cat original
158 $ rhg cat original
156 abort: 'rhg.on-unsupported=fallback' without 'rhg.fallback-executable' set.
159 abort: 'rhg.on-unsupported=fallback' without 'rhg.fallback-executable' set.
157 [255]
160 [255]
158 $ RHG_FALLBACK_EXECUTABLE="$FALLBACK_EXE"
161 $ RHG_FALLBACK_EXECUTABLE="$FALLBACK_EXE"
159 $ export RHG_FALLBACK_EXECUTABLE
162 $ export RHG_FALLBACK_EXECUTABLE
160
163
161 $ rhg cat original --config rhg.fallback-executable=false
164 $ rhg cat original --config rhg.fallback-executable=false
162 [1]
165 [1]
163
166
164 $ rhg cat original --config rhg.fallback-executable=hg-non-existent
167 $ rhg cat original --config rhg.fallback-executable=hg-non-existent
165 tried to fall back to a 'hg-non-existent' sub-process but got error $ENOENT$
168 tried to fall back to a 'hg-non-existent' sub-process but got error $ENOENT$
166 unsupported feature: `rhg cat` without `--rev` / `-r`
169 unsupported feature: `rhg cat` without `--rev` / `-r`
167 [252]
170 [252]
168
171
169 $ rhg cat original --config rhg.fallback-executable=rhg
172 $ rhg cat original --config rhg.fallback-executable=rhg
170 Blocking recursive fallback. The 'rhg.fallback-executable = rhg' config points to `rhg` itself.
173 Blocking recursive fallback. The 'rhg.fallback-executable = rhg' config points to `rhg` itself.
171 unsupported feature: `rhg cat` without `--rev` / `-r`
174 unsupported feature: `rhg cat` without `--rev` / `-r`
172 [252]
175 [252]
173
176
174 Requirements
177 Requirements
175 $ $NO_FALLBACK rhg debugrequirements
178 $ $NO_FALLBACK rhg debugrequirements
176 dotencode
179 dotencode
177 fncache
180 fncache
178 generaldelta
181 generaldelta
179 persistent-nodemap
182 persistent-nodemap
180 revlog-compression-zstd (zstd !)
183 revlog-compression-zstd (zstd !)
181 revlogv1
184 revlogv1
182 sparserevlog
185 sparserevlog
183 store
186 store
184
187
185 $ echo indoor-pool >> .hg/requires
188 $ echo indoor-pool >> .hg/requires
186 $ $NO_FALLBACK rhg files
189 $ $NO_FALLBACK rhg files
187 unsupported feature: repository requires feature unknown to this Mercurial: indoor-pool
190 unsupported feature: repository requires feature unknown to this Mercurial: indoor-pool
188 [252]
191 [252]
189
192
190 $ $NO_FALLBACK rhg cat -r 1 copy_of_original
193 $ $NO_FALLBACK rhg cat -r 1 copy_of_original
191 unsupported feature: repository requires feature unknown to this Mercurial: indoor-pool
194 unsupported feature: repository requires feature unknown to this Mercurial: indoor-pool
192 [252]
195 [252]
193
196
194 $ $NO_FALLBACK rhg debugrequirements
197 $ $NO_FALLBACK rhg debugrequirements
195 unsupported feature: repository requires feature unknown to this Mercurial: indoor-pool
198 unsupported feature: repository requires feature unknown to this Mercurial: indoor-pool
196 [252]
199 [252]
197
200
198 $ echo -e '\xFF' >> .hg/requires
201 $ echo -e '\xFF' >> .hg/requires
199 $ $NO_FALLBACK rhg debugrequirements
202 $ $NO_FALLBACK rhg debugrequirements
200 abort: parse error in 'requires' file
203 abort: parse error in 'requires' file
201 [255]
204 [255]
202
205
203 Persistent nodemap
206 Persistent nodemap
204 $ cd $TESTTMP
207 $ cd $TESTTMP
205 $ rm -rf repository
208 $ rm -rf repository
206 $ hg --config format.use-persistent-nodemap=no init repository
209 $ hg --config format.use-persistent-nodemap=no init repository
207 $ cd repository
210 $ cd repository
208 $ $NO_FALLBACK rhg debugrequirements | grep nodemap
211 $ $NO_FALLBACK rhg debugrequirements | grep nodemap
209 [1]
212 [1]
210 $ hg debugbuilddag .+5000 --overwritten-file --config "storage.revlog.nodemap.mode=warn"
213 $ hg debugbuilddag .+5000 --overwritten-file --config "storage.revlog.nodemap.mode=warn"
211 $ hg id -r tip
214 $ hg id -r tip
212 c3ae8dec9fad tip
215 c3ae8dec9fad tip
213 $ ls .hg/store/00changelog*
216 $ ls .hg/store/00changelog*
214 .hg/store/00changelog.d
217 .hg/store/00changelog.d
215 .hg/store/00changelog.i
218 .hg/store/00changelog.i
216 $ $NO_FALLBACK rhg files -r c3ae8dec9fad
219 $ $NO_FALLBACK rhg files -r c3ae8dec9fad
217 of
220 of
218
221
219 $ cd $TESTTMP
222 $ cd $TESTTMP
220 $ rm -rf repository
223 $ rm -rf repository
221 $ hg --config format.use-persistent-nodemap=True init repository
224 $ hg --config format.use-persistent-nodemap=True init repository
222 $ cd repository
225 $ cd repository
223 $ $NO_FALLBACK rhg debugrequirements | grep nodemap
226 $ $NO_FALLBACK rhg debugrequirements | grep nodemap
224 persistent-nodemap
227 persistent-nodemap
225 $ hg debugbuilddag .+5000 --overwritten-file --config "storage.revlog.nodemap.mode=warn"
228 $ hg debugbuilddag .+5000 --overwritten-file --config "storage.revlog.nodemap.mode=warn"
226 $ hg id -r tip
229 $ hg id -r tip
227 c3ae8dec9fad tip
230 c3ae8dec9fad tip
228 $ ls .hg/store/00changelog*
231 $ ls .hg/store/00changelog*
229 .hg/store/00changelog-*.nd (glob)
232 .hg/store/00changelog-*.nd (glob)
230 .hg/store/00changelog.d
233 .hg/store/00changelog.d
231 .hg/store/00changelog.i
234 .hg/store/00changelog.i
232 .hg/store/00changelog.n
235 .hg/store/00changelog.n
233
236
234 Specifying revisions by changeset ID
237 Specifying revisions by changeset ID
235 $ $NO_FALLBACK rhg files -r c3ae8dec9fad
238 $ $NO_FALLBACK rhg files -r c3ae8dec9fad
236 of
239 of
237 $ $NO_FALLBACK rhg cat -r c3ae8dec9fad of
240 $ $NO_FALLBACK rhg cat -r c3ae8dec9fad of
238 r5000
241 r5000
239
242
240 Crate a shared repository
243 Crate a shared repository
241
244
242 $ echo "[extensions]" >> $HGRCPATH
245 $ echo "[extensions]" >> $HGRCPATH
243 $ echo "share = " >> $HGRCPATH
246 $ echo "share = " >> $HGRCPATH
244
247
245 $ cd $TESTTMP
248 $ cd $TESTTMP
246 $ hg init repo1
249 $ hg init repo1
247 $ echo a > repo1/a
250 $ echo a > repo1/a
248 $ hg -R repo1 commit -A -m'init'
251 $ hg -R repo1 commit -A -m'init'
249 adding a
252 adding a
250
253
251 $ hg share repo1 repo2
254 $ hg share repo1 repo2
252 updating working directory
255 updating working directory
253 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
256 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
254
257
255 And check that basic rhg commands work with sharing
258 And check that basic rhg commands work with sharing
256
259
257 $ $NO_FALLBACK rhg files -R repo2
260 $ $NO_FALLBACK rhg files -R repo2
258 repo2/a
261 repo2/a
259 $ $NO_FALLBACK rhg -R repo2 cat -r 0 repo2/a
262 $ $NO_FALLBACK rhg -R repo2 cat -r 0 repo2/a
260 a
263 a
261
264
262 Same with relative sharing
265 Same with relative sharing
263
266
264 $ hg share repo2 repo3 --relative
267 $ hg share repo2 repo3 --relative
265 updating working directory
268 updating working directory
266 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
269 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
267
270
268 $ $NO_FALLBACK rhg files -R repo3
271 $ $NO_FALLBACK rhg files -R repo3
269 repo3/a
272 repo3/a
270 $ $NO_FALLBACK rhg -R repo3 cat -r 0 repo3/a
273 $ $NO_FALLBACK rhg -R repo3 cat -r 0 repo3/a
271 a
274 a
272
275
273 Same with share-safe
276 Same with share-safe
274
277
275 $ echo "[format]" >> $HGRCPATH
278 $ echo "[format]" >> $HGRCPATH
276 $ echo "use-share-safe = True" >> $HGRCPATH
279 $ echo "use-share-safe = True" >> $HGRCPATH
277
280
278 $ cd $TESTTMP
281 $ cd $TESTTMP
279 $ hg init repo4
282 $ hg init repo4
280 $ cd repo4
283 $ cd repo4
281 $ echo a > a
284 $ echo a > a
282 $ hg commit -A -m'init'
285 $ hg commit -A -m'init'
283 adding a
286 adding a
284
287
285 $ cd ..
288 $ cd ..
286 $ hg share repo4 repo5
289 $ hg share repo4 repo5
287 updating working directory
290 updating working directory
288 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
291 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
289
292
290 And check that basic rhg commands work with sharing
293 And check that basic rhg commands work with sharing
291
294
292 $ cd repo5
295 $ cd repo5
293 $ $NO_FALLBACK rhg files
296 $ $NO_FALLBACK rhg files
294 a
297 a
295 $ $NO_FALLBACK rhg cat -r 0 a
298 $ $NO_FALLBACK rhg cat -r 0 a
296 a
299 a
297
300
298 The blackbox extension is supported
301 The blackbox extension is supported
299
302
300 $ echo "[extensions]" >> $HGRCPATH
303 $ echo "[extensions]" >> $HGRCPATH
301 $ echo "blackbox =" >> $HGRCPATH
304 $ echo "blackbox =" >> $HGRCPATH
302 $ echo "[blackbox]" >> $HGRCPATH
305 $ echo "[blackbox]" >> $HGRCPATH
303 $ echo "maxsize = 1" >> $HGRCPATH
306 $ echo "maxsize = 1" >> $HGRCPATH
304 $ $NO_FALLBACK rhg files > /dev/null
307 $ $NO_FALLBACK rhg files > /dev/null
305 $ cat .hg/blackbox.log
308 $ cat .hg/blackbox.log
306 ????/??/?? ??:??:??.??? * @d3873e73d99ef67873dac33fbcc66268d5d2b6f4 (*)> (rust) files exited 0 after 0.??? seconds (glob)
309 ????/??/?? ??:??:??.??? * @d3873e73d99ef67873dac33fbcc66268d5d2b6f4 (*)> (rust) files exited 0 after 0.??? seconds (glob)
307 $ cat .hg/blackbox.log.1
310 $ cat .hg/blackbox.log.1
308 ????/??/?? ??:??:??.??? * @d3873e73d99ef67873dac33fbcc66268d5d2b6f4 (*)> (rust) files (glob)
311 ????/??/?? ??:??:??.??? * @d3873e73d99ef67873dac33fbcc66268d5d2b6f4 (*)> (rust) files (glob)
309
312
General Comments 0
You need to be logged in to leave comments. Login now