Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/checks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
10 changes: 8 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
18 changes: 18 additions & 0 deletions .scripts/extract_command_reference.py
Original file line number Diff line number Diff line change
@@ -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()
131 changes: 125 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -55,7 +56,7 @@ The new branch is based on the repo's main branch. To specify the base branch, u
gimmegit -b <base-branch> <owner>/<project> <new-branch>
```

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

Expand Down Expand Up @@ -96,7 +97,7 @@ gimmegit https://github.com/<owner>/<project>/tree/<branch>

This clones the repo `<owner>/<project>` into a directory called `<project>/<owner>-<branch>` and checks out the branch `<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

Expand Down Expand Up @@ -148,7 +149,7 @@ The new branch is based on the upstream repo's main branch. (Technically, it's b
gimmegit -b <upstream-base-branch> -u <upstream-owner> <owner>/<project> <new-branch>
```

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

Expand All @@ -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
Expand Down Expand Up @@ -225,7 +227,7 @@ gimmegit -u <upstream-owner> https://github.com/<owner>/<project>/tree/<branch>

This clones `<owner>`'s fork of `<upstream-owner>/<project>` into a directory called `<project>/<owner>-<branch>` and checks out the branch `<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

Expand Down Expand Up @@ -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
Expand All @@ -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 [<options>] <repo> [<new-branch>] [-- <git-options>] (1)
gimmegit [<options>] <branch-url> [-- <git-options>] (2)

1. Clone a GitHub repo and check out a new branch.
<repo> is one of:
• <owner>/<project>. For example, 'dwilding/frogtab'. <owner> is optional if the
GIMMEGIT_GITHUB_TOKEN environment variable contains a personal access token.
• A repo URL. For example, 'https://github.com/dwilding/frogtab'.
<new-branch> is the name of a branch that doesn't already exist. gimmegit generates a
branch name if you omit <new-branch>. For example, 'snapshot0801' on August 1.

2. Clone a GitHub repo and check out an existing branch.
<branch-url> 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:
.
└── <project> The project directory. For example, 'frogtab'.
└── <owner>-<branch> 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> 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|url> Name or URL of the base branch. If '-b <name>', gimmegit uses
<name> instead of the repo's main branch (or upstream main).
If '-b https://github.com/<owner>/<project>/tree/<name>',
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 '-- <git-options>' 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 <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).
```
6 changes: 5 additions & 1 deletion justfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,5 @@ line-length = 99
root = [
"./src",
"./tests/functional",
"./tests/unit"
"./tests/unit",
]
2 changes: 1 addition & 1 deletion src/gimmegit/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
118 changes: 106 additions & 12 deletions src/gimmegit/_help.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,112 @@
help = """\
Usage:
gimmegit [<options>] <repo> [<new-branch>] [-- <git-options>]
gimmegit [<options>] <branch-url> [-- <git-options>]
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 [<options>] <repo> [<new-branch>] [-- <git-options>] (1)
gimmegit [<options>] <branch-url> [-- <git-options>] (2)

gimmegit -h
gimmegit --help
1. Clone a GitHub repo and check out a new branch.
<repo> is one of:
• <owner>/<project>. For example, 'dwilding/frogtab'. <owner> is optional if the
GIMMEGIT_GITHUB_TOKEN environment variable contains a personal access token.
• A repo URL. For example, 'https://github.com/dwilding/frogtab'.
<new-branch> is the name of a branch that doesn't already exist. gimmegit generates a
branch name if you omit <new-branch>. For example, 'snapshot0801' on August 1.

gimmegit --version
2. Clone a GitHub repo and check out an existing branch.
<branch-url> is a URL such as 'https://github.com/dwilding/frogtab/tree/fix-something'.

gimmegit [--ssh {auto,always,never}] --parse-url <url>"""
▶ DIRECTORY STRUCTURE

When you clone a repo, gimmegit creates a dedicated directory for the clone:
.
└── <project> The project directory. For example, 'frogtab'.
└── <owner>-<branch> 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> 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|url> Name or URL of the base branch. If '-b <name>', gimmegit uses
<name> instead of the repo's main branch (or upstream main).
If '-b https://github.com/<owner>/<project>/tree/<name>',
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 '-- <git-options>' 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 <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)."""
2 changes: 1 addition & 1 deletion tests/functional/test_clone.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down