comparison src/hgext3rd/hggit_serve/_http.py @ 13:00bdfac5416c

Create Git SSH commands and add some documentation. Also cleanup. - Adds git-upload-pack and git-receive-pack as hg subcommands, to be run on the server side by git push/pull. - Starts on documentation. - Cleans up a lot of stuff.
author Paul Fisher <paul@pfish.zone>
date Thu, 19 Feb 2026 01:13:56 -0500
parents f630d9904ea7
children
comparison
equal deleted inserted replaced
12:f630d9904ea7 13:00bdfac5416c
14 from . import _export as xp 14 from . import _export as xp
15 15
16 if t.TYPE_CHECKING: 16 if t.TYPE_CHECKING:
17 import mercurial.hgweb.hgweb_mod_inner as web_inner 17 import mercurial.hgweb.hgweb_mod_inner as web_inner
18 import mercurial.hgweb.request as hgreq 18 import mercurial.hgweb.request as hgreq
19 import mercurial.interfaces.repository as hgrepo
20 import mercurial.ui as hgui 19 import mercurial.ui as hgui
21 20
22 PermissionCheck = t.Callable[ 21 PermissionCheck = t.Callable[
23 [web_inner.requestcontext, hgreq.parsedrequest, bytes], 22 [web_inner.requestcontext, hgreq.parsedrequest, bytes],
24 None, 23 None,
37 fixed = { 36 fixed = {
38 k: v 37 k: v
39 for (k, v) in request.rawenv.items() 38 for (k, v) in request.rawenv.items()
40 if isinstance(v, bytes) and _CGI_VAR.match(k) 39 if isinstance(v, bytes) and _CGI_VAR.match(k)
41 } 40 }
41 repo = req_ctx.repo
42 assert xp.is_gitty(repo)
42 fixed.update( 43 fixed.update(
43 { 44 {
44 b'GIT_HTTP_EXPORT_ALL': b'yes', 45 b'GIT_HTTP_EXPORT_ALL': b'yes',
45 b'GIT_PROJECT_ROOT': req_ctx.repo.path, 46 b'GIT_PROJECT_ROOT': repo.githandler.gitdir,
46 b'PATH_INFO': b'/git/' + request.dispatchpath, 47 b'PATH_INFO': b'/' + request.dispatchpath,
47 # Since Mercurial is taking care of authorization checking, 48 # Since Mercurial is taking care of authorization checking,
48 # we tell Git to always allow push. 49 # we tell Git to always allow push.
49 b'GIT_CONFIG_COUNT': b'1', 50 b'GIT_CONFIG_COUNT': b'1',
50 b'GIT_CONFIG_KEY_0': b'http.receivepack', 51 b'GIT_CONFIG_KEY_0': b'http.receivepack',
51 b'GIT_CONFIG_VALUE_0': b'true', 52 b'GIT_CONFIG_VALUE_0': b'true',
99 response: hgreq.wsgiresponse, 100 response: hgreq.wsgiresponse,
100 check_permission: PermissionCheck, 101 check_permission: PermissionCheck,
101 ) -> bool: 102 ) -> bool:
102 """Intercepts requests from Git, if needed.""" 103 """Intercepts requests from Git, if needed."""
103 perm = _git_service_permission(request) 104 perm = _git_service_permission(request)
104 repo: hgrepo.IRepo = req_ctx.repo 105 repo = req_ctx.repo
105 if not perm or not xp.is_gitty(repo): 106 if not perm or not xp.is_gitty(repo):
106 # We only handle Git requests to Gitty repos. 107 # We only handle Git requests to Gitty repos.
107 return original(req_ctx, request, response, check_permission) 108 return original(req_ctx, request, response, check_permission)
108 109
109 # Permission workaround: Mercurial requires POSTs for push, 110 # Permission workaround: Mercurial requires POSTs for push,
153 bs = shutil.COPY_BUFSIZE # type: ignore[attr-defined] 154 bs = shutil.COPY_BUFSIZE # type: ignore[attr-defined]
154 while more := rest.read(bs): 155 while more := rest.read(bs):
155 yield more 156 yield more
156 if perm == xp.PUSH: 157 if perm == xp.PUSH:
157 # If we pushed, we need to import any new refs back into Mercurial. 158 # If we pushed, we need to import any new refs back into Mercurial.
158 xp.importing_enter(repo) 159 xp.import_all(repo, b'git-http-push')
159 try:
160 gh = repo.githandler
161 gh.import_git_objects(
162 b'git-push', remote_names=(), refs=gh.git.refs.as_dict()
163 )
164 finally:
165 xp.importing_exit(repo)
166 160
167 response.setbodygen(write_the_rest()) 161 response.setbodygen(write_the_rest())
168 response.sendresponse() 162 response.sendresponse()
169 return True 163 return True
170 164