##// END OF EJS Templates
rename search_recursively to search_up - that is what it is
Mads Kiilerich -
r3091:291be8fa beta
parent child Browse files
Show More
@@ -1,63 +1,63
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 vcs.backends
3 vcs.backends
4 ~~~~~~~~~~~~
4 ~~~~~~~~~~~~
5
5
6 Main package for scm backends
6 Main package for scm backends
7
7
8 :created_on: Apr 8, 2010
8 :created_on: Apr 8, 2010
9 :copyright: (c) 2010-2011 by Marcin Kuzminski, Lukasz Balcerzak.
9 :copyright: (c) 2010-2011 by Marcin Kuzminski, Lukasz Balcerzak.
10 """
10 """
11 import os
11 import os
12 from pprint import pformat
12 from pprint import pformat
13 from rhodecode.lib.vcs.conf import settings
13 from rhodecode.lib.vcs.conf import settings
14 from rhodecode.lib.vcs.exceptions import VCSError
14 from rhodecode.lib.vcs.exceptions import VCSError
15 from rhodecode.lib.vcs.utils.helpers import get_scm
15 from rhodecode.lib.vcs.utils.helpers import get_scm
16 from rhodecode.lib.vcs.utils.paths import abspath
16 from rhodecode.lib.vcs.utils.paths import abspath
17 from rhodecode.lib.vcs.utils.imports import import_class
17 from rhodecode.lib.vcs.utils.imports import import_class
18
18
19
19
20 def get_repo(path=None, alias=None, create=False):
20 def get_repo(path=None, alias=None, create=False):
21 """
21 """
22 Returns ``Repository`` object of type linked with given ``alias`` at
22 Returns ``Repository`` object of type linked with given ``alias`` at
23 the specified ``path``. If ``alias`` is not given it will try to guess it
23 the specified ``path``. If ``alias`` is not given it will try to guess it
24 using get_scm method
24 using get_scm method
25 """
25 """
26 if create:
26 if create:
27 if not (path or alias):
27 if not (path or alias):
28 raise TypeError("If create is specified, we need path and scm type")
28 raise TypeError("If create is specified, we need path and scm type")
29 return get_backend(alias)(path, create=True)
29 return get_backend(alias)(path, create=True)
30 if path is None:
30 if path is None:
31 path = abspath(os.path.curdir)
31 path = abspath(os.path.curdir)
32 try:
32 try:
33 scm, path = get_scm(path, search_recursively=True)
33 scm, path = get_scm(path, search_up=True)
34 path = abspath(path)
34 path = abspath(path)
35 alias = scm
35 alias = scm
36 except VCSError:
36 except VCSError:
37 raise VCSError("No scm found at %s" % path)
37 raise VCSError("No scm found at %s" % path)
38 if alias is None:
38 if alias is None:
39 alias = get_scm(path)[0]
39 alias = get_scm(path)[0]
40
40
41 backend = get_backend(alias)
41 backend = get_backend(alias)
42 repo = backend(path, create=create)
42 repo = backend(path, create=create)
43 return repo
43 return repo
44
44
45
45
46 def get_backend(alias):
46 def get_backend(alias):
47 """
47 """
48 Returns ``Repository`` class identified by the given alias or raises
48 Returns ``Repository`` class identified by the given alias or raises
49 VCSError if alias is not recognized or backend class cannot be imported.
49 VCSError if alias is not recognized or backend class cannot be imported.
50 """
50 """
51 if alias not in settings.BACKENDS:
51 if alias not in settings.BACKENDS:
52 raise VCSError("Given alias '%s' is not recognized! Allowed aliases:\n"
52 raise VCSError("Given alias '%s' is not recognized! Allowed aliases:\n"
53 "%s" % (alias, pformat(settings.BACKENDS.keys())))
53 "%s" % (alias, pformat(settings.BACKENDS.keys())))
54 backend_path = settings.BACKENDS[alias]
54 backend_path = settings.BACKENDS[alias]
55 klass = import_class(backend_path)
55 klass = import_class(backend_path)
56 return klass
56 return klass
57
57
58
58
59 def get_supported_backends():
59 def get_supported_backends():
60 """
60 """
61 Returns list of aliases of supported backends.
61 Returns list of aliases of supported backends.
62 """
62 """
63 return settings.BACKENDS.keys()
63 return settings.BACKENDS.keys()
@@ -1,255 +1,255
1 """
1 """
2 Utitlites aimed to help achieve mostly basic tasks.
2 Utitlites aimed to help achieve mostly basic tasks.
3 """
3 """
4 from __future__ import division
4 from __future__ import division
5
5
6 import re
6 import re
7 import time
7 import time
8 import datetime
8 import datetime
9 import os.path
9 import os.path
10 from subprocess import Popen, PIPE
10 from subprocess import Popen, PIPE
11 from rhodecode.lib.vcs.exceptions import VCSError
11 from rhodecode.lib.vcs.exceptions import VCSError
12 from rhodecode.lib.vcs.exceptions import RepositoryError
12 from rhodecode.lib.vcs.exceptions import RepositoryError
13 from rhodecode.lib.vcs.utils.paths import abspath
13 from rhodecode.lib.vcs.utils.paths import abspath
14
14
15 ALIASES = ['hg', 'git']
15 ALIASES = ['hg', 'git']
16
16
17
17
18 def get_scm(path, search_recursively=False, explicit_alias=None):
18 def get_scm(path, search_up=False, explicit_alias=None):
19 """
19 """
20 Returns one of alias from ``ALIASES`` (in order of precedence same as
20 Returns one of alias from ``ALIASES`` (in order of precedence same as
21 shortcuts given in ``ALIASES``) and top working dir path for the given
21 shortcuts given in ``ALIASES``) and top working dir path for the given
22 argument. If no scm-specific directory is found or more than one scm is
22 argument. If no scm-specific directory is found or more than one scm is
23 found at that directory, ``VCSError`` is raised.
23 found at that directory, ``VCSError`` is raised.
24
24
25 :param search_recursively: if set to ``True``, this function would try to
25 :param search_up: if set to ``True``, this function would try to
26 move up to parent directory every time no scm is recognized for the
26 move up to parent directory every time no scm is recognized for the
27 currently checked path. Default: ``False``.
27 currently checked path. Default: ``False``.
28 :param explicit_alias: can be one of available backend aliases, when given
28 :param explicit_alias: can be one of available backend aliases, when given
29 it will return given explicit alias in repositories under more than one
29 it will return given explicit alias in repositories under more than one
30 version control, if explicit_alias is different than found it will raise
30 version control, if explicit_alias is different than found it will raise
31 VCSError
31 VCSError
32 """
32 """
33 if not os.path.isdir(path):
33 if not os.path.isdir(path):
34 raise VCSError("Given path %s is not a directory" % path)
34 raise VCSError("Given path %s is not a directory" % path)
35
35
36 def get_scms(path):
36 def get_scms(path):
37 return [(scm, path) for scm in get_scms_for_path(path)]
37 return [(scm, path) for scm in get_scms_for_path(path)]
38
38
39 found_scms = get_scms(path)
39 found_scms = get_scms(path)
40 while not found_scms and search_recursively:
40 while not found_scms and search_up:
41 newpath = abspath(path, '..')
41 newpath = abspath(path, '..')
42 if newpath == path:
42 if newpath == path:
43 break
43 break
44 path = newpath
44 path = newpath
45 found_scms = get_scms(path)
45 found_scms = get_scms(path)
46
46
47 if len(found_scms) > 1:
47 if len(found_scms) > 1:
48 for scm in found_scms:
48 for scm in found_scms:
49 if scm[0] == explicit_alias:
49 if scm[0] == explicit_alias:
50 return scm
50 return scm
51 raise VCSError('More than one [%s] scm found at given path %s'
51 raise VCSError('More than one [%s] scm found at given path %s'
52 % (','.join((x[0] for x in found_scms)), path))
52 % (','.join((x[0] for x in found_scms)), path))
53
53
54 if len(found_scms) is 0:
54 if len(found_scms) is 0:
55 raise VCSError('No scm found at given path %s' % path)
55 raise VCSError('No scm found at given path %s' % path)
56
56
57 return found_scms[0]
57 return found_scms[0]
58
58
59
59
60 def get_scms_for_path(path):
60 def get_scms_for_path(path):
61 """
61 """
62 Returns all scm's found at the given path. If no scm is recognized
62 Returns all scm's found at the given path. If no scm is recognized
63 - empty list is returned.
63 - empty list is returned.
64
64
65 :param path: path to directory which should be checked. May be callable.
65 :param path: path to directory which should be checked. May be callable.
66
66
67 :raises VCSError: if given ``path`` is not a directory
67 :raises VCSError: if given ``path`` is not a directory
68 """
68 """
69 from rhodecode.lib.vcs.backends import get_backend
69 from rhodecode.lib.vcs.backends import get_backend
70 if hasattr(path, '__call__'):
70 if hasattr(path, '__call__'):
71 path = path()
71 path = path()
72 if not os.path.isdir(path):
72 if not os.path.isdir(path):
73 raise VCSError("Given path %r is not a directory" % path)
73 raise VCSError("Given path %r is not a directory" % path)
74
74
75 result = []
75 result = []
76 for key in ALIASES:
76 for key in ALIASES:
77 dirname = os.path.join(path, '.' + key)
77 dirname = os.path.join(path, '.' + key)
78 if os.path.isdir(dirname):
78 if os.path.isdir(dirname):
79 result.append(key)
79 result.append(key)
80 continue
80 continue
81 dirname = os.path.join(path, 'rm__.' + key)
81 dirname = os.path.join(path, 'rm__.' + key)
82 if os.path.isdir(dirname):
82 if os.path.isdir(dirname):
83 return [None]
83 return [None]
84 # We still need to check if it's not bare repository as
84 # We still need to check if it's not bare repository as
85 # bare repos don't have working directories
85 # bare repos don't have working directories
86 try:
86 try:
87 get_backend(key)(path)
87 get_backend(key)(path)
88 result.append(key)
88 result.append(key)
89 continue
89 continue
90 except RepositoryError:
90 except RepositoryError:
91 # Wrong backend
91 # Wrong backend
92 pass
92 pass
93 except VCSError:
93 except VCSError:
94 # No backend at all
94 # No backend at all
95 pass
95 pass
96 return result
96 return result
97
97
98
98
99 def get_repo_paths(path):
99 def get_repo_paths(path):
100 """
100 """
101 Returns path's subdirectories which seems to be a repository.
101 Returns path's subdirectories which seems to be a repository.
102 """
102 """
103 repo_paths = []
103 repo_paths = []
104 dirnames = (os.path.abspath(dirname) for dirname in os.listdir(path))
104 dirnames = (os.path.abspath(dirname) for dirname in os.listdir(path))
105 for dirname in dirnames:
105 for dirname in dirnames:
106 try:
106 try:
107 get_scm(dirname)
107 get_scm(dirname)
108 repo_paths.append(dirname)
108 repo_paths.append(dirname)
109 except VCSError:
109 except VCSError:
110 pass
110 pass
111 return repo_paths
111 return repo_paths
112
112
113
113
114 def run_command(cmd, *args):
114 def run_command(cmd, *args):
115 """
115 """
116 Runs command on the system with given ``args``.
116 Runs command on the system with given ``args``.
117 """
117 """
118 command = ' '.join((cmd, args))
118 command = ' '.join((cmd, args))
119 p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE)
119 p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE)
120 stdout, stderr = p.communicate()
120 stdout, stderr = p.communicate()
121 return p.retcode, stdout, stderr
121 return p.retcode, stdout, stderr
122
122
123
123
124 def get_highlighted_code(name, code, type='terminal'):
124 def get_highlighted_code(name, code, type='terminal'):
125 """
125 """
126 If pygments are available on the system
126 If pygments are available on the system
127 then returned output is colored. Otherwise
127 then returned output is colored. Otherwise
128 unchanged content is returned.
128 unchanged content is returned.
129 """
129 """
130 import logging
130 import logging
131 try:
131 try:
132 import pygments
132 import pygments
133 pygments
133 pygments
134 except ImportError:
134 except ImportError:
135 return code
135 return code
136 from pygments import highlight
136 from pygments import highlight
137 from pygments.lexers import guess_lexer_for_filename, ClassNotFound
137 from pygments.lexers import guess_lexer_for_filename, ClassNotFound
138 from pygments.formatters import TerminalFormatter
138 from pygments.formatters import TerminalFormatter
139
139
140 try:
140 try:
141 lexer = guess_lexer_for_filename(name, code)
141 lexer = guess_lexer_for_filename(name, code)
142 formatter = TerminalFormatter()
142 formatter = TerminalFormatter()
143 content = highlight(code, lexer, formatter)
143 content = highlight(code, lexer, formatter)
144 except ClassNotFound:
144 except ClassNotFound:
145 logging.debug("Couldn't guess Lexer, will not use pygments.")
145 logging.debug("Couldn't guess Lexer, will not use pygments.")
146 content = code
146 content = code
147 return content
147 return content
148
148
149 def parse_changesets(text):
149 def parse_changesets(text):
150 """
150 """
151 Returns dictionary with *start*, *main* and *end* ids.
151 Returns dictionary with *start*, *main* and *end* ids.
152
152
153 Examples::
153 Examples::
154
154
155 >>> parse_changesets('aaabbb')
155 >>> parse_changesets('aaabbb')
156 {'start': None, 'main': 'aaabbb', 'end': None}
156 {'start': None, 'main': 'aaabbb', 'end': None}
157 >>> parse_changesets('aaabbb..cccddd')
157 >>> parse_changesets('aaabbb..cccddd')
158 {'start': 'aaabbb', 'main': None, 'end': 'cccddd'}
158 {'start': 'aaabbb', 'main': None, 'end': 'cccddd'}
159
159
160 """
160 """
161 text = text.strip()
161 text = text.strip()
162 CID_RE = r'[a-zA-Z0-9]+'
162 CID_RE = r'[a-zA-Z0-9]+'
163 if not '..' in text:
163 if not '..' in text:
164 m = re.match(r'^(?P<cid>%s)$' % CID_RE, text)
164 m = re.match(r'^(?P<cid>%s)$' % CID_RE, text)
165 if m:
165 if m:
166 return {
166 return {
167 'start': None,
167 'start': None,
168 'main': text,
168 'main': text,
169 'end': None,
169 'end': None,
170 }
170 }
171 else:
171 else:
172 RE = r'^(?P<start>%s)?\.{2,3}(?P<end>%s)?$' % (CID_RE, CID_RE)
172 RE = r'^(?P<start>%s)?\.{2,3}(?P<end>%s)?$' % (CID_RE, CID_RE)
173 m = re.match(RE, text)
173 m = re.match(RE, text)
174 if m:
174 if m:
175 result = m.groupdict()
175 result = m.groupdict()
176 result['main'] = None
176 result['main'] = None
177 return result
177 return result
178 raise ValueError("IDs not recognized")
178 raise ValueError("IDs not recognized")
179
179
180 def parse_datetime(text):
180 def parse_datetime(text):
181 """
181 """
182 Parses given text and returns ``datetime.datetime`` instance or raises
182 Parses given text and returns ``datetime.datetime`` instance or raises
183 ``ValueError``.
183 ``ValueError``.
184
184
185 :param text: string of desired date/datetime or something more verbose,
185 :param text: string of desired date/datetime or something more verbose,
186 like *yesterday*, *2weeks 3days*, etc.
186 like *yesterday*, *2weeks 3days*, etc.
187 """
187 """
188
188
189 text = text.strip().lower()
189 text = text.strip().lower()
190
190
191 INPUT_FORMATS = (
191 INPUT_FORMATS = (
192 '%Y-%m-%d %H:%M:%S',
192 '%Y-%m-%d %H:%M:%S',
193 '%Y-%m-%d %H:%M',
193 '%Y-%m-%d %H:%M',
194 '%Y-%m-%d',
194 '%Y-%m-%d',
195 '%m/%d/%Y %H:%M:%S',
195 '%m/%d/%Y %H:%M:%S',
196 '%m/%d/%Y %H:%M',
196 '%m/%d/%Y %H:%M',
197 '%m/%d/%Y',
197 '%m/%d/%Y',
198 '%m/%d/%y %H:%M:%S',
198 '%m/%d/%y %H:%M:%S',
199 '%m/%d/%y %H:%M',
199 '%m/%d/%y %H:%M',
200 '%m/%d/%y',
200 '%m/%d/%y',
201 )
201 )
202 for format in INPUT_FORMATS:
202 for format in INPUT_FORMATS:
203 try:
203 try:
204 return datetime.datetime(*time.strptime(text, format)[:6])
204 return datetime.datetime(*time.strptime(text, format)[:6])
205 except ValueError:
205 except ValueError:
206 pass
206 pass
207
207
208 # Try descriptive texts
208 # Try descriptive texts
209 if text == 'tomorrow':
209 if text == 'tomorrow':
210 future = datetime.datetime.now() + datetime.timedelta(days=1)
210 future = datetime.datetime.now() + datetime.timedelta(days=1)
211 args = future.timetuple()[:3] + (23, 59, 59)
211 args = future.timetuple()[:3] + (23, 59, 59)
212 return datetime.datetime(*args)
212 return datetime.datetime(*args)
213 elif text == 'today':
213 elif text == 'today':
214 return datetime.datetime(*datetime.datetime.today().timetuple()[:3])
214 return datetime.datetime(*datetime.datetime.today().timetuple()[:3])
215 elif text == 'now':
215 elif text == 'now':
216 return datetime.datetime.now()
216 return datetime.datetime.now()
217 elif text == 'yesterday':
217 elif text == 'yesterday':
218 past = datetime.datetime.now() - datetime.timedelta(days=1)
218 past = datetime.datetime.now() - datetime.timedelta(days=1)
219 return datetime.datetime(*past.timetuple()[:3])
219 return datetime.datetime(*past.timetuple()[:3])
220 else:
220 else:
221 days = 0
221 days = 0
222 matched = re.match(
222 matched = re.match(
223 r'^((?P<weeks>\d+) ?w(eeks?)?)? ?((?P<days>\d+) ?d(ays?)?)?$', text)
223 r'^((?P<weeks>\d+) ?w(eeks?)?)? ?((?P<days>\d+) ?d(ays?)?)?$', text)
224 if matched:
224 if matched:
225 groupdict = matched.groupdict()
225 groupdict = matched.groupdict()
226 if groupdict['days']:
226 if groupdict['days']:
227 days += int(matched.groupdict()['days'])
227 days += int(matched.groupdict()['days'])
228 if groupdict['weeks']:
228 if groupdict['weeks']:
229 days += int(matched.groupdict()['weeks']) * 7
229 days += int(matched.groupdict()['weeks']) * 7
230 past = datetime.datetime.now() - datetime.timedelta(days=days)
230 past = datetime.datetime.now() - datetime.timedelta(days=days)
231 return datetime.datetime(*past.timetuple()[:3])
231 return datetime.datetime(*past.timetuple()[:3])
232
232
233 raise ValueError('Wrong date: "%s"' % text)
233 raise ValueError('Wrong date: "%s"' % text)
234
234
235
235
236 def get_dict_for_attrs(obj, attrs):
236 def get_dict_for_attrs(obj, attrs):
237 """
237 """
238 Returns dictionary for each attribute from given ``obj``.
238 Returns dictionary for each attribute from given ``obj``.
239 """
239 """
240 data = {}
240 data = {}
241 for attr in attrs:
241 for attr in attrs:
242 data[attr] = getattr(obj, attr)
242 data[attr] = getattr(obj, attr)
243 return data
243 return data
244
244
245
245
246 def get_total_seconds(timedelta):
246 def get_total_seconds(timedelta):
247 """
247 """
248 Backported for Python 2.5.
248 Backported for Python 2.5.
249
249
250 See http://docs.python.org/library/datetime.html.
250 See http://docs.python.org/library/datetime.html.
251 """
251 """
252 return ((timedelta.microseconds + (
252 return ((timedelta.microseconds + (
253 timedelta.seconds +
253 timedelta.seconds +
254 timedelta.days * 24 * 60 * 60
254 timedelta.days * 24 * 60 * 60
255 ) * 10**6) / 10**6)
255 ) * 10**6) / 10**6)
General Comments 0
You need to be logged in to leave comments. Login now