##// END OF EJS Templates
removed duplicate code that set os.environ['RC_SCM_DATA']...
marcink -
r3823:972ad33c beta
parent child Browse files
Show More
@@ -1,337 +1,336
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.middleware.simplegit
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 SimpleGit middleware for handling git protocol request (push/clone etc.)
7 7 It's implemented with basic auth function
8 8
9 9 :created_on: Apr 28, 2010
10 10 :author: marcink
11 11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 14 # This program is free software: you can redistribute it and/or modify
15 15 # it under the terms of the GNU General Public License as published by
16 16 # the Free Software Foundation, either version 3 of the License, or
17 17 # (at your option) any later version.
18 18 #
19 19 # This program is distributed in the hope that it will be useful,
20 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 22 # GNU General Public License for more details.
23 23 #
24 24 # You should have received a copy of the GNU General Public License
25 25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 26
27 27 import os
28 28 import re
29 29 import logging
30 30 import traceback
31 31
32 32 from dulwich import server as dulserver
33 33 from dulwich.web import LimitedInputFilter, GunzipFilter
34 34 from rhodecode.lib.exceptions import HTTPLockedRC
35 35 from rhodecode.lib.hooks import pre_pull
36 36
37 37
38 38 class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
39 39
40 40 def handle(self):
41 41 write = lambda x: self.proto.write_sideband(1, x)
42 42
43 43 graph_walker = dulserver.ProtocolGraphWalker(self,
44 44 self.repo.object_store,
45 45 self.repo.get_peeled)
46 46 objects_iter = self.repo.fetch_objects(
47 47 graph_walker.determine_wants, graph_walker, self.progress,
48 48 get_tagged=self.get_tagged)
49 49
50 50 # Did the process short-circuit (e.g. in a stateless RPC call)? Note
51 51 # that the client still expects a 0-object pack in most cases.
52 52 if objects_iter is None:
53 53 return
54 54
55 55 self.progress("counting objects: %d, done.\n" % len(objects_iter))
56 56 dulserver.write_pack_objects(dulserver.ProtocolFile(None, write),
57 57 objects_iter)
58 58 messages = []
59 59 messages.append('thank you for using rhodecode')
60 60
61 61 for msg in messages:
62 62 self.progress(msg + "\n")
63 63 # we are done
64 64 self.proto.write("0000")
65 65
66 66
67 67 dulserver.DEFAULT_HANDLERS = {
68 68 #git-ls-remote, git-clone, git-fetch and git-pull
69 69 'git-upload-pack': SimpleGitUploadPackHandler,
70 70 #git-push
71 71 'git-receive-pack': dulserver.ReceivePackHandler,
72 72 }
73 73
74 74 # not used for now until dulwich get's fixed
75 75 #from dulwich.repo import Repo
76 76 #from dulwich.web import make_wsgi_chain
77 77
78 78 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
79 79 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \
80 80 HTTPBadRequest, HTTPNotAcceptable
81 81
82 82 from rhodecode.lib.utils2 import safe_str, fix_PATH, get_server_url,\
83 83 _set_extras
84 84 from rhodecode.lib.base import BaseVCSController
85 85 from rhodecode.lib.auth import get_container_username
86 86 from rhodecode.lib.utils import is_valid_repo, make_ui
87 87 from rhodecode.lib.compat import json
88 88 from rhodecode.model.db import User, RhodeCodeUi
89 89
90 90 log = logging.getLogger(__name__)
91 91
92 92
93 93 GIT_PROTO_PAT = re.compile(r'^/(.+)/(info/refs|git-upload-pack|git-receive-pack)')
94 94
95 95
96 96 def is_git(environ):
97 97 path_info = environ['PATH_INFO']
98 98 isgit_path = GIT_PROTO_PAT.match(path_info)
99 99 log.debug('pathinfo: %s detected as GIT %s' % (
100 100 path_info, isgit_path != None)
101 101 )
102 102 return isgit_path
103 103
104 104
105 105 class SimpleGit(BaseVCSController):
106 106
107 107 def _handle_request(self, environ, start_response):
108 108 if not is_git(environ):
109 109 return self.application(environ, start_response)
110 110 if not self._check_ssl(environ, start_response):
111 111 return HTTPNotAcceptable('SSL REQUIRED !')(environ, start_response)
112 112
113 113 ip_addr = self._get_ip_addr(environ)
114 114 username = None
115 115 self._git_first_op = False
116 116 # skip passing error to error controller
117 117 environ['pylons.status_code_redirect'] = True
118 118
119 119 #======================================================================
120 120 # EXTRACT REPOSITORY NAME FROM ENV
121 121 #======================================================================
122 122 try:
123 123 repo_name = self.__get_repository(environ)
124 124 log.debug('Extracted repo name is %s' % repo_name)
125 125 except Exception:
126 126 return HTTPInternalServerError()(environ, start_response)
127 127
128 128 # quick check if that dir exists...
129 129 if not is_valid_repo(repo_name, self.basepath, 'git'):
130 130 return HTTPNotFound()(environ, start_response)
131 131
132 132 #======================================================================
133 133 # GET ACTION PULL or PUSH
134 134 #======================================================================
135 135 action = self.__get_action(environ)
136 136
137 137 #======================================================================
138 138 # CHECK ANONYMOUS PERMISSION
139 139 #======================================================================
140 140 if action in ['pull', 'push']:
141 141 anonymous_user = self.__get_user('default')
142 142 username = anonymous_user.username
143 143 anonymous_perm = self._check_permission(action, anonymous_user,
144 144 repo_name, ip_addr)
145 145
146 146 if not anonymous_perm or not anonymous_user.active:
147 147 if not anonymous_perm:
148 148 log.debug('Not enough credentials to access this '
149 149 'repository as anonymous user')
150 150 if not anonymous_user.active:
151 151 log.debug('Anonymous access is disabled, running '
152 152 'authentication')
153 153 #==============================================================
154 154 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
155 155 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
156 156 #==============================================================
157 157
158 158 # Attempting to retrieve username from the container
159 159 username = get_container_username(environ, self.config)
160 160
161 161 # If not authenticated by the container, running basic auth
162 162 if not username:
163 163 self.authenticate.realm = \
164 164 safe_str(self.config['rhodecode_realm'])
165 165 result = self.authenticate(environ)
166 166 if isinstance(result, str):
167 167 AUTH_TYPE.update(environ, 'basic')
168 168 REMOTE_USER.update(environ, result)
169 169 username = result
170 170 else:
171 171 return result.wsgi_application(environ, start_response)
172 172
173 173 #==============================================================
174 174 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
175 175 #==============================================================
176 176 try:
177 177 user = self.__get_user(username)
178 178 if user is None or not user.active:
179 179 return HTTPForbidden()(environ, start_response)
180 180 username = user.username
181 181 except Exception:
182 182 log.error(traceback.format_exc())
183 183 return HTTPInternalServerError()(environ, start_response)
184 184
185 185 #check permissions for this repository
186 186 perm = self._check_permission(action, user, repo_name, ip_addr)
187 187 if not perm:
188 188 return HTTPForbidden()(environ, start_response)
189 189
190 190 # extras are injected into UI object and later available
191 191 # in hooks executed by rhodecode
192 192 from rhodecode import CONFIG
193 193 server_url = get_server_url(environ)
194 194 extras = {
195 195 'ip': ip_addr,
196 196 'username': username,
197 197 'action': action,
198 198 'repository': repo_name,
199 199 'scm': 'git',
200 200 'config': CONFIG['__file__'],
201 201 'server_url': server_url,
202 202 'make_lock': None,
203 203 'locked_by': [None, None]
204 204 }
205 205
206 206 #===================================================================
207 207 # GIT REQUEST HANDLING
208 208 #===================================================================
209 209 str_repo_name = safe_str(repo_name)
210 210 repo_path = os.path.join(safe_str(self.basepath),str_repo_name)
211 211 log.debug('Repository path is %s' % repo_path)
212 212
213 213 # CHECK LOCKING only if it's not ANONYMOUS USER
214 214 if username != User.DEFAULT_USER:
215 215 log.debug('Checking locking on repository')
216 216 (make_lock,
217 217 locked,
218 218 locked_by) = self._check_locking_state(
219 219 environ=environ, action=action,
220 220 repo=repo_name, user_id=user.user_id
221 221 )
222 222 # store the make_lock for later evaluation in hooks
223 223 extras.update({'make_lock': make_lock,
224 224 'locked_by': locked_by})
225 # set the environ variables for this request
226 os.environ['RC_SCM_DATA'] = json.dumps(extras)
225
227 226 fix_PATH()
228 227 log.debug('HOOKS extras is %s' % extras)
229 228 baseui = make_ui('db')
230 229 self.__inject_extras(repo_path, baseui, extras)
231 230
232 231 try:
233 232 self._handle_githooks(repo_name, action, baseui, environ)
234 233 log.info('%s action on GIT repo "%s" by "%s" from %s' %
235 234 (action, str_repo_name, safe_str(username), ip_addr))
236 235 app = self.__make_app(repo_name, repo_path, extras)
237 236 return app(environ, start_response)
238 237 except HTTPLockedRC, e:
239 238 _code = CONFIG.get('lock_ret_code')
240 239 log.debug('Repository LOCKED ret code %s!' % (_code))
241 240 return e(environ, start_response)
242 241 except Exception:
243 242 log.error(traceback.format_exc())
244 243 return HTTPInternalServerError()(environ, start_response)
245 244 finally:
246 245 # invalidate cache on push
247 246 if action == 'push':
248 247 self._invalidate_cache(repo_name)
249 248
250 249 def __make_app(self, repo_name, repo_path, extras):
251 250 """
252 251 Make an wsgi application using dulserver
253 252
254 253 :param repo_name: name of the repository
255 254 :param repo_path: full path to the repository
256 255 """
257 256
258 257 from rhodecode.lib.middleware.pygrack import make_wsgi_app
259 258 app = make_wsgi_app(
260 259 repo_root=safe_str(self.basepath),
261 260 repo_name=repo_name,
262 261 extras=extras,
263 262 )
264 263 app = GunzipFilter(LimitedInputFilter(app))
265 264 return app
266 265
267 266 def __get_repository(self, environ):
268 267 """
269 268 Get's repository name out of PATH_INFO header
270 269
271 270 :param environ: environ where PATH_INFO is stored
272 271 """
273 272 try:
274 273 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
275 274 repo_name = GIT_PROTO_PAT.match(environ['PATH_INFO']).group(1)
276 275 except Exception:
277 276 log.error(traceback.format_exc())
278 277 raise
279 278
280 279 return repo_name
281 280
282 281 def __get_user(self, username):
283 282 return User.get_by_username(username)
284 283
285 284 def __get_action(self, environ):
286 285 """
287 286 Maps git request commands into a pull or push command.
288 287
289 288 :param environ:
290 289 """
291 290 service = environ['QUERY_STRING'].split('=')
292 291
293 292 if len(service) > 1:
294 293 service_cmd = service[1]
295 294 mapping = {
296 295 'git-receive-pack': 'push',
297 296 'git-upload-pack': 'pull',
298 297 }
299 298 op = mapping[service_cmd]
300 299 self._git_stored_op = op
301 300 return op
302 301 else:
303 302 # try to fallback to stored variable as we don't know if the last
304 303 # operation is pull/push
305 304 op = getattr(self, '_git_stored_op', 'pull')
306 305 return op
307 306
308 307 def _handle_githooks(self, repo_name, action, baseui, environ):
309 308 """
310 309 Handles pull action, push is handled by post-receive hook
311 310 """
312 311 from rhodecode.lib.hooks import log_pull_action
313 312 service = environ['QUERY_STRING'].split('=')
314 313
315 314 if len(service) < 2:
316 315 return
317 316
318 317 from rhodecode.model.db import Repository
319 318 _repo = Repository.get_by_repo_name(repo_name)
320 319 _repo = _repo.scm_instance
321 320
322 321 _hooks = dict(baseui.configitems('hooks')) or {}
323 322 if action == 'pull':
324 323 # stupid git, emulate pre-pull hook !
325 324 pre_pull(ui=baseui, repo=_repo._repo)
326 325 if action == 'pull' and _hooks.get(RhodeCodeUi.HOOK_PULL):
327 326 log_pull_action(ui=baseui, repo=_repo._repo)
328 327
329 328 def __inject_extras(self, repo_path, baseui, extras={}):
330 329 """
331 330 Injects some extra params into baseui instance
332 331
333 332 :param baseui: baseui instance
334 333 :param extras: dict with extra params to put into baseui
335 334 """
336 335
337 336 _set_extras(extras)
@@ -1,288 +1,286
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.middleware.simplehg
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 SimpleHG middleware for handling mercurial protocol request
7 7 (push/clone etc.). It's implemented with basic auth function
8 8
9 9 :created_on: Apr 28, 2010
10 10 :author: marcink
11 11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 14 # This program is free software: you can redistribute it and/or modify
15 15 # it under the terms of the GNU General Public License as published by
16 16 # the Free Software Foundation, either version 3 of the License, or
17 17 # (at your option) any later version.
18 18 #
19 19 # This program is distributed in the hope that it will be useful,
20 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 22 # GNU General Public License for more details.
23 23 #
24 24 # You should have received a copy of the GNU General Public License
25 25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 26
27 27 import os
28 28 import logging
29 29 import traceback
30 30
31 31 from mercurial.error import RepoError
32 32 from mercurial.hgweb import hgweb_mod
33 33
34 34 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
35 35 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \
36 36 HTTPBadRequest, HTTPNotAcceptable
37 37
38 38 from rhodecode.lib.utils2 import safe_str, fix_PATH, get_server_url,\
39 39 _set_extras
40 40 from rhodecode.lib.base import BaseVCSController
41 41 from rhodecode.lib.auth import get_container_username
42 42 from rhodecode.lib.utils import make_ui, is_valid_repo, ui_sections
43 43 from rhodecode.lib.compat import json
44 44 from rhodecode.model.db import User
45 45 from rhodecode.lib.exceptions import HTTPLockedRC
46 46
47 47
48 48 log = logging.getLogger(__name__)
49 49
50 50
51 51 def is_mercurial(environ):
52 52 """
53 53 Returns True if request's target is mercurial server - header
54 54 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
55 55 """
56 56 http_accept = environ.get('HTTP_ACCEPT')
57 57 path_info = environ['PATH_INFO']
58 58 if http_accept and http_accept.startswith('application/mercurial'):
59 59 ishg_path = True
60 60 else:
61 61 ishg_path = False
62 62
63 63 log.debug('pathinfo: %s detected as HG %s' % (
64 64 path_info, ishg_path)
65 65 )
66 66 return ishg_path
67 67
68 68
69 69 class SimpleHg(BaseVCSController):
70 70
71 71 def _handle_request(self, environ, start_response):
72 72 if not is_mercurial(environ):
73 73 return self.application(environ, start_response)
74 74 if not self._check_ssl(environ, start_response):
75 75 return HTTPNotAcceptable('SSL REQUIRED !')(environ, start_response)
76 76
77 77 ip_addr = self._get_ip_addr(environ)
78 78 username = None
79 79 # skip passing error to error controller
80 80 environ['pylons.status_code_redirect'] = True
81 81
82 82 #======================================================================
83 83 # EXTRACT REPOSITORY NAME FROM ENV
84 84 #======================================================================
85 85 try:
86 86 repo_name = environ['REPO_NAME'] = self.__get_repository(environ)
87 87 log.debug('Extracted repo name is %s' % repo_name)
88 88 except Exception:
89 89 return HTTPInternalServerError()(environ, start_response)
90 90
91 91 # quick check if that dir exists...
92 92 if not is_valid_repo(repo_name, self.basepath, 'hg'):
93 93 return HTTPNotFound()(environ, start_response)
94 94
95 95 #======================================================================
96 96 # GET ACTION PULL or PUSH
97 97 #======================================================================
98 98 action = self.__get_action(environ)
99 99
100 100 #======================================================================
101 101 # CHECK ANONYMOUS PERMISSION
102 102 #======================================================================
103 103 if action in ['pull', 'push']:
104 104 anonymous_user = self.__get_user('default')
105 105 username = anonymous_user.username
106 106 anonymous_perm = self._check_permission(action, anonymous_user,
107 107 repo_name, ip_addr)
108 108
109 109 if not anonymous_perm or not anonymous_user.active:
110 110 if not anonymous_perm:
111 111 log.debug('Not enough credentials to access this '
112 112 'repository as anonymous user')
113 113 if not anonymous_user.active:
114 114 log.debug('Anonymous access is disabled, running '
115 115 'authentication')
116 116 #==============================================================
117 117 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
118 118 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
119 119 #==============================================================
120 120
121 121 # Attempting to retrieve username from the container
122 122 username = get_container_username(environ, self.config)
123 123
124 124 # If not authenticated by the container, running basic auth
125 125 if not username:
126 126 self.authenticate.realm = \
127 127 safe_str(self.config['rhodecode_realm'])
128 128 result = self.authenticate(environ)
129 129 if isinstance(result, str):
130 130 AUTH_TYPE.update(environ, 'basic')
131 131 REMOTE_USER.update(environ, result)
132 132 username = result
133 133 else:
134 134 return result.wsgi_application(environ, start_response)
135 135
136 136 #==============================================================
137 137 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
138 138 #==============================================================
139 139 try:
140 140 user = self.__get_user(username)
141 141 if user is None or not user.active:
142 142 return HTTPForbidden()(environ, start_response)
143 143 username = user.username
144 144 except Exception:
145 145 log.error(traceback.format_exc())
146 146 return HTTPInternalServerError()(environ, start_response)
147 147
148 148 #check permissions for this repository
149 149 perm = self._check_permission(action, user, repo_name, ip_addr)
150 150 if not perm:
151 151 return HTTPForbidden()(environ, start_response)
152 152
153 153 # extras are injected into mercurial UI object and later available
154 154 # in hg hooks executed by rhodecode
155 155 from rhodecode import CONFIG
156 156 server_url = get_server_url(environ)
157 157 extras = {
158 158 'ip': ip_addr,
159 159 'username': username,
160 160 'action': action,
161 161 'repository': repo_name,
162 162 'scm': 'hg',
163 163 'config': CONFIG['__file__'],
164 164 'server_url': server_url,
165 165 'make_lock': None,
166 166 'locked_by': [None, None]
167 167 }
168 168 #======================================================================
169 169 # MERCURIAL REQUEST HANDLING
170 170 #======================================================================
171 171 str_repo_name = safe_str(repo_name)
172 172 repo_path = os.path.join(safe_str(self.basepath), str_repo_name)
173 173 log.debug('Repository path is %s' % repo_path)
174 174
175 175 # CHECK LOCKING only if it's not ANONYMOUS USER
176 176 if username != User.DEFAULT_USER:
177 177 log.debug('Checking locking on repository')
178 178 (make_lock,
179 179 locked,
180 180 locked_by) = self._check_locking_state(
181 181 environ=environ, action=action,
182 182 repo=repo_name, user_id=user.user_id
183 183 )
184 184 # store the make_lock for later evaluation in hooks
185 185 extras.update({'make_lock': make_lock,
186 186 'locked_by': locked_by})
187 187
188 # set the environ variables for this request
189 os.environ['RC_SCM_DATA'] = json.dumps(extras)
190 188 fix_PATH()
191 189 log.debug('HOOKS extras is %s' % extras)
192 190 baseui = make_ui('db')
193 191 self.__inject_extras(repo_path, baseui, extras)
194 192
195 193 try:
196 194 log.info('%s action on HG repo "%s" by "%s" from %s' %
197 195 (action, str_repo_name, safe_str(username), ip_addr))
198 196 app = self.__make_app(repo_path, baseui, extras)
199 197 return app(environ, start_response)
200 198 except RepoError, e:
201 199 if str(e).find('not found') != -1:
202 200 return HTTPNotFound()(environ, start_response)
203 201 except HTTPLockedRC, e:
204 202 _code = CONFIG.get('lock_ret_code')
205 203 log.debug('Repository LOCKED ret code %s!' % (_code))
206 204 return e(environ, start_response)
207 205 except Exception:
208 206 log.error(traceback.format_exc())
209 207 return HTTPInternalServerError()(environ, start_response)
210 208 finally:
211 209 # invalidate cache on push
212 210 if action == 'push':
213 211 self._invalidate_cache(repo_name)
214 212
215 213 def __make_app(self, repo_name, baseui, extras):
216 214 """
217 215 Make an wsgi application using hgweb, and inject generated baseui
218 216 instance, additionally inject some extras into ui object
219 217 """
220 218 return hgweb_mod.hgweb(repo_name, name=repo_name, baseui=baseui)
221 219
222 220 def __get_repository(self, environ):
223 221 """
224 222 Get's repository name out of PATH_INFO header
225 223
226 224 :param environ: environ where PATH_INFO is stored
227 225 """
228 226 try:
229 227 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
230 228 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
231 229 if repo_name.endswith('/'):
232 230 repo_name = repo_name.rstrip('/')
233 231 except Exception:
234 232 log.error(traceback.format_exc())
235 233 raise
236 234
237 235 return repo_name
238 236
239 237 def __get_user(self, username):
240 238 return User.get_by_username(username)
241 239
242 240 def __get_action(self, environ):
243 241 """
244 242 Maps mercurial request commands into a clone,pull or push command.
245 243 This should always return a valid command string
246 244
247 245 :param environ:
248 246 """
249 247 mapping = {'changegroup': 'pull',
250 248 'changegroupsubset': 'pull',
251 249 'stream_out': 'pull',
252 250 'listkeys': 'pull',
253 251 'unbundle': 'push',
254 252 'pushkey': 'push', }
255 253 for qry in environ['QUERY_STRING'].split('&'):
256 254 if qry.startswith('cmd'):
257 255 cmd = qry.split('=')[-1]
258 256 if cmd in mapping:
259 257 return mapping[cmd]
260 258
261 259 return 'pull'
262 260
263 261 raise Exception('Unable to detect pull/push action !!'
264 262 'Are you using non standard command or client ?')
265 263
266 264 def __inject_extras(self, repo_path, baseui, extras={}):
267 265 """
268 266 Injects some extra params into baseui instance
269 267
270 268 also overwrites global settings with those takes from local hgrc file
271 269
272 270 :param baseui: baseui instance
273 271 :param extras: dict with extra params to put into baseui
274 272 """
275 273
276 274 hgrc = os.path.join(repo_path, '.hg', 'hgrc')
277 275
278 276 # make our hgweb quiet so it doesn't print output
279 277 baseui.setconfig('ui', 'quiet', 'true')
280 278
281 279 repoui = make_ui('file', hgrc, False)
282 280
283 281 if repoui:
284 282 #overwrite our ui instance with the section from hgrc file
285 283 for section in ui_sections:
286 284 for k, v in repoui.configitems(section):
287 285 baseui.setconfig(section, k, v)
288 286 _set_extras(extras)
General Comments 0
You need to be logged in to leave comments. Login now