##// END OF EJS Templates
branching: merge stable into default
Raphaël Gomès -
r50112:13dfad0f merge default
parent child Browse files
Show More
@@ -0,0 +1,18 b''
1 ---
2 name: Official Review
3 about: Submit a series for review
4 ---
5
6 /assign_reviewer @mercurial.review
7
8 Welcome to the Mercurial Merge Request creation process:
9
10 * Set a simple title for your MR,
11 * All important information should be contained in your changesets' content or description,
12 * You can add some workflow-relevant information here (eg: when this depends on another MR),
13 * If your changes are not ready for review yet, click `Start the title with Draft:` under the title.
14
15 More details here:
16
17 * https://www.mercurial-scm.org/wiki/ContributingChanges
18 * https://www.mercurial-scm.org/wiki/Heptapod
@@ -0,0 +1,23 b''
1 #!/bin/bash
2 #
3 # Make sure to patch mercurial to create the delta against nullrev
4 # if deltainfo is None:
5 #- deltainfo = self._fullsnapshotinfo(fh, revinfo, target_rev)
6 #+ deltainfo = self._builddeltainfo(revinfo, nullrev, fh)
7
8 cd "`dirname \"$0\"`"
9 export HGRCPATH=
10 export HGMODULEPOLICY=py
11
12 rm -rf nullrev-diff
13 ../../hg init nullrev-diff --config format.revlog-compression=zlib
14 cd nullrev-diff
15 echo hi > a
16 ../../../hg commit -Am root-B
17 ../../../hg debugdeltachain a
18 rm -rf .hg/cache/ .hg/wcache/
19 cd ..
20
21 tar cf test-revlog-diff-relative-to-nullrev.tar nullrev-diff
22
23 rm -rf nullrev-diff
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
@@ -76,7 +76,7 b" if __name__ == '__main__':"
76 # specified. When running as test-check-py3-compat.t, we technically
76 # specified. When running as test-check-py3-compat.t, we technically
77 # would import the correct paths, but it's cleaner to have both cases
77 # would import the correct paths, but it's cleaner to have both cases
78 # use the same import logic.
78 # use the same import logic.
79 sys.path.insert(0, '.')
79 sys.path.insert(0, os.getcwd())
80
80
81 for f in sys.argv[1:]:
81 for f in sys.argv[1:]:
82 with warnings.catch_warnings(record=True) as warns:
82 with warnings.catch_warnings(record=True) as warns:
@@ -9,7 +9,7 b' import sys'
9
9
10 # import from the live mercurial repo
10 # import from the live mercurial repo
11 os.environ['HGMODULEPOLICY'] = 'py'
11 os.environ['HGMODULEPOLICY'] = 'py'
12 sys.path.insert(0, "..")
12 sys.path.insert(0, os.path.abspath(".."))
13 from mercurial import demandimport
13 from mercurial import demandimport
14
14
15 demandimport.enable()
15 demandimport.enable()
@@ -68,6 +68,52 b' def ismainthread():'
68 return threading.current_thread() == threading.main_thread()
68 return threading.current_thread() == threading.main_thread()
69
69
70
70
71 class _blockingreader(object):
72 def __init__(self, wrapped):
73 self._wrapped = wrapped
74
75 # Do NOT implement readinto() by making it delegate to
76 # _wrapped.readinto(), since that is unbuffered. The unpickler is fine
77 # with just read() and readline(), so we don't need to implement it.
78
79 if (3, 8, 0) <= sys.version_info[:3] < (3, 8, 2):
80
81 # This is required for python 3.8, prior to 3.8.2. See issue6444.
82 def readinto(self, b):
83 pos = 0
84 size = len(b)
85
86 while pos < size:
87 ret = self._wrapped.readinto(b[pos:])
88 if not ret:
89 break
90 pos += ret
91
92 return pos
93
94 def readline(self):
95 return self._wrapped.readline()
96
97 # issue multiple reads until size is fulfilled
98 def read(self, size=-1):
99 if size < 0:
100 return self._wrapped.readall()
101
102 buf = bytearray(size)
103 view = memoryview(buf)
104 pos = 0
105
106 while pos < size:
107 ret = self._wrapped.readinto(view[pos:])
108 if not ret:
109 break
110 pos += ret
111
112 del view
113 del buf[pos:]
114 return bytes(buf)
115
116
71 class _blockingreader:
117 class _blockingreader:
72 def __init__(self, wrapped):
118 def __init__(self, wrapped):
73 self._wrapped = wrapped
119 self._wrapped = wrapped
@@ -456,29 +456,38 b' impl Repo {'
456 let data_filename = format!("dirstate.{}", uuid);
456 let data_filename = format!("dirstate.{}", uuid);
457 let data_filename = self.hg_vfs().join(data_filename);
457 let data_filename = self.hg_vfs().join(data_filename);
458 let mut options = std::fs::OpenOptions::new();
458 let mut options = std::fs::OpenOptions::new();
459 if append {
459 options.write(true);
460 options.append(true);
460
461 } else {
461 // Why are we not using the O_APPEND flag when appending?
462 options.write(true).create_new(true);
462 //
463 // - O_APPEND makes it trickier to deal with garbage at the end of
464 // the file, left by a previous uncommitted transaction. By
465 // starting the write at [old_data_size] we make sure we erase
466 // all such garbage.
467 //
468 // - O_APPEND requires to special-case 0-byte writes, whereas we
469 // don't need that.
470 //
471 // - Some OSes have bugs in implementation O_APPEND:
472 // revlog.py talks about a Solaris bug, but we also saw some ZFS
473 // bug: https://github.com/openzfs/zfs/pull/3124,
474 // https://github.com/openzfs/zfs/issues/13370
475 //
476 if !append {
477 options.create_new(true);
463 }
478 }
479
464 let data_size = (|| {
480 let data_size = (|| {
465 // TODO: loop and try another random ID if !append and this
481 // TODO: loop and try another random ID if !append and this
466 // returns `ErrorKind::AlreadyExists`? Collision chance of two
482 // returns `ErrorKind::AlreadyExists`? Collision chance of two
467 // random IDs is one in 2**32
483 // random IDs is one in 2**32
468 let mut file = options.open(&data_filename)?;
484 let mut file = options.open(&data_filename)?;
469 if data.is_empty() {
485 if append {
470 // If we're not appending anything, the data size is the
486 file.seek(SeekFrom::Start(old_data_size as u64))?;
471 // same as in the previous docket. It is *not* the file
472 // length, since it could have garbage at the end.
473 // We don't have to worry about it when we do have data
474 // to append since we rewrite the root node in this case.
475 Ok(old_data_size as u64)
476 } else {
477 file.write_all(&data)?;
478 file.flush()?;
479 // TODO: use https://doc.rust-lang.org/std/io/trait.Seek.html#method.stream_position when we require Rust 1.51+
480 file.seek(SeekFrom::Current(0))
481 }
487 }
488 file.write_all(&data)?;
489 file.flush()?;
490 file.seek(SeekFrom::Current(0))
482 })()
491 })()
483 .when_writing_file(&data_filename)?;
492 .when_writing_file(&data_filename)?;
484
493
@@ -32,6 +32,8 b' const REVIDX_KNOWN_FLAGS: u16 = REVISION'
32 | REVISION_FLAG_EXTSTORED
32 | REVISION_FLAG_EXTSTORED
33 | REVISION_FLAG_HASCOPIESINFO;
33 | REVISION_FLAG_HASCOPIESINFO;
34
34
35 const NULL_REVLOG_ENTRY_FLAGS: u16 = 0;
36
35 #[derive(Debug, derive_more::From)]
37 #[derive(Debug, derive_more::From)]
36 pub enum RevlogError {
38 pub enum RevlogError {
37 InvalidRevision,
39 InvalidRevision,
@@ -265,11 +267,29 b' impl Revlog {'
265 }
267 }
266 }
268 }
267
269
270 pub fn make_null_entry(&self) -> RevlogEntry {
271 RevlogEntry {
272 revlog: self,
273 rev: NULL_REVISION,
274 bytes: b"",
275 compressed_len: 0,
276 uncompressed_len: 0,
277 base_rev_or_base_of_delta_chain: None,
278 p1: NULL_REVISION,
279 p2: NULL_REVISION,
280 flags: NULL_REVLOG_ENTRY_FLAGS,
281 hash: NULL_NODE,
282 }
283 }
284
268 /// Get an entry of the revlog.
285 /// Get an entry of the revlog.
269 pub fn get_entry(
286 pub fn get_entry(
270 &self,
287 &self,
271 rev: Revision,
288 rev: Revision,
272 ) -> Result<RevlogEntry, RevlogError> {
289 ) -> Result<RevlogEntry, RevlogError> {
290 if rev == NULL_REVISION {
291 return Ok(self.make_null_entry());
292 }
273 let index_entry = self
293 let index_entry = self
274 .index
294 .index
275 .get_entry(rev)
295 .get_entry(rev)
@@ -2505,29 +2505,33 b' class TestSuite(unittest.TestSuite):'
2505 done = queue.Queue()
2505 done = queue.Queue()
2506 running = 0
2506 running = 0
2507
2507
2508 channels_lock = threading.Lock()
2508 channels = [""] * self._jobs
2509 channels = [""] * self._jobs
2509
2510
2510 def job(test, result):
2511 def job(test, result):
2511 for n, v in enumerate(channels):
2512 with channels_lock:
2512 if not v:
2513 for n, v in enumerate(channels):
2513 channel = n
2514 if not v:
2514 break
2515 channel = n
2515 else:
2516 break
2516 raise ValueError('Could not find output channel')
2517 else:
2517 channels[channel] = "=" + test.name[5:].split(".")[0]
2518 raise ValueError('Could not find output channel')
2519 channels[channel] = "=" + test.name[5:].split(".")[0]
2520
2521 r = None
2518 try:
2522 try:
2519 test(result)
2523 test(result)
2520 done.put(None)
2521 except KeyboardInterrupt:
2524 except KeyboardInterrupt:
2522 pass
2525 pass
2523 except: # re-raises
2526 except: # re-raises
2524 done.put(('!', test, 'run-test raised an error, see traceback'))
2527 r = ('!', test, 'run-test raised an error, see traceback')
2525 raise
2528 raise
2526 finally:
2529 finally:
2527 try:
2530 try:
2528 channels[channel] = ''
2531 channels[channel] = ''
2529 except IndexError:
2532 except IndexError:
2530 pass
2533 pass
2534 done.put(r)
2531
2535
2532 def stat():
2536 def stat():
2533 count = 0
2537 count = 0
@@ -131,6 +131,10 b' valid.'
131 > hg debugstate --docket | grep uuid | sed 's/.*uuid: \(.*\)/\1/'
131 > hg debugstate --docket | grep uuid | sed 's/.*uuid: \(.*\)/\1/'
132 > }
132 > }
133
133
134 $ find_dirstate_data_size () {
135 > hg debugstate --docket | grep 'size of dirstate data' | sed 's/.*size of dirstate data: \(.*\)/\1/'
136 > }
137
134 $ dirstate_uuid_has_not_changed () {
138 $ dirstate_uuid_has_not_changed () {
135 > # Non-Rust always rewrites the whole dirstate
139 > # Non-Rust always rewrites the whole dirstate
136 > if [ $# -eq 1 ] || ([ -n "$HGMODULEPOLICY" ] && [ -z "${HGMODULEPOLICY##*rust*}" ]) || [ -n "$RHG_INSTALLED_AS_HG" ]; then
140 > if [ $# -eq 1 ] || ([ -n "$HGMODULEPOLICY" ] && [ -z "${HGMODULEPOLICY##*rust*}" ]) || [ -n "$RHG_INSTALLED_AS_HG" ]; then
@@ -161,13 +165,19 b' Nothing changes here'
161
165
162 Trigger an append with a small change
166 Trigger an append with a small change
163
167
164 $ echo "modified" > dir2/f
168 $ current_data_size=$(find_dirstate_data_size)
169 $ rm dir2/f
165 $ hg st
170 $ hg st
166 M dir2/f
171 ! dir2/f
167 $ dirstate_data_files | wc -l
172 $ dirstate_data_files | wc -l
168 *1 (re)
173 *1 (re)
169 $ dirstate_uuid_has_not_changed
174 $ dirstate_uuid_has_not_changed
170 not testing because using Python implementation (no-rust no-rhg !)
175 not testing because using Python implementation (no-rust no-rhg !)
176 $ new_data_size=$(find_dirstate_data_size)
177 $ [ "$current_data_size" -eq "$new_data_size" ]; echo $?
178 0 (no-rust no-rhg !)
179 1 (rust !)
180 1 (no-rust rhg !)
171
181
172 Unused bytes counter is non-0 when appending
182 Unused bytes counter is non-0 when appending
173 $ touch file
183 $ touch file
@@ -176,8 +186,8 b' Unused bytes counter is non-0 when appen'
176
186
177 Trigger a rust/rhg run which updates the unused bytes value
187 Trigger a rust/rhg run which updates the unused bytes value
178 $ hg st
188 $ hg st
179 M dir2/f
180 A file
189 A file
190 ! dir2/f
181 $ dirstate_data_files | wc -l
191 $ dirstate_data_files | wc -l
182 *1 (re)
192 *1 (re)
183 $ dirstate_uuid_has_not_changed
193 $ dirstate_uuid_has_not_changed
@@ -32,6 +32,7 b' Unknown version is rejected'
32
32
33 Test for CVE-2016-3630
33 Test for CVE-2016-3630
34
34
35 $ mkdir test2; cd test2
35 $ hg init
36 $ hg init
36
37
37 >>> import codecs
38 >>> import codecs
@@ -52,3 +53,31 b' Test for CVE-2016-3630'
52 >>> rl = revlog.revlog(tvfs, target=(KIND_OTHER, b'test'), radix=b'a')
53 >>> rl = revlog.revlog(tvfs, target=(KIND_OTHER, b'test'), radix=b'a')
53 >>> rl.revision(1)
54 >>> rl.revision(1)
54 mpatchError(*'patch cannot be decoded'*) (glob)
55 mpatchError(*'patch cannot be decoded'*) (glob)
56
57 $ cd ..
58
59
60 Regression test for support for the old repos with strange diff encoding.
61 Apparently it used to be possible (maybe it's still possible, but we don't know how)
62 to create commits whose diffs are encoded relative to a nullrev.
63 This test checks that a repo with that encoding can still be read.
64
65 This is what we did to produce the repo in test-revlog-diff-relative-to-nullrev.tar:
66
67 - tweak the code in mercurial/revlogutils/deltas.py to produce such "trivial" deltas:
68 > if deltainfo is None:
69 > - deltainfo = self._fullsnapshotinfo(fh, revinfo, target_rev)
70 > + deltainfo = self._builddeltainfo(revinfo, nullrev, fh)
71 - hg init
72 - echo hi > a
73 - hg commit -Am_
74 - remove some cache files
75
76 $ tar --force-local -xf "$TESTDIR"/bundles/test-revlog-diff-relative-to-nullrev.tar
77 $ cd nullrev-diff
78 $ hg debugdeltachain a
79 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio readsize largestblk rddensity srchunks
80 0 1 2 -1 p1 15 3 15 5.00000 15 0 0.00000 15 15 1.00000 1
81 $ hg cat --config rhg.cat=true -r 0 a
82 hi
83 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now