Merge lp:~mbp/bzr/integration into lp:bzr

Proposed by Martin Pool
Status: Merged
Approved by: Martin Pool
Approved revision: no longer in the source branch.
Merged at revision: 6047
Proposed branch: lp:~mbp/bzr/integration
Merge into: lp:bzr
Diff against target: 643 lines (+314/-35)
18 files modified
bzrlib/config.py (+12/-0)
bzrlib/dirstate.py (+38/-26)
bzrlib/errors.py (+13/-0)
bzrlib/help_topics/en/configuration.txt (+14/-0)
bzrlib/index.py (+5/-0)
bzrlib/osutils.py (+11/-0)
bzrlib/repofmt/pack_repo.py (+14/-4)
bzrlib/tests/__init__.py (+23/-0)
bzrlib/tests/test_http.py (+66/-0)
bzrlib/tests/test_selftest.py (+12/-0)
bzrlib/tests/test_transport.py (+19/-0)
bzrlib/transport/__init__.py (+25/-1)
bzrlib/transport/http/__init__.py (+1/-1)
bzrlib/transport/http/response.py (+8/-1)
bzrlib/transport/local.py (+1/-2)
doc/developers/testing.txt (+17/-0)
doc/en/release-notes/bzr-2.3.txt (+3/-0)
doc/en/release-notes/bzr-2.4.txt (+32/-0)
To merge this branch: bzr merge lp:~mbp/bzr/integration
Reviewer Review Type Date Requested Status
bzr-core Pending
Review via email: mp+70097@code.launchpad.net

Commit message

merge 2.3 and 2.4 to trunk

Description of the change

merge up 2.3 and 2.4 into trunk

To post a comment you must log in.
Revision history for this message
Martin Pool (mbp) wrote :

sent to pqm by email

lp:~mbp/bzr/integration updated
6047. By Canonical.com Patch Queue Manager <email address hidden>

(mbp) merge 2.3 and 2.4 to trunk (Martin Pool)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bzrlib/config.py'
2--- bzrlib/config.py 2011-07-23 16:33:38 +0000
3+++ bzrlib/config.py 2011-08-02 01:26:10 +0000
4@@ -2291,6 +2291,15 @@
5 'editor', Option('editor'),
6 help='The command called to launch an editor to enter a message.')
7
8+option_registry.register(
9+ 'dirstate.fdatasync', Option('dirstate.fdatasync', default=True),
10+ help='Flush dirstate changes onto physical disk?')
11+
12+option_registry.register(
13+ 'repository.fdatasync',
14+ Option('repository.fdatasync', default=True),
15+ help='Flush repository changes onto physical disk?')
16+
17
18 class Section(object):
19 """A section defines a dict of option name => value.
20@@ -2821,6 +2830,9 @@
21 class LocationStack(_CompatibleStack):
22
23 def __init__(self, location):
24+ """Make a new stack for a location and global configuration.
25+
26+ :param location: A URL prefix to """
27 lstore = LocationStore()
28 matcher = LocationMatcher(lstore, location)
29 gstore = GlobalStore()
30
31=== modified file 'bzrlib/dirstate.py'
32--- bzrlib/dirstate.py 2011-05-19 18:20:37 +0000
33+++ bzrlib/dirstate.py 2011-08-02 01:26:10 +0000
34@@ -232,6 +232,7 @@
35
36 from bzrlib import (
37 cache_utf8,
38+ config,
39 debug,
40 errors,
41 inventory,
42@@ -239,6 +240,7 @@
43 osutils,
44 static_tuple,
45 trace,
46+ urlutils,
47 )
48
49
50@@ -448,6 +450,8 @@
51 self._known_hash_changes = set()
52 # How many hash changed entries can we have without saving
53 self._worth_saving_limit = worth_saving_limit
54+ self._config_stack = config.LocationStack(urlutils.local_path_to_url(
55+ path))
56
57 def __repr__(self):
58 return "%s(%r)" % \
59@@ -2508,33 +2512,41 @@
60 # IN_MEMORY_HASH_MODIFIED, we should only fail quietly if we fail
61 # to save an IN_MEMORY_HASH_MODIFIED, and fail *noisily* if we
62 # fail to save IN_MEMORY_MODIFIED
63- if self._worth_saving():
64- grabbed_write_lock = False
65- if self._lock_state != 'w':
66- grabbed_write_lock, new_lock = self._lock_token.temporary_write_lock()
67- # Switch over to the new lock, as the old one may be closed.
68+ if not self._worth_saving():
69+ return
70+
71+ grabbed_write_lock = False
72+ if self._lock_state != 'w':
73+ grabbed_write_lock, new_lock = self._lock_token.temporary_write_lock()
74+ # Switch over to the new lock, as the old one may be closed.
75+ # TODO: jam 20070315 We should validate the disk file has
76+ # not changed contents, since temporary_write_lock may
77+ # not be an atomic operation.
78+ self._lock_token = new_lock
79+ self._state_file = new_lock.f
80+ if not grabbed_write_lock:
81+ # We couldn't grab a write lock, so we switch back to a read one
82+ return
83+ try:
84+ lines = self.get_lines()
85+ self._state_file.seek(0)
86+ self._state_file.writelines(lines)
87+ self._state_file.truncate()
88+ self._state_file.flush()
89+ self._maybe_fdatasync()
90+ self._mark_unmodified()
91+ finally:
92+ if grabbed_write_lock:
93+ self._lock_token = self._lock_token.restore_read_lock()
94+ self._state_file = self._lock_token.f
95 # TODO: jam 20070315 We should validate the disk file has
96- # not changed contents. Since temporary_write_lock may
97- # not be an atomic operation.
98- self._lock_token = new_lock
99- self._state_file = new_lock.f
100- if not grabbed_write_lock:
101- # We couldn't grab a write lock, so we switch back to a read one
102- return
103- try:
104- lines = self.get_lines()
105- self._state_file.seek(0)
106- self._state_file.writelines(lines)
107- self._state_file.truncate()
108- self._state_file.flush()
109- self._mark_unmodified()
110- finally:
111- if grabbed_write_lock:
112- self._lock_token = self._lock_token.restore_read_lock()
113- self._state_file = self._lock_token.f
114- # TODO: jam 20070315 We should validate the disk file has
115- # not changed contents. Since restore_read_lock may
116- # not be an atomic operation.
117+ # not changed contents. Since restore_read_lock may
118+ # not be an atomic operation.
119+
120+ def _maybe_fdatasync(self):
121+ """Flush to disk if possible and if not configured off."""
122+ if self._config_stack.get('dirstate.fdatasync'):
123+ osutils.fdatasync(self._state_file.fileno())
124
125 def _worth_saving(self):
126 """Is it worth saving the dirstate or not?"""
127
128=== modified file 'bzrlib/errors.py'
129--- bzrlib/errors.py 2011-07-16 20:06:11 +0000
130+++ bzrlib/errors.py 2011-08-02 01:26:10 +0000
131@@ -1737,6 +1737,19 @@
132 InvalidHttpResponse.__init__(self, path, msg)
133
134
135+class HttpBoundaryMissing(InvalidHttpResponse):
136+ """A multipart response ends with no boundary marker.
137+
138+ This is a special case caused by buggy proxies, described in
139+ <https://bugs.launchpad.net/bzr/+bug/198646>.
140+ """
141+
142+ _fmt = "HTTP MIME Boundary missing for %(path)s: %(msg)s"
143+
144+ def __init__(self, path, msg):
145+ InvalidHttpResponse.__init__(self, path, msg)
146+
147+
148 class InvalidHttpContentType(InvalidHttpResponse):
149
150 _fmt = 'Invalid http Content-type "%(ctype)s" for %(path)s: %(msg)s'
151
152=== modified file 'bzrlib/help_topics/en/configuration.txt'
153--- bzrlib/help_topics/en/configuration.txt 2011-07-11 10:53:46 +0000
154+++ bzrlib/help_topics/en/configuration.txt 2011-08-02 01:26:10 +0000
155@@ -415,6 +415,13 @@
156 committed revisions only when the branch requires them. ``never`` will refuse
157 to sign newly committed revisions, even if the branch requires signatures.
158
159+dirstate.fdatasync
160+~~~~~~~~~~~~~~~~~~
161+
162+If true (default), working tree metadata changes are flushed through the
163+OS buffers to physical disk. This is somewhat slower, but means data
164+should not be lost if the machine crashes. See also repository.fdatasync.
165+
166 gpg_signing_key
167 ~~~~~~~~~~~
168
169@@ -505,6 +512,13 @@
170 :mapi: Use your preferred e-mail client on Windows.
171 :xdg-email: Use xdg-email to run your preferred mail program
172
173+repository.fdatasync
174+~~~~~~~~~~~~~~~~~~~~
175+
176+If true (default), repository changes are flushed through the OS buffers
177+to physical disk. This is somewhat slower, but means data should not be
178+lost if the machine crashes. See also dirstate.fdatasync.
179+
180 submit_branch
181 ~~~~~~~~~~~~~
182
183
184=== modified file 'bzrlib/index.py'
185--- bzrlib/index.py 2011-05-19 09:32:38 +0000
186+++ bzrlib/index.py 2011-08-02 01:26:10 +0000
187@@ -245,6 +245,11 @@
188 """
189
190 def finish(self):
191+ """Finish the index.
192+
193+ :returns: cStringIO holding the full context of the index as it
194+ should be written to disk.
195+ """
196 lines = [_SIGNATURE]
197 lines.append(_OPTION_NODE_REFS + str(self.reference_lists) + '\n')
198 lines.append(_OPTION_KEY_ELEMENTS + str(self._key_length) + '\n')
199
200=== modified file 'bzrlib/osutils.py'
201--- bzrlib/osutils.py 2011-06-16 18:34:26 +0000
202+++ bzrlib/osutils.py 2011-08-02 01:26:10 +0000
203@@ -2487,3 +2487,14 @@
204 is_local_pid_dead = win32utils.is_local_pid_dead
205 else:
206 is_local_pid_dead = _posix_is_local_pid_dead
207+
208+
209+def fdatasync(fileno):
210+ """Flush file contents to disk if possible.
211+
212+ :param fileno: Integer OS file handle.
213+ :raises TransportNotPossible: If flushing to disk is not possible.
214+ """
215+ fn = getattr(os, 'fdatasync', getattr(os, 'fsync', None))
216+ if fn is not None:
217+ fn(fileno)
218
219=== modified file 'bzrlib/repofmt/pack_repo.py'
220--- bzrlib/repofmt/pack_repo.py 2011-05-27 12:01:22 +0000
221+++ bzrlib/repofmt/pack_repo.py 2011-08-02 01:26:10 +0000
222@@ -25,6 +25,7 @@
223 from bzrlib import (
224 chk_map,
225 cleanup,
226+ config,
227 debug,
228 graph,
229 osutils,
230@@ -478,7 +479,8 @@
231 # visible is smaller. On the other hand none will be seen until
232 # they're in the names list.
233 self.index_sizes = [None, None, None, None]
234- self._write_index('revision', self.revision_index, 'revision', suspend)
235+ self._write_index('revision', self.revision_index, 'revision',
236+ suspend)
237 self._write_index('inventory', self.inventory_index, 'inventory',
238 suspend)
239 self._write_index('text', self.text_index, 'file texts', suspend)
240@@ -488,7 +490,8 @@
241 self.index_sizes.append(None)
242 self._write_index('chk', self.chk_index,
243 'content hash bytes', suspend)
244- self.write_stream.close()
245+ self.write_stream.close(
246+ want_fdatasync=self._pack_collection.config_stack.get('repository.fdatasync'))
247 # Note that this will clobber an existing pack with the same name,
248 # without checking for hash collisions. While this is undesirable this
249 # is something that can be rectified in a subsequent release. One way
250@@ -537,8 +540,14 @@
251 transport = self.upload_transport
252 else:
253 transport = self.index_transport
254- self.index_sizes[self.index_offset(index_type)] = transport.put_file(
255- index_name, index.finish(), mode=self._file_mode)
256+ index_tempfile = index.finish()
257+ index_bytes = index_tempfile.read()
258+ write_stream = transport.open_write_stream(index_name,
259+ mode=self._file_mode)
260+ write_stream.write(index_bytes)
261+ write_stream.close(
262+ want_fdatasync=self._pack_collection.config_stack.get('repository.fdatasync'))
263+ self.index_sizes[self.index_offset(index_type)] = len(index_bytes)
264 if 'pack' in debug.debug_flags:
265 # XXX: size might be interesting?
266 mutter('%s: create_pack: wrote %s index: %s%s t+%6.3fs',
267@@ -822,6 +831,7 @@
268 set(all_combined).difference([combined_idx]))
269 # resumed packs
270 self._resumed_packs = []
271+ self.config_stack = config.LocationStack(self.transport.base)
272
273 def __repr__(self):
274 return '%s(%r)' % (self.__class__.__name__, self.repo)
275
276=== modified file 'bzrlib/tests/__init__.py'
277--- bzrlib/tests/__init__.py 2011-07-25 11:19:19 +0000
278+++ bzrlib/tests/__init__.py 2011-08-02 01:26:10 +0000
279@@ -1728,6 +1728,9 @@
280 def overrideAttr(self, obj, attr_name, new=_unitialized_attr):
281 """Overrides an object attribute restoring it after the test.
282
283+ :note: This should be used with discretion; you should think about
284+ whether it's better to make the code testable without monkey-patching.
285+
286 :param obj: The object that will be mutated.
287
288 :param attr_name: The attribute name we want to preserve/override in
289@@ -1758,6 +1761,26 @@
290 self.addCleanup(osutils.set_or_unset_env, name, value)
291 return value
292
293+ def recordCalls(self, obj, attr_name):
294+ """Monkeypatch in a wrapper that will record calls.
295+
296+ The monkeypatch is automatically removed when the test concludes.
297+
298+ :param obj: The namespace holding the reference to be replaced;
299+ typically a module, class, or object.
300+ :param attr_name: A string for the name of the attribute to
301+ patch.
302+ :returns: A list that will be extended with one item every time the
303+ function is called, with a tuple of (args, kwargs).
304+ """
305+ calls = []
306+
307+ def decorator(*args, **kwargs):
308+ calls.append((args, kwargs))
309+ return orig(*args, **kwargs)
310+ orig = self.overrideAttr(obj, attr_name, decorator)
311+ return calls
312+
313 def _cleanEnvironment(self):
314 for name, value in isolated_environ.iteritems():
315 self.overrideEnv(name, value)
316
317=== modified file 'bzrlib/tests/test_http.py'
318--- bzrlib/tests/test_http.py 2011-06-14 01:26:41 +0000
319+++ bzrlib/tests/test_http.py 2011-08-02 01:26:10 +0000
320@@ -1048,6 +1048,72 @@
321 self.assertEqual('single', t._range_hint)
322
323
324+class TruncatedBeforeBoundaryRequestHandler(
325+ http_server.TestingHTTPRequestHandler):
326+ """Truncation before a boundary, like in bug 198646"""
327+
328+ _truncated_ranges = 1
329+
330+ def get_multiple_ranges(self, file, file_size, ranges):
331+ self.send_response(206)
332+ self.send_header('Accept-Ranges', 'bytes')
333+ boundary = 'tagada'
334+ self.send_header('Content-Type',
335+ 'multipart/byteranges; boundary=%s' % boundary)
336+ boundary_line = '--%s\r\n' % boundary
337+ # Calculate the Content-Length
338+ content_length = 0
339+ for (start, end) in ranges:
340+ content_length += len(boundary_line)
341+ content_length += self._header_line_length(
342+ 'Content-type', 'application/octet-stream')
343+ content_length += self._header_line_length(
344+ 'Content-Range', 'bytes %d-%d/%d' % (start, end, file_size))
345+ content_length += len('\r\n') # end headers
346+ content_length += end - start # + 1
347+ content_length += len(boundary_line)
348+ self.send_header('Content-length', content_length)
349+ self.end_headers()
350+
351+ # Send the multipart body
352+ cur = 0
353+ for (start, end) in ranges:
354+ if cur + self._truncated_ranges >= len(ranges):
355+ # Abruptly ends the response and close the connection
356+ self.close_connection = 1
357+ return
358+ self.wfile.write(boundary_line)
359+ self.send_header('Content-type', 'application/octet-stream')
360+ self.send_header('Content-Range', 'bytes %d-%d/%d'
361+ % (start, end, file_size))
362+ self.end_headers()
363+ self.send_range_content(file, start, end - start + 1)
364+ cur += 1
365+ # Final boundary
366+ self.wfile.write(boundary_line)
367+
368+
369+class TestTruncatedBeforeBoundary(TestSpecificRequestHandler):
370+ """Tests the case of bug 198646, disconnecting before a boundary."""
371+
372+ _req_handler_class = TruncatedBeforeBoundaryRequestHandler
373+
374+ def setUp(self):
375+ super(TestTruncatedBeforeBoundary, self).setUp()
376+ self.build_tree_contents([('a', '0123456789')],)
377+
378+ def test_readv_with_short_reads(self):
379+ server = self.get_readonly_server()
380+ t = self.get_readonly_transport()
381+ # Force separate ranges for each offset
382+ t._bytes_to_read_before_seek = 0
383+ ireadv = iter(t.readv('a', ((0, 1), (2, 1), (4, 2), (9, 1))))
384+ self.assertEqual((0, '0'), ireadv.next())
385+ self.assertEqual((2, '2'), ireadv.next())
386+ self.assertEqual((4, '45'), ireadv.next())
387+ self.assertEqual((9, '9'), ireadv.next())
388+
389+
390 class LimitedRangeRequestHandler(http_server.TestingHTTPRequestHandler):
391 """Errors out when range specifiers exceed the limit"""
392
393
394=== modified file 'bzrlib/tests/test_selftest.py'
395--- bzrlib/tests/test_selftest.py 2011-07-11 06:47:32 +0000
396+++ bzrlib/tests/test_selftest.py 2011-08-02 01:26:10 +0000
397@@ -1666,6 +1666,18 @@
398 test.run(unittest.TestResult())
399 self.assertEqual('original', obj.test_attr)
400
401+ def test_recordCalls(self):
402+ from bzrlib.tests import test_selftest
403+ calls = self.recordCalls(
404+ test_selftest, '_add_numbers')
405+ self.assertEqual(test_selftest._add_numbers(2, 10),
406+ 12)
407+ self.assertEquals(calls, [((2, 10), {})])
408+
409+
410+def _add_numbers(a, b):
411+ return a + b
412+
413
414 class _MissingFeature(features.Feature):
415 def _probe(self):
416
417=== modified file 'bzrlib/tests/test_transport.py'
418--- bzrlib/tests/test_transport.py 2011-07-25 11:19:19 +0000
419+++ bzrlib/tests/test_transport.py 2011-08-02 01:26:10 +0000
420@@ -740,6 +740,25 @@
421 self.assertEquals(t.local_abspath(''), here)
422
423
424+class TestLocalTransportWriteStream(tests.TestCaseWithTransport):
425+
426+ def test_local_fdatasync_calls_fdatasync(self):
427+ """Check fdatasync on a stream tries to flush the data to the OS.
428+
429+ We can't easily observe the external effect but we can at least see
430+ it's called.
431+ """
432+ t = self.get_transport('.')
433+ calls = self.recordCalls(os, 'fdatasync')
434+ w = t.open_write_stream('out')
435+ w.write('foo')
436+ w.fdatasync()
437+ with open('out', 'rb') as f:
438+ # Should have been flushed.
439+ self.assertEquals(f.read(), 'foo')
440+ self.assertEquals(len(calls), 1, calls)
441+
442+
443 class TestWin32LocalTransport(tests.TestCase):
444
445 def test_unc_clone_to_root(self):
446
447=== modified file 'bzrlib/transport/__init__.py'
448--- bzrlib/transport/__init__.py 2011-07-27 03:03:49 +0000
449+++ bzrlib/transport/__init__.py 2011-08-02 01:26:10 +0000
450@@ -27,6 +27,7 @@
451 """
452
453 from cStringIO import StringIO
454+import os
455 import sys
456
457 from bzrlib.lazy_import import lazy_import
458@@ -227,10 +228,24 @@
459 def _close(self):
460 """A hook point for subclasses that need to take action on close."""
461
462- def close(self):
463+ def close(self, want_fdatasync=False):
464+ if want_fdatasync:
465+ try:
466+ self.fdatasync()
467+ except errors.TransportNotPossible:
468+ pass
469 self._close()
470 del _file_streams[self.transport.abspath(self.relpath)]
471
472+ def fdatasync(self):
473+ """Force data out to physical disk if possible.
474+
475+ :raises TransportNotPossible: If this transport has no way to
476+ flush to disk.
477+ """
478+ raise errors.TransportNotPossible(
479+ "%s cannot fdatasync" % (self.transport,))
480+
481
482 class FileFileStream(FileStream):
483 """A file stream object returned by open_write_stream.
484@@ -245,6 +260,15 @@
485 def _close(self):
486 self.file_handle.close()
487
488+ def fdatasync(self):
489+ """Force data out to physical disk if possible."""
490+ self.file_handle.flush()
491+ try:
492+ fileno = self.file_handle.fileno()
493+ except AttributeError:
494+ raise errors.TransportNotPossible()
495+ osutils.fdatasync(fileno)
496+
497 def write(self, bytes):
498 osutils.pump_string_file(bytes, self.file_handle)
499
500
501=== modified file 'bzrlib/transport/http/__init__.py'
502--- bzrlib/transport/http/__init__.py 2011-05-27 07:39:41 +0000
503+++ bzrlib/transport/http/__init__.py 2011-08-02 01:26:10 +0000
504@@ -271,7 +271,7 @@
505 cur_offset_and_size = iter_offsets.next()
506
507 except (errors.ShortReadvError, errors.InvalidRange,
508- errors.InvalidHttpRange), e:
509+ errors.InvalidHttpRange, errors.HttpBoundaryMissing), e:
510 mutter('Exception %r: %s during http._readv',e, e)
511 if (not isinstance(e, errors.ShortReadvError)
512 or retried_offset == cur_offset_and_size):
513
514=== modified file 'bzrlib/transport/http/response.py'
515--- bzrlib/transport/http/response.py 2009-03-23 14:59:43 +0000
516+++ bzrlib/transport/http/response.py 2011-08-02 01:26:10 +0000
517@@ -1,4 +1,4 @@
518-# Copyright (C) 2006, 2007 Canonical Ltd
519+# Copyright (C) 2006-2011 Canonical Ltd
520 #
521 # This program is free software; you can redistribute it and/or modify
522 # it under the terms of the GNU General Public License as published by
523@@ -109,6 +109,13 @@
524 # To be on the safe side we allow it before any boundary line
525 boundary_line = self._file.readline()
526
527+ if boundary_line == '':
528+ # A timeout in the proxy server caused the response to end early.
529+ # See launchpad bug 198646.
530+ raise errors.HttpBoundaryMissing(
531+ self._path,
532+ self._boundary)
533+
534 if boundary_line != '--' + self._boundary + '\r\n':
535 # rfc822.unquote() incorrectly unquotes strings enclosed in <>
536 # IIS 6 and 7 incorrectly wrap boundary strings in <>
537
538=== modified file 'bzrlib/transport/local.py'
539--- bzrlib/transport/local.py 2011-04-07 10:36:24 +0000
540+++ bzrlib/transport/local.py 2011-08-02 01:26:10 +0000
541@@ -327,10 +327,9 @@
542
543 def open_write_stream(self, relpath, mode=None):
544 """See Transport.open_write_stream."""
545- # initialise the file
546- self.put_bytes_non_atomic(relpath, "", mode=mode)
547 abspath = self._abspath(relpath)
548 handle = osutils.open_file(abspath, 'wb')
549+ handle.truncate()
550 if mode is not None:
551 self._check_mode_and_size(abspath, handle.fileno(), mode)
552 transport._file_streams[self.abspath(relpath)] = handle
553
554=== modified file 'doc/developers/testing.txt'
555--- doc/developers/testing.txt 2011-05-30 07:36:53 +0000
556+++ doc/developers/testing.txt 2011-08-02 01:26:10 +0000
557@@ -1018,6 +1018,23 @@
558
559 self.overrideAttr(osutils, '_cached_user_encoding', 'latin-1')
560
561+This should be used with discretion; sometimes it's better to make the
562+underlying code more testable so that you don't need to rely on monkey
563+patching.
564+
565+
566+Observing calls to a function
567+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
568+
569+Sometimes it's useful to observe how a function is called, typically when
570+calling it has side effects but the side effects are not easy to observe
571+from a test case. For instance the function may be expensive and we want
572+to assert it is not called too many times, or it has effects on the
573+machine that are safe to run during a test but not easy to measure. In
574+these cases, you can use `recordCalls` which will monkey-patch in a
575+wrapper that records when the function is called.
576+
577+
578 Temporarily changing environment variables
579 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
580
581
582=== modified file 'doc/en/release-notes/bzr-2.3.txt'
583--- doc/en/release-notes/bzr-2.3.txt 2011-07-15 08:25:00 +0000
584+++ doc/en/release-notes/bzr-2.3.txt 2011-08-02 01:26:10 +0000
585@@ -32,6 +32,9 @@
586 .. Fixes for situations where bzr would previously crash or give incorrect
587 or undesirable results.
588
589+* Cope cleanly with buggy HTTP proxies that close the socket in the middle
590+ of a multipart response. (Martin Pool, #198646).
591+
592 Documentation
593 *************
594
595
596=== modified file 'doc/en/release-notes/bzr-2.4.txt'
597--- doc/en/release-notes/bzr-2.4.txt 2011-07-25 02:51:30 +0000
598+++ doc/en/release-notes/bzr-2.4.txt 2011-08-02 01:26:10 +0000
599@@ -32,6 +32,31 @@
600 .. Fixes for situations where bzr would previously crash or give incorrect
601 or undesirable results.
602
603+* Accessing a packaging branch on Launchpad (eg, ``lp:ubuntu/bzr``) now
604+ checks to see if the most recent published source package version for
605+ that project is present in the branch tags. This should help developers
606+ trust whether the packaging branch is up-to-date and can be used for new
607+ changes. The level of verbosity is controlled by the config item
608+ ``launchpad.packaging_verbosity``. It can be set to one of
609+
610+ off
611+ disable all checks
612+
613+
614+ minimal
615+ only display if the branch is out-of-date
616+
617+ short
618+ also display single-line up-to-date and missing,
619+
620+
621+ all
622+ (default) display multi-line content for all states
623+
624+
625+ (John Arbash Meinel, #609187, #812928)
626+
627+
628 * The fix for bug #513709 caused us to open a new connection when
629 switching a lightweight checkout that was pointing at a bound branch.
630 This isn't necessary because we know the master URL without opening it,
631@@ -114,6 +139,13 @@
632 * ``Branch.open`` is now about 3x faster (about 2ms instead of 6.5ms).
633 (Andrew Bennetts).
634
635+* Pack, dirstate, and index files are synced to persistent storage if
636+ possible when writing finishes, to reduce the risk of problems caused by
637+ a machine crash or similar problem. This can be turned off through the
638+ ``dirstate.fdatasync`` and ``repository.fdatasync`` options, which can
639+ be set in ``locations.conf`` or ``bazaar.conf``. (Martin Pool,
640+ #343427)
641+
642 Bug Fixes
643 *********
644