Mercurial > hg-git-serve
comparison src/hgext3rd/hggit_serve/_export.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 | 959ef686193f |
comparison
equal
deleted
inserted
replaced
| 12:f630d9904ea7 | 13:00bdfac5416c |
|---|---|
| 46 when we're done. It's not just a bool in case somebody sets up some crazy | 46 when we're done. It's not just a bool in case somebody sets up some crazy |
| 47 recursive hook situation where we start importing inside another import. | 47 recursive hook situation where we start importing inside another import. |
| 48 """ | 48 """ |
| 49 | 49 |
| 50 | 50 |
| 51 def importing_enter(repo: hgrepo.IRepo) -> None: | 51 def _importing_enter(repo: hgrepo.IRepo) -> None: |
| 52 """Call this before you start importing from Git.""" | 52 """Call this before you start importing from Git.""" |
| 53 level = getattr(repo, _ILEVEL_ATTR, 0) + 1 | 53 level = getattr(repo, _ILEVEL_ATTR, 0) + 1 |
| 54 setattr(repo, _ILEVEL_ATTR, level) | 54 setattr(repo, _ILEVEL_ATTR, level) |
| 55 | 55 |
| 56 | 56 |
| 57 def is_importing(repo: hgrepo.IRepo) -> bool: | 57 def _is_importing(repo: hgrepo.IRepo) -> bool: |
| 58 """Call this to check if you're currently importing.""" | 58 """Call this to check if you're currently importing.""" |
| 59 return hasattr(repo, _ILEVEL_ATTR) | 59 return hasattr(repo, _ILEVEL_ATTR) |
| 60 | 60 |
| 61 | 61 |
| 62 def importing_exit(repo: hgrepo.IRepo) -> None: | 62 def _importing_exit(repo: hgrepo.IRepo) -> None: |
| 63 """Call this after you finish importing from Git.""" | 63 """Call this after you finish importing from Git.""" |
| 64 level = getattr(repo, _ILEVEL_ATTR) - 1 | 64 level = getattr(repo, _ILEVEL_ATTR) - 1 |
| 65 if level: | 65 if level: |
| 66 setattr(repo, _ILEVEL_ATTR, level) | 66 setattr(repo, _ILEVEL_ATTR, level) |
| 67 else: | 67 else: |
| 68 delattr(repo, _ILEVEL_ATTR) | 68 delattr(repo, _ILEVEL_ATTR) |
| 69 | 69 |
| 70 | 70 |
| 71 def import_all(repo: GittyRepo, command: bytes = b'(unknown)') -> None: | |
| 72 _importing_enter(repo) | |
| 73 try: | |
| 74 gh = repo.githandler | |
| 75 gh.import_git_objects( | |
| 76 command, remote_names=(), refs=gh.git.refs.as_dict() | |
| 77 ) | |
| 78 finally: | |
| 79 _importing_exit(repo) | |
| 80 | |
| 81 | |
| 71 # | 82 # |
| 72 # Export handling. | 83 # Export handling. |
| 73 # | 84 # |
| 74 | 85 |
| 75 | 86 |
| 76 def clean_all_refs(refs: dulwich.refs.RefsContainer) -> None: | 87 def _clean_all_refs(refs: dulwich.refs.RefsContainer) -> None: |
| 77 """Removes all refs from the Git repository.""" | 88 """Removes all refs from the Git repository.""" |
| 78 | 89 # dump to allkeys so we explicitly are iterating over a snapshot |
| 79 | 90 # and not over something while we mutate |
| 80 def set_head(ui: hgui.ui, repo: GittyRepo, at_name: bytes) -> None: | 91 for ref in refs.allkeys(): |
| 92 refs.remove_if_equals(ref, None) | |
| 93 | |
| 94 | |
| 95 def _set_head(ui: hgui.ui, repo: GittyRepo, at_name: bytes) -> None: | |
| 81 """Creates a HEAD reference in Git referring to the current HEAD.""" | 96 """Creates a HEAD reference in Git referring to the current HEAD.""" |
| 82 # By default, we use '@', since that's what will be auto checked out. | 97 # By default, we use '@', since that's what will be auto checked out. |
| 83 current = b'@' | 98 current = b'@' |
| 84 if current not in repo._bookmarks: | 99 if current not in repo._bookmarks: |
| 85 current = repo._bookmarks.active or current | 100 current = repo._bookmarks.active or current |
| 115 refs = repo.githandler.git.refs | 130 refs = repo.githandler.git.refs |
| 116 refs.add_packed_refs({git_branch: gitsha}) | 131 refs.add_packed_refs({git_branch: gitsha}) |
| 117 refs.set_symbolic_ref(b'HEAD', git_branch) | 132 refs.set_symbolic_ref(b'HEAD', git_branch) |
| 118 | 133 |
| 119 | 134 |
| 120 def fix_refs(ui: hgui.ui, repo: GittyRepo) -> None: | 135 def _fix_refs(ui: hgui.ui, repo: GittyRepo) -> None: |
| 121 """After a git export, fix up the refs. | 136 """After a git export, fix up the refs. |
| 122 | 137 |
| 123 This ensures that there are no leftover refs from older, removed bookmarks | 138 This ensures that there are no leftover refs from older, removed bookmarks |
| 124 and that there is a proper HEAD set so that cloning works. | 139 and that there is a proper HEAD set so that cloning works. |
| 125 """ | 140 """ |
| 126 refs = repo.githandler.git.refs | 141 _clean_all_refs(repo.githandler.git.refs) |
| 127 # dump to allkeys so we explicitly are iterating over a snapshot | |
| 128 # and not over something while we mutate | |
| 129 for ref in refs.allkeys(): | |
| 130 refs.remove_if_equals(ref, None) | |
| 131 repo.githandler.export_hg_tags() | 142 repo.githandler.export_hg_tags() |
| 132 repo.githandler.update_references() | 143 repo.githandler.update_references() |
| 133 default_branch_name = ui.config( | 144 default_branch_name = ui.config( |
| 134 b'hggit-serve', b'default-branch', b'default' | 145 b'hggit-serve', b'default-branch', b'default' |
| 135 ) | 146 ) |
| 136 set_head(ui, repo, default_branch_name) | 147 _set_head(ui, repo, default_branch_name) |
| 137 | 148 |
| 138 | 149 |
| 139 # | 150 # |
| 140 # Hooks | 151 # Hooks |
| 141 # | 152 # |
| 147 return | 158 return |
| 148 auto_export = ui.config(b'hggit-serve', b'auto-export') | 159 auto_export = ui.config(b'hggit-serve', b'auto-export') |
| 149 if auto_export == b'never': | 160 if auto_export == b'never': |
| 150 return | 161 return |
| 151 if auto_export == b'always' or git_handler.has_gitrepo(repo): | 162 if auto_export == b'always' or git_handler.has_gitrepo(repo): |
| 152 if is_importing(repo): | 163 if _is_importing(repo): |
| 153 ui.note(b'currently importing revs from git; not exporting\n') | 164 ui.note(b'currently importing revs from git; not exporting\n') |
| 154 return | 165 return |
| 155 repo.githandler.export_commits() | 166 repo.githandler.export_commits() |
| 156 fix_refs(ui, repo) | 167 _fix_refs(ui, repo) |
| 157 | 168 |
| 158 | 169 |
| 159 def _fix_refs_hook(ui: hgui.ui, repo: hgrepo.IRepo, **__: object) -> None: | 170 def _fix_refs_hook(ui: hgui.ui, repo: hgrepo.IRepo, **__: object) -> None: |
| 160 """Exports to Git and sets up for serving. See ``_fix_refs``.""" | 171 """Exports to Git and sets up for serving. See ``_fix_refs``.""" |
| 161 if not is_gitty(repo): | 172 if not is_gitty(repo): |
| 162 return | 173 return |
| 163 fix_refs(ui, repo) | 174 _fix_refs(ui, repo) |
| 164 | 175 |
| 165 | 176 |
| 166 # | 177 # |
| 167 # Interfacing with Mercurial | 178 # Interfacing with Mercurial |
| 168 # | 179 # |
| 169 | 180 |
| 170 | 181 |
| 171 def uipopulate(ui: hgui.ui) -> None: | 182 def uipopulate(ui: hgui.ui) -> None: |
| 172 # Fix up our tags after a Git export. | 183 # Fix up our tags after a Git export. |
| 173 ui.setconfig( | 184 ui.setconfig( |
| 174 b'hooks', b'post-git-export.__gitserve_add_tag__', _fix_refs_hook | 185 b'hooks', |
| 186 b'post-git-export.__gitserve_add_tag__', | |
| 187 _fix_refs_hook, | |
| 188 source=b'hggit_serve', | |
| 175 ) | 189 ) |
| 176 # Whenever we get new revisions, export them to the Git repository. | 190 # Whenever we get new revisions, export them to the Git repository. |
| 177 ui.setconfig(b'hooks', b'txnclose.__gitserve_export__', _export_hook) | 191 ui.setconfig( |
| 192 b'hooks', | |
| 193 b'txnclose.__gitserve_export__', | |
| 194 _export_hook, | |
| 195 source=b'hggit_serve', | |
| 196 ) | |
| 178 # Don't step on ourselves when importing data from Git. | 197 # Don't step on ourselves when importing data from Git. |
| 179 ui.setconfig( | 198 ui.setconfig( |
| 180 b'hooks', | 199 b'hooks', |
| 181 b'pre-git-import.__gitserve_suppress_export__', | 200 b'pre-git-import.__gitserve_suppress_export__', |
| 182 lambda _, repo, **__: importing_enter(repo), | 201 lambda _, repo, **__: _importing_enter(repo), |
| 202 source=b'hggit_serve', | |
| 183 ) | 203 ) |
| 184 ui.setconfig( | 204 ui.setconfig( |
| 185 b'hooks', | 205 b'hooks', |
| 186 b'post-git-import.__gitserve_suppress_export__', | 206 b'post-git-import.__gitserve_suppress_export__', |
| 187 lambda _, repo, **__: importing_exit(repo), | 207 lambda _, repo, **__: _importing_exit(repo), |
| 188 ) | 208 source=b'hggit-serve', |
| 209 ) |
