##// END OF EJS Templates
vcs-lib: bulk of changes for python3 support
super-admin -
r5074:09a42e1d default
parent child Browse files
Show More
@@ -140,8 +140,8 b' class CurlSession(object):'
140 curl.perform()
140 curl.perform()
141
141
142 status_code = curl.getinfo(pycurl.HTTP_CODE)
142 status_code = curl.getinfo(pycurl.HTTP_CODE)
143
143 content_type = curl.getinfo(pycurl.CONTENT_TYPE)
144 return CurlResponse(response_buffer, status_code)
144 return CurlResponse(response_buffer, status_code, content_type)
145
145
146
146
147 class CurlResponse(object):
147 class CurlResponse(object):
@@ -153,9 +153,13 b' class CurlResponse(object):'
153 `requests` as a drop in replacement for benchmarking purposes.
153 `requests` as a drop in replacement for benchmarking purposes.
154 """
154 """
155
155
156 def __init__(self, response_buffer, status_code):
156 def __init__(self, response_buffer, status_code, content_type=''):
157 self._response_buffer = response_buffer
157 self._response_buffer = response_buffer
158 self._status_code = status_code
158 self._status_code = status_code
159 self._content_type = content_type
160
161 def __repr__(self):
162 return f'CurlResponse(code={self._status_code}, content_type={self._content_type})'
159
163
160 @property
164 @property
161 def content(self):
165 def content(self):
@@ -168,6 +172,10 b' class CurlResponse(object):'
168 def status_code(self):
172 def status_code(self):
169 return self._status_code
173 return self._status_code
170
174
175 @property
176 def content_type(self):
177 return self._content_type
178
171 def iter_content(self, chunk_size):
179 def iter_content(self, chunk_size):
172 self._response_buffer.seek(0)
180 self._response_buffer.seek(0)
173 while 1:
181 while 1:
@@ -25,7 +25,7 b' VCS Backends module'
25 import os
25 import os
26 import logging
26 import logging
27
27
28 from pprint import pformat
28 from rhodecode import typing
29
29
30 from rhodecode.lib.vcs.conf import settings
30 from rhodecode.lib.vcs.conf import settings
31 from rhodecode.lib.vcs.exceptions import VCSError
31 from rhodecode.lib.vcs.exceptions import VCSError
@@ -36,7 +36,7 b' from rhodecode.lib.vcs.utils.imports imp'
36 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
37
37
38
38
39 def get_vcs_instance(repo_path, *args, **kwargs):
39 def get_vcs_instance(repo_path, *args, **kwargs) -> typing.VCSRepo | None:
40 """
40 """
41 Given a path to a repository an instance of the corresponding vcs backend
41 Given a path to a repository an instance of the corresponding vcs backend
42 repository class is created and returned. If no repository can be found
42 repository class is created and returned. If no repository can be found
@@ -54,10 +54,10 b' def get_vcs_instance(repo_path, *args, *'
54 backend = get_backend(vcs_alias)
54 backend = get_backend(vcs_alias)
55
55
56 if explicit_vcs_alias:
56 if explicit_vcs_alias:
57 # do final verification of existance of the path, this does the
57 # do final verification of existence of the path, this does the
58 # same as get_scm() call which we skip in explicit_vcs_alias
58 # same as get_scm() call which we skip in explicit_vcs_alias
59 if not os.path.isdir(repo_path):
59 if not os.path.isdir(repo_path):
60 raise VCSError("Given path %s is not a directory" % repo_path)
60 raise VCSError(f"Given path {repo_path} is not a directory")
61 except VCSError:
61 except VCSError:
62 log.exception(
62 log.exception(
63 'Perhaps this repository is in db and not in '
63 'Perhaps this repository is in db and not in '
@@ -68,15 +68,15 b' def get_vcs_instance(repo_path, *args, *'
68 return backend(repo_path=repo_path, *args, **kwargs)
68 return backend(repo_path=repo_path, *args, **kwargs)
69
69
70
70
71 def get_backend(alias):
71 def get_backend(alias) -> typing.VCSRepoClass:
72 """
72 """
73 Returns ``Repository`` class identified by the given alias or raises
73 Returns ``Repository`` class identified by the given alias or raises
74 VCSError if alias is not recognized or backend class cannot be imported.
74 VCSError if alias is not recognized or backend class cannot be imported.
75 """
75 """
76 if alias not in settings.BACKENDS:
76 if alias not in settings.BACKENDS:
77 raise VCSError(
77 raise VCSError(
78 "Given alias '%s' is not recognized! Allowed aliases:\n%s" %
78 f"Given alias '{alias}' is not recognized! "
79 (alias, pformat(settings.BACKENDS.keys())))
79 f"Allowed aliases:{settings.BACKENDS.keys()}")
80 backend_path = settings.BACKENDS[alias]
80 backend_path = settings.BACKENDS[alias]
81 klass = import_class(backend_path)
81 klass = import_class(backend_path)
82 return klass
82 return klass
@@ -29,7 +29,7 b' import datetime'
29 import fnmatch
29 import fnmatch
30 import itertools
30 import itertools
31 import logging
31 import logging
32 import collections
32 import dataclasses
33 import warnings
33 import warnings
34
34
35 from zope.cachedescriptors.property import Lazy as LazyProperty
35 from zope.cachedescriptors.property import Lazy as LazyProperty
@@ -55,10 +55,17 b' FILEMODE_DEFAULT = 0o100644'
55 FILEMODE_EXECUTABLE = 0o100755
55 FILEMODE_EXECUTABLE = 0o100755
56 EMPTY_COMMIT_ID = '0' * 40
56 EMPTY_COMMIT_ID = '0' * 40
57
57
58 _Reference = collections.namedtuple('Reference', ('type', 'name', 'commit_id'))
59
58
59 @dataclasses.dataclass
60 class Reference:
61 type: str
62 name: str
63 commit_id: str
60
64
61 class Reference(_Reference):
65 def __iter__(self):
66 yield self.type
67 yield self.name
68 yield self.commit_id
62
69
63 @property
70 @property
64 def branch(self):
71 def branch(self):
@@ -74,8 +81,15 b' class Reference(_Reference):'
74 def to_str(self):
81 def to_str(self):
75 return reference_to_unicode(self)
82 return reference_to_unicode(self)
76
83
84 def asdict(self):
85 return dict(
86 type=self.type,
87 name=self.name,
88 commit_id=self.commit_id
89 )
77
90
78 def unicode_to_reference(raw):
91
92 def unicode_to_reference(raw: str):
79 """
93 """
80 Convert a unicode (or string) to a reference object.
94 Convert a unicode (or string) to a reference object.
81 If unicode evaluates to False it returns None.
95 If unicode evaluates to False it returns None.
@@ -220,8 +234,8 b' class MergeResponse(object):'
220
234
221 # Deprecations
235 # Deprecations
222 MergeFailureReason._DEPRECATED_MISSING_COMMIT: lazy_ugettext(
236 MergeFailureReason._DEPRECATED_MISSING_COMMIT: lazy_ugettext(
223 u'This pull request cannot be merged because the target or the '
237 'This pull request cannot be merged because the target or the '
224 u'source reference is missing.'),
238 'source reference is missing.'),
225
239
226 }
240 }
227
241
@@ -972,6 +986,9 b' class BaseCommit(object):'
972 d.pop('repository', None)
986 d.pop('repository', None)
973 return d
987 return d
974
988
989 def get_remote(self):
990 return self._remote
991
975 def serialize(self):
992 def serialize(self):
976 return self.__json__()
993 return self.__json__()
977
994
@@ -1097,7 +1114,7 b' class BaseCommit(object):'
1097
1114
1098 return author_email(self.author)
1115 return author_email(self.author)
1099
1116
1100 def get_file_mode(self, path):
1117 def get_file_mode(self, path: bytes):
1101 """
1118 """
1102 Returns stat mode of the file at `path`.
1119 Returns stat mode of the file at `path`.
1103 """
1120 """
@@ -1115,7 +1132,13 b' class BaseCommit(object):'
1115 """
1132 """
1116 raise NotImplementedError
1133 raise NotImplementedError
1117
1134
1118 def get_file_content(self, path):
1135 def node_md5_hash(self, path):
1136 """
1137 Returns md5 hash of a node data
1138 """
1139 raise NotImplementedError
1140
1141 def get_file_content(self, path) -> bytes:
1119 """
1142 """
1120 Returns content of the file at the given `path`.
1143 Returns content of the file at the given `path`.
1121 """
1144 """
@@ -1168,7 +1191,7 b' class BaseCommit(object):'
1168 """
1191 """
1169 raise NotImplementedError
1192 raise NotImplementedError
1170
1193
1171 def get_nodes(self, path):
1194 def get_nodes(self, path, pre_load=None):
1172 """
1195 """
1173 Returns combined ``DirNode`` and ``FileNode`` objects list representing
1196 Returns combined ``DirNode`` and ``FileNode`` objects list representing
1174 state of commit at the given ``path``.
1197 state of commit at the given ``path``.
@@ -1194,13 +1217,13 b' class BaseCommit(object):'
1194 """
1217 """
1195 return None
1218 return None
1196
1219
1197 def archive_repo(self, archive_dest_path, kind='tgz', subrepos=None,
1220 def archive_repo(self, archive_name_key, kind='tgz', subrepos=None,
1198 archive_dir_name=None, write_metadata=False, mtime=None,
1221 archive_dir_name=None, write_metadata=False, mtime=None,
1199 archive_at_path='/'):
1222 archive_at_path='/', cache_config=None):
1200 """
1223 """
1201 Creates an archive containing the contents of the repository.
1224 Creates an archive containing the contents of the repository.
1202
1225
1203 :param archive_dest_path: path to the file which to create the archive.
1226 :param archive_name_key: unique key under this archive should be generated
1204 :param kind: one of following: ``"tbz2"``, ``"tgz"``, ``"zip"``.
1227 :param kind: one of following: ``"tbz2"``, ``"tgz"``, ``"zip"``.
1205 :param archive_dir_name: name of root directory in archive.
1228 :param archive_dir_name: name of root directory in archive.
1206 Default is repository name and commit's short_id joined with dash:
1229 Default is repository name and commit's short_id joined with dash:
@@ -1209,9 +1232,11 b' class BaseCommit(object):'
1209 :param mtime: custom modification time for archive creation, defaults
1232 :param mtime: custom modification time for archive creation, defaults
1210 to time.time() if not given.
1233 to time.time() if not given.
1211 :param archive_at_path: pack files at this path (default '/')
1234 :param archive_at_path: pack files at this path (default '/')
1235 :param cache_config: config spec to send to vcsserver to configure the backend to store files
1212
1236
1213 :raise VCSError: If prefix has a problem.
1237 :raise VCSError: If prefix has a problem.
1214 """
1238 """
1239 cache_config = cache_config or {}
1215 allowed_kinds = [x[0] for x in settings.ARCHIVE_SPECS]
1240 allowed_kinds = [x[0] for x in settings.ARCHIVE_SPECS]
1216 if kind not in allowed_kinds:
1241 if kind not in allowed_kinds:
1217 raise ImproperArchiveTypeError(
1242 raise ImproperArchiveTypeError(
@@ -1223,8 +1248,8 b' class BaseCommit(object):'
1223 commit_id = self.raw_id
1248 commit_id = self.raw_id
1224
1249
1225 return self.repository._remote.archive_repo(
1250 return self.repository._remote.archive_repo(
1226 archive_dest_path, kind, mtime, archive_at_path,
1251 archive_name_key, kind, mtime, archive_at_path,
1227 archive_dir_name, commit_id)
1252 archive_dir_name, commit_id, cache_config)
1228
1253
1229 def _validate_archive_prefix(self, archive_dir_name):
1254 def _validate_archive_prefix(self, archive_dir_name):
1230 if archive_dir_name is None:
1255 if archive_dir_name is None:
@@ -1232,11 +1257,13 b' class BaseCommit(object):'
1232 repo_name=safe_str(self.repository.name),
1257 repo_name=safe_str(self.repository.name),
1233 short_id=self.short_id)
1258 short_id=self.short_id)
1234 elif not isinstance(archive_dir_name, str):
1259 elif not isinstance(archive_dir_name, str):
1235 raise ValueError("prefix not a bytes object: %s" % repr(archive_dir_name))
1260 raise ValueError(f"archive_dir_name is not str object but: {type(archive_dir_name)}")
1236 elif archive_dir_name.startswith('/'):
1261 elif archive_dir_name.startswith('/'):
1237 raise VCSError("Prefix cannot start with leading slash")
1262 raise VCSError("Prefix cannot start with leading slash")
1238 elif archive_dir_name.strip() == '':
1263 elif archive_dir_name.strip() == '':
1239 raise VCSError("Prefix cannot be empty")
1264 raise VCSError("Prefix cannot be empty")
1265 elif not archive_dir_name.isascii():
1266 raise VCSError("Prefix cannot contain non ascii characters")
1240 return archive_dir_name
1267 return archive_dir_name
1241
1268
1242 @LazyProperty
1269 @LazyProperty
@@ -1321,14 +1348,28 b' class BaseCommit(object):'
1321 """
1348 """
1322 Similar to os.walk method. Insted of filesystem it walks through
1349 Similar to os.walk method. Insted of filesystem it walks through
1323 commit starting at given ``topurl``. Returns generator of tuples
1350 commit starting at given ``topurl``. Returns generator of tuples
1324 (topnode, dirnodes, filenodes).
1351 (top_node, dirnodes, filenodes).
1325 """
1352 """
1326 topnode = self.get_node(topurl)
1353 from rhodecode.lib.vcs.nodes import DirNode
1327 if not topnode.is_dir():
1354
1355 if isinstance(topurl, DirNode):
1356 top_node = topurl
1357 else:
1358 top_node = self.get_node(topurl)
1359
1360 has_default_pre_load = False
1361 if isinstance(top_node, DirNode):
1362 # used to inject as we walk same defaults as given top_node
1363 default_pre_load = top_node.default_pre_load
1364 has_default_pre_load = True
1365
1366 if not top_node.is_dir():
1328 return
1367 return
1329 yield (topnode, topnode.dirs, topnode.files)
1368 yield top_node, top_node.dirs, top_node.files
1330 for dirnode in topnode.dirs:
1369 for dir_node in top_node.dirs:
1331 for tup in self.walk(dirnode.path):
1370 if has_default_pre_load:
1371 dir_node.default_pre_load = default_pre_load
1372 for tup in self.walk(dir_node):
1332 yield tup
1373 yield tup
1333
1374
1334 def get_filenodes_generator(self):
1375 def get_filenodes_generator(self):
@@ -1345,15 +1386,15 b' class BaseCommit(object):'
1345
1386
1346 def no_node_at_path(self, path):
1387 def no_node_at_path(self, path):
1347 return NodeDoesNotExistError(
1388 return NodeDoesNotExistError(
1348 u"There is no file nor directory at the given path: "
1389 f"There is no file nor directory at the given path: "
1349 u"`%s` at commit %s" % (safe_unicode(path), self.short_id))
1390 f"`{safe_str(path)}` at commit {self.short_id}")
1350
1391
1351 def _fix_path(self, path):
1392 def _fix_path(self, path: str) -> str:
1352 """
1393 """
1353 Paths are stored without trailing slash so we need to get rid off it if
1394 Paths are stored without trailing slash so we need to get rid off it if
1354 needed.
1395 needed.
1355 """
1396 """
1356 return path.rstrip('/')
1397 return safe_str(path).rstrip('/')
1357
1398
1358 #
1399 #
1359 # Deprecated API based on changesets
1400 # Deprecated API based on changesets
@@ -1380,9 +1421,7 b' class BaseChangesetClass(type):'
1380 return isinstance(instance, BaseCommit)
1421 return isinstance(instance, BaseCommit)
1381
1422
1382
1423
1383 class BaseChangeset(BaseCommit):
1424 class BaseChangeset(BaseCommit, metaclass=BaseChangesetClass):
1384
1385 __metaclass__ = BaseChangesetClass
1386
1425
1387 def __new__(cls, *args, **kwargs):
1426 def __new__(cls, *args, **kwargs):
1388 warnings.warn(
1427 warnings.warn(
@@ -1624,9 +1663,7 b' class BaseInMemoryChangesetClass(type):'
1624 return isinstance(instance, BaseInMemoryCommit)
1663 return isinstance(instance, BaseInMemoryCommit)
1625
1664
1626
1665
1627 class BaseInMemoryChangeset(BaseInMemoryCommit):
1666 class BaseInMemoryChangeset(BaseInMemoryCommit, metaclass=BaseInMemoryChangesetClass):
1628
1629 __metaclass__ = BaseInMemoryChangesetClass
1630
1667
1631 def __new__(cls, *args, **kwargs):
1668 def __new__(cls, *args, **kwargs):
1632 warnings.warn(
1669 warnings.warn(
@@ -1676,14 +1713,14 b' class EmptyCommit(BaseCommit):'
1676 def id(self):
1713 def id(self):
1677 return self.raw_id
1714 return self.raw_id
1678
1715
1679 def get_path_commit(self, path):
1716 def get_path_commit(self, path, pre_load=None):
1680 return self
1717 return self
1681
1718
1682 def get_file_content(self, path):
1719 def get_file_content(self, path) -> bytes:
1683 return u''
1720 return b''
1684
1721
1685 def get_file_content_streamed(self, path):
1722 def get_file_content_streamed(self, path):
1686 yield self.get_file_content()
1723 yield self.get_file_content(path)
1687
1724
1688 def get_file_size(self, path):
1725 def get_file_size(self, path):
1689 return 0
1726 return 0
@@ -1695,9 +1732,7 b' class EmptyChangesetClass(type):'
1695 return isinstance(instance, EmptyCommit)
1732 return isinstance(instance, EmptyCommit)
1696
1733
1697
1734
1698 class EmptyChangeset(EmptyCommit):
1735 class EmptyChangeset(EmptyCommit, metaclass=EmptyChangesetClass):
1699
1700 __metaclass__ = EmptyChangesetClass
1701
1736
1702 def __new__(cls, *args, **kwargs):
1737 def __new__(cls, *args, **kwargs):
1703 warnings.warn(
1738 warnings.warn(
@@ -1731,7 +1766,7 b' class EmptyRepository(BaseRepository):'
1731
1766
1732 def get_diff(self, *args, **kwargs):
1767 def get_diff(self, *args, **kwargs):
1733 from rhodecode.lib.vcs.backends.git.diff import GitDiff
1768 from rhodecode.lib.vcs.backends.git.diff import GitDiff
1734 return GitDiff('')
1769 return GitDiff(b'')
1735
1770
1736
1771
1737 class CollectionGenerator(object):
1772 class CollectionGenerator(object):
@@ -1739,8 +1774,7 b' class CollectionGenerator(object):'
1739 def __init__(self, repo, commit_ids, collection_size=None, pre_load=None, translate_tag=None):
1774 def __init__(self, repo, commit_ids, collection_size=None, pre_load=None, translate_tag=None):
1740 self.repo = repo
1775 self.repo = repo
1741 self.commit_ids = commit_ids
1776 self.commit_ids = commit_ids
1742 # TODO: (oliver) this isn't currently hooked up
1777 self.collection_size = collection_size
1743 self.collection_size = None
1744 self.pre_load = pre_load
1778 self.pre_load = pre_load
1745 self.translate_tag = translate_tag
1779 self.translate_tag = translate_tag
1746
1780
@@ -1762,11 +1796,16 b' class CollectionGenerator(object):'
1762 commit_id=commit_id, pre_load=self.pre_load,
1796 commit_id=commit_id, pre_load=self.pre_load,
1763 translate_tag=self.translate_tag)
1797 translate_tag=self.translate_tag)
1764
1798
1765 def __getslice__(self, i, j):
1799 def __getitem__(self, key):
1766 """
1800 """Return either a single element by index, or a sliced collection."""
1767 Returns an iterator of sliced repository
1801
1768 """
1802 if isinstance(key, slice):
1769 commit_ids = self.commit_ids[i:j]
1803 commit_ids = self.commit_ids[key.start:key.stop]
1804
1805 else:
1806 # single item
1807 commit_ids = self.commit_ids[key]
1808
1770 return self.__class__(
1809 return self.__class__(
1771 self.repo, commit_ids, pre_load=self.pre_load,
1810 self.repo, commit_ids, pre_load=self.pre_load,
1772 translate_tag=self.translate_tag)
1811 translate_tag=self.translate_tag)
@@ -1830,10 +1869,16 b' class Diff(object):'
1830 :attr:`_header_re` and :attr:`_meta_re`.
1869 :attr:`_header_re` and :attr:`_meta_re`.
1831 """
1870 """
1832 _meta_re = None
1871 _meta_re = None
1833 _header_re = None
1872 _header_re: bytes = re.compile(br"")
1834
1873
1835 def __init__(self, raw_diff):
1874 def __init__(self, raw_diff: bytes):
1836 self.raw = raw_diff
1875 if not isinstance(raw_diff, bytes):
1876 raise Exception(f'raw_diff must be bytes - got {type(raw_diff)}')
1877
1878 self.raw = memoryview(raw_diff)
1879
1880 def get_header_re(self):
1881 return self._header_re
1837
1882
1838 def chunks(self):
1883 def chunks(self):
1839 """
1884 """
@@ -1842,35 +1887,44 b' class Diff(object):'
1842 we can detect last chunk as this was also has special rule
1887 we can detect last chunk as this was also has special rule
1843 """
1888 """
1844
1889
1845 diff_parts = ('\n' + self.raw).split('\ndiff --git')
1890 diff_parts = (b'\n' + bytes(self.raw)).split(b'\ndiff --git')
1846 header = diff_parts[0]
1847
1848 if self._meta_re:
1849 match = self._meta_re.match(header)
1850
1891
1851 chunks = diff_parts[1:]
1892 chunks = diff_parts[1:]
1852 total_chunks = len(chunks)
1893 total_chunks = len(chunks)
1853
1894
1854 return (
1895 def diff_iter(_chunks):
1855 DiffChunk(chunk, self, cur_chunk == total_chunks)
1896 for cur_chunk, chunk in enumerate(_chunks, start=1):
1856 for cur_chunk, chunk in enumerate(chunks, start=1))
1897 yield DiffChunk(chunk, self, cur_chunk == total_chunks)
1898 return diff_iter(chunks)
1857
1899
1858
1900
1859 class DiffChunk(object):
1901 class DiffChunk(object):
1860
1902
1861 def __init__(self, chunk, diff, last_chunk):
1903 def __init__(self, chunk: bytes, diff_obj: Diff, is_last_chunk: bool):
1862 self._diff = diff
1904 self.diff_obj = diff_obj
1863
1905
1864 # since we split by \ndiff --git that part is lost from original diff
1906 # since we split by \ndiff --git that part is lost from original diff
1865 # we need to re-apply it at the end, EXCEPT ! if it's last chunk
1907 # we need to re-apply it at the end, EXCEPT ! if it's last chunk
1866 if not last_chunk:
1908 if not is_last_chunk:
1867 chunk += '\n'
1909 chunk += b'\n'
1868
1910 header_re = self.diff_obj.get_header_re()
1869 match = self._diff._header_re.match(chunk)
1911 match = header_re.match(chunk)
1870 self.header = match.groupdict()
1912 self.header = match.groupdict()
1871 self.diff = chunk[match.end():]
1913 self.diff = chunk[match.end():]
1872 self.raw = chunk
1914 self.raw = chunk
1873
1915
1916 @property
1917 def header_as_str(self):
1918 if self.header:
1919 def safe_str_on_bytes(val):
1920 if isinstance(val, bytes):
1921 return safe_str(val)
1922 return val
1923 return {safe_str(k): safe_str_on_bytes(v) for k, v in self.header.items()}
1924
1925 def __repr__(self):
1926 return f'DiffChunk({self.header_as_str})'
1927
1874
1928
1875 class BasePathPermissionChecker(object):
1929 class BasePathPermissionChecker(object):
1876
1930
@@ -1885,10 +1939,10 b' class BasePathPermissionChecker(object):'
1885
1939
1886 @property
1940 @property
1887 def has_full_access(self):
1941 def has_full_access(self):
1888 raise NotImplemented()
1942 raise NotImplementedError()
1889
1943
1890 def has_access(self, path):
1944 def has_access(self, path):
1891 raise NotImplemented()
1945 raise NotImplementedError()
1892
1946
1893
1947
1894 class AllPathPermissionChecker(BasePathPermissionChecker):
1948 class AllPathPermissionChecker(BasePathPermissionChecker):
@@ -22,8 +22,6 b''
22 GIT commit module
22 GIT commit module
23 """
23 """
24
24
25 import re
26 import io
27 import stat
25 import stat
28 import configparser
26 import configparser
29 from itertools import chain
27 from itertools import chain
@@ -31,9 +29,7 b' from itertools import chain'
31 from zope.cachedescriptors.property import Lazy as LazyProperty
29 from zope.cachedescriptors.property import Lazy as LazyProperty
32
30
33 from rhodecode.lib.datelib import utcdate_fromtimestamp
31 from rhodecode.lib.datelib import utcdate_fromtimestamp
34 from rhodecode.lib.utils import safe_unicode, safe_str
32 from rhodecode.lib.str_utils import safe_bytes, safe_str
35 from rhodecode.lib.utils2 import safe_int
36 from rhodecode.lib.vcs.conf import settings
37 from rhodecode.lib.vcs.backends import base
33 from rhodecode.lib.vcs.backends import base
38 from rhodecode.lib.vcs.exceptions import CommitError, NodeDoesNotExistError
34 from rhodecode.lib.vcs.exceptions import CommitError, NodeDoesNotExistError
39 from rhodecode.lib.vcs.nodes import (
35 from rhodecode.lib.vcs.nodes import (
@@ -92,7 +88,7 b' class GitCommit(base.BaseCommit):'
92 for attr, value in result.items():
88 for attr, value in result.items():
93 if attr in ["author", "message"]:
89 if attr in ["author", "message"]:
94 if value:
90 if value:
95 value = safe_unicode(value)
91 value = safe_str(value)
96 elif attr == "date":
92 elif attr == "date":
97 value = utcdate_fromtimestamp(*value)
93 value = utcdate_fromtimestamp(*value)
98 elif attr == "parents":
94 elif attr == "parents":
@@ -119,15 +115,15 b' class GitCommit(base.BaseCommit):'
119
115
120 @LazyProperty
116 @LazyProperty
121 def message(self):
117 def message(self):
122 return safe_unicode(self._remote.message(self.id))
118 return safe_str(self._remote.message(self.id))
123
119
124 @LazyProperty
120 @LazyProperty
125 def committer(self):
121 def committer(self):
126 return safe_unicode(self._remote.author(self.id))
122 return safe_str(self._remote.author(self.id))
127
123
128 @LazyProperty
124 @LazyProperty
129 def author(self):
125 def author(self):
130 return safe_unicode(self._remote.author(self.id))
126 return safe_str(self._remote.author(self.id))
131
127
132 @LazyProperty
128 @LazyProperty
133 def date(self):
129 def date(self):
@@ -143,7 +139,7 b' class GitCommit(base.BaseCommit):'
143
139
144 @LazyProperty
140 @LazyProperty
145 def tags(self):
141 def tags(self):
146 tags = [safe_unicode(name) for name,
142 tags = [safe_str(name) for name,
147 commit_id in self.repository.tags.items()
143 commit_id in self.repository.tags.items()
148 if commit_id == self.raw_id]
144 if commit_id == self.raw_id]
149 return tags
145 return tags
@@ -159,7 +155,7 b' class GitCommit(base.BaseCommit):'
159 def _set_branch(self, branches):
155 def _set_branch(self, branches):
160 if branches:
156 if branches:
161 # actually commit can have multiple branches in git
157 # actually commit can have multiple branches in git
162 return safe_unicode(branches[0])
158 return safe_str(branches[0])
163
159
164 @LazyProperty
160 @LazyProperty
165 def branch(self):
161 def branch(self):
@@ -167,6 +163,7 b' class GitCommit(base.BaseCommit):'
167 return self._set_branch(branches)
163 return self._set_branch(branches)
168
164
169 def _get_tree_id_for_path(self, path):
165 def _get_tree_id_for_path(self, path):
166
170 path = safe_str(path)
167 path = safe_str(path)
171 if path in self._paths:
168 if path in self._paths:
172 return self._paths[path]
169 return self._paths[path]
@@ -202,11 +199,10 b' class GitCommit(base.BaseCommit):'
202 return NodeKind.SUBMODULE
199 return NodeKind.SUBMODULE
203 return None
200 return None
204
201
205 def _get_filectx(self, path):
202 def _assert_is_path(self, path):
206 path = self._fix_path(path)
203 path = self._fix_path(path)
207 if self._get_kind(path) != NodeKind.FILE:
204 if self._get_kind(path) != NodeKind.FILE:
208 raise CommitError(
205 raise CommitError(f"File does not exist for commit {self.raw_id} at '{path}'")
209 "File does not exist for commit %s at '%s'" % (self.raw_id, path))
210 return path
206 return path
211
207
212 def _get_file_nodes(self):
208 def _get_file_nodes(self):
@@ -231,17 +227,19 b' class GitCommit(base.BaseCommit):'
231
227
232 def _make_commits(self, commit_ids):
228 def _make_commits(self, commit_ids):
233 def commit_maker(_commit_id):
229 def commit_maker(_commit_id):
234 return self.repository.get_commit(commit_id=commit_id)
230 return self.repository.get_commit(commit_id=_commit_id)
235
231
236 return [commit_maker(commit_id) for commit_id in commit_ids]
232 return [commit_maker(commit_id) for commit_id in commit_ids]
237
233
238 def get_file_mode(self, path):
234 def get_file_mode(self, path: bytes):
239 """
235 """
240 Returns stat mode of the file at the given `path`.
236 Returns stat mode of the file at the given `path`.
241 """
237 """
242 path = safe_str(path)
238 path = self._assert_is_path(path)
239
243 # ensure path is traversed
240 # ensure path is traversed
244 self._get_tree_id_for_path(path)
241 self._get_tree_id_for_path(path)
242
245 return self._stat_modes[path]
243 return self._stat_modes[path]
246
244
247 def is_link(self, path):
245 def is_link(self, path):
@@ -251,6 +249,10 b' class GitCommit(base.BaseCommit):'
251 tree_id, _ = self._get_tree_id_for_path(path)
249 tree_id, _ = self._get_tree_id_for_path(path)
252 return self._remote.is_binary(tree_id)
250 return self._remote.is_binary(tree_id)
253
251
252 def node_md5_hash(self, path):
253 path = self._assert_is_path(path)
254 return self._remote.md5_hash(self.raw_id, path)
255
254 def get_file_content(self, path):
256 def get_file_content(self, path):
255 """
257 """
256 Returns content of the file at given `path`.
258 Returns content of the file at given `path`.
@@ -276,7 +278,7 b' class GitCommit(base.BaseCommit):'
276 which file at given `path` has been modified.
278 which file at given `path` has been modified.
277 """
279 """
278
280
279 path = self._get_filectx(path)
281 path = self._assert_is_path(path)
280 hist = self._remote.node_history(self.raw_id, path, limit)
282 hist = self._remote.node_history(self.raw_id, path, limit)
281 return [
283 return [
282 self.repository.get_commit(commit_id=commit_id, pre_load=pre_load)
284 self.repository.get_commit(commit_id=commit_id, pre_load=pre_load)
@@ -296,11 +298,11 b' class GitCommit(base.BaseCommit):'
296 lambda: self.repository.get_commit(commit_id=commit_id, pre_load=pre_load),
298 lambda: self.repository.get_commit(commit_id=commit_id, pre_load=pre_load),
297 content)
299 content)
298
300
299 def get_nodes(self, path):
301 def get_nodes(self, path, pre_load=None):
300
302
301 if self._get_kind(path) != NodeKind.DIR:
303 if self._get_kind(path) != NodeKind.DIR:
302 raise CommitError(
304 raise CommitError(
303 "Directory does not exist for commit %s at '%s'" % (self.raw_id, path))
305 f"Directory does not exist for commit {self.raw_id} at '{path}'")
304 path = self._fix_path(path)
306 path = self._fix_path(path)
305
307
306 tree_id, _ = self._get_tree_id_for_path(path)
308 tree_id, _ = self._get_tree_id_for_path(path)
@@ -325,12 +327,11 b' class GitCommit(base.BaseCommit):'
325 self._stat_modes[obj_path] = stat_
327 self._stat_modes[obj_path] = stat_
326
328
327 if type_ == 'tree':
329 if type_ == 'tree':
328 dirnodes.append(DirNode(obj_path, commit=self))
330 dirnodes.append(DirNode(safe_bytes(obj_path), commit=self))
329 elif type_ == 'blob':
331 elif type_ == 'blob':
330 filenodes.append(FileNode(obj_path, commit=self, mode=stat_))
332 filenodes.append(FileNode(safe_bytes(obj_path), commit=self, mode=stat_, pre_load=pre_load))
331 else:
333 else:
332 raise CommitError(
334 raise CommitError(f"Requested object should be Tree or Blob, is {type_}")
333 "Requested object should be Tree or Blob, is %s", type_)
334
335
335 nodes = dirnodes + filenodes
336 nodes = dirnodes + filenodes
336 for node in nodes:
337 for node in nodes:
@@ -346,8 +347,8 b' class GitCommit(base.BaseCommit):'
346 tree_id, type_ = self._get_tree_id_for_path(path)
347 tree_id, type_ = self._get_tree_id_for_path(path)
347 except CommitError:
348 except CommitError:
348 raise NodeDoesNotExistError(
349 raise NodeDoesNotExistError(
349 "Cannot find one of parents' directories for a given "
350 f"Cannot find one of parents' directories for a given "
350 "path: %s" % path)
351 f"path: {path}")
351
352
352 if type_ in ['link', 'commit']:
353 if type_ in ['link', 'commit']:
353 url = self._get_submodule_url(path)
354 url = self._get_submodule_url(path)
@@ -357,9 +358,9 b' class GitCommit(base.BaseCommit):'
357 if path == '':
358 if path == '':
358 node = RootNode(commit=self)
359 node = RootNode(commit=self)
359 else:
360 else:
360 node = DirNode(path, commit=self)
361 node = DirNode(safe_bytes(path), commit=self)
361 elif type_ == 'blob':
362 elif type_ == 'blob':
362 node = FileNode(path, commit=self, pre_load=pre_load)
363 node = FileNode(safe_bytes(path), commit=self, pre_load=pre_load)
363 self._stat_modes[path] = node.mode
364 self._stat_modes[path] = node.mode
364 else:
365 else:
365 raise self.no_node_at_path(path)
366 raise self.no_node_at_path(path)
@@ -378,7 +379,7 b' class GitCommit(base.BaseCommit):'
378 file_id = pointer_spec.get('oid_hash')
379 file_id = pointer_spec.get('oid_hash')
379 if self._remote.in_largefiles_store(file_id):
380 if self._remote.in_largefiles_store(file_id):
380 lf_path = self._remote.store_path(file_id)
381 lf_path = self._remote.store_path(file_id)
381 return LargeFileNode(lf_path, commit=self, org_path=path)
382 return LargeFileNode(safe_bytes(lf_path), commit=self, org_path=path)
382
383
383 @LazyProperty
384 @LazyProperty
384 def affected_files(self):
385 def affected_files(self):
@@ -393,7 +394,6 b' class GitCommit(base.BaseCommit):'
393 added = set()
394 added = set()
394 modified = set()
395 modified = set()
395 deleted = set()
396 deleted = set()
396 _r = self._remote
397
397
398 parents = self.parents
398 parents = self.parents
399 if not self.parents:
399 if not self.parents:
@@ -403,14 +403,11 b' class GitCommit(base.BaseCommit):'
403 oid = None
403 oid = None
404 else:
404 else:
405 oid = parent.raw_id
405 oid = parent.raw_id
406 changes = _r.tree_changes(oid, self.raw_id)
406 _added, _modified, _deleted = self._remote.tree_changes(oid, self.raw_id)
407 for (oldpath, newpath), (_, _), (_, _) in changes:
407 added = added | set(_added)
408 if newpath and oldpath:
408 modified = modified | set(_modified)
409 modified.add(newpath)
409 deleted = deleted | set(_deleted)
410 elif newpath and not oldpath:
410
411 added.add(newpath)
412 elif not newpath and oldpath:
413 deleted.add(oldpath)
414 return added, modified, deleted
411 return added, modified, deleted
415
412
416 def _get_paths_for_status(self, status):
413 def _get_paths_for_status(self, status):
@@ -29,7 +29,7 b' from rhodecode.lib.vcs.backends import b'
29
29
30 class GitDiff(base.Diff):
30 class GitDiff(base.Diff):
31
31
32 _header_re = re.compile(r"""
32 _header_re = re.compile(br"""
33 #^diff[ ]--git
33 #^diff[ ]--git
34 [ ]"?a/(?P<a_path>.+?)"?[ ]"?b/(?P<b_path>.+?)"?\n
34 [ ]"?a/(?P<a_path>.+?)"?[ ]"?b/(?P<b_path>.+?)"?\n
35 (?:^old[ ]mode[ ](?P<old_mode>\d+)\n
35 (?:^old[ ]mode[ ](?P<old_mode>\d+)\n
@@ -23,7 +23,7 b' GIT inmemory module'
23 """
23 """
24
24
25 from rhodecode.lib.datelib import date_to_timestamp_plus_offset
25 from rhodecode.lib.datelib import date_to_timestamp_plus_offset
26 from rhodecode.lib.utils import safe_str
26 from rhodecode.lib.str_utils import safe_str, get_default_encodings
27 from rhodecode.lib.vcs.backends import base
27 from rhodecode.lib.vcs.backends import base
28
28
29
29
@@ -50,23 +50,23 b' class GitInMemoryCommit(base.BaseInMemor'
50 if branch is None:
50 if branch is None:
51 branch = self.repository.DEFAULT_BRANCH_NAME
51 branch = self.repository.DEFAULT_BRANCH_NAME
52
52
53 ENCODING = "UTF-8"
54
55 commit_tree = None
53 commit_tree = None
56 if self.parents[0]:
54 if self.parents[0]:
57 commit_tree = self.parents[0]._commit['tree']
55 commit_tree = self.parents[0]._commit['tree']
58
56
57 encoding = get_default_encodings()[0]
59 updated = []
58 updated = []
60 for node in self.added + self.changed:
59 for node in self.added + self.changed:
61
60 content = node.content
62 if node.is_binary:
61 # TODO: left for reference pre py3 migration, probably need to be removed
63 content = node.content
62 # if node.is_binary:
64 else:
63 # content = node.content
65 content = node.content.encode(ENCODING)
64 # else:
65 # content = node.content.encode(ENCODING)
66
66
67 updated.append({
67 updated.append({
68 'path': node.path,
68 'path': node.path,
69 'node_path': node.name.encode(ENCODING),
69 'node_path': node.name,
70 'content': content,
70 'content': content,
71 'mode': node.mode,
71 'mode': node.mode,
72 })
72 })
@@ -75,7 +75,6 b' class GitInMemoryCommit(base.BaseInMemor'
75
75
76 date, tz = date_to_timestamp_plus_offset(date)
76 date, tz = date_to_timestamp_plus_offset(date)
77
77
78 # TODO: johbo: Make kwargs explicit and check if this is needed.
79 author_time = kwargs.pop('author_time', date)
78 author_time = kwargs.pop('author_time', date)
80 author_tz = kwargs.pop('author_timezone', tz)
79 author_tz = kwargs.pop('author_timezone', tz)
81
80
@@ -83,11 +82,13 b' class GitInMemoryCommit(base.BaseInMemor'
83 'parents': [p._commit['id'] for p in self.parents if p],
82 'parents': [p._commit['id'] for p in self.parents if p],
84 'author': safe_str(author),
83 'author': safe_str(author),
85 'committer': safe_str(author),
84 'committer': safe_str(author),
86 'encoding': ENCODING,
85 'encoding': encoding,
87 'message': safe_str(message),
86 'message': safe_str(message),
87
88 'commit_time': int(date),
88 'commit_time': int(date),
89 'commit_timezone': tz,
90
89 'author_time': int(author_time),
91 'author_time': int(author_time),
90 'commit_timezone': tz,
91 'author_timezone': author_tz,
92 'author_timezone': author_tz,
92 }
93 }
93
94
@@ -31,7 +31,7 b' from zope.cachedescriptors.property impo'
31 from collections import OrderedDict
31 from collections import OrderedDict
32 from rhodecode.lib.datelib import (
32 from rhodecode.lib.datelib import (
33 utcdate_fromtimestamp, makedate, date_astimestamp)
33 utcdate_fromtimestamp, makedate, date_astimestamp)
34 from rhodecode.lib.utils import safe_unicode, safe_str
34 from rhodecode.lib.hash_utils import safe_str
35 from rhodecode.lib.utils2 import CachedProperty
35 from rhodecode.lib.utils2 import CachedProperty
36 from rhodecode.lib.vcs import connection, path as vcspath
36 from rhodecode.lib.vcs import connection, path as vcspath
37 from rhodecode.lib.vcs.backends.base import (
37 from rhodecode.lib.vcs.backends.base import (
@@ -107,7 +107,7 b' class GitRepository(BaseRepository):'
107 :param opts: env options to pass into Subprocess command
107 :param opts: env options to pass into Subprocess command
108 """
108 """
109 if not isinstance(cmd, list):
109 if not isinstance(cmd, list):
110 raise ValueError('cmd must be a list, got %s instead' % type(cmd))
110 raise ValueError(f'cmd must be a list, got {type(cmd)} instead')
111
111
112 skip_stderr_log = opts.pop('skip_stderr_log', False)
112 skip_stderr_log = opts.pop('skip_stderr_log', False)
113 out, err = self._remote.run_git_command(cmd, **opts)
113 out, err = self._remote.run_git_command(cmd, **opts)
@@ -310,7 +310,7 b' class GitRepository(BaseRepository):'
310 @LazyProperty
310 @LazyProperty
311 def description(self):
311 def description(self):
312 description = self._remote.get_description()
312 description = self._remote.get_description()
313 return safe_unicode(description or self.DEFAULT_DESCRIPTION)
313 return safe_str(description or self.DEFAULT_DESCRIPTION)
314
314
315 def _get_refs_entries(self, prefix='', reverse=False, strip_prefix=True):
315 def _get_refs_entries(self, prefix='', reverse=False, strip_prefix=True):
316 if self.is_empty():
316 if self.is_empty():
@@ -322,7 +322,7 b' class GitRepository(BaseRepository):'
322 ref_name = ref
322 ref_name = ref
323 if strip_prefix:
323 if strip_prefix:
324 ref_name = ref[len(prefix):]
324 ref_name = ref[len(prefix):]
325 result.append((safe_unicode(ref_name), sha))
325 result.append((safe_str(ref_name), sha))
326
326
327 def get_name(entry):
327 def get_name(entry):
328 return entry[0]
328 return entry[0]
@@ -561,10 +561,12 b' class GitRepository(BaseRepository):'
561 ``self.EMPTY_COMMIT`` - in this case, patch showing all
561 ``self.EMPTY_COMMIT`` - in this case, patch showing all
562 the changes since empty state of the repository until ``commit2``
562 the changes since empty state of the repository until ``commit2``
563 :param commit2: Until which commits changes should be shown.
563 :param commit2: Until which commits changes should be shown.
564 :param path:
564 :param ignore_whitespace: If set to ``True``, would not show whitespace
565 :param ignore_whitespace: If set to ``True``, would not show whitespace
565 changes. Defaults to ``False``.
566 changes. Defaults to ``False``.
566 :param context: How many lines before/after changed lines should be
567 :param context: How many lines before/after changed lines should be
567 shown. Defaults to ``3``.
568 shown. Defaults to ``3``.
569 :param path1:
568 """
570 """
569 self._validate_diff_commits(commit1, commit2)
571 self._validate_diff_commits(commit1, commit2)
570 if path1 is not None and path1 != path:
572 if path1 is not None and path1 != path:
@@ -579,6 +581,7 b' class GitRepository(BaseRepository):'
579 commit1.raw_id, commit2.raw_id, file_filter=file_filter,
581 commit1.raw_id, commit2.raw_id, file_filter=file_filter,
580 opt_ignorews=ignore_whitespace,
582 opt_ignorews=ignore_whitespace,
581 context=context)
583 context=context)
584
582 return GitDiff(diff)
585 return GitDiff(diff)
583
586
584 def strip(self, commit_id, branch_name):
587 def strip(self, commit_id, branch_name):
@@ -865,8 +868,8 b' class GitRepository(BaseRepository):'
865
868
866 # N.B.(skreft): the --no-ff option is used to enforce the creation of a
869 # N.B.(skreft): the --no-ff option is used to enforce the creation of a
867 # commit message. We also specify the user who is doing the merge.
870 # commit message. We also specify the user who is doing the merge.
868 cmd = ['-c', 'user.name="%s"' % safe_str(user_name),
871 cmd = ['-c', f'user.name="{user_name}"',
869 '-c', 'user.email=%s' % safe_str(user_email),
872 '-c', f'user.email={user_email}',
870 'merge', '--no-ff', '-m', safe_str(merge_message)]
873 'merge', '--no-ff', '-m', safe_str(merge_message)]
871
874
872 merge_cmd = cmd + heads
875 merge_cmd = cmd + heads
@@ -27,15 +27,14 b' import os'
27 from zope.cachedescriptors.property import Lazy as LazyProperty
27 from zope.cachedescriptors.property import Lazy as LazyProperty
28
28
29 from rhodecode.lib.datelib import utcdate_fromtimestamp
29 from rhodecode.lib.datelib import utcdate_fromtimestamp
30 from rhodecode.lib.utils import safe_str, safe_unicode
30 from rhodecode.lib.str_utils import safe_bytes, safe_str
31 from rhodecode.lib.vcs import path as vcspath
31 from rhodecode.lib.vcs import path as vcspath
32 from rhodecode.lib.vcs.backends import base
32 from rhodecode.lib.vcs.backends import base
33 from rhodecode.lib.vcs.backends.hg.diff import MercurialDiff
34 from rhodecode.lib.vcs.exceptions import CommitError
33 from rhodecode.lib.vcs.exceptions import CommitError
35 from rhodecode.lib.vcs.nodes import (
34 from rhodecode.lib.vcs.nodes import (
36 AddedFileNodesGenerator, ChangedFileNodesGenerator, DirNode, FileNode,
35 AddedFileNodesGenerator, ChangedFileNodesGenerator, DirNode, FileNode,
37 NodeKind, RemovedFileNodesGenerator, RootNode, SubModuleNode,
36 NodeKind, RemovedFileNodesGenerator, RootNode, SubModuleNode,
38 LargeFileNode, LARGEFILE_PREFIX)
37 LargeFileNode)
39 from rhodecode.lib.vcs.utils.paths import get_dirs_for_path
38 from rhodecode.lib.vcs.utils.paths import get_dirs_for_path
40
39
41
40
@@ -62,6 +61,7 b' class MercurialCommit(base.BaseCommit):'
62
61
63 # caches
62 # caches
64 self.nodes = {}
63 self.nodes = {}
64 self._stat_modes = {} # stat info for paths
65
65
66 def _set_bulk_properties(self, pre_load):
66 def _set_bulk_properties(self, pre_load):
67 if not pre_load:
67 if not pre_load:
@@ -75,9 +75,9 b' class MercurialCommit(base.BaseCommit):'
75
75
76 for attr, value in result.items():
76 for attr, value in result.items():
77 if attr in ["author", "branch", "message"]:
77 if attr in ["author", "branch", "message"]:
78 value = safe_unicode(value)
78 value = safe_str(value)
79 elif attr == "affected_files":
79 elif attr == "affected_files":
80 value = map(safe_unicode, value)
80 value = list(map(safe_str, value))
81 elif attr == "date":
81 elif attr == "date":
82 value = utcdate_fromtimestamp(*value)
82 value = utcdate_fromtimestamp(*value)
83 elif attr in ["children", "parents"]:
83 elif attr in ["children", "parents"]:
@@ -94,7 +94,7 b' class MercurialCommit(base.BaseCommit):'
94
94
95 @LazyProperty
95 @LazyProperty
96 def branch(self):
96 def branch(self):
97 return safe_unicode(self._remote.ctx_branch(self.raw_id))
97 return safe_str(self._remote.ctx_branch(self.raw_id))
98
98
99 @LazyProperty
99 @LazyProperty
100 def bookmarks(self):
100 def bookmarks(self):
@@ -105,15 +105,15 b' class MercurialCommit(base.BaseCommit):'
105
105
106 @LazyProperty
106 @LazyProperty
107 def message(self):
107 def message(self):
108 return safe_unicode(self._remote.ctx_description(self.raw_id))
108 return safe_str(self._remote.ctx_description(self.raw_id))
109
109
110 @LazyProperty
110 @LazyProperty
111 def committer(self):
111 def committer(self):
112 return safe_unicode(self.author)
112 return safe_str(self.author)
113
113
114 @LazyProperty
114 @LazyProperty
115 def author(self):
115 def author(self):
116 return safe_unicode(self._remote.ctx_user(self.raw_id))
116 return safe_str(self._remote.ctx_user(self.raw_id))
117
117
118 @LazyProperty
118 @LazyProperty
119 def date(self):
119 def date(self):
@@ -132,9 +132,10 b' class MercurialCommit(base.BaseCommit):'
132
132
133 @LazyProperty
133 @LazyProperty
134 def _dir_paths(self):
134 def _dir_paths(self):
135 p = list(set(get_dirs_for_path(*self._file_paths)))
135 dir_paths = ['']
136 p.insert(0, '')
136 dir_paths.extend(list(set(get_dirs_for_path(*self._file_paths))))
137 return p
137
138 return dir_paths
138
139
139 @LazyProperty
140 @LazyProperty
140 def _paths(self):
141 def _paths(self):
@@ -143,7 +144,7 b' class MercurialCommit(base.BaseCommit):'
143 @LazyProperty
144 @LazyProperty
144 def id(self):
145 def id(self):
145 if self.last:
146 if self.last:
146 return u'tip'
147 return 'tip'
147 return self.short_id
148 return self.short_id
148
149
149 @LazyProperty
150 @LazyProperty
@@ -174,7 +175,7 b' class MercurialCommit(base.BaseCommit):'
174 phase_id = self._remote.ctx_phase(self.raw_id)
175 phase_id = self._remote.ctx_phase(self.raw_id)
175 phase_text = self._get_phase_text(phase_id)
176 phase_text = self._get_phase_text(phase_id)
176
177
177 return safe_unicode(phase_text)
178 return safe_str(phase_text)
178
179
179 @LazyProperty
180 @LazyProperty
180 def obsolete(self):
181 def obsolete(self):
@@ -194,13 +195,6 b' class MercurialCommit(base.BaseCommit):'
194 children = self._remote.ctx_children(self.raw_id)
195 children = self._remote.ctx_children(self.raw_id)
195 return self._make_commits(children)
196 return self._make_commits(children)
196
197
197 def _fix_path(self, path):
198 """
199 Mercurial keeps filenodes as str so we need to encode from unicode
200 to str.
201 """
202 return safe_str(super(MercurialCommit, self)._fix_path(path))
203
204 def _get_kind(self, path):
198 def _get_kind(self, path):
205 path = self._fix_path(path)
199 path = self._fix_path(path)
206 if path in self._file_paths:
200 if path in self._file_paths:
@@ -208,43 +202,52 b' class MercurialCommit(base.BaseCommit):'
208 elif path in self._dir_paths:
202 elif path in self._dir_paths:
209 return NodeKind.DIR
203 return NodeKind.DIR
210 else:
204 else:
211 raise CommitError(
205 raise CommitError(f"Node does not exist at the given path '{path}'")
212 "Node does not exist at the given path '%s'" % (path, ))
213
206
214 def _get_filectx(self, path):
207 def _assert_is_path(self, path) -> str:
215 path = self._fix_path(path)
208 path = self._fix_path(path)
216 if self._get_kind(path) != NodeKind.FILE:
209 if self._get_kind(path) != NodeKind.FILE:
217 raise CommitError(
210 raise CommitError(f"File does not exist for commit {self.raw_id} at '{path}'")
218 "File does not exist for idx %s at '%s'" % (self.raw_id, path))
211
219 return path
212 return path
220
213
221 def get_file_mode(self, path):
214 def get_file_mode(self, path: bytes):
222 """
215 """
223 Returns stat mode of the file at the given ``path``.
216 Returns stat mode of the file at the given ``path``.
224 """
217 """
225 path = self._get_filectx(path)
218 path = self._assert_is_path(path)
226 if 'x' in self._remote.fctx_flags(self.raw_id, path):
219
220 if path not in self._stat_modes:
221 self._stat_modes[path] = self._remote.fctx_flags(self.raw_id, path)
222
223 if 'x' in self._stat_modes[path]:
227 return base.FILEMODE_EXECUTABLE
224 return base.FILEMODE_EXECUTABLE
228 else:
225 return base.FILEMODE_DEFAULT
229 return base.FILEMODE_DEFAULT
230
226
231 def is_link(self, path):
227 def is_link(self, path):
232 path = self._get_filectx(path)
228 path = self._assert_is_path(path)
233 return 'l' in self._remote.fctx_flags(self.raw_id, path)
229 if path not in self._stat_modes:
230 self._stat_modes[path] = self._remote.fctx_flags(self.raw_id, path)
231
232 return 'l' in self._stat_modes[path]
234
233
235 def is_node_binary(self, path):
234 def is_node_binary(self, path):
236 path = self._get_filectx(path)
235 path = self._assert_is_path(path)
237 return self._remote.is_binary(self.raw_id, path)
236 return self._remote.is_binary(self.raw_id, path)
238
237
238 def node_md5_hash(self, path):
239 path = self._assert_is_path(path)
240 return self._remote.md5_hash(self.raw_id, path)
241
239 def get_file_content(self, path):
242 def get_file_content(self, path):
240 """
243 """
241 Returns content of the file at given ``path``.
244 Returns content of the file at given ``path``.
242 """
245 """
243 path = self._get_filectx(path)
246 path = self._assert_is_path(path)
244 return self._remote.fctx_node_data(self.raw_id, path)
247 return self._remote.fctx_node_data(self.raw_id, path)
245
248
246 def get_file_content_streamed(self, path):
249 def get_file_content_streamed(self, path):
247 path = self._get_filectx(path)
250 path = self._assert_is_path(path)
248 stream_method = getattr(self._remote, 'stream:fctx_node_data')
251 stream_method = getattr(self._remote, 'stream:fctx_node_data')
249 return stream_method(self.raw_id, path)
252 return stream_method(self.raw_id, path)
250
253
@@ -252,7 +255,7 b' class MercurialCommit(base.BaseCommit):'
252 """
255 """
253 Returns size of the file at given ``path``.
256 Returns size of the file at given ``path``.
254 """
257 """
255 path = self._get_filectx(path)
258 path = self._assert_is_path(path)
256 return self._remote.fctx_size(self.raw_id, path)
259 return self._remote.fctx_size(self.raw_id, path)
257
260
258 def get_path_history(self, path, limit=None, pre_load=None):
261 def get_path_history(self, path, limit=None, pre_load=None):
@@ -260,7 +263,7 b' class MercurialCommit(base.BaseCommit):'
260 Returns history of file as reversed list of `MercurialCommit` objects
263 Returns history of file as reversed list of `MercurialCommit` objects
261 for which file at given ``path`` has been modified.
264 for which file at given ``path`` has been modified.
262 """
265 """
263 path = self._get_filectx(path)
266 path = self._assert_is_path(path)
264 hist = self._remote.node_history(self.raw_id, path, limit)
267 hist = self._remote.node_history(self.raw_id, path, limit)
265 return [
268 return [
266 self.repository.get_commit(commit_id=commit_id, pre_load=pre_load)
269 self.repository.get_commit(commit_id=commit_id, pre_load=pre_load)
@@ -279,7 +282,7 b' class MercurialCommit(base.BaseCommit):'
279 lambda: self.repository.get_commit(commit_id=commit_id, pre_load=pre_load),
282 lambda: self.repository.get_commit(commit_id=commit_id, pre_load=pre_load),
280 content)
283 content)
281
284
282 def get_nodes(self, path):
285 def get_nodes(self, path, pre_load=None):
283 """
286 """
284 Returns combined ``DirNode`` and ``FileNode`` objects list representing
287 Returns combined ``DirNode`` and ``FileNode`` objects list representing
285 state of commit at the given ``path``. If node at the given ``path``
288 state of commit at the given ``path``. If node at the given ``path``
@@ -292,14 +295,14 b' class MercurialCommit(base.BaseCommit):'
292 path = self._fix_path(path)
295 path = self._fix_path(path)
293
296
294 filenodes = [
297 filenodes = [
295 FileNode(f, commit=self) for f in self._file_paths
298 FileNode(safe_bytes(f), commit=self, pre_load=pre_load) for f in self._file_paths
296 if os.path.dirname(f) == path]
299 if os.path.dirname(f) == path]
297 # TODO: johbo: Check if this can be done in a more obvious way
300 # TODO: johbo: Check if this can be done in a more obvious way
298 dirs = path == '' and '' or [
301 dirs = path == '' and '' or [
299 d for d in self._dir_paths
302 d for d in self._dir_paths
300 if d and vcspath.dirname(d) == path]
303 if d and vcspath.dirname(d) == path]
301 dirnodes = [
304 dirnodes = [
302 DirNode(d, commit=self) for d in dirs
305 DirNode(safe_bytes(d), commit=self) for d in dirs
303 if os.path.dirname(d) == path]
306 if os.path.dirname(d) == path]
304
307
305 alias = self.repository.alias
308 alias = self.repository.alias
@@ -326,12 +329,12 b' class MercurialCommit(base.BaseCommit):'
326
329
327 if path not in self.nodes:
330 if path not in self.nodes:
328 if path in self._file_paths:
331 if path in self._file_paths:
329 node = FileNode(path, commit=self, pre_load=pre_load)
332 node = FileNode(safe_bytes(path), commit=self, pre_load=pre_load)
330 elif path in self._dir_paths:
333 elif path in self._dir_paths:
331 if path == '':
334 if path == '':
332 node = RootNode(commit=self)
335 node = RootNode(commit=self)
333 else:
336 else:
334 node = DirNode(path, commit=self)
337 node = DirNode(safe_bytes(path), commit=self)
335 else:
338 else:
336 raise self.no_node_at_path(path)
339 raise self.no_node_at_path(path)
337
340
@@ -347,11 +350,11 b' class MercurialCommit(base.BaseCommit):'
347
350
348 if self._remote.in_largefiles_store(file_id):
351 if self._remote.in_largefiles_store(file_id):
349 lf_path = self._remote.store_path(file_id)
352 lf_path = self._remote.store_path(file_id)
350 return LargeFileNode(lf_path, commit=self, org_path=path)
353 return LargeFileNode(safe_bytes(lf_path), commit=self, org_path=path)
351 elif self._remote.in_user_cache(file_id):
354 elif self._remote.in_user_cache(file_id):
352 lf_path = self._remote.store_path(file_id)
355 lf_path = self._remote.store_path(file_id)
353 self._remote.link(file_id, path)
356 self._remote.link(file_id, path)
354 return LargeFileNode(lf_path, commit=self, org_path=path)
357 return LargeFileNode(safe_bytes(lf_path), commit=self, org_path=path)
355
358
356 @LazyProperty
359 @LazyProperty
357 def _submodules(self):
360 def _submodules(self):
@@ -29,7 +29,7 b' from rhodecode.lib.vcs.backends import b'
29
29
30 class MercurialDiff(base.Diff):
30 class MercurialDiff(base.Diff):
31
31
32 _header_re = re.compile(r"""
32 _header_re = re.compile(br"""
33 #^diff[ ]--git
33 #^diff[ ]--git
34 [ ]"?a/(?P<a_path>.+?)"?[ ]"?b/(?P<b_path>.+?)"?\n
34 [ ]"?a/(?P<a_path>.+?)"?[ ]"?b/(?P<b_path>.+?)"?\n
35 (?:^old[ ]mode[ ](?P<old_mode>\d+)\n
35 (?:^old[ ]mode[ ](?P<old_mode>\d+)\n
@@ -23,7 +23,7 b' HG inmemory module'
23 """
23 """
24
24
25 from rhodecode.lib.datelib import date_to_timestamp_plus_offset
25 from rhodecode.lib.datelib import date_to_timestamp_plus_offset
26 from rhodecode.lib.utils import safe_str
26 from rhodecode.lib.str_utils import safe_str
27 from rhodecode.lib.vcs.backends.base import BaseInMemoryCommit
27 from rhodecode.lib.vcs.backends.base import BaseInMemoryCommit
28 from rhodecode.lib.vcs.exceptions import RepositoryError
28 from rhodecode.lib.vcs.exceptions import RepositoryError
29
29
@@ -65,14 +65,14 b' class MercurialInMemoryCommit(BaseInMemo'
65
65
66 parent_ids = [p.raw_id if p else None for p in self.parents]
66 parent_ids = [p.raw_id if p else None for p in self.parents]
67
67
68 ENCODING = "UTF-8"
69
70 updated = []
68 updated = []
71 for node in self.added + self.changed:
69 for node in self.added + self.changed:
72 if node.is_binary:
70 content = node.content
73 content = node.content
71 # TODO: left for reference pre py3 migration, probably need to be removed
74 else:
72 # if node.is_binary:
75 content = node.content.encode(ENCODING)
73 # content = node.content
74 # else:
75 # content = node.content.encode(ENCODING)
76 updated.append({
76 updated.append({
77 'path': node.path,
77 'path': node.path,
78 'content': content,
78 'content': content,
@@ -25,14 +25,16 b' import os'
25 import logging
25 import logging
26 import binascii
26 import binascii
27 import configparser
27 import configparser
28 import urllib.request, urllib.parse, urllib.error
28 import urllib.request
29 import urllib.parse
30 import urllib.error
29
31
30 from zope.cachedescriptors.property import Lazy as LazyProperty
32 from zope.cachedescriptors.property import Lazy as LazyProperty
31
33
32 from collections import OrderedDict
34 from collections import OrderedDict
33 from rhodecode.lib.datelib import (
35 from rhodecode.lib.datelib import (
34 date_to_timestamp_plus_offset, utcdate_fromtimestamp, makedate)
36 date_to_timestamp_plus_offset, utcdate_fromtimestamp, makedate)
35 from rhodecode.lib.utils import safe_unicode, safe_str
37 from rhodecode.lib.str_utils import safe_str
36 from rhodecode.lib.utils2 import CachedProperty
38 from rhodecode.lib.utils2 import CachedProperty
37 from rhodecode.lib.vcs import connection, exceptions
39 from rhodecode.lib.vcs import connection, exceptions
38 from rhodecode.lib.vcs.backends.base import (
40 from rhodecode.lib.vcs.backends.base import (
@@ -135,7 +137,7 b' class MercurialRepository(BaseRepository'
135 def get_name(ctx):
137 def get_name(ctx):
136 return ctx[0]
138 return ctx[0]
137
139
138 _branches = [(safe_unicode(n), hexlify(h),) for n, h in
140 _branches = [(n, h,) for n, h in
139 self._remote.branches(active, closed).items()]
141 self._remote.branches(active, closed).items()]
140
142
141 return OrderedDict(sorted(_branches, key=get_name, reverse=False))
143 return OrderedDict(sorted(_branches, key=get_name, reverse=False))
@@ -154,7 +156,7 b' class MercurialRepository(BaseRepository'
154 def get_name(ctx):
156 def get_name(ctx):
155 return ctx[0]
157 return ctx[0]
156
158
157 _tags = [(safe_unicode(n), hexlify(h),) for n, h in
159 _tags = [(n, h,) for n, h in
158 self._remote.tags().items()]
160 self._remote.tags().items()]
159
161
160 return OrderedDict(sorted(_tags, key=get_name, reverse=True))
162 return OrderedDict(sorted(_tags, key=get_name, reverse=True))
@@ -230,7 +232,7 b' class MercurialRepository(BaseRepository'
230 return ctx[0]
232 return ctx[0]
231
233
232 _bookmarks = [
234 _bookmarks = [
233 (safe_unicode(n), hexlify(h)) for n, h in
235 (n, h) for n, h in
234 self._remote.bookmarks().items()]
236 self._remote.bookmarks().items()]
235
237
236 return OrderedDict(sorted(_bookmarks, key=get_name))
238 return OrderedDict(sorted(_bookmarks, key=get_name))
@@ -365,8 +367,7 b' class MercurialRepository(BaseRepository'
365 """
367 """
366 if create and os.path.exists(self.path):
368 if create and os.path.exists(self.path):
367 raise RepositoryError(
369 raise RepositoryError(
368 "Cannot create repository at %s, location already exist"
370 f"Cannot create repository at {self.path}, location already exist")
369 % self.path)
370
371
371 if src_url:
372 if src_url:
372 url = str(self._get_url(src_url))
373 url = str(self._get_url(src_url))
@@ -379,6 +380,7 b' class MercurialRepository(BaseRepository'
379
380
380 if create:
381 if create:
381 os.makedirs(self.path, mode=0o755)
382 os.makedirs(self.path, mode=0o755)
383
382 self._remote.localrepository(create)
384 self._remote.localrepository(create)
383
385
384 @LazyProperty
386 @LazyProperty
@@ -389,14 +391,14 b' class MercurialRepository(BaseRepository'
389 def description(self):
391 def description(self):
390 description = self._remote.get_config_value(
392 description = self._remote.get_config_value(
391 'web', 'description', untrusted=True)
393 'web', 'description', untrusted=True)
392 return safe_unicode(description or self.DEFAULT_DESCRIPTION)
394 return safe_str(description or self.DEFAULT_DESCRIPTION)
393
395
394 @LazyProperty
396 @LazyProperty
395 def contact(self):
397 def contact(self):
396 contact = (
398 contact = (
397 self._remote.get_config_value("web", "contact") or
399 self._remote.get_config_value("web", "contact") or
398 self._remote.get_config_value("ui", "username"))
400 self._remote.get_config_value("ui", "username"))
399 return safe_unicode(contact or self.DEFAULT_CONTACT)
401 return safe_str(contact or self.DEFAULT_CONTACT)
400
402
401 @LazyProperty
403 @LazyProperty
402 def last_change(self):
404 def last_change(self):
@@ -425,7 +427,6 b' class MercurialRepository(BaseRepository'
425 to filesystem
427 to filesystem
426 (``file:///``) schema.
428 (``file:///``) schema.
427 """
429 """
428 url = url.encode('utf8')
429 if url != 'default' and '://' not in url:
430 if url != 'default' and '://' not in url:
430 url = "file:" + urllib.request.pathname2url(url)
431 url = "file:" + urllib.request.pathname2url(url)
431 return url
432 return url
@@ -467,10 +468,7 b' class MercurialRepository(BaseRepository'
467 else:
468 else:
468 commit_id = "tip"
469 commit_id = "tip"
469
470
470 #TODO: decide if we pass bytes or str into lookup ?
471 # case here is no cached version, do an actual lookup instead
471 # if isinstance(commit_id, unicode):
472 # commit_id = safe_str(commit_id)
473
474 try:
472 try:
475 raw_id, idx = self._remote.lookup(commit_id, both=True)
473 raw_id, idx = self._remote.lookup(commit_id, both=True)
476 except CommitDoesNotExistError:
474 except CommitDoesNotExistError:
@@ -1009,5 +1007,9 b' class MercurialRepository(BaseRepository'
1009 class MercurialIndexBasedCollectionGenerator(CollectionGenerator):
1007 class MercurialIndexBasedCollectionGenerator(CollectionGenerator):
1010
1008
1011 def _commit_factory(self, commit_id):
1009 def _commit_factory(self, commit_id):
1012 return self.repo.get_commit(
1010 if isinstance(commit_id, int):
1013 commit_idx=commit_id, pre_load=self.pre_load)
1011 return self.repo.get_commit(
1012 commit_idx=commit_id, pre_load=self.pre_load)
1013 else:
1014 return self.repo.get_commit(
1015 commit_id=commit_id, pre_load=self.pre_load)
@@ -26,10 +26,10 b' SVN commit module'
26 import dateutil.parser
26 import dateutil.parser
27 from zope.cachedescriptors.property import Lazy as LazyProperty
27 from zope.cachedescriptors.property import Lazy as LazyProperty
28
28
29 from rhodecode.lib.utils import safe_str, safe_unicode
29 from rhodecode.lib.str_utils import safe_bytes, safe_str
30 from rhodecode.lib.vcs import nodes, path as vcspath
30 from rhodecode.lib.vcs import nodes, path as vcspath
31 from rhodecode.lib.vcs.backends import base
31 from rhodecode.lib.vcs.backends import base
32 from rhodecode.lib.vcs.exceptions import CommitError, NodeDoesNotExistError
32 from rhodecode.lib.vcs.exceptions import CommitError
33
33
34
34
35 _SVN_PROP_TRUE = '*'
35 _SVN_PROP_TRUE = '*'
@@ -63,7 +63,7 b' class SubversionCommit(base.BaseCommit):'
63
63
64 @property
64 @property
65 def author(self):
65 def author(self):
66 return safe_unicode(self._properties.get('svn:author'))
66 return safe_str(self._properties.get('svn:author'))
67
67
68 @property
68 @property
69 def date(self):
69 def date(self):
@@ -71,7 +71,7 b' class SubversionCommit(base.BaseCommit):'
71
71
72 @property
72 @property
73 def message(self):
73 def message(self):
74 return safe_unicode(self._properties.get('svn:log'))
74 return safe_str(self._properties.get('svn:log'))
75
75
76 @LazyProperty
76 @LazyProperty
77 def _properties(self):
77 def _properties(self):
@@ -93,7 +93,7 b' class SubversionCommit(base.BaseCommit):'
93 return [child]
93 return [child]
94 return []
94 return []
95
95
96 def get_file_mode(self, path):
96 def get_file_mode(self, path: bytes):
97 # Note: Subversion flags files which are executable with a special
97 # Note: Subversion flags files which are executable with a special
98 # property `svn:executable` which is set to the value ``"*"``.
98 # property `svn:executable` which is set to the value ``"*"``.
99 if self._get_file_property(path, 'svn:executable') == _SVN_PROP_TRUE:
99 if self._get_file_property(path, 'svn:executable') == _SVN_PROP_TRUE:
@@ -105,13 +105,17 b' class SubversionCommit(base.BaseCommit):'
105 # Note: Subversion has a flag for special files, the content of the
105 # Note: Subversion has a flag for special files, the content of the
106 # file contains the type of that file.
106 # file contains the type of that file.
107 if self._get_file_property(path, 'svn:special') == _SVN_PROP_TRUE:
107 if self._get_file_property(path, 'svn:special') == _SVN_PROP_TRUE:
108 return self.get_file_content(path).startswith('link')
108 return self.get_file_content(path).startswith(b'link')
109 return False
109 return False
110
110
111 def is_node_binary(self, path):
111 def is_node_binary(self, path):
112 path = self._fix_path(path)
112 path = self._fix_path(path)
113 return self._remote.is_binary(self._svn_rev, safe_str(path))
113 return self._remote.is_binary(self._svn_rev, safe_str(path))
114
114
115 def node_md5_hash(self, path):
116 path = self._fix_path(path)
117 return self._remote.md5_hash(self._svn_rev, safe_str(path))
118
115 def _get_file_property(self, path, name):
119 def _get_file_property(self, path, name):
116 file_properties = self._remote.node_properties(
120 file_properties = self._remote.node_properties(
117 safe_str(path), self._svn_rev)
121 safe_str(path), self._svn_rev)
@@ -119,16 +123,17 b' class SubversionCommit(base.BaseCommit):'
119
123
120 def get_file_content(self, path):
124 def get_file_content(self, path):
121 path = self._fix_path(path)
125 path = self._fix_path(path)
122 return self._remote.get_file_content(safe_str(path), self._svn_rev)
126 return self._remote.get_file_content(self._svn_rev, safe_str(path))
123
127
124 def get_file_content_streamed(self, path):
128 def get_file_content_streamed(self, path):
125 path = self._fix_path(path)
129 path = self._fix_path(path)
130
126 stream_method = getattr(self._remote, 'stream:get_file_content')
131 stream_method = getattr(self._remote, 'stream:get_file_content')
127 return stream_method(safe_str(path), self._svn_rev)
132 return stream_method(self._svn_rev, safe_str(path))
128
133
129 def get_file_size(self, path):
134 def get_file_size(self, path):
130 path = self._fix_path(path)
135 path = self._fix_path(path)
131 return self._remote.get_file_size(safe_str(path), self._svn_rev)
136 return self._remote.get_file_size(self._svn_rev, safe_str(path))
132
137
133 def get_path_history(self, path, limit=None, pre_load=None):
138 def get_path_history(self, path, limit=None, pre_load=None):
134 path = safe_str(self._fix_path(path))
139 path = safe_str(self._fix_path(path))
@@ -156,34 +161,32 b' class SubversionCommit(base.BaseCommit):'
156 if path == '':
161 if path == '':
157 node = nodes.RootNode(commit=self)
162 node = nodes.RootNode(commit=self)
158 else:
163 else:
159 node_type = self._remote.get_node_type(
164 node_type = self._remote.get_node_type(self._svn_rev, safe_str(path))
160 safe_str(path), self._svn_rev)
161 if node_type == 'dir':
165 if node_type == 'dir':
162 node = nodes.DirNode(path, commit=self)
166 node = nodes.DirNode(safe_bytes(path), commit=self)
163 elif node_type == 'file':
167 elif node_type == 'file':
164 node = nodes.FileNode(path, commit=self, pre_load=pre_load)
168 node = nodes.FileNode(safe_bytes(path), commit=self, pre_load=pre_load)
165 else:
169 else:
166 raise self.no_node_at_path(path)
170 raise self.no_node_at_path(path)
167
171
168 self.nodes[path] = node
172 self.nodes[path] = node
169 return self.nodes[path]
173 return self.nodes[path]
170
174
171 def get_nodes(self, path):
175 def get_nodes(self, path, pre_load=None):
172 if self._get_kind(path) != nodes.NodeKind.DIR:
176 if self._get_kind(path) != nodes.NodeKind.DIR:
173 raise CommitError(
177 raise CommitError(
174 "Directory does not exist for commit %s at "
178 f"Directory does not exist for commit {self.raw_id} at '{path}'")
175 " '%s'" % (self.raw_id, path))
176 path = safe_str(self._fix_path(path))
179 path = safe_str(self._fix_path(path))
177
180
178 path_nodes = []
181 path_nodes = []
179 for name, kind in self._remote.get_nodes(path, revision=self._svn_rev):
182 for name, kind in self._remote.get_nodes(self._svn_rev, path):
180 node_path = vcspath.join(path, name)
183 node_path = vcspath.join(path, name)
181 if kind == 'dir':
184 if kind == 'dir':
182 node = nodes.DirNode(node_path, commit=self)
185 node = nodes.DirNode(safe_bytes(node_path), commit=self)
183 elif kind == 'file':
186 elif kind == 'file':
184 node = nodes.FileNode(node_path, commit=self)
187 node = nodes.FileNode(safe_bytes(node_path), commit=self, pre_load=pre_load)
185 else:
188 else:
186 raise ValueError("Node kind %s not supported." % (kind, ))
189 raise ValueError(f"Node kind {kind} not supported.")
187 self.nodes[node_path] = node
190 self.nodes[node_path] = node
188 path_nodes.append(node)
191 path_nodes.append(node)
189
192
@@ -191,7 +194,7 b' class SubversionCommit(base.BaseCommit):'
191
194
192 def _get_kind(self, path):
195 def _get_kind(self, path):
193 path = self._fix_path(path)
196 path = self._fix_path(path)
194 kind = self._remote.get_node_type(path, self._svn_rev)
197 kind = self._remote.get_node_type(self._svn_rev, path)
195 if kind == 'file':
198 if kind == 'file':
196 return nodes.NodeKind.FILE
199 return nodes.NodeKind.FILE
197 elif kind == 'dir':
200 elif kind == 'dir':
@@ -29,11 +29,11 b' from rhodecode.lib.vcs.backends import b'
29
29
30 class SubversionDiff(base.Diff):
30 class SubversionDiff(base.Diff):
31
31
32 _meta_re = re.compile(r"""
32 _meta_re = re.compile(br"""
33 (?:^(?P<svn_bin_patch>Cannot[ ]display:[ ]file[ ]marked[ ]as[ ]a[ ]binary[ ]type.)(?:\n|$))?
33 (?:^(?P<svn_bin_patch>Cannot[ ]display:[ ]file[ ]marked[ ]as[ ]a[ ]binary[ ]type.)(?:\n|$))?
34 """, re.VERBOSE | re.MULTILINE)
34 """, re.VERBOSE | re.MULTILINE)
35
35
36 _header_re = re.compile(r"""
36 _header_re = re.compile(br"""
37 #^diff[ ]--git
37 #^diff[ ]--git
38 [ ]"?a/(?P<a_path>.+?)"?[ ]"?b/(?P<b_path>.+?)"?\n
38 [ ]"?a/(?P<a_path>.+?)"?[ ]"?b/(?P<b_path>.+?)"?\n
39 (?:^similarity[ ]index[ ](?P<similarity_index>\d+)%\n
39 (?:^similarity[ ]index[ ](?P<similarity_index>\d+)%\n
@@ -24,7 +24,7 b' SVN inmemory module'
24 """
24 """
25
25
26 from rhodecode.lib.datelib import date_astimestamp
26 from rhodecode.lib.datelib import date_astimestamp
27 from rhodecode.lib.utils import safe_str
27 from rhodecode.lib.str_utils import safe_str, safe_bytes
28 from rhodecode.lib.vcs.backends import base
28 from rhodecode.lib.vcs.backends import base
29
29
30
30
@@ -42,8 +42,8 b' class SubversionInMemoryCommit(base.Base'
42 updated = []
42 updated = []
43 for node in self.added:
43 for node in self.added:
44 node_data = {
44 node_data = {
45 'path': node.path,
45 'path': safe_bytes(node.path),
46 'content': safe_str(node.content),
46 'content': node.content,
47 'mode': node.mode,
47 'mode': node.mode,
48 }
48 }
49 if node.is_binary:
49 if node.is_binary:
@@ -53,15 +53,15 b' class SubversionInMemoryCommit(base.Base'
53 updated.append(node_data)
53 updated.append(node_data)
54 for node in self.changed:
54 for node in self.changed:
55 updated.append({
55 updated.append({
56 'path': node.path,
56 'path': safe_bytes(node.path),
57 'content': safe_str(node.content),
57 'content': node.content,
58 'mode': node.mode,
58 'mode': node.mode,
59 })
59 })
60
60
61 removed = []
61 removed = []
62 for node in self.removed:
62 for node in self.removed:
63 removed.append({
63 removed.append({
64 'path': node.path,
64 'path': safe_bytes(node.path),
65 })
65 })
66
66
67 timestamp = date_astimestamp(date) if date else None
67 timestamp = date_astimestamp(date) if date else None
@@ -24,13 +24,15 b' SVN repository module'
24
24
25 import logging
25 import logging
26 import os
26 import os
27 import urllib.request, urllib.parse, urllib.error
27 import urllib.request
28 import urllib.parse
29 import urllib.error
28
30
29 from zope.cachedescriptors.property import Lazy as LazyProperty
31 from zope.cachedescriptors.property import Lazy as LazyProperty
30
32
31 from collections import OrderedDict
33 from collections import OrderedDict
32 from rhodecode.lib.datelib import date_astimestamp
34 from rhodecode.lib.datelib import date_astimestamp
33 from rhodecode.lib.utils import safe_str, safe_unicode
35 from rhodecode.lib.str_utils import safe_str
34 from rhodecode.lib.utils2 import CachedProperty
36 from rhodecode.lib.utils2 import CachedProperty
35 from rhodecode.lib.vcs import connection, path as vcspath
37 from rhodecode.lib.vcs import connection, path as vcspath
36 from rhodecode.lib.vcs.backends import base
38 from rhodecode.lib.vcs.backends import base
@@ -88,8 +90,8 b' class SubversionRepository(base.BaseRepo'
88 def _init_repo(self, create, src_url):
90 def _init_repo(self, create, src_url):
89 if create and os.path.exists(self.path):
91 if create and os.path.exists(self.path):
90 raise RepositoryError(
92 raise RepositoryError(
91 "Cannot create repository at %s, location already exist"
93 f"Cannot create repository at {self.path}, location already exist"
92 % self.path)
94 )
93
95
94 if create:
96 if create:
95 self._remote.create_repository(settings.SVN_COMPATIBLE_VERSION)
97 self._remote.create_repository(settings.SVN_COMPATIBLE_VERSION)
@@ -116,7 +118,7 b' class SubversionRepository(base.BaseRepo'
116 :param opts: env options to pass into Subprocess command
118 :param opts: env options to pass into Subprocess command
117 """
119 """
118 if not isinstance(cmd, list):
120 if not isinstance(cmd, list):
119 raise ValueError('cmd must be a list, got %s instead' % type(cmd))
121 raise ValueError(f'cmd must be a list, got {type(cmd)} instead')
120
122
121 skip_stderr_log = opts.pop('skip_stderr_log', False)
123 skip_stderr_log = opts.pop('skip_stderr_log', False)
122 out, err = self._remote.run_svn_command(cmd, **opts)
124 out, err = self._remote.run_svn_command(cmd, **opts)
@@ -165,10 +167,7 b' class SubversionRepository(base.BaseRepo'
165 directories = (tip.get_node(pattern), )
167 directories = (tip.get_node(pattern), )
166 except NodeDoesNotExistError:
168 except NodeDoesNotExistError:
167 continue
169 continue
168 found_items.update(
170 found_items.update((safe_str(n.path), self.commit_ids[-1]) for n in directories)
169 (safe_unicode(n.path),
170 self.commit_ids[-1])
171 for n in directories)
172
171
173 def get_name(item):
172 def get_name(item):
174 return item[0]
173 return item[0]
@@ -329,7 +328,7 b' class SubversionRepository(base.BaseRepo'
329
328
330 # TODO: johbo: Reconsider impact of DEFAULT_BRANCH_NAME here
329 # TODO: johbo: Reconsider impact of DEFAULT_BRANCH_NAME here
331 if branch_name not in [None, self.DEFAULT_BRANCH_NAME]:
330 if branch_name not in [None, self.DEFAULT_BRANCH_NAME]:
332 svn_rev = long(self.commit_ids[-1])
331 svn_rev = int(self.commit_ids[-1])
333 commit_ids = self._remote.node_history(
332 commit_ids = self._remote.node_history(
334 path=branch_name, revision=svn_rev, limit=None)
333 path=branch_name, revision=svn_rev, limit=None)
335 commit_ids = [str(i) for i in reversed(commit_ids)]
334 commit_ids = [str(i) for i in reversed(commit_ids)]
@@ -356,8 +355,8 b' class SubversionRepository(base.BaseRepo'
356 self, commit1, commit2, path=None, ignore_whitespace=False,
355 self, commit1, commit2, path=None, ignore_whitespace=False,
357 context=3, path1=None):
356 context=3, path1=None):
358 self._validate_diff_commits(commit1, commit2)
357 self._validate_diff_commits(commit1, commit2)
359 svn_rev1 = long(commit1.raw_id)
358 svn_rev1 = int(commit1.raw_id)
360 svn_rev2 = long(commit2.raw_id)
359 svn_rev2 = int(commit2.raw_id)
361 diff = self._remote.diff(
360 diff = self._remote.diff(
362 svn_rev1, svn_rev2, path1=path1, path2=path,
361 svn_rev1, svn_rev2, path1=path1, path2=path,
363 ignore_whitespace=ignore_whitespace, context=context)
362 ignore_whitespace=ignore_whitespace, context=context)
General Comments 0
You need to be logged in to leave comments. Login now