Merge lp:~jelmer/brz/osutils-fstype into lp:brz

Proposed by Jelmer Vernooij
Status: Merged
Merge reported by: Jelmer Vernooij
Merged at revision: not available
Proposed branch: lp:~jelmer/brz/osutils-fstype
Merge into: lp:brz
Prerequisite: lp:~jelmer/brz/wt-executable
Diff against target: 480 lines (+129/-45)
13 files modified
breezy/bzr/dirstate.py (+15/-11)
breezy/bzr/workingtree.py (+11/-2)
breezy/bzr/workingtree_4.py (+6/-4)
breezy/git/memorytree.py (+1/-0)
breezy/git/tree.py (+8/-1)
breezy/git/workingtree.py (+11/-5)
breezy/osutils.py (+43/-2)
breezy/tests/per_workingtree/test_executable.py (+1/-1)
breezy/tests/per_workingtree/test_workingtree.py (+13/-7)
breezy/tests/test_osutils.py (+15/-0)
breezy/transform.py (+2/-3)
breezy/workingtree.py (+1/-9)
setup.py (+2/-0)
To merge this branch: bzr merge lp:~jelmer/brz/osutils-fstype
Reviewer Review Type Date Requested Status
Martin Packman Needs Fixing
Review via email: mp+355466@code.launchpad.net

Commit message

Use basis tree executability status if the filesystem doesn't support executable bit.

Description of the change

Use basis tree executability status if the filesystem doesn't support executable bit.

For now, just support vfat and ntfs as filesystems without the executable bit as well as all filesystems on Windows.

To post a comment you must log in.
lp:~jelmer/brz/osutils-fstype updated
7128. By Jelmer Vernooij

Fix GitMemoryTree tests.

Revision history for this message
Martin Packman (gz) wrote :

Good change to make, some inline questions about specifics to address.

review: Needs Fixing
lp:~jelmer/brz/osutils-fstype updated
7129. By Jelmer Vernooij

cope with psutil not being available.

7130. By Jelmer Vernooij

Move case_sensitive_filename attribute to bzr-specific moduke.

7131. By Jelmer Vernooij

Add trust_executable_bit.

7132. By Jelmer Vernooij

Merge trunk.

7133. By Jelmer Vernooij

Review feedback:

* Cache disk partitions
* Rename supports_executable => fs_supports_executable

7134. By Jelmer Vernooij

Add dependency on psutil.

Revision history for this message
Jelmer Vernooij (jelmer) :
lp:~jelmer/brz/osutils-fstype updated
7135. By Jelmer Vernooij

merge trunk.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'breezy/bzr/dirstate.py'
--- breezy/bzr/dirstate.py 2018-11-18 19:48:57 +0000
+++ breezy/bzr/dirstate.py 2019-03-04 02:41:29 +0000
@@ -366,7 +366,8 @@
366 HEADER_FORMAT_2 = b'#bazaar dirstate flat format 2\n'366 HEADER_FORMAT_2 = b'#bazaar dirstate flat format 2\n'
367 HEADER_FORMAT_3 = b'#bazaar dirstate flat format 3\n'367 HEADER_FORMAT_3 = b'#bazaar dirstate flat format 3\n'
368368
369 def __init__(self, path, sha1_provider, worth_saving_limit=0):369 def __init__(self, path, sha1_provider, worth_saving_limit=0,
370 use_filesystem_for_exec=True):
370 """Create a DirState object.371 """Create a DirState object.
371372
372 :param path: The path at which the dirstate file on disk should live.373 :param path: The path at which the dirstate file on disk should live.
@@ -375,6 +376,8 @@
375 entries is known, only bother saving the dirstate if more than376 entries is known, only bother saving the dirstate if more than
376 this count of entries have changed.377 this count of entries have changed.
377 -1 means never save hash changes, 0 means always save hash changes.378 -1 means never save hash changes, 0 means always save hash changes.
379 :param use_filesystem_for_exec: Whether to trust the filesystem
380 for executable bit information
378 """381 """
379 # _header_state and _dirblock_state represent the current state382 # _header_state and _dirblock_state represent the current state
380 # of the dirstate metadata and the per-row data respectiely.383 # of the dirstate metadata and the per-row data respectiely.
@@ -423,6 +426,7 @@
423 self._worth_saving_limit = worth_saving_limit426 self._worth_saving_limit = worth_saving_limit
424 self._config_stack = config.LocationStack(urlutils.local_path_to_url(427 self._config_stack = config.LocationStack(urlutils.local_path_to_url(
425 path))428 path))
429 self._use_filesystem_for_exec = use_filesystem_for_exec
426430
427 def __repr__(self):431 def __repr__(self):
428 return "%s(%r)" % \432 return "%s(%r)" % \
@@ -1949,14 +1953,10 @@
19491953
1950 def _is_executable(self, mode, old_executable):1954 def _is_executable(self, mode, old_executable):
1951 """Is this file executable?"""1955 """Is this file executable?"""
1952 return bool(S_IEXEC & mode)1956 if self._use_filesystem_for_exec:
19531957 return bool(S_IEXEC & mode)
1954 def _is_executable_win32(self, mode, old_executable):1958 else:
1955 """On win32 the executable bit is stored in the dirstate."""1959 return old_executable
1956 return old_executable
1957
1958 if sys.platform == 'win32':
1959 _is_executable = _is_executable_win32
19601960
1961 def _read_link(self, abspath, old_link):1961 def _read_link(self, abspath, old_link):
1962 """Read the target of a symlink"""1962 """Read the target of a symlink"""
@@ -2403,7 +2403,8 @@
2403 return len(self._parents) - len(self._ghosts)2403 return len(self._parents) - len(self._ghosts)
24042404
2405 @classmethod2405 @classmethod
2406 def on_file(cls, path, sha1_provider=None, worth_saving_limit=0):2406 def on_file(cls, path, sha1_provider=None, worth_saving_limit=0,
2407 use_filesystem_for_exec=True):
2407 """Construct a DirState on the file at path "path".2408 """Construct a DirState on the file at path "path".
24082409
2409 :param path: The path at which the dirstate file on disk should live.2410 :param path: The path at which the dirstate file on disk should live.
@@ -2412,12 +2413,15 @@
2412 :param worth_saving_limit: when the exact number of hash changed2413 :param worth_saving_limit: when the exact number of hash changed
2413 entries is known, only bother saving the dirstate if more than2414 entries is known, only bother saving the dirstate if more than
2414 this count of entries have changed. -1 means never save.2415 this count of entries have changed. -1 means never save.
2416 :param use_filesystem_for_exec: Whether to trust the filesystem
2417 for executable bit information
2415 :return: An unlocked DirState object, associated with the given path.2418 :return: An unlocked DirState object, associated with the given path.
2416 """2419 """
2417 if sha1_provider is None:2420 if sha1_provider is None:
2418 sha1_provider = DefaultSHA1Provider()2421 sha1_provider = DefaultSHA1Provider()
2419 result = cls(path, sha1_provider,2422 result = cls(path, sha1_provider,
2420 worth_saving_limit=worth_saving_limit)2423 worth_saving_limit=worth_saving_limit,
2424 use_filesystem_for_exec=use_filesystem_for_exec)
2421 return result2425 return result
24222426
2423 def _read_dirblocks_if_needed(self):2427 def _read_dirblocks_if_needed(self):
24242428
=== modified file 'breezy/bzr/workingtree.py'
--- breezy/bzr/workingtree.py 2018-12-11 00:51:46 +0000
+++ breezy/bzr/workingtree.py 2019-03-04 02:41:29 +0000
@@ -126,6 +126,7 @@
126126
127 self._control_files = _control_files127 self._control_files = _control_files
128 self._detect_case_handling()128 self._detect_case_handling()
129 self._detect_trust_executable()
129130
130 if _inventory is None:131 if _inventory is None:
131 # This will be acquired on lock_read() or lock_write()132 # This will be acquired on lock_read() or lock_write()
@@ -162,6 +163,12 @@
162163
163 self._setup_directory_is_tree_reference()164 self._setup_directory_is_tree_reference()
164165
166 def _detect_trust_executable(self):
167 config_stack = self.get_config_stack()
168 self.trust_executable_bit = config_stack.get('trust_executable_bit')
169 if self.trust_executable_bit is None:
170 self.trust_executable_bit = osutils.fs_supports_executable(self.basedir)
171
165 def _serialize(self, inventory, out_file):172 def _serialize(self, inventory, out_file):
166 xml5.serializer_v5.write_inventory(173 xml5.serializer_v5.write_inventory(
167 self._inventory, out_file, working=True)174 self._inventory, out_file, working=True)
@@ -751,7 +758,7 @@
751 return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)758 return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
752759
753 def is_executable(self, path):760 def is_executable(self, path):
754 if not self._supports_executable():761 if not self.trust_executable_bit:
755 ie = self._path2ie(path)762 ie = self._path2ie(path)
756 return ie.executable763 return ie.executable
757 else:764 else:
@@ -759,7 +766,7 @@
759 return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)766 return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
760767
761 def _is_executable_from_path_and_stat(self, path, stat_result):768 def _is_executable_from_path_and_stat(self, path, stat_result):
762 if not self._supports_executable():769 if not self.trust_executable_bit:
763 return self._is_executable_from_path_and_stat_from_basis(770 return self._is_executable_from_path_and_stat_from_basis(
764 path, stat_result)771 path, stat_result)
765 else:772 else:
@@ -1753,6 +1760,8 @@
17531760
1754 ignore_filename = '.bzrignore'1761 ignore_filename = '.bzrignore'
17551762
1763 case_sensitive_filename = "FoRMaT"
1764
1756 def __init__(self):1765 def __init__(self):
1757 WorkingTreeFormat.__init__(self)1766 WorkingTreeFormat.__init__(self)
1758 bzrdir.BzrFormat.__init__(self)1767 bzrdir.BzrFormat.__init__(self)
17591768
=== modified file 'breezy/bzr/workingtree_4.py'
--- breezy/bzr/workingtree_4.py 2019-02-04 19:39:30 +0000
+++ breezy/bzr/workingtree_4.py 2019-03-04 02:41:29 +0000
@@ -121,6 +121,7 @@
121 # -------------121 # -------------
122 self._setup_directory_is_tree_reference()122 self._setup_directory_is_tree_reference()
123 self._detect_case_handling()123 self._detect_case_handling()
124 self._detect_trust_executable()
124 self._rules_searcher = None125 self._rules_searcher = None
125 self.views = self._make_views()126 self.views = self._make_views()
126 # --- allow tests to select the dirstate iter_changes implementation127 # --- allow tests to select the dirstate iter_changes implementation
@@ -255,8 +256,9 @@
255 return self._dirstate256 return self._dirstate
256 local_path = self.controldir.get_workingtree_transport(257 local_path = self.controldir.get_workingtree_transport(
257 None).local_abspath('dirstate')258 None).local_abspath('dirstate')
258 self._dirstate = dirstate.DirState.on_file(259 self._dirstate = dirstate.DirState.on_file(local_path,
259 local_path, self._sha1_provider(), self._worth_saving_limit())260 self._sha1_provider(), self._worth_saving_limit(),
261 self.trust_executable_bit)
260 return self._dirstate262 return self._dirstate
261263
262 def _sha1_provider(self):264 def _sha1_provider(self):
@@ -504,7 +506,7 @@
504506
505 Note: The caller is expected to take a read-lock before calling this.507 Note: The caller is expected to take a read-lock before calling this.
506 """508 """
507 if not self._supports_executable():509 if not self.trust_executable_bit:
508 entry = self._get_entry(path=path)510 entry = self._get_entry(path=path)
509 if entry == (None, None):511 if entry == (None, None):
510 return False512 return False
@@ -2244,7 +2246,7 @@
2244 search_specific_files_utf8.add(path.encode('utf8'))2246 search_specific_files_utf8.add(path.encode('utf8'))
22452247
2246 iter_changes = self.target._iter_changes(2248 iter_changes = self.target._iter_changes(
2247 include_unchanged, self.target._supports_executable(),2249 include_unchanged, self.target.trust_executable_bit,
2248 search_specific_files_utf8, state, source_index, target_index,2250 search_specific_files_utf8, state, source_index, target_index,
2249 want_unversioned, self.target)2251 want_unversioned, self.target)
2250 return iter_changes.iter_changes()2252 return iter_changes.iter_changes()
22512253
=== modified file 'breezy/git/memorytree.py'
--- breezy/git/memorytree.py 2018-11-18 00:25:19 +0000
+++ breezy/git/memorytree.py 2019-03-04 02:41:29 +0000
@@ -57,6 +57,7 @@
57 self._locks = 057 self._locks = 0
58 self._lock_mode = None58 self._lock_mode = None
59 self._populate_from_branch()59 self._populate_from_branch()
60 self.trust_executable_bit = True
6061
61 @property62 @property
62 def controldir(self):63 def controldir(self):
6364
=== modified file 'breezy/git/tree.py'
--- breezy/git/tree.py 2018-12-18 20:55:37 +0000
+++ breezy/git/tree.py 2019-03-04 02:41:29 +0000
@@ -1447,6 +1447,7 @@
1447 # Report dirified directories to commit_tree first, so that they can be1447 # Report dirified directories to commit_tree first, so that they can be
1448 # replaced with non-empty directories if they have contents.1448 # replaced with non-empty directories if they have contents.
1449 dirified = []1449 dirified = []
1450 trust_executable = target.trust_executable_bit
1450 for path, index_entry in target._recurse_index_entries():1451 for path, index_entry in target._recurse_index_entries():
1451 try:1452 try:
1452 live_entry = target._live_entry(path)1453 live_entry = target._live_entry(path)
@@ -1465,7 +1466,13 @@
1465 else:1466 else:
1466 raise1467 raise
1467 else:1468 else:
1468 blobs[path] = (live_entry.sha, cleanup_mode(live_entry.mode))1469 mode = live_entry.mode
1470 if not trust_executable:
1471 if mode_is_executable(index_entry.mode):
1472 mode |= 0o111
1473 else:
1474 mode &= ~0o111
1475 blobs[path] = (live_entry.sha, cleanup_mode(mode))
1469 if want_unversioned:1476 if want_unversioned:
1470 for e in target.extras():1477 for e in target.extras():
1471 st = target._lstat(e)1478 st = target._lstat(e)
14721479
=== modified file 'breezy/git/workingtree.py'
--- breezy/git/workingtree.py 2019-02-05 04:00:02 +0000
+++ breezy/git/workingtree.py 2019-03-04 02:41:29 +0000
@@ -103,6 +103,7 @@
103 self.views = self._make_views()103 self.views = self._make_views()
104 self._rules_searcher = None104 self._rules_searcher = None
105 self._detect_case_handling()105 self._detect_case_handling()
106 self._detect_trust_executable_bit()
106 self._reset_data()107 self._reset_data()
107108
108 def supports_tree_reference(self):109 def supports_tree_reference(self):
@@ -209,6 +210,12 @@
209 else:210 else:
210 self.case_sensitive = False211 self.case_sensitive = False
211212
213 def _detect_trust_executable_bit(self):
214 config_stack = self.repository._git.get_config_stack()
215 self.trust_executable_bit = config_stack.get_boolean(('core', ), 'filemode', default=None)
216 if self.trust_executable_bit is None:
217 self.trust_executable_bit = osutils.fs_supports_executable(self.basedir)
218
212 def merge_modified(self):219 def merge_modified(self):
213 return {}220 return {}
214221
@@ -743,8 +750,7 @@
743750
744 def is_executable(self, path):751 def is_executable(self, path):
745 with self.lock_read():752 with self.lock_read():
746 if getattr(self, "_supports_executable",753 if self.trust_executable_bit:
747 osutils.supports_executable)():
748 mode = self._lstat(path).st_mode754 mode = self._lstat(path).st_mode
749 else:755 else:
750 (index, subpath) = self._lookup_index(path.encode('utf-8'))756 (index, subpath) = self._lookup_index(path.encode('utf-8'))
@@ -755,8 +761,7 @@
755 return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)761 return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
756762
757 def _is_executable_from_path_and_stat(self, path, stat_result):763 def _is_executable_from_path_and_stat(self, path, stat_result):
758 if getattr(self, "_supports_executable",764 if self.trust_executable_bit:
759 osutils.supports_executable)():
760 return self._is_executable_from_path_and_stat_from_stat(765 return self._is_executable_from_path_and_stat_from_stat(
761 path, stat_result)766 path, stat_result)
762 else:767 else:
@@ -1166,7 +1171,8 @@
1166 self.store,1171 self.store,
1167 None1172 None
1168 if self.branch.head is None1173 if self.branch.head is None
1169 else self.store[self.branch.head].tree)1174 else self.store[self.branch.head].tree,
1175 honor_filemode=self.trust_executable_bit)
11701176
1171 def reset_state(self, revision_ids=None):1177 def reset_state(self, revision_ids=None):
1172 """Reset the state of the working tree.1178 """Reset the state of the working tree.
11731179
=== modified file 'breezy/osutils.py'
--- breezy/osutils.py 2019-03-02 23:49:52 +0000
+++ breezy/osutils.py 2019-03-04 02:41:29 +0000
@@ -1662,8 +1662,24 @@
1662 _terminal_size = _ioctl_terminal_size1662 _terminal_size = _ioctl_terminal_size
16631663
16641664
1665def supports_executable():1665def fs_supports_executable(path):
1666 return sys.platform != "win32"1666 """Return if filesystem at path supports executable bit.
1667
1668 :param path: Path for which to check the file system
1669 :return: boolean indicating whether executable bit can be stored/relied upon
1670 """
1671 if sys.platform == 'win32':
1672 return False
1673 try:
1674 fs_type = get_fs_type(path)
1675 except errors.DependencyNotPresent:
1676 # TODO(jelmer): Warn here?
1677 pass
1678 else:
1679 if fs_type in ('vfat', 'ntfs'):
1680 # filesystems known to not support executable bit
1681 return False
1682 return True
16671683
16681684
1669def supports_posix_readonly():1685def supports_posix_readonly():
@@ -2602,6 +2618,31 @@
2602 return False2618 return False
26032619
26042620
2621_disk_partitions = None
2622
2623
2624def get_fs_type(path):
2625 """Return the filesystem type for the partition a path is in.
2626
2627 :param path: Path to search filesystem type for
2628 :return: A FS type, as string. E.g. "ext2"
2629 """
2630 global _disk_partitions
2631 # TODO(jelmer): It would be nice to avoid an extra dependency here, but the only
2632 # alternative is reading platform-specific files under /proc :(
2633 try:
2634 import psutil
2635 except ImportError as e:
2636 raise errors.DependencyNotPresent('psutil', e)
2637 if _disk_partitions is None:
2638 _disk_partitions = psutil.disk_partitions()
2639 for part in sorted(_disk_partitions, key=lambda x: len(x.mountpoint), reverse=True):
2640 if is_inside(part.mountpoint, path):
2641 return part.fstype
2642 # Unable to parse the file? Since otherwise at least the entry for / should match..
2643 return None
2644
2645
2605if PY3:2646if PY3:
2606 perf_counter = time.perf_counter2647 perf_counter = time.perf_counter
2607else:2648else:
26082649
=== modified file 'breezy/tests/per_workingtree/test_executable.py'
--- breezy/tests/per_workingtree/test_executable.py 2018-06-18 02:13:57 +0000
+++ breezy/tests/per_workingtree/test_executable.py 2019-03-04 02:41:29 +0000
@@ -186,7 +186,7 @@
186 self.assertFalse(b_executable)186 self.assertFalse(b_executable)
187187
188 def test_use_exec_from_basis(self):188 def test_use_exec_from_basis(self):
189 self.wt._supports_executable = lambda: False189 self.wt.trust_executable_bit = False
190 self.addCleanup(self.wt.lock_read().unlock)190 self.addCleanup(self.wt.lock_read().unlock)
191 self.assertTrue(self.wt.is_executable('a'))191 self.assertTrue(self.wt.is_executable('a'))
192 self.assertFalse(self.wt.is_executable('b'))192 self.assertFalse(self.wt.is_executable('b'))
193193
=== modified file 'breezy/tests/per_workingtree/test_workingtree.py'
--- breezy/tests/per_workingtree/test_workingtree.py 2019-02-14 03:30:18 +0000
+++ breezy/tests/per_workingtree/test_workingtree.py 2019-03-04 02:41:29 +0000
@@ -1061,17 +1061,14 @@
1061 tree = tree.controldir.open_workingtree()1061 tree = tree.controldir.open_workingtree()
1062 self.assertFalse(tree.case_sensitive)1062 self.assertFalse(tree.case_sensitive)
10631063
1064 def test_supports_executable(self):1064 def test_trust_executable(self):
1065 self.build_tree(['filename'])1065 self.build_tree(['filename'])
1066 tree = self.make_branch_and_tree('.')1066 tree = self.make_branch_and_tree('.')
1067 tree.add('filename')1067 tree.add('filename')
1068 self.assertIsInstance(tree._supports_executable(), bool)1068 self.assertIsInstance(tree.trust_executable_bit, bool)
1069 if tree._supports_executable():1069 if tree.trust_executable_bit:
1070 tree.lock_read()1070 with tree.lock_read():
1071 try:
1072 self.assertFalse(tree.is_executable('filename'))1071 self.assertFalse(tree.is_executable('filename'))
1073 finally:
1074 tree.unlock()
1075 os.chmod('filename', 0o755)1072 os.chmod('filename', 0o755)
1076 self.addCleanup(tree.lock_read().unlock)1073 self.addCleanup(tree.lock_read().unlock)
1077 self.assertTrue(tree.is_executable('filename'))1074 self.assertTrue(tree.is_executable('filename'))
@@ -1326,3 +1323,12 @@
1326 self.assertSubset(1323 self.assertSubset(
1327 [self.workingtree_format.supports_store_uncommitted],1324 [self.workingtree_format.supports_store_uncommitted],
1328 (True, False))1325 (True, False))
1326
1327
1328class TestTreeAttributes(TestCaseWithWorkingTree):
1329
1330 def test_trust_executable_bit(self):
1331 wt = self.make_branch_and_tree('wt')
1332 self.assertSubset(
1333 [wt.trust_executable_bit],
1334 (True, False))
13291335
=== modified file 'breezy/tests/test_osutils.py'
--- breezy/tests/test_osutils.py 2018-11-17 18:49:41 +0000
+++ breezy/tests/test_osutils.py 2019-03-04 02:41:29 +0000
@@ -62,6 +62,8 @@
6262
63term_ios_feature = features.ModuleAvailableFeature('termios')63term_ios_feature = features.ModuleAvailableFeature('termios')
6464
65psutil_feature = features.ModuleAvailableFeature('psutil')
66
6567
66def _already_unicode(s):68def _already_unicode(s):
67 return s69 return s
@@ -2340,3 +2342,16 @@
2340 import pywintypes2342 import pywintypes
2341 self.assertTrue(osutils.is_environment_error(2343 self.assertTrue(osutils.is_environment_error(
2342 pywintypes.error(errno.EINVAL, "Invalid parameter", "Caller")))2344 pywintypes.error(errno.EINVAL, "Invalid parameter", "Caller")))
2345
2346
2347class SupportsExecutableTests(tests.TestCaseInTempDir):
2348
2349 def test_returns_bool(self):
2350 self.assertIsInstance(osutils.fs_supports_executable(self.test_dir), bool)
2351
2352
2353class GetFsTypeTests(tests.TestCaseInTempDir):
2354
2355 def test_returns_string(self):
2356 self.requireFeature(psutil_feature)
2357 self.assertIsInstance(osutils.get_fs_type(self.test_dir), str)
23432358
=== modified file 'breezy/transform.py'
--- breezy/transform.py 2019-02-02 15:13:30 +0000
+++ breezy/transform.py 2019-03-04 02:41:29 +0000
@@ -761,7 +761,7 @@
761761
762 def _set_executability(self, path, trans_id):762 def _set_executability(self, path, trans_id):
763 """Set the executability of versioned files """763 """Set the executability of versioned files """
764 if self._tree._supports_executable():764 if self._tree.trust_executable_bit:
765 new_executability = self._new_executability[trans_id]765 new_executability = self._new_executability[trans_id]
766 abspath = self._tree.abspath(path)766 abspath = self._tree.abspath(path)
767 current_mode = os.stat(abspath).st_mode767 current_mode = os.stat(abspath).st_mode
@@ -1263,8 +1263,7 @@
12631263
1264 def _limbo_supports_executable(self):1264 def _limbo_supports_executable(self):
1265 """Check if the limbo path supports the executable bit."""1265 """Check if the limbo path supports the executable bit."""
1266 # FIXME: Check actual file system capabilities of limbodir1266 return osutils.fs_supports_executable(self._limbodir)
1267 return osutils.supports_executable()
12681267
1269 def _limbo_name(self, trans_id):1268 def _limbo_name(self, trans_id):
1270 """Generate the limbo name of a file"""1269 """Generate the limbo name of a file"""
12711270
=== modified file 'breezy/workingtree.py'
--- breezy/workingtree.py 2019-02-14 22:18:59 +0000
+++ breezy/workingtree.py 2019-03-04 02:41:29 +0000
@@ -152,12 +152,6 @@
152 """152 """
153 return self._format.supports_merge_modified153 return self._format.supports_merge_modified
154154
155 def _supports_executable(self):
156 if sys.platform == 'win32':
157 return False
158 # FIXME: Ideally this should check the file system
159 return True
160
161 def break_lock(self):155 def break_lock(self):
162 """Break a lock if one is present from another instance.156 """Break a lock if one is present from another instance.
163157
@@ -950,7 +944,7 @@
950 else:944 else:
951 mode = stat_value.st_mode945 mode = stat_value.st_mode
952 kind = osutils.file_kind_from_stat_mode(mode)946 kind = osutils.file_kind_from_stat_mode(mode)
953 if not self._supports_executable():947 if not self.trust_executable_bit:
954 executable = entry is not None and entry.executable948 executable = entry is not None and entry.executable
955 else:949 else:
956 executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)950 executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
@@ -1426,8 +1420,6 @@
14261420
1427 requires_normalized_unicode_filenames = False1421 requires_normalized_unicode_filenames = False
14281422
1429 case_sensitive_filename = "FoRMaT"
1430
1431 missing_parent_conflicts = False1423 missing_parent_conflicts = False
1432 """If this format supports missing parent conflicts."""1424 """If this format supports missing parent conflicts."""
14331425
14341426
=== modified file 'setup.py'
--- setup.py 2019-02-06 05:44:37 +0000
+++ setup.py 2019-03-04 02:41:29 +0000
@@ -61,6 +61,8 @@
61 # no way to enable them by default and let users opt out.61 # no way to enable them by default and let users opt out.
62 'fastimport>=0.9.8',62 'fastimport>=0.9.8',
63 'dulwich>=0.19.11',63 'dulwich>=0.19.11',
64 # Used for getting filesystem information
65 'psutil',
64 ],66 ],
65 'extras_require': {67 'extras_require': {
66 'fastimport': [],68 'fastimport': [],

Subscribers

People subscribed via source and target branches