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
=== modified file 'bzrlib/config.py'
--- bzrlib/config.py 2011-07-23 16:33:38 +0000
+++ bzrlib/config.py 2011-08-02 01:26:10 +0000
@@ -2291,6 +2291,15 @@
2291 'editor', Option('editor'),2291 'editor', Option('editor'),
2292 help='The command called to launch an editor to enter a message.')2292 help='The command called to launch an editor to enter a message.')
22932293
2294option_registry.register(
2295 'dirstate.fdatasync', Option('dirstate.fdatasync', default=True),
2296 help='Flush dirstate changes onto physical disk?')
2297
2298option_registry.register(
2299 'repository.fdatasync',
2300 Option('repository.fdatasync', default=True),
2301 help='Flush repository changes onto physical disk?')
2302
22942303
2295class Section(object):2304class Section(object):
2296 """A section defines a dict of option name => value.2305 """A section defines a dict of option name => value.
@@ -2821,6 +2830,9 @@
2821class LocationStack(_CompatibleStack):2830class LocationStack(_CompatibleStack):
28222831
2823 def __init__(self, location):2832 def __init__(self, location):
2833 """Make a new stack for a location and global configuration.
2834
2835 :param location: A URL prefix to """
2824 lstore = LocationStore()2836 lstore = LocationStore()
2825 matcher = LocationMatcher(lstore, location)2837 matcher = LocationMatcher(lstore, location)
2826 gstore = GlobalStore()2838 gstore = GlobalStore()
28272839
=== modified file 'bzrlib/dirstate.py'
--- bzrlib/dirstate.py 2011-05-19 18:20:37 +0000
+++ bzrlib/dirstate.py 2011-08-02 01:26:10 +0000
@@ -232,6 +232,7 @@
232232
233from bzrlib import (233from bzrlib import (
234 cache_utf8,234 cache_utf8,
235 config,
235 debug,236 debug,
236 errors,237 errors,
237 inventory,238 inventory,
@@ -239,6 +240,7 @@
239 osutils,240 osutils,
240 static_tuple,241 static_tuple,
241 trace,242 trace,
243 urlutils,
242 )244 )
243245
244246
@@ -448,6 +450,8 @@
448 self._known_hash_changes = set()450 self._known_hash_changes = set()
449 # How many hash changed entries can we have without saving451 # How many hash changed entries can we have without saving
450 self._worth_saving_limit = worth_saving_limit452 self._worth_saving_limit = worth_saving_limit
453 self._config_stack = config.LocationStack(urlutils.local_path_to_url(
454 path))
451455
452 def __repr__(self):456 def __repr__(self):
453 return "%s(%r)" % \457 return "%s(%r)" % \
@@ -2508,33 +2512,41 @@
2508 # IN_MEMORY_HASH_MODIFIED, we should only fail quietly if we fail2512 # IN_MEMORY_HASH_MODIFIED, we should only fail quietly if we fail
2509 # to save an IN_MEMORY_HASH_MODIFIED, and fail *noisily* if we2513 # to save an IN_MEMORY_HASH_MODIFIED, and fail *noisily* if we
2510 # fail to save IN_MEMORY_MODIFIED2514 # fail to save IN_MEMORY_MODIFIED
2511 if self._worth_saving():2515 if not self._worth_saving():
2512 grabbed_write_lock = False2516 return
2513 if self._lock_state != 'w':2517
2514 grabbed_write_lock, new_lock = self._lock_token.temporary_write_lock()2518 grabbed_write_lock = False
2515 # Switch over to the new lock, as the old one may be closed.2519 if self._lock_state != 'w':
2520 grabbed_write_lock, new_lock = self._lock_token.temporary_write_lock()
2521 # Switch over to the new lock, as the old one may be closed.
2522 # TODO: jam 20070315 We should validate the disk file has
2523 # not changed contents, since temporary_write_lock may
2524 # not be an atomic operation.
2525 self._lock_token = new_lock
2526 self._state_file = new_lock.f
2527 if not grabbed_write_lock:
2528 # We couldn't grab a write lock, so we switch back to a read one
2529 return
2530 try:
2531 lines = self.get_lines()
2532 self._state_file.seek(0)
2533 self._state_file.writelines(lines)
2534 self._state_file.truncate()
2535 self._state_file.flush()
2536 self._maybe_fdatasync()
2537 self._mark_unmodified()
2538 finally:
2539 if grabbed_write_lock:
2540 self._lock_token = self._lock_token.restore_read_lock()
2541 self._state_file = self._lock_token.f
2516 # TODO: jam 20070315 We should validate the disk file has2542 # TODO: jam 20070315 We should validate the disk file has
2517 # not changed contents. Since temporary_write_lock may2543 # not changed contents. Since restore_read_lock may
2518 # not be an atomic operation.2544 # not be an atomic operation.
2519 self._lock_token = new_lock2545
2520 self._state_file = new_lock.f2546 def _maybe_fdatasync(self):
2521 if not grabbed_write_lock:2547 """Flush to disk if possible and if not configured off."""
2522 # We couldn't grab a write lock, so we switch back to a read one2548 if self._config_stack.get('dirstate.fdatasync'):
2523 return2549 osutils.fdatasync(self._state_file.fileno())
2524 try:
2525 lines = self.get_lines()
2526 self._state_file.seek(0)
2527 self._state_file.writelines(lines)
2528 self._state_file.truncate()
2529 self._state_file.flush()
2530 self._mark_unmodified()
2531 finally:
2532 if grabbed_write_lock:
2533 self._lock_token = self._lock_token.restore_read_lock()
2534 self._state_file = self._lock_token.f
2535 # TODO: jam 20070315 We should validate the disk file has
2536 # not changed contents. Since restore_read_lock may
2537 # not be an atomic operation.
25382550
2539 def _worth_saving(self):2551 def _worth_saving(self):
2540 """Is it worth saving the dirstate or not?"""2552 """Is it worth saving the dirstate or not?"""
25412553
=== modified file 'bzrlib/errors.py'
--- bzrlib/errors.py 2011-07-16 20:06:11 +0000
+++ bzrlib/errors.py 2011-08-02 01:26:10 +0000
@@ -1737,6 +1737,19 @@
1737 InvalidHttpResponse.__init__(self, path, msg)1737 InvalidHttpResponse.__init__(self, path, msg)
17381738
17391739
1740class HttpBoundaryMissing(InvalidHttpResponse):
1741 """A multipart response ends with no boundary marker.
1742
1743 This is a special case caused by buggy proxies, described in
1744 <https://bugs.launchpad.net/bzr/+bug/198646>.
1745 """
1746
1747 _fmt = "HTTP MIME Boundary missing for %(path)s: %(msg)s"
1748
1749 def __init__(self, path, msg):
1750 InvalidHttpResponse.__init__(self, path, msg)
1751
1752
1740class InvalidHttpContentType(InvalidHttpResponse):1753class InvalidHttpContentType(InvalidHttpResponse):
17411754
1742 _fmt = 'Invalid http Content-type "%(ctype)s" for %(path)s: %(msg)s'1755 _fmt = 'Invalid http Content-type "%(ctype)s" for %(path)s: %(msg)s'
17431756
=== modified file 'bzrlib/help_topics/en/configuration.txt'
--- bzrlib/help_topics/en/configuration.txt 2011-07-11 10:53:46 +0000
+++ bzrlib/help_topics/en/configuration.txt 2011-08-02 01:26:10 +0000
@@ -415,6 +415,13 @@
415committed revisions only when the branch requires them. ``never`` will refuse415committed revisions only when the branch requires them. ``never`` will refuse
416to sign newly committed revisions, even if the branch requires signatures.416to sign newly committed revisions, even if the branch requires signatures.
417417
418dirstate.fdatasync
419~~~~~~~~~~~~~~~~~~
420
421If true (default), working tree metadata changes are flushed through the
422OS buffers to physical disk. This is somewhat slower, but means data
423should not be lost if the machine crashes. See also repository.fdatasync.
424
418gpg_signing_key425gpg_signing_key
419~~~~~~~~~~~426~~~~~~~~~~~
420427
@@ -505,6 +512,13 @@
505:mapi: Use your preferred e-mail client on Windows.512:mapi: Use your preferred e-mail client on Windows.
506:xdg-email: Use xdg-email to run your preferred mail program513:xdg-email: Use xdg-email to run your preferred mail program
507514
515repository.fdatasync
516~~~~~~~~~~~~~~~~~~~~
517
518If true (default), repository changes are flushed through the OS buffers
519to physical disk. This is somewhat slower, but means data should not be
520lost if the machine crashes. See also dirstate.fdatasync.
521
508submit_branch522submit_branch
509~~~~~~~~~~~~~523~~~~~~~~~~~~~
510524
511525
=== modified file 'bzrlib/index.py'
--- bzrlib/index.py 2011-05-19 09:32:38 +0000
+++ bzrlib/index.py 2011-08-02 01:26:10 +0000
@@ -245,6 +245,11 @@
245 """245 """
246 246
247 def finish(self):247 def finish(self):
248 """Finish the index.
249
250 :returns: cStringIO holding the full context of the index as it
251 should be written to disk.
252 """
248 lines = [_SIGNATURE]253 lines = [_SIGNATURE]
249 lines.append(_OPTION_NODE_REFS + str(self.reference_lists) + '\n')254 lines.append(_OPTION_NODE_REFS + str(self.reference_lists) + '\n')
250 lines.append(_OPTION_KEY_ELEMENTS + str(self._key_length) + '\n')255 lines.append(_OPTION_KEY_ELEMENTS + str(self._key_length) + '\n')
251256
=== modified file 'bzrlib/osutils.py'
--- bzrlib/osutils.py 2011-06-16 18:34:26 +0000
+++ bzrlib/osutils.py 2011-08-02 01:26:10 +0000
@@ -2487,3 +2487,14 @@
2487 is_local_pid_dead = win32utils.is_local_pid_dead2487 is_local_pid_dead = win32utils.is_local_pid_dead
2488else:2488else:
2489 is_local_pid_dead = _posix_is_local_pid_dead2489 is_local_pid_dead = _posix_is_local_pid_dead
2490
2491
2492def fdatasync(fileno):
2493 """Flush file contents to disk if possible.
2494
2495 :param fileno: Integer OS file handle.
2496 :raises TransportNotPossible: If flushing to disk is not possible.
2497 """
2498 fn = getattr(os, 'fdatasync', getattr(os, 'fsync', None))
2499 if fn is not None:
2500 fn(fileno)
24902501
=== modified file 'bzrlib/repofmt/pack_repo.py'
--- bzrlib/repofmt/pack_repo.py 2011-05-27 12:01:22 +0000
+++ bzrlib/repofmt/pack_repo.py 2011-08-02 01:26:10 +0000
@@ -25,6 +25,7 @@
25from bzrlib import (25from bzrlib import (
26 chk_map,26 chk_map,
27 cleanup,27 cleanup,
28 config,
28 debug,29 debug,
29 graph,30 graph,
30 osutils,31 osutils,
@@ -478,7 +479,8 @@
478 # visible is smaller. On the other hand none will be seen until479 # visible is smaller. On the other hand none will be seen until
479 # they're in the names list.480 # they're in the names list.
480 self.index_sizes = [None, None, None, None]481 self.index_sizes = [None, None, None, None]
481 self._write_index('revision', self.revision_index, 'revision', suspend)482 self._write_index('revision', self.revision_index, 'revision',
483 suspend)
482 self._write_index('inventory', self.inventory_index, 'inventory',484 self._write_index('inventory', self.inventory_index, 'inventory',
483 suspend)485 suspend)
484 self._write_index('text', self.text_index, 'file texts', suspend)486 self._write_index('text', self.text_index, 'file texts', suspend)
@@ -488,7 +490,8 @@
488 self.index_sizes.append(None)490 self.index_sizes.append(None)
489 self._write_index('chk', self.chk_index,491 self._write_index('chk', self.chk_index,
490 'content hash bytes', suspend)492 'content hash bytes', suspend)
491 self.write_stream.close()493 self.write_stream.close(
494 want_fdatasync=self._pack_collection.config_stack.get('repository.fdatasync'))
492 # Note that this will clobber an existing pack with the same name,495 # Note that this will clobber an existing pack with the same name,
493 # without checking for hash collisions. While this is undesirable this496 # without checking for hash collisions. While this is undesirable this
494 # is something that can be rectified in a subsequent release. One way497 # is something that can be rectified in a subsequent release. One way
@@ -537,8 +540,14 @@
537 transport = self.upload_transport540 transport = self.upload_transport
538 else:541 else:
539 transport = self.index_transport542 transport = self.index_transport
540 self.index_sizes[self.index_offset(index_type)] = transport.put_file(543 index_tempfile = index.finish()
541 index_name, index.finish(), mode=self._file_mode)544 index_bytes = index_tempfile.read()
545 write_stream = transport.open_write_stream(index_name,
546 mode=self._file_mode)
547 write_stream.write(index_bytes)
548 write_stream.close(
549 want_fdatasync=self._pack_collection.config_stack.get('repository.fdatasync'))
550 self.index_sizes[self.index_offset(index_type)] = len(index_bytes)
542 if 'pack' in debug.debug_flags:551 if 'pack' in debug.debug_flags:
543 # XXX: size might be interesting?552 # XXX: size might be interesting?
544 mutter('%s: create_pack: wrote %s index: %s%s t+%6.3fs',553 mutter('%s: create_pack: wrote %s index: %s%s t+%6.3fs',
@@ -822,6 +831,7 @@
822 set(all_combined).difference([combined_idx]))831 set(all_combined).difference([combined_idx]))
823 # resumed packs832 # resumed packs
824 self._resumed_packs = []833 self._resumed_packs = []
834 self.config_stack = config.LocationStack(self.transport.base)
825835
826 def __repr__(self):836 def __repr__(self):
827 return '%s(%r)' % (self.__class__.__name__, self.repo)837 return '%s(%r)' % (self.__class__.__name__, self.repo)
828838
=== modified file 'bzrlib/tests/__init__.py'
--- bzrlib/tests/__init__.py 2011-07-25 11:19:19 +0000
+++ bzrlib/tests/__init__.py 2011-08-02 01:26:10 +0000
@@ -1728,6 +1728,9 @@
1728 def overrideAttr(self, obj, attr_name, new=_unitialized_attr):1728 def overrideAttr(self, obj, attr_name, new=_unitialized_attr):
1729 """Overrides an object attribute restoring it after the test.1729 """Overrides an object attribute restoring it after the test.
17301730
1731 :note: This should be used with discretion; you should think about
1732 whether it's better to make the code testable without monkey-patching.
1733
1731 :param obj: The object that will be mutated.1734 :param obj: The object that will be mutated.
17321735
1733 :param attr_name: The attribute name we want to preserve/override in1736 :param attr_name: The attribute name we want to preserve/override in
@@ -1758,6 +1761,26 @@
1758 self.addCleanup(osutils.set_or_unset_env, name, value)1761 self.addCleanup(osutils.set_or_unset_env, name, value)
1759 return value1762 return value
17601763
1764 def recordCalls(self, obj, attr_name):
1765 """Monkeypatch in a wrapper that will record calls.
1766
1767 The monkeypatch is automatically removed when the test concludes.
1768
1769 :param obj: The namespace holding the reference to be replaced;
1770 typically a module, class, or object.
1771 :param attr_name: A string for the name of the attribute to
1772 patch.
1773 :returns: A list that will be extended with one item every time the
1774 function is called, with a tuple of (args, kwargs).
1775 """
1776 calls = []
1777
1778 def decorator(*args, **kwargs):
1779 calls.append((args, kwargs))
1780 return orig(*args, **kwargs)
1781 orig = self.overrideAttr(obj, attr_name, decorator)
1782 return calls
1783
1761 def _cleanEnvironment(self):1784 def _cleanEnvironment(self):
1762 for name, value in isolated_environ.iteritems():1785 for name, value in isolated_environ.iteritems():
1763 self.overrideEnv(name, value)1786 self.overrideEnv(name, value)
17641787
=== modified file 'bzrlib/tests/test_http.py'
--- bzrlib/tests/test_http.py 2011-06-14 01:26:41 +0000
+++ bzrlib/tests/test_http.py 2011-08-02 01:26:10 +0000
@@ -1048,6 +1048,72 @@
1048 self.assertEqual('single', t._range_hint)1048 self.assertEqual('single', t._range_hint)
10491049
10501050
1051class TruncatedBeforeBoundaryRequestHandler(
1052 http_server.TestingHTTPRequestHandler):
1053 """Truncation before a boundary, like in bug 198646"""
1054
1055 _truncated_ranges = 1
1056
1057 def get_multiple_ranges(self, file, file_size, ranges):
1058 self.send_response(206)
1059 self.send_header('Accept-Ranges', 'bytes')
1060 boundary = 'tagada'
1061 self.send_header('Content-Type',
1062 'multipart/byteranges; boundary=%s' % boundary)
1063 boundary_line = '--%s\r\n' % boundary
1064 # Calculate the Content-Length
1065 content_length = 0
1066 for (start, end) in ranges:
1067 content_length += len(boundary_line)
1068 content_length += self._header_line_length(
1069 'Content-type', 'application/octet-stream')
1070 content_length += self._header_line_length(
1071 'Content-Range', 'bytes %d-%d/%d' % (start, end, file_size))
1072 content_length += len('\r\n') # end headers
1073 content_length += end - start # + 1
1074 content_length += len(boundary_line)
1075 self.send_header('Content-length', content_length)
1076 self.end_headers()
1077
1078 # Send the multipart body
1079 cur = 0
1080 for (start, end) in ranges:
1081 if cur + self._truncated_ranges >= len(ranges):
1082 # Abruptly ends the response and close the connection
1083 self.close_connection = 1
1084 return
1085 self.wfile.write(boundary_line)
1086 self.send_header('Content-type', 'application/octet-stream')
1087 self.send_header('Content-Range', 'bytes %d-%d/%d'
1088 % (start, end, file_size))
1089 self.end_headers()
1090 self.send_range_content(file, start, end - start + 1)
1091 cur += 1
1092 # Final boundary
1093 self.wfile.write(boundary_line)
1094
1095
1096class TestTruncatedBeforeBoundary(TestSpecificRequestHandler):
1097 """Tests the case of bug 198646, disconnecting before a boundary."""
1098
1099 _req_handler_class = TruncatedBeforeBoundaryRequestHandler
1100
1101 def setUp(self):
1102 super(TestTruncatedBeforeBoundary, self).setUp()
1103 self.build_tree_contents([('a', '0123456789')],)
1104
1105 def test_readv_with_short_reads(self):
1106 server = self.get_readonly_server()
1107 t = self.get_readonly_transport()
1108 # Force separate ranges for each offset
1109 t._bytes_to_read_before_seek = 0
1110 ireadv = iter(t.readv('a', ((0, 1), (2, 1), (4, 2), (9, 1))))
1111 self.assertEqual((0, '0'), ireadv.next())
1112 self.assertEqual((2, '2'), ireadv.next())
1113 self.assertEqual((4, '45'), ireadv.next())
1114 self.assertEqual((9, '9'), ireadv.next())
1115
1116
1051class LimitedRangeRequestHandler(http_server.TestingHTTPRequestHandler):1117class LimitedRangeRequestHandler(http_server.TestingHTTPRequestHandler):
1052 """Errors out when range specifiers exceed the limit"""1118 """Errors out when range specifiers exceed the limit"""
10531119
10541120
=== modified file 'bzrlib/tests/test_selftest.py'
--- bzrlib/tests/test_selftest.py 2011-07-11 06:47:32 +0000
+++ bzrlib/tests/test_selftest.py 2011-08-02 01:26:10 +0000
@@ -1666,6 +1666,18 @@
1666 test.run(unittest.TestResult())1666 test.run(unittest.TestResult())
1667 self.assertEqual('original', obj.test_attr)1667 self.assertEqual('original', obj.test_attr)
16681668
1669 def test_recordCalls(self):
1670 from bzrlib.tests import test_selftest
1671 calls = self.recordCalls(
1672 test_selftest, '_add_numbers')
1673 self.assertEqual(test_selftest._add_numbers(2, 10),
1674 12)
1675 self.assertEquals(calls, [((2, 10), {})])
1676
1677
1678def _add_numbers(a, b):
1679 return a + b
1680
16691681
1670class _MissingFeature(features.Feature):1682class _MissingFeature(features.Feature):
1671 def _probe(self):1683 def _probe(self):
16721684
=== modified file 'bzrlib/tests/test_transport.py'
--- bzrlib/tests/test_transport.py 2011-07-25 11:19:19 +0000
+++ bzrlib/tests/test_transport.py 2011-08-02 01:26:10 +0000
@@ -740,6 +740,25 @@
740 self.assertEquals(t.local_abspath(''), here)740 self.assertEquals(t.local_abspath(''), here)
741741
742742
743class TestLocalTransportWriteStream(tests.TestCaseWithTransport):
744
745 def test_local_fdatasync_calls_fdatasync(self):
746 """Check fdatasync on a stream tries to flush the data to the OS.
747
748 We can't easily observe the external effect but we can at least see
749 it's called.
750 """
751 t = self.get_transport('.')
752 calls = self.recordCalls(os, 'fdatasync')
753 w = t.open_write_stream('out')
754 w.write('foo')
755 w.fdatasync()
756 with open('out', 'rb') as f:
757 # Should have been flushed.
758 self.assertEquals(f.read(), 'foo')
759 self.assertEquals(len(calls), 1, calls)
760
761
743class TestWin32LocalTransport(tests.TestCase):762class TestWin32LocalTransport(tests.TestCase):
744763
745 def test_unc_clone_to_root(self):764 def test_unc_clone_to_root(self):
746765
=== modified file 'bzrlib/transport/__init__.py'
--- bzrlib/transport/__init__.py 2011-07-27 03:03:49 +0000
+++ bzrlib/transport/__init__.py 2011-08-02 01:26:10 +0000
@@ -27,6 +27,7 @@
27"""27"""
2828
29from cStringIO import StringIO29from cStringIO import StringIO
30import os
30import sys31import sys
3132
32from bzrlib.lazy_import import lazy_import33from bzrlib.lazy_import import lazy_import
@@ -227,10 +228,24 @@
227 def _close(self):228 def _close(self):
228 """A hook point for subclasses that need to take action on close."""229 """A hook point for subclasses that need to take action on close."""
229230
230 def close(self):231 def close(self, want_fdatasync=False):
232 if want_fdatasync:
233 try:
234 self.fdatasync()
235 except errors.TransportNotPossible:
236 pass
231 self._close()237 self._close()
232 del _file_streams[self.transport.abspath(self.relpath)]238 del _file_streams[self.transport.abspath(self.relpath)]
233239
240 def fdatasync(self):
241 """Force data out to physical disk if possible.
242
243 :raises TransportNotPossible: If this transport has no way to
244 flush to disk.
245 """
246 raise errors.TransportNotPossible(
247 "%s cannot fdatasync" % (self.transport,))
248
234249
235class FileFileStream(FileStream):250class FileFileStream(FileStream):
236 """A file stream object returned by open_write_stream.251 """A file stream object returned by open_write_stream.
@@ -245,6 +260,15 @@
245 def _close(self):260 def _close(self):
246 self.file_handle.close()261 self.file_handle.close()
247262
263 def fdatasync(self):
264 """Force data out to physical disk if possible."""
265 self.file_handle.flush()
266 try:
267 fileno = self.file_handle.fileno()
268 except AttributeError:
269 raise errors.TransportNotPossible()
270 osutils.fdatasync(fileno)
271
248 def write(self, bytes):272 def write(self, bytes):
249 osutils.pump_string_file(bytes, self.file_handle)273 osutils.pump_string_file(bytes, self.file_handle)
250274
251275
=== modified file 'bzrlib/transport/http/__init__.py'
--- bzrlib/transport/http/__init__.py 2011-05-27 07:39:41 +0000
+++ bzrlib/transport/http/__init__.py 2011-08-02 01:26:10 +0000
@@ -271,7 +271,7 @@
271 cur_offset_and_size = iter_offsets.next()271 cur_offset_and_size = iter_offsets.next()
272272
273 except (errors.ShortReadvError, errors.InvalidRange,273 except (errors.ShortReadvError, errors.InvalidRange,
274 errors.InvalidHttpRange), e:274 errors.InvalidHttpRange, errors.HttpBoundaryMissing), e:
275 mutter('Exception %r: %s during http._readv',e, e)275 mutter('Exception %r: %s during http._readv',e, e)
276 if (not isinstance(e, errors.ShortReadvError)276 if (not isinstance(e, errors.ShortReadvError)
277 or retried_offset == cur_offset_and_size):277 or retried_offset == cur_offset_and_size):
278278
=== modified file 'bzrlib/transport/http/response.py'
--- bzrlib/transport/http/response.py 2009-03-23 14:59:43 +0000
+++ bzrlib/transport/http/response.py 2011-08-02 01:26:10 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2006, 2007 Canonical Ltd1# Copyright (C) 2006-2011 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -109,6 +109,13 @@
109 # To be on the safe side we allow it before any boundary line109 # To be on the safe side we allow it before any boundary line
110 boundary_line = self._file.readline()110 boundary_line = self._file.readline()
111111
112 if boundary_line == '':
113 # A timeout in the proxy server caused the response to end early.
114 # See launchpad bug 198646.
115 raise errors.HttpBoundaryMissing(
116 self._path,
117 self._boundary)
118
112 if boundary_line != '--' + self._boundary + '\r\n':119 if boundary_line != '--' + self._boundary + '\r\n':
113 # rfc822.unquote() incorrectly unquotes strings enclosed in <>120 # rfc822.unquote() incorrectly unquotes strings enclosed in <>
114 # IIS 6 and 7 incorrectly wrap boundary strings in <>121 # IIS 6 and 7 incorrectly wrap boundary strings in <>
115122
=== modified file 'bzrlib/transport/local.py'
--- bzrlib/transport/local.py 2011-04-07 10:36:24 +0000
+++ bzrlib/transport/local.py 2011-08-02 01:26:10 +0000
@@ -327,10 +327,9 @@
327327
328 def open_write_stream(self, relpath, mode=None):328 def open_write_stream(self, relpath, mode=None):
329 """See Transport.open_write_stream."""329 """See Transport.open_write_stream."""
330 # initialise the file
331 self.put_bytes_non_atomic(relpath, "", mode=mode)
332 abspath = self._abspath(relpath)330 abspath = self._abspath(relpath)
333 handle = osutils.open_file(abspath, 'wb')331 handle = osutils.open_file(abspath, 'wb')
332 handle.truncate()
334 if mode is not None:333 if mode is not None:
335 self._check_mode_and_size(abspath, handle.fileno(), mode)334 self._check_mode_and_size(abspath, handle.fileno(), mode)
336 transport._file_streams[self.abspath(relpath)] = handle335 transport._file_streams[self.abspath(relpath)] = handle
337336
=== modified file 'doc/developers/testing.txt'
--- doc/developers/testing.txt 2011-05-30 07:36:53 +0000
+++ doc/developers/testing.txt 2011-08-02 01:26:10 +0000
@@ -1018,6 +1018,23 @@
10181018
1019 self.overrideAttr(osutils, '_cached_user_encoding', 'latin-1')1019 self.overrideAttr(osutils, '_cached_user_encoding', 'latin-1')
10201020
1021This should be used with discretion; sometimes it's better to make the
1022underlying code more testable so that you don't need to rely on monkey
1023patching.
1024
1025
1026Observing calls to a function
1027~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1028
1029Sometimes it's useful to observe how a function is called, typically when
1030calling it has side effects but the side effects are not easy to observe
1031from a test case. For instance the function may be expensive and we want
1032to assert it is not called too many times, or it has effects on the
1033machine that are safe to run during a test but not easy to measure. In
1034these cases, you can use `recordCalls` which will monkey-patch in a
1035wrapper that records when the function is called.
1036
1037
1021Temporarily changing environment variables1038Temporarily changing environment variables
1022~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~1039~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10231040
10241041
=== modified file 'doc/en/release-notes/bzr-2.3.txt'
--- doc/en/release-notes/bzr-2.3.txt 2011-07-15 08:25:00 +0000
+++ doc/en/release-notes/bzr-2.3.txt 2011-08-02 01:26:10 +0000
@@ -32,6 +32,9 @@
32.. Fixes for situations where bzr would previously crash or give incorrect32.. Fixes for situations where bzr would previously crash or give incorrect
33 or undesirable results.33 or undesirable results.
3434
35* Cope cleanly with buggy HTTP proxies that close the socket in the middle
36 of a multipart response. (Martin Pool, #198646).
37
35Documentation38Documentation
36*************39*************
3740
3841
=== modified file 'doc/en/release-notes/bzr-2.4.txt'
--- doc/en/release-notes/bzr-2.4.txt 2011-07-25 02:51:30 +0000
+++ doc/en/release-notes/bzr-2.4.txt 2011-08-02 01:26:10 +0000
@@ -32,6 +32,31 @@
32.. Fixes for situations where bzr would previously crash or give incorrect32.. Fixes for situations where bzr would previously crash or give incorrect
33 or undesirable results.33 or undesirable results.
3434
35* Accessing a packaging branch on Launchpad (eg, ``lp:ubuntu/bzr``) now
36 checks to see if the most recent published source package version for
37 that project is present in the branch tags. This should help developers
38 trust whether the packaging branch is up-to-date and can be used for new
39 changes. The level of verbosity is controlled by the config item
40 ``launchpad.packaging_verbosity``. It can be set to one of
41
42 off
43 disable all checks
44
45
46 minimal
47 only display if the branch is out-of-date
48
49 short
50 also display single-line up-to-date and missing,
51
52
53 all
54 (default) display multi-line content for all states
55
56
57 (John Arbash Meinel, #609187, #812928)
58
59
35* The fix for bug #513709 caused us to open a new connection when60* The fix for bug #513709 caused us to open a new connection when
36 switching a lightweight checkout that was pointing at a bound branch.61 switching a lightweight checkout that was pointing at a bound branch.
37 This isn't necessary because we know the master URL without opening it,62 This isn't necessary because we know the master URL without opening it,
@@ -114,6 +139,13 @@
114* ``Branch.open`` is now about 3x faster (about 2ms instead of 6.5ms).139* ``Branch.open`` is now about 3x faster (about 2ms instead of 6.5ms).
115 (Andrew Bennetts).140 (Andrew Bennetts).
116141
142* Pack, dirstate, and index files are synced to persistent storage if
143 possible when writing finishes, to reduce the risk of problems caused by
144 a machine crash or similar problem. This can be turned off through the
145 ``dirstate.fdatasync`` and ``repository.fdatasync`` options, which can
146 be set in ``locations.conf`` or ``bazaar.conf``. (Martin Pool,
147 #343427)
148
117Bug Fixes149Bug Fixes
118*********150*********
119151