{"id":18084943,"url":"https://github.com/jwodder/ghrepo","last_synced_at":"2025-06-27T21:31:19.328Z","repository":{"id":41995103,"uuid":"371759453","full_name":"jwodder/ghrepo","owner":"jwodder","description":"Parse \u0026 construct GitHub repository URLs \u0026 specifiers","archived":false,"fork":false,"pushed_at":"2025-01-23T14:37:26.000Z","size":89,"stargazers_count":0,"open_issues_count":4,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-16T13:05:30.944Z","etag":null,"topics":["available-on-pypi","github","python","repository","url-parsing"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jwodder.png","metadata":{"files":{"readme":"README.rst","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-05-28T16:28:05.000Z","updated_at":"2025-01-23T14:37:29.000Z","dependencies_parsed_at":"2023-01-31T01:55:12.973Z","dependency_job_id":"47d62123-c087-4ec7-9068-478a12a779a0","html_url":"https://github.com/jwodder/ghrepo","commit_stats":{"total_commits":67,"total_committers":1,"mean_commits":67.0,"dds":0.0,"last_synced_commit":"c9cbdb2dd8b11bc4c171f71f6bedde50d6fb4e7e"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/jwodder/ghrepo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jwodder%2Fghrepo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jwodder%2Fghrepo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jwodder%2Fghrepo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jwodder%2Fghrepo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jwodder","download_url":"https://codeload.github.com/jwodder/ghrepo/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jwodder%2Fghrepo/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261325915,"owners_count":23141989,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["available-on-pypi","github","python","repository","url-parsing"],"created_at":"2024-10-31T15:08:44.624Z","updated_at":"2025-06-27T21:31:19.213Z","avatar_url":"https://github.com/jwodder.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"|repostatus| |ci-status| |coverage| |pyversions| |license|\n\n.. |repostatus| image:: https://www.repostatus.org/badges/latest/active.svg\n    :target: https://www.repostatus.org/#active\n    :alt: Project Status: Active — The project has reached a stable, usable\n          state and is being actively developed.\n\n.. |ci-status| image:: https://github.com/jwodder/ghrepo/actions/workflows/test.yml/badge.svg\n    :target: https://github.com/jwodder/ghrepo/actions/workflows/test.yml\n    :alt: CI Status\n\n.. |coverage| image:: https://codecov.io/gh/jwodder/ghrepo/branch/master/graph/badge.svg\n    :target: https://codecov.io/gh/jwodder/ghrepo\n\n.. |pyversions| image:: https://img.shields.io/pypi/pyversions/ghrepo.svg\n    :target: https://pypi.org/project/ghrepo/\n\n.. |license| image:: https://img.shields.io/github/license/jwodder/ghrepo.svg\n    :target: https://opensource.org/licenses/MIT\n    :alt: MIT License\n\n`GitHub \u003chttps://github.com/jwodder/ghrepo\u003e`_\n| `PyPI \u003chttps://pypi.org/project/ghrepo/\u003e`_\n| `Issues \u003chttps://github.com/jwodder/ghrepo/issues\u003e`_\n| `Changelog \u003chttps://github.com/jwodder/ghrepo/blob/master/CHANGELOG.md\u003e`_\n\n``ghrepo`` extracts a GitHub repository's owner \u0026 name from various GitHub URL\nformats (or just from a string of the form ``OWNER/REPONAME`` or ``REPONAME``),\nand the resulting object provides properties for going in reverse to determine\nthe possible URLs.  Also included is a function for determining the GitHub\nowner \u0026 name for a local Git repository, plus a couple of other useful Git\nrepository inspection functions.\n\nInstallation\n============\n``ghrepo`` requires Python 3.8 or higher.  Just use `pip\n\u003chttps://pip.pypa.io\u003e`_ for Python 3 (You have pip, right?) to install it::\n\n    python3 -m pip install ghrepo\n\n\nAPI\n===\n\n``GHRepo``\n----------\n\n.. code:: python\n\n    class GHRepo(typing.NamedTuple):\n        owner: str\n        name: str\n\nA pair of a GitHub repository's owner and base name.  Stringifying a ``GHRepo``\ninstance produces a repository \"fullname\" of the form ``{owner}/{name}``.\n\n.. code:: python\n\n    property api_url: str\n\nThe base URL for accessing the repository via the GitHub REST API; this is a\nstring of the form ``https://api.github.com/repos/{owner}/{name}``.\n\n.. code:: python\n\n    property clone_url: str\n\nThe URL for cloning the repository over HTTPS\n\n.. code:: python\n\n    property git_url: str\n\nThe URL for cloning the repository via the native Git protocol\n\n.. code:: python\n\n    property html_url: str\n\nThe URL for the repository's web interface\n\n.. code:: python\n\n    property ssh_url: str\n\nThe URL for cloning the repository over SSH\n\n.. code:: python\n\n    classmethod parse_url(url: str) -\u003e GHRepo\n\nParse a GitHub repository URL.  The following URL formats are recognized:\n\n- ``[https://[\u003cusername\u003e[:\u003cpassword\u003e]@]][www.]github.com/\u003cowner\u003e/\u003cname\u003e[.git][/]``\n- ``[https://]api.github.com/repos/\u003cowner\u003e/\u003cname\u003e``\n- ``git://github.com/\u003cowner\u003e/\u003cname\u003e[.git]``\n- ``[ssh://]git@github.com:\u003cowner\u003e/\u003cname\u003e[.git]``\n\nAll other formats produce a ``ValueError``.\n\n.. code:: python\n\n    classmethod parse(\n        spec: str,\n        default_owner: Optional[Union[str, Callable[[], str]]] = None,\n    ) -\u003e GHRepo\n\nParse a GitHub repository specifier.  This can be either a URL (as accepted by\n``parse_url()``) or a string in the form ``{owner}/{name}``.  If\n``default_owner`` is specified (as either a string or a zero-argument callable\nthat returns a string), strings that are just a repository name are also\naccepted, and the resulting ``GHRepo`` instances will have their ``owner`` set\nto the given value.\n\n\nFunctions \u0026 Constants\n---------------------\n\n**Note**: All of the functions require Git to be installed in order to work.  I\nam not certain of the minimal viable Git version, but the functions should work\nwith any Git as least as far back as version 1.7.\n\n.. code:: python\n\n    get_local_repo(dirpath: Optional[AnyPath] = None, remote: str = \"origin\") -\u003e GHRepo\n\nDetermine the GitHub repository for the Git repository located at or containing\nthe directory ``dirpath`` (default: the current directory) by parsing the URL\nfor the specified remote.  Raises ``NoSuchRemoteError`` if the given remote\ndoes not exist.  Raises ``subprocess.CalledProcessError`` if a different Git\nerror occurs, such as the given path not being in a Git repository.\n\n.. code:: python\n\n    get_branch_upstream(branch: str, dirpath: Optional[AnyPath] = None) -\u003e GHRepo\n\n*(New in version 0.5.0)* Determine the GitHub repository for the upstream\nremote of the given branch in the Git repository located at or containing the\ndirectory ``dirpath`` (default: the current directory).\n\nRaises ``NoUpstreamError`` if the given branch does not have an upstream remote\nconfigured (This includes the situation in which the branch does not exist).\nRaises ``subprocess.CalledProcessError`` if a different Git error occurs, such\nas the given path not being in a Git repository.\n\n.. code:: python\n\n    get_current_branch(dirpath: Optional[AnyPath] = None) -\u003e str\n\nGet the current branch for the Git repository located at or containing the\ndirectory ``dirpath`` (default: the current directory).  Raises\n``DetachedHeadError`` if the repository is in a detached ``HEAD`` state.\nRaises ``subprocess.CalledProcessError`` if a different Git error occurs, such\nas the given path not being in a Git repository.\n\n.. code:: python\n\n    is_git_repo(dirpath: Optional[AnyPath] = None) -\u003e bool\n\nTests whether the given directory (default: the current directory) is either a\nGit repository or contained in one\n\n.. code:: python\n\n    GH_USER_RGX: str\n\nA regular expression string (unanchored) for a valid GitHub username or\norganization name.\n\n.. code:: python\n\n    GH_REPO_RGX: str\n\nA regular expression string (unanchored) for a valid GitHub repository name\n(without \".git\" extension).\n\n\nExceptions\n----------\n\n.. code:: python\n\n    class DetachedHeadError(Exception)\n\n*(New in version 0.6.0)* Raised by ``get_current_branch()`` if the Git\nrepository is in a detached ``HEAD`` state\n\n.. code:: python\n\n    class NoSuchRemoteError(Exception)\n\n*(New in version 0.6.0)* Raised by ``get_local_repo()`` when the given remote\ndoes not exist in the GitHub repository.  The queried remote is available as\nthe ``remote`` attribute.\n\n.. code:: python\n\n    class NoUpstreamError(Exception)\n\n*(New in version 0.6.0)* Raised by ``get_branch_upstream()`` if the given\nbranch does not have a remote configured.  The queried branch is available as\nthe ``branch`` attribute.\n\n\nCommand\n=======\n\n``ghrepo`` also provides a command of the same name for getting the GitHub\nrepository associated with a local Git repository::\n\n    ghrepo [\u003coptions\u003e] [\u003cdirpath\u003e]\n\nBy default, the ``ghrepo`` command just outputs the repository \"fullname\" (a\nstring of the form ``{owner}/{name}``).  If the ``-J`` or ``--json`` option is\nsupplied, a JSON object is instead output, containing fields for the repository\nowner, name, fullname, and individual URLs, like so:\n\n.. code:: json\n\n    {\n        \"owner\": \"jwodder\",\n        \"name\": \"ghrepo\",\n        \"fullname\": \"jwodder/ghrepo\",\n        \"api_url\": \"https://api.github.com/repos/jwodder/ghrepo\",\n        \"clone_url\": \"https://github.com/jwodder/ghrepo.git\",\n        \"git_url\": \"git://github.com/jwodder/ghrepo.git\",\n        \"html_url\": \"https://github.com/jwodder/ghrepo\",\n        \"ssh_url\": \"git@github.com:jwodder/ghrepo.git\"\n    }\n\nOptions\n-------\n\n-J, --json                  Output JSON\n\n-r REMOTE, --remote REMOTE  Parse the GitHub URL from the given remote\n                            [default: origin]\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjwodder%2Fghrepo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjwodder%2Fghrepo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjwodder%2Fghrepo/lists"}