diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index 6dcd7aa..31aaf5c 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -23,3 +23,5 @@ jobs: run: uvx --from rust-just just lint - name: Run unit tests run: uvx --from rust-just just test-unit + - name: Check the command reference + run: uvx --from rust-just just check-command-reference diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index acdb2fc..99af0a6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,9 +10,15 @@ repos: - id: check-toml - repo: local hooks: - - id: just-default - name: Run default 'just' recipe + - id: just + name: Run 'just' entry: just language: system pass_filenames: false always_run: true + - id: just-check-command-reference + name: Run 'just check-command-reference' + entry: just check-command-reference + language: system + pass_filenames: false + always_run: true diff --git a/.scripts/extract_command_reference.py b/.scripts/extract_command_reference.py new file mode 100644 index 0000000..97471d5 --- /dev/null +++ b/.scripts/extract_command_reference.py @@ -0,0 +1,18 @@ +"""Extracts gimmegit's help output from the "Command reference" section of README.md.""" + +from pathlib import Path + + +def main() -> None: + text = Path("README.md").read_text() + start_marker = "# Command reference\n\n```text\n" + start_index = text.find(start_marker) + assert start_index > 0 + start_index += len(start_marker) + end_index = text.find("\n```", start_index) + assert end_index > start_index + print(text[start_index:end_index]) + + +if __name__ == "__main__": + main() diff --git a/README.md b/README.md index aa8e7e9..e472f6f 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ In this README: - [Clone a fork and create a branch](#clone-a-fork-and-create-a-branch) - [Clone a fork on an existing branch](#clone-a-fork-on-an-existing-branch) - [Provide clone options](#provide-clone-options) + - [Command reference](#command-reference) ## Install gimmegit @@ -55,7 +56,7 @@ The new branch is based on the repo's main branch. To specify the base branch, u gimmegit -b / ``` -After working on the new branch for a while, you might want to merge remote changes from the base branch. To merge remote changes, run `git update-branch` in the clone directory. `update-branch` is a git alias that gimmegit created. +After working on the new branch for a while, you might want to merge remote changes from the base branch. To merge remote changes, run `git update-branch` in the clone directory. `update-branch` is a Git alias that gimmegit created. ### Example @@ -96,7 +97,7 @@ gimmegit https://github.com///tree/ This clones the repo `/` into a directory called `/-` and checks out the branch ``. -After working on the branch for a while, you might want to merge remote changes from the repo's main branch. To merge remote changes, run `git update-branch` in the clone directory. `update-branch` is a git alias that gimmegit created. +After working on the branch for a while, you might want to merge remote changes from the repo's main branch. To merge remote changes, run `git update-branch` in the clone directory. `update-branch` is a Git alias that gimmegit created. ### Example @@ -148,7 +149,7 @@ The new branch is based on the upstream repo's main branch. (Technically, it's b gimmegit -b -u / ``` -After working on the new branch for a while, you might want to merge changes from the upstream base branch. To merge changes from upstream, run `git update-branch` in the clone directory. `update-branch` is a git alias that gimmegit created. +After working on the new branch for a while, you might want to merge changes from the upstream base branch. To merge changes from upstream, run `git update-branch` in the clone directory. `update-branch` is a Git alias that gimmegit created. ### Example @@ -173,7 +174,8 @@ cd ../.. gimmegit -b 2.23-maintenance -u canonical dwilding/operator backport-docs # This does the same thing: -# gimmegit -b https://github.com/canonical/operator/tree/2.23-maintenance dwilding/operator backport-docs +# gimmegit -b https://github.com/canonical/operator/tree/2.23-maintenance \ +# dwilding/operator backport-docs # Change to the second clone directory cd operator/dwilding-backport-docs @@ -225,7 +227,7 @@ gimmegit -u https://github.com///tree/ This clones ``'s fork of `/` into a directory called `/-` and checks out the branch ``. -After working on the branch for a while, you might want to merge changes from the upstream repo's main branch. To merge changes from upstream, run `git update-branch` in the clone directory. `update-branch` is a git alias that gimmegit created. +After working on the branch for a while, you might want to merge changes from the upstream repo's main branch. To merge changes from upstream, run `git update-branch` in the clone directory. `update-branch` is a Git alias that gimmegit created. ### Example @@ -271,7 +273,8 @@ If the branch wasn't based on the upstream repo's main branch, use `-b` to set t gimmegit -b 2.23-maintenance -u canonical https://github.com/dwilding/operator/tree/backport-fix # This does the same thing: -# gimmegit -b https://github.com/canonical/operator/tree/2.23-maintenance https://github.com/dwilding/operator/tree/backport-fix +# gimmegit -b https://github.com/canonical/operator/tree/2.23-maintenance \ +# https://github.com/dwilding/operator/tree/backport-fix # If GIMMEGIT_GITHUB_TOKEN is set, this also does the same thing: # gimmegit -b 2.23-maintenance https://github.com/dwilding/operator/tree/backport-fix @@ -294,3 +297,119 @@ To provide [clone options](https://git-scm.com/docs/git-clone#_options) to gimme # create a branch called update-profile, cloning with --recurse-submodules gimmegit -u canonical dwilding/charmcraft update-profile -- --recurse-submodules ``` + +# Command reference + +```text +gimmegit is a tool for cloning GitHub repos and creating branches. gimmegit puts each clone +in a dedicated directory, based on the project, owner, and branch name. + +▶ USAGE + +gimmegit [] [] [-- ] (1) +gimmegit [] [-- ] (2) + +1. Clone a GitHub repo and check out a new branch. + is one of: + • /. For example, 'dwilding/frogtab'. is optional if the + GIMMEGIT_GITHUB_TOKEN environment variable contains a personal access token. + • A repo URL. For example, 'https://github.com/dwilding/frogtab'. + is the name of a branch that doesn't already exist. gimmegit generates a + branch name if you omit . For example, 'snapshot0801' on August 1. + +2. Clone a GitHub repo and check out an existing branch. + is a URL such as 'https://github.com/dwilding/frogtab/tree/fix-something'. + +▶ DIRECTORY STRUCTURE + +When you clone a repo, gimmegit creates a dedicated directory for the clone: + . + └── The project directory. For example, 'frogtab'. + └── - The clone directory. For example, 'dwilding-my-feature'. + +If the clone directory already exists, gimmegit skips cloning. If the clone directory would +be inside an existing repo, gimmegit exits with an error. gimmegit also exits with an error +if it detects that the working directory is a project directory (specifically, if the latest +modified subdirectory is a gimmegit clone). + +▶ BRANCH MAPPING + +gimmegit creates a Git alias 'update-branch' that merges remote changes from the base branch. +The base branch is the repo's main branch. If the repo is a fork and GIMMEGIT_GITHUB_TOKEN is +set, the base branch is the upstream version of the repo's main branch. + +For new branches: + • gimmegit branches off the base branch. + • gimmegit doesn't push the branch to GitHub. + +▶ OPTIONS + +-u, --upstream-owner Owner of the base branch. For example, provide '-u canonical' + to clone a fork of a repo from https://github.com/canonical. + If you provide -u, gimmegit doesn't try to use + GIMMEGIT_GITHUB_TOKEN to look for an upstream repo. + +-b, --base-branch Name or URL of the base branch. If '-b ', gimmegit uses + instead of the repo's main branch (or upstream main). + If '-b https://github.com///tree/', + gimmegit sets the base branch and ignores -u. + +--no-pre-commit Don't try to install a pre-commit hook after cloning the repo. + +--allow-outer-repo Allow the clone directory to be inside a repo. + +--force-project-dir Create the project directory even if gimmegit finds a gimmegit + clone in the working directory. + +--ssh auto|always|never Controls whether Git remotes use SSH or HTTPS. + • auto (default): Use SSH if ~/.ssh contains an SSH key. + +--color auto|always|never Controls whether the output has colored text. + • auto (default): Use colors if the NO_COLOR environment + variable is empty and the output is going to a terminal. + +--return-dir Output the clone directory path to stdout and send full + progress to stderr. + +▶ GIT OPTIONS + +gimmegit sets --no-tags when cloning. Use '-- ' to provide extra clone options. +For example, use '-- --tags' to clone tags. + +▶ PRE-COMMIT + +If the repo contains a file '.pre-commit-config.yaml', gimmegit installs a pre-commit hook +after cloning the repo. For more information, see https://pre-commit.com/. + +▶ ADDITIONAL COMMANDS + +gimmegit [--color auto|always|never] (1) +gimmegit -c | --compare (2) +gimmegit -h | --help (3) +gimmegit --version (4) +gimmegit [--ssh auto|always|never] --parse-url (5) + +1. Display the branch mapping if the working directory is inside a gimmegit clone. +2. Compare branches in GitHub if the working directory is inside a gimmegit clone. +3. Display a summary of how to use gimmegit. +4. Display the installed version of gimmegit. +5. Display a JSON representation of a GitHub URL. Intended for extensions to gimmegit. + +▶ EXAMPLES + +gimmegit dwilding/frogtab my-feature (1) +gimmegit -b candidate dwilding/frogtab bump-version (2) +gimmegit https://github.com/dwilding/frogtab/tree/fix-something (3) +gimmegit -u canonical dwilding/operator update-docs (4) +gimmegit -b 2.23-maintenance -u canonical dwilding/operator backport-docs (5) +gimmegit -b https://github.com/canonical/operator/tree/2.23-maintenance \ (6) + dwilding/operator backport-docs + +1. Clone https://github.com/dwilding/frogtab and check out a new branch, branching off main. +2. Clone the same repo and check out a new branch, branching off a dev branch. +3. Clone the same repo and check out an existing branch. +4. Clone dwilding's fork of https://github.com/canonical/operator and check out a new branch, + branching off upstream main. +5. Clone the same fork and check out a new branch, branching off an upstream dev branch. +6. Equivalent to (5). +``` diff --git a/justfile b/justfile index ecb8b50..c7da977 100644 --- a/justfile +++ b/justfile @@ -15,8 +15,12 @@ test-functional: (test "tests/functional") test args="tests/unit tests/functional": uv run pytest -vv {{args}} +check-command-reference: + #!/bin/bash + diff <(uv run .scripts/extract_command_reference.py) <(uv run gimmegit -h) + demo: - #!/bin/sh + #!/bin/bash package_dir="$PWD" mkdir -p demo cd demo diff --git a/pyproject.toml b/pyproject.toml index 69f94c8..cfd7720 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,5 +38,5 @@ line-length = 99 root = [ "./src", "./tests/functional", - "./tests/unit" + "./tests/unit", ] diff --git a/src/gimmegit/_cli.py b/src/gimmegit/_cli.py index 8a17011..245d6ad 100644 --- a/src/gimmegit/_cli.py +++ b/src/gimmegit/_cli.py @@ -639,7 +639,7 @@ def primary_usage(args: argparse.Namespace, cloning_args: list[str]) -> None: candidate = _inspect.get_repo_from_latest_dir(Path.cwd()) if candidate and _status.get_status(candidate): exit_with_error( - "The working directory has a gimmegit clone. Try running gimmegit in the parent directory." + "The working directory contains a gimmegit clone. Try running gimmegit in the parent directory." ) try: clone(context, cloning_args) diff --git a/src/gimmegit/_help.py b/src/gimmegit/_help.py index 6e715dc..0d9b131 100644 --- a/src/gimmegit/_help.py +++ b/src/gimmegit/_help.py @@ -1,18 +1,112 @@ help = """\ -Usage: - gimmegit [] [] [-- ] - gimmegit [] [-- ] +gimmegit is a tool for cloning GitHub repos and creating branches. gimmegit puts each clone +in a dedicated directory, based on the project, owner, and branch name. -Show status: - gimmegit [--color {auto,always,never}] +▶ USAGE -Additional commands: - gimmegit -c - gimmegit --compare +gimmegit [] [] [-- ] (1) +gimmegit [] [-- ] (2) - gimmegit -h - gimmegit --help +1. Clone a GitHub repo and check out a new branch. + is one of: + • /. For example, 'dwilding/frogtab'. is optional if the + GIMMEGIT_GITHUB_TOKEN environment variable contains a personal access token. + • A repo URL. For example, 'https://github.com/dwilding/frogtab'. + is the name of a branch that doesn't already exist. gimmegit generates a + branch name if you omit . For example, 'snapshot0801' on August 1. - gimmegit --version +2. Clone a GitHub repo and check out an existing branch. + is a URL such as 'https://github.com/dwilding/frogtab/tree/fix-something'. - gimmegit [--ssh {auto,always,never}] --parse-url """ +▶ DIRECTORY STRUCTURE + +When you clone a repo, gimmegit creates a dedicated directory for the clone: + . + └── The project directory. For example, 'frogtab'. + └── - The clone directory. For example, 'dwilding-my-feature'. + +If the clone directory already exists, gimmegit skips cloning. If the clone directory would +be inside an existing repo, gimmegit exits with an error. gimmegit also exits with an error +if it detects that the working directory is a project directory (specifically, if the latest +modified subdirectory is a gimmegit clone). + +▶ BRANCH MAPPING + +gimmegit creates a Git alias 'update-branch' that merges remote changes from the base branch. +The base branch is the repo's main branch. If the repo is a fork and GIMMEGIT_GITHUB_TOKEN is +set, the base branch is the upstream version of the repo's main branch. + +For new branches: + • gimmegit branches off the base branch. + • gimmegit doesn't push the branch to GitHub. + +▶ OPTIONS + +-u, --upstream-owner Owner of the base branch. For example, provide '-u canonical' + to clone a fork of a repo from https://github.com/canonical. + If you provide -u, gimmegit doesn't try to use + GIMMEGIT_GITHUB_TOKEN to look for an upstream repo. + +-b, --base-branch Name or URL of the base branch. If '-b ', gimmegit uses + instead of the repo's main branch (or upstream main). + If '-b https://github.com///tree/', + gimmegit sets the base branch and ignores -u. + +--no-pre-commit Don't try to install a pre-commit hook after cloning the repo. + +--allow-outer-repo Allow the clone directory to be inside a repo. + +--force-project-dir Create the project directory even if gimmegit finds a gimmegit + clone in the working directory. + +--ssh auto|always|never Controls whether Git remotes use SSH or HTTPS. + • auto (default): Use SSH if ~/.ssh contains an SSH key. + +--color auto|always|never Controls whether the output has colored text. + • auto (default): Use colors if the NO_COLOR environment + variable is empty and the output is going to a terminal. + +--return-dir Output the clone directory path to stdout and send full + progress to stderr. + +▶ GIT OPTIONS + +gimmegit sets --no-tags when cloning. Use '-- ' to provide extra clone options. +For example, use '-- --tags' to clone tags. + +▶ PRE-COMMIT + +If the repo contains a file '.pre-commit-config.yaml', gimmegit installs a pre-commit hook +after cloning the repo. For more information, see https://pre-commit.com/. + +▶ ADDITIONAL COMMANDS + +gimmegit [--color auto|always|never] (1) +gimmegit -c | --compare (2) +gimmegit -h | --help (3) +gimmegit --version (4) +gimmegit [--ssh auto|always|never] --parse-url (5) + +1. Display the branch mapping if the working directory is inside a gimmegit clone. +2. Compare branches in GitHub if the working directory is inside a gimmegit clone. +3. Display a summary of how to use gimmegit. +4. Display the installed version of gimmegit. +5. Display a JSON representation of a GitHub URL. Intended for extensions to gimmegit. + +▶ EXAMPLES + +gimmegit dwilding/frogtab my-feature (1) +gimmegit -b candidate dwilding/frogtab bump-version (2) +gimmegit https://github.com/dwilding/frogtab/tree/fix-something (3) +gimmegit -u canonical dwilding/operator update-docs (4) +gimmegit -b 2.23-maintenance -u canonical dwilding/operator backport-docs (5) +gimmegit -b https://github.com/canonical/operator/tree/2.23-maintenance \\ (6) + dwilding/operator backport-docs + +1. Clone https://github.com/dwilding/frogtab and check out a new branch, branching off main. +2. Clone the same repo and check out a new branch, branching off a dev branch. +3. Clone the same repo and check out an existing branch. +4. Clone dwilding's fork of https://github.com/canonical/operator and check out a new branch, + branching off upstream main. +5. Clone the same fork and check out a new branch, branching off an upstream dev branch. +6. Equivalent to (5).""" diff --git a/tests/functional/test_clone.py b/tests/functional/test_clone.py index 4c77d7b..ad8c8bb 100644 --- a/tests/functional/test_clone.py +++ b/tests/functional/test_clone.py @@ -225,7 +225,7 @@ def test_in_project_dir(uv_run, test_dir): """ assert result.stdout == expected_stdout expected_stderr = """\ -Error: The working directory has a gimmegit clone. Try running gimmegit in the parent directory. +Error: The working directory contains a gimmegit clone. Try running gimmegit in the parent directory. """ assert result.stderr == expected_stderr assert not (working_dir / "jubilant").exists()