Mercurial > hg-git-serve
diff src/hgext3rd/hggit_serve/_ssh.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 | |
| children | b65d5922b8ee |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hgext3rd/hggit_serve/_ssh.py Thu Feb 19 01:13:56 2026 -0500 @@ -0,0 +1,112 @@ +from __future__ import annotations + +import subprocess +import typing as t + +from mercurial import error as hgerr +from mercurial import registrar + +from . import _export as xp + +if t.TYPE_CHECKING: + import mercurial.interfaces.repository as hgrepo + import mercurial.ui as hgui + +cmdtable: dict[bytes, object] = {} +_command = registrar.command(cmdtable) + + +def _not_git() -> hgerr.StateError: + return hgerr.StateError( + b'Git is not enabled for this repository.', + hint=b'The server administrator should enable the ``hggit`` extension ' + b'and run ``hg git-export`` to enable Git access.', + ) + + +def _maybe(flag: bytes, include: bool) -> tuple[bytes, ...]: + return (flag,) if include else () + + +@_command( + b'git-upload-pack', + [ + *( + (b'', opt, False, b'flag passed to git upload-pack') + for opt in ( + b'strict', + b'no-strict', + b'stateless-rpc', + b'advertise-refs', + ) + ), + (b'', b'timeout', -1, b'flag passed to git upload-pack'), + ], + helpcategory=b'import', + intents=(b'readonly',), +) +def _git_upload_pack( + ui: hgui.ui, + repo: hgrepo.IRepo, + *, + strict: bool, + no_strict: bool, + stateless_rpc: bool, + advertise_refs: bool, + timeout: int, +) -> int: + """Server-side handler for ``git pull``/``git clone``.""" + if not xp.is_gitty(repo): + raise _not_git() + timeout_flag = ( + (b'--timeout=' + str(timeout).encode(),) if timeout != -1 else () + ) + upload_pack = ui.configlist( + b'hggit-serve', b'upload-pack', default=(b'git', b'upload-pack') + ) + return subprocess.call( + ( + *upload_pack, + *_maybe(b'--strict', strict), + *_maybe(b'--no-strict', no_strict), + *_maybe(b'--stateless-rpc', stateless_rpc), + *_maybe(b'--advertise_refs', advertise_refs), + *timeout_flag, + repo.githandler.gitdir, + ), + close_fds=True, + ) + + +@_command( + b'git-receive-pack', + [ + (b'', b'skip-connectivity-check', False, b'passed to git receive-pack'), + ], + helpcategory=b'import', +) +def _git_receive_pack( + ui: hgui.ui, repo: hgrepo.IRepo, *, skip_connectivity_check: bool +) -> int: + """Server-side handler for ``git push``.""" + if not xp.is_gitty(repo): + raise _not_git() + receive_pack = ui.configlist( + b'hggit-serve', b'receive-pack', default=(b'git', b'receive-pack') + ) + try: + subprocess.check_call( + ( + *receive_pack, + *_maybe(b'--skip-connectivity-check', skip_connectivity_check), + repo.githandler.gitdir, + ), + close_fds=True, + ) + except subprocess.CalledProcessError as cpe: + return cpe.returncode + xp.import_all(repo, b'git-receive-pack') + return 0 + + +__all__ = ('cmdtable',)
