Repository: VirtusLab/git-machete
Pull request #1025 (2023-08-12 14:30:00)

Release v3.18.0


Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 9bc7954..40b92b1 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -30,10 +30,21 @@ jobs:
           name: Install dependencies
           # language=sh
           command: |
+            set -x
+
+            # For completion tests
+
+            # `add-apt-repository` was getting stuck on CI for some reason
+            echo 'deb     https://ppa.launchpadcontent.net/fish-shell/release-3/ubuntu jammy main' | sudo tee -a /etc/apt/sources.list
+            echo 'deb-src https://ppa.launchpadcontent.net/fish-shell/release-3/ubuntu jammy main' | sudo tee -a /etc/apt/sources.list
+            sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 59FDA1CE1B84B3FAD89366C027557F056DC33CA5
             sudo apt-get update
+            sudo apt-get install fish
+            fish --version
 
-            # For enforce-completion-scripts-correct.sh
-            sudo apt-get install zsh fish
+            sudo apt-get install zsh
+
+            pip3 install tox
 
             # For enforce-shell-scripts-pass-shellcheck.sh
             sudo apt-get install shellcheck
@@ -43,6 +54,11 @@ jobs:
       - run:
           name: Perform general checks
           command: ci/checks/run-all-checks.sh
+      - run:
+          name: Test shell completions
+          command: tox -e test-completions -- -vv
+      - store_test_results:
+          path: test-results/
 
   # Earliest versions of python/git supported by git-machete
   "python 3_6 git 1_8_0":
@@ -146,8 +162,11 @@ jobs:
     executor: macos_executor
     steps:
       - checkout
+      - run: brew install fish zsh
       - run: pip install tox
-      - run: PYTHON_VERSION=3.11-macos  tox -e coverage-py311 -- -vv
+      # TODO (#1005): make zsh completion tests pass on macOS machine on CI
+      - run: tox -e test-completions -- -vv -k "not zsh"
+      - run: PYTHON_VERSION=3.11-macos  tox -e coverage -- -vv
       - store_test_results:
           path: test-results/
       - persist_to_workspace:
@@ -161,6 +180,8 @@ jobs:
       - run: pip install tox
       # We don't collect coverage on Windows as it massively extends execution time for some reason.
       - run: tox -e testenv -- -vv
+      - store_test_results:
+          path: test-results/
 
   "coverage upload":
     executor: ubuntu_executor
diff --git a/PACKAGES.md b/PACKAGES.md
index 57b7c46..29fd71f 100644
--- a/PACKAGES.md
+++ b/PACKAGES.md
@@ -4,33 +4,37 @@ In case the given package system provides multiple channels (stable/edge etc.),
 the **edge-most** channel that corresponds to the **released** versions of git-machete is listed
 (so as to make spotting potential blockers/errors in the given system easier).
 
-| Package system                        | Version                                                                                                 | Links                                                                                                                                                                                                                                                                                                                                                        | Controlled by                                                                               | Released by                                                                                                                                                                           | Downloads, last modified etc.                                                                                                                                                                                                                                                                                         |
-|---------------------------------------|---------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| Alpine Linux                          | ![Alpine Linux Edge package](https://repology.org/badge/version-for-repo/alpine_edge/git-machete.svg?header=)   | [**Alpine Linux Edge package**](https://pkgs.alpinelinux.org/package/edge/community/x86/git-machete)                                                                                                                                                                                                                                                         | Alpine Linux package maintainers                                                            | Alpine Linux package maintainers                                                                                                                                                      |                                                                                                                                                                                                                                                                                                                       |
-| Arch User Repository                  | ![AUR package](https://img.shields.io/aur/version/git-machete.svg?label=)                               | [**AUR package**](https://aur.archlinux.org/packages/git-machete)                                                                                                                                                                                                                                                                                            | AUR maintainers, incl. [Ila&iuml; Deutel](https://github.com/ilai-deutel)                   | AUR maintainers[^1]                                                                                                                                                                   | ![AUR last modified](https://img.shields.io/aur/last-modified/git-machete.svg)                                                                                                                                                                                                                                        |
-| Conda Forge                           | ![Conda package](https://img.shields.io/conda/vn/conda-forge/git-machete.svg?label=)                    | [**Conda package**](https://anaconda.org/conda-forge/git-machete) <br/> [conda-forge/git-machete-feedstock PRs](https://github.com/conda-forge/git-machete-feedstock/pulls?q=is%3Apr)                                                                                                                                                                        | Feedstock maintainers, incl. [@asford (Alex Ford)](https://github.com/asford) and ourselves | Feedstock GitHub bot                                                                                                                                                                  | ![Conda downloads](https://img.shields.io/conda/dn/conda-forge/git-machete.svg)                                                                                                                                                                                                                                       |
-| Gentoo                                | ![Gentoo package](https://repology.org/badge/version-for-repo/gentoo/git-machete.svg?header=)           | [**Gentoo package**](https://packages.gentoo.org/packages/dev-vcs/git-machete)                                                                                                                                                                                                                                                                               | Gentoo package maintainers                                                                  | Gentoo package maintainers                                                                                                                                                            |                                                                                                                                                                                                                                                                                                                       |
-| GitHub Releases                       | ![GitHub latest release](https://img.shields.io/github/v/release/VirtusLab/git-machete?label=)          | [**GitHub releases**](https://github.com/VirtusLab/git-machete/releases)                                                                                                                                                                                                                                                                                     | Ourselves                                                                                   | Our CI on `master` builds                                                                                                                                                             | ![GitHub releases total downloads](https://img.shields.io/github/downloads/VirtusLab/git-machete/total)[^2]                                                                                                                                                                                                           |
-| Homebrew                              | ![homebrew formula](https://img.shields.io/homebrew/v/git-machete.svg?label=)                           | [**Formula**](https://formulae.brew.sh/formula/git-machete) <br/> [Homebrew/homebrew-core -> Formula/git-machete.rb](https://github.com/Homebrew/homebrew-core/blob/master/Formula/git-machete.rb) <br/> [Homebrew/homebrew-core PRs](https://github.com/Homebrew/homebrew-core/pulls?q=is%3Apr+git-machete)                                                 | Homebrew maintainers                                                                        | Our CI on `master` builds (opens formula-bump PR), then homebrew-core GitHub Actions                                                                                                  | ![homebrew formula monthly downloads](https://img.shields.io/homebrew/installs/dm/git-machete.svg) <br/> ![homebrew formula quarterly downloads](https://img.shields.io/homebrew/installs/dq/git-machete.svg) <br/> ![homebrew formula yearly downloads](https://img.shields.io/homebrew/installs/dy/git-machete.svg) |
-| [LiGurOS](https://liguros.gitlab.io/) | ![LiGurOS develop package](https://repology.org/badge/version-for-repo/liguros_develop/git-machete.svg?header=) | [**Repology**](https://repology.org/project/git-machete/versions)                                                                                                                                                                                                                                                                                            | LiGurOS package maintainers                                                                 | LiGurOS package maintainers                                                                                                                                                           |                                                                                                                                                                                                                                                                                                                       |
-| nixpkgs                               | ![nixpkgs unstable package](https://repology.org/badge/version-for-repo/nix_unstable/git-machete.svg?header=)   | [**nixpkgs unstable package**](https://search.nixos.org/packages?channel=unstable&show=git-machete&query=git-machete) <br/> [NixOS/nixpkgs -> expression](https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/version-management/git-machete/default.nix) <br/> [NixOS/nixpkgs PRs](https://github.com/NixOS/nixpkgs/pulls?q=is%3Apr+git-machete) | nixpkgs maintainers, incl. [@blitz (Julian Stecklina)](https://github.com/blitz)            | [@r-ryantm bot](https://github.com/r-ryantm) ([logs](https://r.ryantm.com/log/updatescript/git-machete/))[^3]                                                                         |                                                                                                                                                                                                                                                                                                                       |
-| PyPI                                  | ![PyPI package](https://img.shields.io/pypi/v/git-machete.svg?label=)                                   | [**PyPI package**](https://pypi.org/project/git-machete) <br/> [Test PyPI package](https://test.pypi.org/project/git-machete)                                                                                                                                                                                                                                | Ourselves                                                                                   | Our CI on `master` builds[^4]                                                                                                                                                         | ![PyPI daily downloads](https://img.shields.io/pypi/dd/git-machete.svg) <br/> ![PyPI weekly downloads](https://img.shields.io/pypi/dw/git-machete.svg) <br/> ![PyPI monthly downloads](https://img.shields.io/pypi/dm/git-machete.svg) <br/> ![PyPI wheel?](https://img.shields.io/pypi/wheel/git-machete.svg)        |
-| PPA (Ubuntu)                          | [^5]                                                                                                    | [**PPA packages**](https://launchpad.net/~virtuslab/+archive/ubuntu/git-machete/+packages)                                                                                                                                                                                                                                                                   | Ourselves                                                                                   | Our CI on `master` builds (uploads source package), then Launchpad build system (see [builds](https://launchpad.net/~virtuslab/+archive/ubuntu/git-machete/+builds?&build_state=all)) |                                                                                                                                                                                                                                                                                                                       |
-| Read the Docs                         | ![Read the Docs](https://readthedocs.org/projects/git-machete/badge/?version=stable)                    | [**Read the Docs**](https://git-machete.readthedocs.io/en/stable)                                                                                                                                                                                                                                                                                            | Ourselves                                                                                   | Read the Docs itself (see [builds](https://readthedocs.org/projects/git-machete/builds)), triggered by GitHub webhook                                                                 |                                                                                                                                                                                                                                                                                                                       |
-| Snap                                  | ![Snap](https://snapcraft.io/git-machete/badge.svg)                                                     | [**Snap package**](https://snapcraft.io/git-machete)                                                                                                                                                                                                                                                                                                         | Ourselves                                                                                   | Our CI on `master` builds[^6]                                                                                                                                                         |                                                                                                                                                                                                                                                                                                                       |
-| [T2 SDE](https://t2sde.org/)          | ![T2 SDE package](https://repology.org/badge/version-for-repo/t2/git-machete.svg?header=)                       | [**T2 SDE package**](https://t2sde.org/packages/git-machete)                                                                                                                                                                                                                                                                                                 | T2 SDE package maintainers                                                                  | T2 SDE package maintainers                                                                                                                                                            |                                                                                                                                                                                                                                                                                                                       |
-
-[^1]: If the release introduces significant changes/critical bugfixes, please [flag the package as out of date](https://aur.archlinux.org/pkgbase/git-machete/flag).
-
-[^2]: Downloads most likely correspond to the RPM packages attached to the releases.
-
-[^3]: Releases used by done by our CI until @blitz's [PR #131141 to NixOS/nixpkgs](https://github.com/NixOS/nixpkgs/pull/131141) which set up the bot.
-
-[^4]: `develop` CI builds release to Test PyPI, `master` builds to main (production) PyPI.
+| Package system                                      | Version                                                                                                         | Package link                                                                                                                                                                                                              | Definition link                                                                                                                            | PRs link                                                                     | Controlled by                                                                               | Released by                                                                                                                                                                           | Installs completions and/or man page? | Downloads, last modified etc.                                                                                                                                                                                                                                                                                         |
+|-----------------------------------------------------|-----------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Alpine Linux                                        | ![Alpine Linux Edge package](https://repology.org/badge/version-for-repo/alpine_edge/git-machete.svg?header=)   | [**Alpine Linux Edge package**](https://pkgs.alpinelinux.org/package/edge/community/x86/git-machete)                                                                                                                      | [APKBUILD](https://git.alpinelinux.org/aports/tree/community/git-machete/APKBUILD) (points to GitHub)                                      |                                                                              | Alpine Linux package maintainers                                                            | Alpine Linux package maintainers                                                                                                                                                      | bash, fish, zsh                       |                                                                                                                                                                                                                                                                                                                       |
+| Arch User Repository                                | ![AUR package](https://img.shields.io/aur/version/git-machete.svg?label=)                                       | [**AUR package**](https://aur.archlinux.org/packages/git-machete)                                                                                                                                                         | [PKGBUILD](https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=git-machete) (points to GitHub)                                          |                                                                              | AUR maintainers, incl. [Ila&iuml; Deutel](https://github.com/ilai-deutel)                   | AUR maintainers[^1]                                                                                                                                                                   | bash, zsh                             | ![AUR last modified](https://img.shields.io/aur/last-modified/git-machete.svg)                                                                                                                                                                                                                                        |
+| Conda Forge                                         | ![Conda package](https://img.shields.io/conda/vn/conda-forge/git-machete.svg?label=)                            | [**Conda package**](https://anaconda.org/conda-forge/git-machete)                                                                                                                                                         | [Recipe](https://github.com/conda-forge/git-machete-feedstock/blob/main/recipe/meta.yaml) (points to PyPI)                                 | [PRs](https://github.com/conda-forge/git-machete-feedstock/pulls?q=is%3Apr)  | Feedstock maintainers, incl. [@asford (Alex Ford)](https://github.com/asford) and ourselves | Feedstock GitHub bot                                                                                                                                                                  | -                                     | ![Conda downloads](https://img.shields.io/conda/dn/conda-forge/git-machete.svg)                                                                                                                                                                                                                                       |
+| Gentoo                                              | ![Gentoo package](https://repology.org/badge/version-for-repo/gentoo/git-machete.svg?header=)                   | [**Gentoo package**](https://packages.gentoo.org/packages/dev-vcs/git-machete)                                                                                                                                            | [ebuild](https://gitweb.gentoo.org/repo/gentoo.git/tree/dev-vcs/git-machete) (points to GitHub)                                            |                                                                              | Gentoo package maintainers                                                                  | Gentoo package maintainers                                                                                                                                                            | bash, fish, zsh                       |                                                                                                                                                                                                                                                                                                                       |
+| GitHub Releases (rpm)                               | ![GitHub latest release](https://img.shields.io/github/v/release/VirtusLab/git-machete?label=)                  | [**GitHub releases**](https://github.com/VirtusLab/git-machete/releases)                                                                                                                                                  |                                                                                                                                            |                                                                              | Ourselves                                                                                   | Our CI on `master` builds                                                                                                                                                             | -                                     | ![GitHub releases total downloads](https://img.shields.io/github/downloads/VirtusLab/git-machete/total)[^2]                                                                                                                                                                                                           |
+| Homebrew                                            | ![homebrew formula](https://img.shields.io/homebrew/v/git-machete.svg?label=)                                   | [**Formula**](https://formulae.brew.sh/formula/git-machete)                                                                                                                                                               | [Formula source](https://github.com/Homebrew/homebrew-core/blob/master/Formula/git-machete.rb) (points to PyPI)                            | [PRs](https://github.com/Homebrew/homebrew-core/pulls?q=is%3Apr+git-machete) | Homebrew maintainers                                                                        | Our CI on `master` builds (opens formula-bump PR), then homebrew-core GitHub Actions                                                                                                  | bash, fish, zsh + man page            | ![homebrew formula monthly downloads](https://img.shields.io/homebrew/installs/dm/git-machete.svg) <br/> ![homebrew formula quarterly downloads](https://img.shields.io/homebrew/installs/dq/git-machete.svg) <br/> ![homebrew formula yearly downloads](https://img.shields.io/homebrew/installs/dy/git-machete.svg) |
+| [LiGurOS (Gentoo fork)](https://liguros.gitlab.io/) | ![LiGurOS develop package](https://repology.org/badge/version-for-repo/liguros_develop/git-machete.svg?header=) |                                                                                                                                                                                                                           | [ebuild](https://gitlab.com/liguros/liguros-repo/-/tree/develop/dev-vcs/git-machete) (points to GitHub)                                    |                                                                              | LiGurOS package maintainers                                                                 | LiGurOS package maintainers                                                                                                                                                           | bash, fish, zsh                       |                                                                                                                                                                                                                                                                                                                       |
+| nixpkgs                                             | ![nixpkgs unstable package](https://repology.org/badge/version-for-repo/nix_unstable/git-machete.svg?header=)   | [**nixpkgs unstable package**](https://search.nixos.org/packages?channel=unstable&show=git-machete&query=git-machete)                                                                                                     | [Expression](https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/version-management/git-machete/default.nix) (points to GitHub) | [PRs](https://github.com/NixOS/nixpkgs/pulls?q=is%3Apr+git-machete)          | nixpkgs maintainers, incl. [@blitz (Julian Stecklina)](https://github.com/blitz)            | [@r-ryantm bot](https://github.com/r-ryantm) ([logs](https://r.ryantm.com/log/updatescript/git-machete/))[^3]                                                                         | bash, fish, zsh                       |                                                                                                                                                                                                                                                                                                                       |
+| PyPI                                                | ![PyPI package](https://img.shields.io/pypi/v/git-machete.svg?label=)                                           | [**PyPI package**](https://pypi.org/project/git-machete) <br/> [Test PyPI package](https://test.pypi.org/project/git-machete)                                                                                             |                                                                                                                                            |                                                                              | Ourselves                                                                                   | Our CI on `master` builds[^4]                                                                                                                                                         | -                                     | ![PyPI daily downloads](https://img.shields.io/pypi/dd/git-machete.svg) <br/> ![PyPI weekly downloads](https://img.shields.io/pypi/dw/git-machete.svg) <br/> ![PyPI monthly downloads](https://img.shields.io/pypi/dm/git-machete.svg) <br/> ![PyPI wheel?](https://img.shields.io/pypi/wheel/git-machete.svg)        |
+| PPA (Ubuntu)                                        | [^5]                                                                                                            | [**PPA packages**](https://launchpad.net/~virtuslab/+archive/ubuntu/git-machete/+packages)                                                                                                                                | [debian/ folder (in this repo)](debian)                                                                                                    |                                                                              | Ourselves                                                                                   | Our CI on `master` builds (uploads source package), then Launchpad build system (see [builds](https://launchpad.net/~virtuslab/+archive/ubuntu/git-machete/+builds?&build_state=all)) | -                                     |                                                                                                                                                                                                                                                                                                                       |
+| Read the Docs                                       | ![Read the Docs](https://readthedocs.org/projects/git-machete/badge/?version=stable)                            | [**Read the Docs (stable)**](https://git-machete.readthedocs.io/en/stable) <br/> [Read the Docs (latest)](https://git-machete.readthedocs.io/en/latest) <br/> [Admin page](https://readthedocs.org/projects/git-machete/) |                                                                                                                                            |                                                                              | Ourselves                                                                                   | Read the Docs itself (see [builds](https://readthedocs.org/projects/git-machete/builds)), triggered by GitHub webhook [^6]                                                            | N/A                                   |                                                                                                                                                                                                                                                                                                                       |
+| Snap                                                | ![Snap](https://snapcraft.io/git-machete/badge.svg)                                                             | [**Snap package**](https://snapcraft.io/git-machete)                                                                                                                                                                      | [snapcraft.yaml (in this repo)](snap/snapcraft.yaml)                                                                                       |                                                                              | Ourselves                                                                                   | Our CI on `master` builds[^7]                                                                                                                                                         | -                                     |                                                                                                                                                                                                                                                                                                                       |
+| [T2 SDE](https://t2sde.org/)                        | ![T2 SDE package](https://repology.org/badge/version-for-repo/t2/git-machete.svg?header=)                       | [**T2 SDE package**](https://t2sde.org/packages/git-machete)                                                                                                                                                              | [Package definition](http://svn.exactcode.de/t2/trunk/package/contrib/git-machete/) (points to GitHub)                                     |                                                                              | T2 SDE package maintainers                                                                  | T2 SDE package maintainers                                                                                                                                                            | -                                     |                                                                                                                                                                                                                                                                                                                       |
+
+[^1]: If a release introduces significant changes/critical bugfixes, we should [flag the package as out of date](https://aur.archlinux.org/pkgbase/git-machete/flag).
+
+[^2]: GitHub downloads most likely correspond to the RPM packages attached to the releases.
+
+[^3]: nixpkgs releases used to be done by our CI until @blitz's [PR #131141 to NixOS/nixpkgs](https://github.com/NixOS/nixpkgs/pull/131141) which set up the bot.
+
+[^4]: CI builds of `develop` publish the package to Test PyPI.
+      CI builds of `master`  publish the package to main (production) PyPI.
 
 [^5]: There are no badges available for PPA packages; see [badges/shields#560](https://github.com/badges/shields/issues/560).
 
-[^6]: Releases used to be done by Snap Store itself, triggered by webhook. [We moved that process to CI](https://github.com/VirtusLab/git-machete/issues/436) to skip a manual promotion step.
+[^6]: ReadTheDocs builds of `develop` are published as [latest](https://git-machete.readthedocs.io/en/latest).
+      ReadTheDocs builds of `master`  are published as [stable](https://git-machete.readthedocs.io/en/stable).
+
+[^7]: Releases used to be done by Snap Store itself, triggered by webhook. [We moved that process to CI](https://github.com/VirtusLab/git-machete/issues/436) to skip a manual promotion step.
 
 ## [Repology](https://repology.org/project/git-machete/versions)
 
diff --git a/README.md b/README.md
index a3fcf93..71066e0 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,7 @@
 # git-machete
 
 [![homebrew formula](https://img.shields.io/homebrew/v/git-machete)](https://formulae.brew.sh/formula/git-machete)
+[![homebrew formula monthly downloads](https://img.shields.io/homebrew/installs/dm/git-machete.svg)](https://formulae.brew.sh/formula/git-machete)
 [![PyPI package](https://img.shields.io/pypi/v/git-machete.svg)](https://pypi.org/project/git-machete)
 [![PyPI package monthly downloads](https://img.shields.io/pypi/dm/git-machete.svg)](https://pypistats.org/packages/git-machete)
 [![Conda package](https://img.shields.io/conda/vn/conda-forge/git-machete.svg)](https://anaconda.org/conda-forge/git-machete)
@@ -43,8 +44,6 @@ a port into a plugin for the IntelliJ Platform products, including PyCharm, WebS
 
 We provide a couple of alternative ways of installation. See [PACKAGES.md](PACKAGES.md) for the full list.
 
-**Instructions for installing bash, zsh, and fish completion scripts are provided in [completion/README.md](completion/README.md).**
-
 git-machete requires Python >= 3.6. Python 2.x is no longer supported.
 
 ### Using Homebrew (macOS & most Linux distributions)
@@ -181,8 +180,39 @@ This will be resolved from the first of:
 3. Auth token from the current [`gh`](https://cli.github.com/) configuration.
 4. Auth token from the current [`hub`](https://github.com/github/hub) configuration.
 
-<br/>
+### Shell completions
+
+When git-machete is installed via **Homebrew** (and a few other supported package managers, see [PACKAGES.md](PACKAGES.md)),
+shell completions should be installed automatically. <br/>
+For other package managers (like **pip**), or when your shell doesn't pick up the Homebrew-installed completion, use the following:
+
+#### Bash
+
+Put the following into `~/.bashrc` or `~/.bash_profile`:
+
+```shell script
+eval "$(git machete completion bash)"  # or, if it doesn't work:
+source <(git machete completion bash)
+```
+
+#### Fish
+
+Put the following into `~/.config/fish/config.fish`:
 
+```shell script
+git machete completion fish | source
+```
+
+#### Zsh
+
+Put the following into `~/.zshrc`:
+
+```shell script
+eval "$(git machete completion zsh)"  # or, if it doesn't work:
+source <(git machete completion zsh)
+```
+
+<br/>
 
 ## FAQ
 
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 42ab6df..06943c5 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,5 +1,10 @@
 # Release notes
 
+## New in git-machete 3.18.0
+
+- added: `git machete completion bash|fish|zsh` command
+- fixed: multiple glitches in the existing bash/fish/zsh completions
+
 ## New in git-machete 3.17.9
 
 - improved: layout of documentation at ReadTheDocs
diff --git a/ci/checks/enforce-completion-scripts-correct.sh b/ci/checks/enforce-completion-scripts-correct.sh
deleted file mode 100755
index bc35c71..0000000
--- a/ci/checks/enforce-completion-scripts-correct.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/usr/bin/env bash
-
-set -e -o pipefail -u
-
-bash completion/git-machete.completion.bash >/dev/null
-zsh  completion/git-machete.completion.zsh  >/dev/null
-fish completion/git-machete.fish            >/dev/null
diff --git a/ci/checks/enforce-mocking-only-whitelisted-methods.sh b/ci/checks/enforce-mocking-only-whitelisted-methods.sh
index 84cb31e..7abeee3 100755
--- a/ci/checks/enforce-mocking-only-whitelisted-methods.sh
+++ b/ci/checks/enforce-mocking-only-whitelisted-methods.sh
@@ -6,7 +6,7 @@ self_dir=$(cd "$(dirname "$0")" &>/dev/null; pwd -P)
 source "$self_dir"/utils.sh
 self_name=$(basename "$0")
 
-whitelisted_methods='
+whitelisted_methods="\
 builtins.input
 builtins.open
 git_machete.client.MacheteClient.is_stdout_a_tty
@@ -20,9 +20,8 @@ git_machete.utils.find_executable
 os.path.isfile
 shutil.which
 sys.argv
-urllib.request.urlopen
-'
-actual_methods=$(git grep -Pho "(?<=self\.patch_symbol\(mocker, ['\"]).*?(?=['\"])" | sort -u)
+urllib.request.urlopen"
+actual_methods=$(git grep -Pho "(?<=self\.patch_symbol\(mocker, ['\"]).*?(?=['\"])" | LC_COLLATE=C sort -u)
 
 # `comm -13` to list lines only present in the second input (actual methods) and not in the first (whitelisted methods)
 # `grep ''` to check if the output is non-empty (true if non-empty, false if empty)
diff --git a/ci/checks/enforce-shell-scripts-pass-shellcheck.sh b/ci/checks/enforce-shell-scripts-pass-shellcheck.sh
index b1b1adc..d310868 100755
--- a/ci/checks/enforce-shell-scripts-pass-shellcheck.sh
+++ b/ci/checks/enforce-shell-scripts-pass-shellcheck.sh
@@ -2,6 +2,6 @@
 
 set -e -o pipefail -u
 
-{ git grep --name-only '#!.*sh$' -- ':!*.md'; git ls-files '*.sh'; } \
+{ git grep --name-only '#!.*sh$' -- ':!*.md' ':!*.fish' ':!*.zsh'; git ls-files '*.sh'; } \
   | sort --unique \
   | xargs shellcheck --check-sourced --exclude=SC1090,SC1091,SC2016,SC2086,SC2090,SC2125 --severity=info --shell=bash
diff --git a/ci/checks/prohibit-bash-usages-from-python.sh b/ci/checks/prohibit-bash-usages-from-python.sh
index 666979c..3c6671d 100755
--- a/ci/checks/prohibit-bash-usages-from-python.sh
+++ b/ci/checks/prohibit-bash-usages-from-python.sh
@@ -2,7 +2,7 @@
 
 set -e -o pipefail -u
 
-if git grep -n bash -- tests/ '*.py'; then
+if git grep -n -e bash --and --not -e compl -- '*.py' ':!git_machete/generated_docs.py' 'tests/' ':!tests/completion_e2e/'; then
   echo
   echo 'Do not rely on bash being installed, not even in tests.'
   echo 'See e.g. https://github.com/VirtusLab/git-machete/pull/929.'
diff --git a/ci/checks/run-all-checks.sh b/ci/checks/run-all-checks.sh
index 17b4ae7..c7a3dd9 100755
--- a/ci/checks/run-all-checks.sh
+++ b/ci/checks/run-all-checks.sh
@@ -10,7 +10,6 @@ PATH=./ci/checks:$PATH
 set -e -x
 
 enforce-bumped-version.sh
-enforce-completion-scripts-correct.sh
 enforce-consistent-style-for-fork-point.sh
 enforce-consistent-style-for-github.sh
 enforce-correct-shebangs.sh
diff --git a/ci/deb-ppa-upload/build-context/run.sh b/ci/deb-ppa-upload/build-context/run.sh
index 187010b..5743f9a 100644
--- a/ci/deb-ppa-upload/build-context/run.sh
+++ b/ci/deb-ppa-upload/build-context/run.sh
@@ -51,6 +51,7 @@ cat ../python3-git-machete_*_source.buildinfo
 cat ../python3-git-machete_*_source.changes
 
 tar_output=$(tar tvf ../python3-git-machete_*.tar.gz)
+echo "$tar_output"
 grep completion/ <<< "$tar_output"
 grep docs/man/git-machete\.1 <<< "$tar_output"
 
diff --git a/ci/homebrew/ci-deploy.sh b/ci/homebrew/ci-deploy.sh
index 860c72e..3feea29 100644
--- a/ci/homebrew/ci-deploy.sh
+++ b/ci/homebrew/ci-deploy.sh
@@ -75,5 +75,10 @@ else
     echo "man page has not been installed, aborting"
     exit 1
   fi
+  if ! git machete completion bash | grep '#!.*bash'; then
+    echo "shell completion is not available in runtime, aborting"
+    exit 1
+  fi
+
   brew remove git-machete
 fi
diff --git a/ci/rpm/build-context/run.sh b/ci/rpm/build-context/run.sh
index 2de210b..da0fa68 100644
--- a/ci/rpm/build-context/run.sh
+++ b/ci/rpm/build-context/run.sh
@@ -10,3 +10,7 @@ fi
 python3 setup.py bdist_rpm
 rpm -i dist/git-machete-*.rpm
 git machete version
+if ! git machete completion bash | grep '#!.*bash'; then
+  echo "shell completion is not available in runtime, aborting"
+  exit 1
+fi
diff --git a/ci/snap/ci-deploy.sh b/ci/snap/ci-deploy.sh
index 2791e1b..c1a78f0 100644
--- a/ci/snap/ci-deploy.sh
+++ b/ci/snap/ci-deploy.sh
@@ -25,6 +25,10 @@ if [[ ${1-} == "--dry-run" || ${CIRCLE_BRANCH-} != "master" ]]; then
   if command -v git-machete; then exit 1; fi
   sudo snap install git-machete*.snap --dangerous --classic
   git machete version
+  if ! git machete completion bash | grep '#!.*bash'; then
+    echo "shell completion is not available in runtime, aborting"
+    exit 1
+  fi
   sudo snap remove git-machete
 else
   # Relying on SNAPCRAFT_STORE_CREDENTIALS, provided by the CI
diff --git a/ci/tox/build-context/run.sh b/ci/tox/build-context/run.sh
index cacfd40..f326532 100644
--- a/ci/tox/build-context/run.sh
+++ b/ci/tox/build-context/run.sh
@@ -11,8 +11,7 @@ env | sort | head -4
 
 set -x
 
-suffix=py${PYTHON_VERSION/./}
-TOX_ENV_LIST="mypy-$suffix,coverage-$suffix"
+TOX_ENV_LIST="mypy,coverage"
 
 if [[ $BUILD_SPHINX_HTML = true ]]; then
   TOX_ENV_LIST="$TOX_ENV_LIST,sphinx-html"
@@ -38,8 +37,16 @@ $PYTHON -m venv venv/bdist_wheel/
 
 . venv/sdist/bin/activate
 pip install dist/git-machete-*.tar.gz
-git machete --version
+git machete version
+if ! git machete completion fish | grep 'complete -c git-machete'; then
+  echo "shell completion is not available in runtime, aborting"
+  exit 1
+fi
 
 . venv/bdist_wheel/bin/activate
 pip install dist/git_machete-*.whl
-git machete --version
+git machete version
+if ! git machete completion zsh | grep '#compdef git-machete'; then
+  echo "shell completion is not available in runtime, aborting"
+  exit 1
+fi
diff --git a/completion/README.md b/completion/README.md
deleted file mode 100644
index fce7fc0..0000000
--- a/completion/README.md
+++ /dev/null
@@ -1,124 +0,0 @@
-## Bash
-
-### Mac (via Homebrew)
-
-Make sure you have bash completion installed (with `brew install bash-completion`).
-
-`brew install git-machete` automatically installs bash completion files for `git machete`.
-
-If the bash completion doesn't work:
-1. Install git bash completions with `brew install git bash-completion`
-2. Add the below code to `~/.bash_profile` and reload the shell
-```shell script
-[ -f /usr/local/etc/bash_completion ] && . /usr/local/etc/bash_completion \
-  || # if not found in /usr/local/etc, try the brew --prefix location
-    {
-      [ -f "$(brew --prefix)/etc/bash_completion.d/git-completion.bash" ] \
-        && . $(brew --prefix)/etc/bash_completion.d/git-completion.bash
-      [ -f "$(brew --prefix)/etc/bash_completion.d/git-machete.completion.bash" ] \
-        && . $(brew --prefix)/etc/bash_completion.d/git-machete.completion.bash
-    }
-```
-
-
-### Linux
-
-1. In a non-minimal installation of Linux, bash completion should be available.
-2. Place the completion script in `/etc/bash_completion.d/`.
-
-```shell script
-sudo curl -L https://raw.githubusercontent.com/VirtusLab/git-machete/master/completion/git-machete.completion.bash -o /etc/bash_completion.d/git-machete
-```
-
-
-## Zsh
-
-### Linux/Mac: with [oh-my-zsh](https://ohmyz.sh/) shell
-
-```shell script
-$ mkdir -p ~/.oh-my-zsh/custom/plugins/git-machete/
-$ curl -L https://raw.githubusercontent.com/VirtusLab/git-machete/master/completion/git-machete.completion.zsh -o ~/.oh-my-zsh/custom/plugins/git-machete/git-machete.plugin.zsh
-```
-
-Add `git-machete` to the plugins list in `~/.zshrc` to run autocompletion within the oh-my-zsh shell.
-In the following example, `...` represents other zsh plugins you may have installed.
-
-```shell script
-plugins=(... git-machete
-)
-```
-
-#### Workarounds for Zsh on Mac
-
-On Mac, unfortunately there might be a problem that `git machete` subcommands still don't complete even when the zsh plugin is active.
-This issue also affects other non-standard `git` subcommands like `git flow` and `git lfs`.
-To work the issue around, first establish how `git` is installed in your system.
-```shell script
-which git
-```
-
-If `git` resolves to `/usr/bin/git`, then likely `git` is the default installation provided in Mac OS.
-As a workaround, add the following line directly at the end of `~/.zshrc`:
-```shell script
-source ~/.oh-my-zsh/custom/plugins/git-machete/git-machete.plugin.zsh
-```
-and reload the shell.
-
-If `git` resolves to `/usr/local/bin/git`, then likely `git` has been installed via `brew`.
-Up to our current knowledge, workaround is much harder to provide in such scenario.
-
-One option is to `brew uninstall git` and then use the solution for Mac's default `git` provided above,
-but that's likely undesired since `git` shipped with Mac OS is almost always an older version than what's available via `brew`.
-
-Another, less intrusive workaround is to make sure that the zsh `_git` function
-is NOT taken from brew-git's `/usr/local/share/zsh/site-functions/_git`,
-but instead from `/usr/share/zsh/5.7.1/functions/_git` (zsh version path fragment can be different from `5.7.1`).
-Add the following at the end of `~/.zshrc`:
-```shell script
-source /usr/share/zsh/5.7.1/functions/_git  # or other zsh version instead of 5.7.1, depending on what's available in the system
-```
-and reload the shell.
-
-
-### Linux/Mac: without oh-my-zsh shell
-
-1. Place the completion script in your `/path/to/zsh/completion` (typically `~/.zsh/completion/`):
-
-```shell script
-$ mkdir -p ~/.zsh/completion
-$ curl -L https://raw.githubusercontent.com/VirtusLab/git-machete/master/completion/git-machete.completion.zsh -o ~/.zsh/completion/_git-machete
-```
-
-2. Include the directory in your `$fpath` by adding in `~/.zshrc`:
-
-```shell script
-fpath=(~/.zsh/completion $fpath)
-```
-
-3. Make sure `compinit` is loaded or do it by adding in `~/.zshrc`:
-
-```shell script
-autoload -Uz compinit && compinit -i
-```
-
-4. Then reload your shell:
-
-```shell script
-exec $SHELL -l
-```
-
-## Fish
-
-### Mac (via Homebrew)
-Please look at the section about [installation via Homebrew](../README.md#using-homebrew-macos--most-linux-distributions).
-``brew install git-machete`` automatically installs fish completion files for ``git machete``.
-
-### Linux
-
-Place the completion script in `/path/to/fish/completions/` (typically `~/.config/fish/completions/git-machete.fish`).
-
-```shell script
-mkdir -p ~/.config/fish/completions
-curl -L https://raw.githubusercontent.com/VirtusLab/git-machete/master/completion/git-machete.fish -o ~/.config/fish/completions/git-machete.fish
-echo "source ~/.config/fish/completions/git-machete.fish >/dev/null" >> ~/.config/fish/config.fish
-```
diff --git a/completion/__init__.py b/completion/__init__.py
new file mode 100644
index 0000000..0d59b50
--- /dev/null
+++ b/completion/__init__.py
@@ -0,0 +1,1 @@
+# See https://github.com/python/cpython/issues/89838 for why this file is needed
diff --git a/completion/git-machete.completion.bash b/completion/git-machete.completion.bash
index fe06f1c..845079f 100644
--- a/completion/git-machete.completion.bash
+++ b/completion/git-machete.completion.bash
@@ -1,7 +1,7 @@
 #!/usr/bin/env bash
 
 _git_machete() {
-  local cmds="add advance anno clean d delete-unmanaged diff discover e edit file fork-point g github go help is-managed l list log reapply s show slide-out squash status t traverse update version"
+  local cmds="add advance anno clean completion delete-unmanaged diff discover edit file fork-point github go help is-managed list log reapply show slide-out squash status traverse update version"
   local help_topics="$cmds config format hooks"
 
   local categories="addable childless managed slidable slidable-after unmanaged with-overridden-fork-point"
@@ -11,8 +11,9 @@ _git_machete() {
   local opt_color_args="always auto never"
   local opt_return_to_args="here nearest-remaining stay"
   local opt_start_from_args="here root first-root"
+  local shells="bash fish zsh"
 
-  local common_opts="--debug -h --help -v --verbose --version"
+  local common_opts="--debug -h --help -v --verbose"
   local add_opts="-o --onto= -R --as-root -y --yes"
   local advance_opts="-y --yes"
   local anno_opts="-b --branch= -H --sync-github-prs"
@@ -31,7 +32,7 @@ _git_machete() {
   local traverse_opts="-F --fetch -l --list-commits -M --merge -n --no-detect-squash-merges --no-edit-merge --no-interactive-rebase --no-push --no-push-untracked --push --push-untracked --return-to= --start-from= -w --whole -W -y --yes"
   local update_opts="-f --fork-point= -M --merge -n --no-edit-merge --no-interactive-rebase"
 
-  # shellcheck disable=SC2154
+  cur=${COMP_WORDS[$COMP_CWORD]}
   case $cur in
     --branch=*|--onto=*) __gitcomp_nl "$(git machete list managed 2>/dev/null)" "" "${cur##--*=}" ;;
     --by=*|--checked-out-since=*) COMPREPLY=('');;
@@ -64,9 +65,14 @@ _git_machete() {
         slide-out) __gitcomp "$common_opts $slide_out_opts" ;;
         squash) __gitcomp "$common_opts $squash_opts" ;;
         s|status) __gitcomp "$common_opts $status_opts" ;;
-        traverse) __gitcomp "$common_opts $traverse_opts" ;;
+        t|traverse) __gitcomp "$common_opts $traverse_opts" ;;
         update) __gitcomp "$common_opts $update_opts" ;;
-        *) __gitcomp "$common_opts" ;;
+        *)
+          if [[ $COMP_CWORD -eq 2 ]]; then
+            __gitcomp "$common_opts --version"
+          else
+            __gitcomp "$common_opts"
+          fi ;;
       esac ;;
     *)
       if [[ $COMP_CWORD -eq 2 ]]; then
@@ -82,8 +88,9 @@ _git_machete() {
           --by=*|-C|--checked-out-since=*) COMPREPLY=('');;
           --color) __gitcomp "$opt_color_args" ;;
           -d|--down-fork-point|-f|--fork-point|--override-to) __gitcomp "$(__git_refs)" ;;
-          # TODO (#25): We don't complete --help since it's going to be captured by git anyway
-          # (and results in redirection to yet non-existent man for `git-machete`).
+          # TODO (#895): we don't complete --help since it's going to be captured by git anyway
+          # and results in redirection to man for `git-machete`,
+          # which is only properly installed by some package systems as for now (see PACKAGES.md).
           -h) __gitcomp "$help_topics" ;;
           --return-to) __gitcomp "$opt_return_to_args" ;;
           # TODO (#111): complete the comma-separated list of roots
@@ -94,6 +101,8 @@ _git_machete() {
             case ${COMP_WORDS[2]} in
               add)
                 __gitcomp_nl "$(git machete list addable 2>/dev/null)" ;;
+              completion)
+                __gitcomp "$shells" ;;
               d|diff|fork-point|is-managed|l|log)
                 __gitcomp "$(__git_heads)" ;;
               g|go)
diff --git a/completion/git-machete.completion.zsh b/completion/git-machete.completion.zsh
index 5f1480d..66efc4c 100644
--- a/completion/git-machete.completion.zsh
+++ b/completion/git-machete.completion.zsh
@@ -1,16 +1,17 @@
 #compdef git-machete
 
 _git-machete() {
-  local ret=1
+  local common_flags=(
+    '(--debug)'--debug'[Log detailed diagnostic info, including outputs of the executed git commands]'
+    '(-h --help)'{-h,--help}'[Print help and exit]'
+    '(-v --verbose)'{-v,--verbose}'[Log the executed git commands]'
+  )
 
   _arguments -C \
     '1: :__git_machete_commands' \
     '*::arg:->args' \
-    '(--debug)'--debug'[Log detailed diagnostic info, including outputs of the executed git commands]' \
-    '(-h --help)'{-h,--help}'[Print help and exit]' \
-    '(-v --verbose)'{-v,--verbose}'[Log the executed git commands]' \
-    '(--version)'--version'[Print version and exit]' \
-  && ret=0
+    "${common_flags[@]}" \
+    '(--version)'--version'[Print version and exit]'
 
   case $state in
     (args)
@@ -21,35 +22,40 @@ _git-machete() {
             '(-o --onto)'{-o,--onto=}'[Specify the target parent branch to add the given branch onto]: :__git_machete_list_managed' \
             '(-R --as-root)'{-R,--as-root}'[Add the given branch as a new root]' \
             '(-y --yes)'{-y,--yes}'[Do not ask for confirmation whether to create the branch or whether to add onto the inferred upstream]' \
-          && ret=0
+            "${common_flags[@]}"
           ;;
         (advance)
           _arguments \
             '(-y --yes)'{-y,--yes}'[Do not ask for confirmation]' \
-          && ret=0
+            "${common_flags[@]}"
           ;;
         (anno)
           _arguments \
             '(-b --branch)'{-b,--branch=}'[Branch to set the annotation for]: :__git_machete_list_managed' \
             '(-H --sync-github-prs)'{-H,--sync-github-prs}'[Annotate with GitHub PR numbers and authors where applicable]' \
-          && ret=0
+            "${common_flags[@]}"
           ;;
         (clean)
           _arguments \
             '(-H --checkout-my-github-prs)'{-H,--checkout-my-github-prs}'[Checkout your open PRs into local branches]' \
             '(-y --yes)'{-y,--yes}'[Do not ask for confirmation when deleting unmanaged or untracked branches]' \
-          && ret=0
+            "${common_flags[@]}"
+          ;;
+        (completion)
+          _arguments \
+            '1:: :__git_machete_completion_shells' \
+            "${common_flags[@]}"
           ;;
         (delete-unmanaged)
           _arguments \
             '(-y --yes)'{-y,--yes}'[Do not ask for confirmation when deleting unmanaged branches]' \
-          && ret=0
+            "${common_flags[@]}"
           ;;
         (d|diff)
           _arguments \
             '1:: :__git_branch_names' \
             '(-s --stat)'{-s,--stat}'[Pass --stat option to git diff, so that only summary (diffstat) is printed]' \
-          && ret=0
+            "${common_flags[@]}"
           ;;
         (discover)
           # TODO (#111): complete the comma-separated list of roots
@@ -58,44 +64,53 @@ _git-machete() {
             '(-l --list-commits)'{-l,--list-commits}'[List the messages of commits introduced on each branch]' \
             '(-r --roots)'{-r,--roots=}'[Comma-separated list of branches to be considered roots of trees of branch dependencies (typically develop and/or master)]: :__git_branch_names' \
             '(-y --yes)'{-y,--yes}'[Do not ask for confirmation]' \
-          && ret=0
+            "${common_flags[@]}"
           ;;
         (e|edit|file)
+          _arguments "${common_flags[@]}"
           ;;
         (fork-point)
-          # TODO (#112): correctly suggest branches for `--unset-override`
           _arguments '1:: :__git_branch_names' \
             '(--inferred)'--inferred'[Display the fork point ignoring any potential override]' \
             '(--override-to)'--override-to='[Override fork point to the given revision]: :__git_references' \
             '(--override-to-inferred)'--override-to-inferred'[Override fork point to the inferred location]' \
             '(--override-to-parent)'--override-to-parent'[Override fork point to the upstream (parent) branch]' \
-            '(--unset-override)'--unset-override'[Unset fork point override by removing machete.overrideForkPoint.<branch>.* configs]' \
-          && ret=0
+            '(--unset-override)'--unset-override='[Unset fork point override by removing machete.overrideForkPoint.<branch>.* configs]: :__git_machete_list_with_overridden_fork_point' \
+            "${common_flags[@]}"
           ;;
         (g|go)
-          _arguments '1:: :__git_machete_directions_go' && ret=0
+          _arguments \
+            '1:: :__git_machete_directions_go' \
+            "${common_flags[@]}"
           ;;
         (github)
           __git_machete_github_subcommands
           ;;
         (help)
-          _arguments '1:: :__git_machete_help_topics' && ret=0
+          _arguments \
+            '1:: :__git_machete_help_topics' \
+            "${common_flags[@]}"
           ;;
         (is-managed|l|log)
-          _arguments '1:: :__git_branch_names' && ret=0
+          _arguments \
+            '1:: :__git_branch_names' \
+            "${common_flags[@]}"
           ;;
         (list)
-          _arguments '1:: :__git_machete_categories' && ret=0
+          _arguments \
+            '1:: :__git_machete_categories' \
+            "${common_flags[@]}"
           ;;
         (reapply)
           _arguments \
             '(-f --fork-point)'{-f,--fork-point=}'[Fork point commit after which the rebased part of history is meant to start]: :__git_references' \
-          && ret=0
+            "${common_flags[@]}"
           ;;
         (show)
-          _arguments '1:: :__git_machete_directions_show' \
+          _arguments \
+            '1:: :__git_machete_directions_show' \
             '2:: :__git_machete_list_managed' \
-            && ret=0
+            "${common_flags[@]}"
           ;;
         (slide-out)
           # TODO (#113): suggest further branches based on the previous specified branch (like in Bash completion script)
@@ -107,12 +122,12 @@ _git-machete() {
             '(-n)'-n'[If updating by rebase, equivalent to --no-interactive-rebase. If updating by merge, equivalent to --no-edit-merge]' \
             '(--no-edit-merge)'--no-edit-merge'[If updating by merge, pass --no-edit flag to underlying git merge]' \
             '(--no-interactive-rebase)'--no-interactive-rebase'[If updating by rebase, do NOT pass --interactive flag to underlying git rebase]' \
-          && ret=0
+            "${common_flags[@]}"
           ;;
         (squash)
           _arguments \
             '(-f --fork-point)'{-f,--fork-point=}'[Fork point commit after which the squashed part of history is meant to start]: :__git_references' \
-          && ret=0
+            "${common_flags[@]}"
           ;;
         (s|status)
           _arguments \
@@ -120,7 +135,7 @@ _git-machete() {
             '(-L --list-commits-with-hashes)'{-L,--list-commits-with-hashes}'[List the short hashes and messages of commits introduced on each branch]' \
             '(-l --list-commits)'{-l,--list-commits}'[List the messages of commits introduced on each branch]' \
             '(--no-detect-squash-merges)'--no-detect-squash-merges'[Only consider "strict" (fast-forward or 2-parent) merges, rather than rebase/squash merges, when detecting if a branch is merged into its upstream]' \
-          && ret=0
+            "${common_flags[@]}"
           ;;
         (t|traverse)
           _arguments \
@@ -140,7 +155,7 @@ _git-machete() {
             '(-w --whole)'{-w,--whole}'[Equivalent to -n --start-from=first-root --return-to=nearest-remaining]' \
             '(-W)'-W'[Equivalent to --fetch --whole]' \
             '(-y --yes)'{-y,--yes}'[Do not ask for any interactive input; implicates -n]' \
-          && ret=0
+            "${common_flags[@]}"
           ;;
          (update)
           _arguments \
@@ -149,7 +164,7 @@ _git-machete() {
             '(-n)'-n'[If updating by rebase, equivalent to --no-interactive-rebase. If updating by merge, equivalent to --no-edit-merge]' \
             '(--no-edit-merge)'--no-edit-merge'[If updating by merge, pass --no-edit flag to underlying git merge]' \
             '(--no-interactive-rebase)'--no-interactive-rebase'[If updating by rebase, do NOT pass --interactive flag to underlying git rebase]' \
-          && ret=0
+            "${common_flags[@]}"
           ;;
       esac
   esac
@@ -160,30 +175,41 @@ __git_machete_cmds=(
   'advance:Fast-forward the current branch to match one of its downstreams and subsequently slide out this downstream'
   'anno:Manage custom annotations'
   'clean:Delete untracked and unmanaged branches and optionally check out open GitHub PRs'
+  'completion:Print completion script for the given shell'
   'delete-unmanaged:Delete local branches that are not present in the branch layout file'
-  {diff,d}':Diff current working directory or a given branch against its fork point'
+  'diff:Diff current working directory or a given branch against its fork point'
   'discover:Automatically discover tree of branch dependencies'
-  {edit,e}':Edit the branch layout file'
+  'edit:Edit the branch layout file'
   'file:Display the location of the branch layout file'
   'fork-point:Display hash of the fork point commit of a branch'
-  {go,g}':Check out the branch relative to the position of the current branch'
-  'github:Creates, checks out and manages GitHub PRs while keeping them reflected in branch layout file'
+  'github:Creates, checks out and manages GitHub PRs while keeping them reflected in branch branch layout file'
+  'go:Check out the branch relative to the position of the current branch'
   'help:Display this overview, or detailed help for a specified command'
   'is-managed:Check if the current branch is managed by git-machete (mostly for scripts)'
   'list:List all branches that fall into one of pre-defined categories (mostly for internal use)'
-  {log,l}':Log the part of history specific to the given branch'
+  'log:Log the part of history specific to the given branch'
   'reapply:Rebase the current branch onto its own fork point'
   'show:Show name(s) of the branch(es) relative to the position of the current branch'
   'slide-out:Slide the current branch out and sync its downstream (child) branch with its upstream (parent) branch via rebase or merge'
   'squash:Squash the unique history of the current branch into a single commit'
-  {status,s}':Display formatted tree of branch dependencies, including info on their sync with upstream branch and with remote'
+  'status:Display formatted tree of branch dependencies, including info on their sync with upstream branch and with remote'
   'traverse:Walk through the tree of branch dependencies and rebase, merge, slide out, push and/or pull each branch one by one'
   'update:Sync the current branch with its upstream (parent) branch via rebase or merge'
   'version:Display version and exit'
 )
 
+__git_machete_directions=(
+  'down:child(ren) in tree of branch dependencies'
+  'first:first child of the current root branch'
+  'last:last branch located under current root branch'
+  'next:the one defined in the following line in .git/machete file'
+  'prev:the one defined in the preceding line in .git/machete file'
+  'root:root of the tree of branch dependencies where current branch belongs'
+  'up:parent in tree of branch dependencies'
+)
+
 __git_machete_commands() {
-  _describe -t __git_machete_cmds 'git machete command' __git_machete_cmds "$@"
+  _describe 'git machete command' __git_machete_cmds
 }
 
 __git_machete_help_topics() {
@@ -194,42 +220,23 @@ __git_machete_help_topics() {
     'format:Format of the .git/machete branch layout file'
     'hooks:Display docs for the extra hooks added by git machete'
   )
-  _describe -t topics 'git machete help topic' topics "$@"
+  _describe 'git machete help topic' topics
 }
 
 __git_machete_directions_go() {
-  local directions
-  directions=(
-    {d,down}':child(ren) in tree of branch dependencies'
-    {f,first}':first child of the current root branch'
-    {l,last}':last branch located under current root branch'
-    {n,next}':the one defined in the following line in .git/machete file'
-    {p,prev}':the one defined in the preceding line in .git/machete file'
-    {r,root}':root of the tree of branch dependencies where current branch belongs'
-    {u,up}':parent in tree of branch dependencies'
-  )
-  _describe -t directions 'direction' directions "$@"
+  _describe 'direction' __git_machete_directions
 }
 
-# TODO (#114): extract the part shared with __git_machete_go_directions
 __git_machete_directions_show() {
   local directions
   directions=(
-    {c,current}':the currently checked out branch'
-    {d,down}':child(ren) in tree of branch dependencies'
-    {f,first}':first child of the current root branch'
-    {l,last}':last branch located under current root branch'
-    {n,next}':the one defined in the following line in .git/machete file'
-    {p,prev}':the one defined in the preceding line in .git/machete file'
-    {r,root}':root of the tree of branch dependencies where current branch belongs'
-    {u,up}':parent in tree of branch dependencies'
+    'current'
+    "${__git_machete_directions[@]}"
   )
-  _describe -t directions 'direction' directions "$@"
+  _describe 'direction' directions
 }
 
-
-__git_machete_github_subcommands ()
-{
+__git_machete_github_subcommands() {
   local curcontext="$curcontext" state line
   typeset -A opt_args
 
@@ -248,26 +255,31 @@ __git_machete_github_subcommands ()
         'retarget-pr:set the base of the current branch PR to upstream (parent) branch'
         'sync:synchronize with the remote repository: checkout open PRs for the current user associated with the GitHub token, delete unmanaged branches and also delete untracked managed branches with no downstream branch'
       )
-      _describe -t commands 'subcommand' github_subcommands
+      _describe 'subcommand' github_subcommands
       ;;
 
     (options)
       case $line[1] in
 
-        (create-pr)
-          _arguments '(--draft)'--draft'[Creates the new PR as draft]'
-        ;;
-
         (checkout-prs)
           _arguments \
             '(--all)'--all'[Checkout all open PRs.]' \
-            '(--by)'--by'[Checkout open PRs authored by the given GitHub user]' \
-            '(--mine)'--mine'[Checkout open PRs for the current user associated with the GitHub token.]'
+            '(--by)'--by='[Checkout open PRs authored by the given GitHub user]' \
+            '(--mine)'--mine='[Checkout open PRs for the current user associated with the GitHub token.]' \
+            "${common_flags[@]}"
+        ;;
+
+        (create-pr)
+          _arguments \
+            '(--draft)'--draft'[Creates the new PR as draft]' \
+            "${common_flags[@]}"
         ;;
 
         (retarget-pr)
-          _arguments '(-b --branch)'{-b,--branch=}'[Specify the branch for which the associated PR base will be set to its upstream (parent) branch.]'
-          _arguments '(--ignore-if-missing)'--ignore-if-missing'[Ignore errors and quietly terminate execution if there is no PR opened for current (or specified) branch.]'
+          _arguments \
+            '(-b --branch)'{-b,--branch=}'[Specify the branch for which the associated PR base will be set to its upstream (parent) branch.]: :__git_machete_list_managed' \
+            '(--ignore-if-missing)'--ignore-if-missing'[Ignore errors and quietly terminate execution if there is no PR opened for current (or specified) branch.]' \
+            "${common_flags[@]}"
         ;;
       esac
     ;;
@@ -286,7 +298,7 @@ __git_machete_categories() {
     'unmanaged:all local branches that do not appear in the branch layout file'
     'with-overridden-fork-point:all local branches that have a fork point override config'
   )
-  _describe -t categories 'category' categories "$@"
+  _describe 'category' categories
 }
 
 __git_machete_opt_color_args() {
@@ -296,7 +308,7 @@ __git_machete_opt_color_args() {
     'auto:emits colors only when standard output is connected to a terminal'
     'never:colors are disabled'
   )
-  _describe -t opt_color_args 'color argument' opt_color_args "$@"
+  _describe 'color argument' opt_color_args
 }
 
 __git_machete_opt_return_to_args() {
@@ -306,7 +318,7 @@ __git_machete_opt_return_to_args() {
     'nearest-remaining:nearest remaining branch in case the "here" branch has been slid out by the traversal'
     'stay:the default - just stay wherever the traversal stops'
   )
-  _describe -t opt_return_to 'return-to argument' opt_return_to "$@"
+  _describe 'return-to argument' opt_return_to
 }
 
 __git_machete_opt_start_from_args() {
@@ -316,25 +328,37 @@ __git_machete_opt_start_from_args() {
     'root:root branch of the current branch, as in git machete show root'
     'first-root:first listed managed branch'
   )
-  _describe -t opt_start_from 'start-from argument' opt_start_from "$@"
+  _describe 'start-from argument' opt_start_from
 }
 
 __git_machete_list_addable() {
-  local list_addable
-  IFS=$'\n' list_addable=($(git machete list addable 2>/dev/null))
-  _describe -t list_addable 'addable branch' list_addable "$@"
+  local result
+  IFS=$'\n' result=($(git machete list addable 2>/dev/null))
+  _describe 'addable branch' result
 }
 
 __git_machete_list_managed() {
-  local list_managed
-  IFS=$'\n' list_managed=($(git machete list managed 2>/dev/null))
-  _describe -t list_managed 'managed branch' list_managed "$@"
+  local result
+  IFS=$'\n' result=($(git machete list managed 2>/dev/null))
+  _describe 'managed branch' result
 }
 
 __git_machete_list_slidable() {
-  local list_slidable
-  IFS=$'\n' list_slidable=($(git machete list slidable 2>/dev/null))
-  _describe -t list_slidable 'slidable branch' list_slidable "$@"
+  local result
+  IFS=$'\n' result=($(git machete list slidable 2>/dev/null))
+  _describe 'slidable branch' result
+}
+
+__git_machete_list_with_overridden_fork_point() {
+  local result
+  IFS=$'\n' result=($(git machete list with-overridden-fork-point 2>/dev/null))
+  _describe 'branch with overridden fork point' result
+}
+
+__git_machete_completion_shells() {
+  local shells
+  shells=(bash fish zsh)
+  _describe 'shell' shells
 }
 
 zstyle ':completion:*:*:git:*' user-commands machete:'organize your repo, instantly rebase/merge/push/pull and more'
diff --git a/completion/git-machete.fish b/completion/git-machete.fish
index 4ab7d7e..376491a 100644
--- a/completion/git-machete.fish
+++ b/completion/git-machete.fish
@@ -1,189 +1,210 @@
-# This file will be loaded by git.fish completions and also provide all __fish_git_* functions here.
+# git.fish completions will provide all __fish_git_* functions referenced from this script
 
-set -l __mcht_help_topics config format hooks
-set -l __mcht_commands_long add advance anno delete-unmanaged diff discover edit file fork-point \
+set -l __machete_help_topics config format hooks
+set -l __machete_commands_long add advance anno clean completion delete-unmanaged diff discover edit file fork-point \
   github go help is-managed list log reapply show slide-out squash status traverse update version
-set -l __mcht_commands_short d e g l s t
-set -l __mcht_commands $__mcht_commands_long $__mcht_commands_short
+set -l __machete_commands_short d e g l s t
+set -l __machete_commands $__machete_commands_long $__machete_commands_short
 
 # git
-complete -c git-machete -f -d 'Tool for managing git workflows'
+complete -c git-machete -f -d 'Git repository organizer & rebase/merge workflow automation tool'
 
 # git machete (general options)
-complete -c git-machete -n "not __fish_seen_subcommand_from $__mcht_commands; and not __fish_seen_subcommand_from --help -h" -f -s h -d 'Print help and exit'
-
-#     Parameters --help work for subcommands only.
-#     See: https://github.com/VirtusLab/git-machete/issues/25
-complete -c git-machete -n "__fish_seen_subcommand_from $__mcht_commands;     and not __fish_seen_subcommand_from --help -h" -f -l help -s h -d 'Print help and exit'
+# TODO (#895): `--help` works for subcommands only, not for `git machete --help` (unless under Homebrew-installed package)
+complete -c git-machete -n "    __fish_seen_subcommand_from $__machete_commands; and not __fish_seen_subcommand_from --help -h"    -f -l help -s h -d 'Print help and exit'
+complete -c git-machete -n "not __fish_seen_subcommand_from $__machete_commands; and not __fish_seen_subcommand_from --version"    -f -l version   -d 'Print version and exit'
 
 complete -c git-machete -n "not __fish_seen_subcommand_from --verbose -v" -f -l verbose -s v -d 'Log the executed git commands'
 complete -c git-machete -n "not __fish_seen_subcommand_from --debug"      -f -l debug        -d 'Log detailed diagnostic info, including outputs of the executed git commands'
-complete -c git-machete -n "not __fish_seen_subcommand_from --version"    -f -l version      -d 'Print version and exit'
+
+function __machete_addable_branches
+  git machete list addable | sed 's/$/\tBranch/'
+end
+
+function __machete_managed_branches
+  git machete list managed | sed 's/$/\tManaged Branch/'
+end
+
+function __machete_slidable_branches
+  git machete list slidable | sed 's/$/\tSlidable Branch/'
+end
+
+function __machete_with_overridden_fork_point_branches
+  git machete list with-overridden-fork-point | sed 's/$/\tBranch with overridden fork point/'
+end
 
 # git machete add
-complete -c git-machete -n "not __fish_seen_subcommand_from $__mcht_commands"                                            -f                 -a add                           -d 'Add a branch to the tree of branch dependencies'
-complete -c git-machete -n "__fish_seen_subcommand_from add; and not __fish_seen_subcommand_from --as-root -R"           -x -l onto    -s o -a '(__fish_git_local_branches)' -d 'Specifies the target parent branch to add the given branch onto. Cannot be specified together with -R/--as-root'
-complete -c git-machete -n "__fish_seen_subcommand_from add; and not __fish_seen_subcommand_from --onto -o --as-root -R" -f -l as-root -s R                                  -d 'Add the given branch as a new root (and not onto any other branch). Cannot be specified together with -o/--onto'
-complete -c git-machete -n "__fish_seen_subcommand_from add; and not __fish_seen_subcommand_from --yes -y"               -f -l yes     -s y                                  -d 'Don\'t ask for confirmation whether to create the branch or whether to add onto the inferred upstream'
+complete -c git-machete -n "not __fish_seen_subcommand_from $__machete_commands"                                         -f                 -a add                            -d 'Add a branch to the tree of branch dependencies'
+complete -c git-machete -n "__fish_seen_subcommand_from add"                                                             -f                 -a '(__machete_addable_branches)'
+complete -c git-machete -n "__fish_seen_subcommand_from add; and not __fish_seen_subcommand_from --as-root -R"           -x -l onto    -s o -a '(__machete_managed_branches)' -d 'Specifies the target parent branch to add the given branch onto. Cannot be specified together with -R/--as-root'
+complete -c git-machete -n "__fish_seen_subcommand_from add; and not __fish_seen_subcommand_from --onto -o --as-root -R" -f -l as-root -s R                                   -d 'Add the given branch as a new root (and not onto any other branch). Cannot be specified together with -o/--onto'
+complete -c git-machete -n "__fish_seen_subcommand_from add; and not __fish_seen_subcommand_from --yes -y"               -f -l yes     -s y                                   -d 'Do not ask for confirmation whether to create the branch or whether to add onto the inferred upstream'
 
 # git machete advance
-complete -c git-machete -n "not __fish_seen_subcommand_from $__mcht_commands"                                  -f -a advance  -d 'Fast-forward merge one of children to the current branch and then slide out this child'
-complete -c git-machete -n "__fish_seen_subcommand_from advance; and not __fish_seen_subcommand_from --yes -y" -f -l yes -s y -d 'Don\'t ask for confirmation whether to fast-forward the current branch or whether to slide-out the downstream. Fails if the current branch has more than one green-edge downstream branch'
+complete -c git-machete -n "not __fish_seen_subcommand_from $__machete_commands"                               -f -a advance  -d 'Fast-forward merge one of children to the current branch and then slide out this child'
+complete -c git-machete -n "__fish_seen_subcommand_from advance; and not __fish_seen_subcommand_from --yes -y" -f -l yes -s y -d 'Do not ask for confirmation whether to fast-forward the current branch or whether to slide-out the downstream. Fails if the current branch has more than one green-edge downstream branch'
 
 # git machete anno
-complete -c git-machete -n "not __fish_seen_subcommand_from $__mcht_commands"                                           -f                         -a anno                          -d 'Manage custom annotations'
-complete -c git-machete -n "__fish_seen_subcommand_from anno"                                                           -x -l branch          -s b -a '(__fish_git_local_branches)' -d 'Branch to set the annotation for'
-complete -c git-machete -n "__fish_seen_subcommand_from anno; and not __fish_seen_subcommand_from --sync-github-prs -H" -f -l sync-github-prs -s H                                  -d 'Annotate with GitHub PR numbers and authors where applicable'
+complete -c git-machete -n "not __fish_seen_subcommand_from $__machete_commands"                                        -f                         -a anno                           -d 'Manage custom annotations'
+complete -c git-machete -n "__fish_seen_subcommand_from anno"                                                           -x -l branch          -s b -a '(__machete_managed_branches)' -d 'Branch to set the annotation for'
+complete -c git-machete -n "__fish_seen_subcommand_from anno; and not __fish_seen_subcommand_from --sync-github-prs -H" -f -l sync-github-prs -s H                                   -d 'Annotate with GitHub PR numbers and authors where applicable'
 
 # git machete clean
-complete -c git-machete -n "not __fish_seen_subcommand_from $__mcht_commands"                                                     -f -a clean                          -d 'Delete untracked and unmanaged branches, and optionally check out open GitHub PRs'
-complete -c git-machete -n "__fish_seen_subcommand_from clean; and not __fish_seen_subcommand_from --checkout-my-github-prs -H"   -f -l checkout-my-github-prs -s H    -d 'Checkout open PRs for the current user associated with the GitHub token'
-complete -c git-machete -n "__fish_seen_subcommand_from clean; and not __fish_seen_subcommand_from --yes -y"                      -f -l yes -s y                       -d 'Don\'t ask for confirmation'
+complete -c git-machete -n "not __fish_seen_subcommand_from $__machete_commands"                                                  -f -a clean                       -d 'Delete untracked and unmanaged branches, and optionally check out open GitHub PRs'
+complete -c git-machete -n "__fish_seen_subcommand_from clean; and not __fish_seen_subcommand_from --checkout-my-github-prs -H"   -f -l checkout-my-github-prs -s H -d 'Checkout open PRs for the current user associated with the GitHub token'
+complete -c git-machete -n "__fish_seen_subcommand_from clean; and not __fish_seen_subcommand_from --yes -y"                      -f -l yes                    -s y -d 'Do not ask for confirmation'
 
+# git machete completion
+complete -c git-machete -n "not __fish_seen_subcommand_from $__machete_commands"                                       -f -a completion -d 'Print completion script for the given shell'
+complete -c git-machete -n "__fish_seen_subcommand_from completion; and not __fish_seen_subcommand_from bash fish zsh" -f -a bash
+complete -c git-machete -n "__fish_seen_subcommand_from completion; and not __fish_seen_subcommand_from bash fish zsh" -f -a fish
+complete -c git-machete -n "__fish_seen_subcommand_from completion; and not __fish_seen_subcommand_from bash fish zsh" -f -a zsh
 
 # git machete delete-unmanaged
-complete -c git-machete -n "not __fish_seen_subcommand_from $__mcht_commands"                                           -f -a delete-unmanaged -d 'Delete local branches that are not present in the branch layout file'
-complete -c git-machete -n "__fish_seen_subcommand_from delete-unmanaged; and not __fish_seen_subcommand_from --yes -y" -f -l yes -s y         -d 'Don\'t ask for confirmation'
+complete -c git-machete -n "not __fish_seen_subcommand_from $__machete_commands"                                        -f -a delete-unmanaged -d 'Delete local branches that are not present in the branch layout file'
+complete -c git-machete -n "__fish_seen_subcommand_from delete-unmanaged; and not __fish_seen_subcommand_from --yes -y" -f -l yes -s y         -d 'Do not ask for confirmation'
+
+# git machete diff
+complete -c git-machete -n "not __fish_seen_subcommand_from $__machete_commands"                               -f                -a diff                          -d 'Diff current working directory or a given branch against its fork point'
+complete -c git-machete -n "__fish_seen_subcommand_from diff d"                                                -x                -a '(__fish_git_local_branches)' -d 'Branch to diff against its fork point'
+complete -c git-machete -n "__fish_seen_subcommand_from diff d; and not __fish_seen_subcommand_from --stat -s" -f -l stat   -s s                                  -d 'Makes git machete diff pass --stat option to git diff, so that only summary (diffstat) is printed'
 
 # git machete discover
-complete -c git-machete -n "not __fish_seen_subcommand_from $__mcht_commands"                                                 -f                           -a discover                      -d 'Automatically discover tree of branch dependencies'
-complete -c git-machete -n "__fish_seen_subcommand_from discover; and not __fish_seen_subcommand_from --checked-out-since -C" -x -l checked-out-since -s C                                  -d 'Only consider branches checked out at least once since the given date. <date> can be e.g. 2 weeks ago or 2020-06-01, as in git log --since=<date>. If not present, the date is selected automatically so that around 10 branches are included'
+complete -c git-machete -n "not __fish_seen_subcommand_from $__machete_commands"                                              -f                           -a discover                      -d 'Automatically discover tree of branch dependencies'
+complete -c git-machete -n "__fish_seen_subcommand_from discover; and not __fish_seen_subcommand_from --checked-out-since -C" -x -l checked-out-since -s C                                  -d 'Only consider branches checked out at least once since the given date. <date> can be e.g. 2 weeks ago or 2020-06-01, as in git log --since=<date>'
 complete -c git-machete -n "__fish_seen_subcommand_from discover; and not __fish_seen_subcommand_from --list-commits -l"      -f -l list-commits      -s l                                  -d 'When printing the discovered tree, additionally lists the messages of commits introduced on each branch (as for git machete status)'
-complete -c git-machete -n "__fish_seen_subcommand_from discover"                                                             -x -l roots             -s r -a '(__fish_git_local_branches)' -d 'Comma-separated list of branches that should be considered roots of trees of branch dependencies. If not present, master is assumed to be a root. Note that in the process of discovery, certain other branches can also be additionally deemed to be roots as well'
-complete -c git-machete -n "__fish_seen_subcommand_from discover; and not __fish_seen_subcommand_from --yes -y"               -f -l yes               -s y                                  -d 'Don\'t ask for confirmation before saving the newly-discovered tree. Mostly useful in scripts; not recommended for manual use'
-
-# git machete diff
-complete -c git-machete -n "not __fish_seen_subcommand_from $__mcht_commands"                                  -f -a diff      -d 'Diff current working directory or a given branch against its computed fork point'
-complete -c git-machete -n "__fish_seen_subcommand_from diff d; and not __fish_seen_subcommand_from --stat -s" -f -l stat -s s -d 'Makes git machete diff pass --stat option to git diff, so that only summary (diffstat) is printed'
+complete -c git-machete -n "__fish_seen_subcommand_from discover"                                                             -x -l roots             -s r -a '(__fish_git_local_branches)' -d 'Comma-separated list of branches that should be considered roots of trees of branch dependencies'
+complete -c git-machete -n "__fish_seen_subcommand_from discover; and not __fish_seen_subcommand_from --yes -y"               -f -l yes               -s y                                  -d 'Do not ask for confirmation before saving the newly-discovered tree'
 
 # git machete edit
-complete -c git-machete -n "not __fish_seen_subcommand_from $__mcht_commands" -f -a edit -d 'Edit the branch layout file'
+complete -c git-machete -n "not __fish_seen_subcommand_from $__machete_commands" -f -a edit -d 'Edit the branch layout file'
 
 # git machete file
-complete -c git-machete -n "not __fish_seen_subcommand_from $__mcht_commands" -f -a file -d 'Display the location of the branch layout file'
+complete -c git-machete -n "not __fish_seen_subcommand_from $__machete_commands" -f -a file -d 'Display the location of the branch layout file'
 
 # git machete fork-point
-complete -c git-machete -n "not __fish_seen_subcommand_from $__mcht_commands" -f -a fork-point -d 'Display or override fork point for a branch'
+complete -c git-machete -n "not __fish_seen_subcommand_from $__machete_commands" -f -a fork-point -d 'Display or override fork point for a branch'
 # all forms
-complete -c git-machete -n "__fish_seen_subcommand_from fork-point" -f -a '(__fish_git_local_branches)'
+complete -c git-machete -n "__fish_seen_subcommand_from fork-point; and not __fish_seen_subcommand_from --unset-override"                                                                      -f                          -a '(__fish_git_local_branches)'
 # form 1
 complete -c git-machete -n "__fish_seen_subcommand_from fork-point; and not __fish_seen_subcommand_from --inferred --unset-override --override-to --override-to-inferred --override-to-parent" -f -l inferred
 # form 2
-complete -c git-machete -n "__fish_seen_subcommand_from fork-point; and not __fish_seen_subcommand_from --inferred --unset-override --override-to --override-to-inferred --override-to-parent" -x -l override-to
+complete -c git-machete -n "__fish_seen_subcommand_from fork-point; and not __fish_seen_subcommand_from --inferred --unset-override --override-to --override-to-inferred --override-to-parent" -x -l override-to           -a '(__fish_git_refs)'
 complete -c git-machete -n "__fish_seen_subcommand_from fork-point; and not __fish_seen_subcommand_from --inferred --unset-override --override-to --override-to-inferred --override-to-parent" -f -l override-to-inferred
 complete -c git-machete -n "__fish_seen_subcommand_from fork-point; and not __fish_seen_subcommand_from --inferred --unset-override --override-to --override-to-inferred --override-to-parent" -f -l override-to-parent
 # form 3
-complete -c git-machete -n "__fish_seen_subcommand_from fork-point; and not __fish_seen_subcommand_from --inferred --unset-override --override-to --override-to-inferred --override-to-parent" -f -l unset-override
+complete -c git-machete -n "__fish_seen_subcommand_from fork-point; and not __fish_seen_subcommand_from --inferred                  --override-to --override-to-inferred --override-to-parent" -x -l unset-override        -a '(__machete_with_overridden_fork_point_branches)'
 
 # git machete github
-complete -c git-machete -n "not __fish_seen_subcommand_from $__mcht_commands" -f -a github -d 'Create, check out and manage GitHub PRs while keeping them reflected in git machete'
+complete -c git-machete -n "not __fish_seen_subcommand_from $__machete_commands"                                                                                          -f -a github                                               -d 'Create, check out and manage GitHub PRs while keeping them reflected in git machete'
 complete -c git-machete -n "__fish_seen_subcommand_from github; and not __fish_seen_subcommand_from anno-prs checkout-prs create-pr retarget-pr sync"                     -f -a anno-prs                                             -d 'Annotates the branches based on their corresponding GitHub PR numbers and authors'
 complete -c git-machete -n "__fish_seen_subcommand_from github; and not __fish_seen_subcommand_from anno-prs checkout-prs create-pr retarget-pr sync"                     -x -a checkout-prs                                         -d 'Check out the head branch of the given pull requests (specified by number), also traverse chain of pull requests upwards, adding branches one by one to git-machete and check them out locally'
 complete -c git-machete -n "__fish_seen_subcommand_from github; and not __fish_seen_subcommand_from anno-prs checkout-prs create-pr retarget-pr sync"                     -f -a create-pr                                            -d 'Creates a PR for the current branch, using the upstream (parent) branch as the PR base'
-complete -c git-machete -n "__fish_seen_subcommand_from github; and not __fish_seen_subcommand_from anno-prs checkout-prs create-pr retarget-pr sync"                     -f -a retarget-pr                                          -d 'Sets the base of the current branch\'s PR to upstream (parent) branch, as seen by git machete (see git machete show up)'
+complete -c git-machete -n "__fish_seen_subcommand_from github; and not __fish_seen_subcommand_from anno-prs checkout-prs create-pr retarget-pr sync"                     -f -a retarget-pr                                          -d 'Sets the base of PR for the current branch to upstream (parent) branch, as seen by git machete (see git machete show up)'
 complete -c git-machete -n "__fish_seen_subcommand_from github; and not __fish_seen_subcommand_from anno-prs checkout-prs create-pr retarget-pr sync"                     -f -a sync                                                 -d 'Synchronize with the remote repository: checkout open PRs for the current user associated with the GitHub token, delete unmanaged branches and also delete untracked managed branches with no downstream branch'
 complete -c git-machete -n "__fish_seen_subcommand_from github; and __fish_seen_subcommand_from checkout-prs; and not __fish_seen_subcommand_from --all"                  -f -l all                                                  -d 'Checkout all open PRs'
-complete -c git-machete -n "__fish_seen_subcommand_from github; and __fish_seen_subcommand_from checkout-prs; and not __fish_seen_subcommand_from --by"                   -f -l by                                                   -d "Checkout someone's open PRs"
-complete -c git-machete -n "__fish_seen_subcommand_from github; and __fish_seen_subcommand_from checkout-prs; and not __fish_seen_subcommand_from --mine"                 -f -l mine                                                 -d 'Checkout open PRs for the current user associated with the GitHub token'
+complete -c git-machete -n "__fish_seen_subcommand_from github; and __fish_seen_subcommand_from checkout-prs; and not __fish_seen_subcommand_from --by"                   -x -l by                                                   -d "Checkout someone's open PRs"
+complete -c git-machete -n "__fish_seen_subcommand_from github; and __fish_seen_subcommand_from checkout-prs; and not __fish_seen_subcommand_from --mine"                 -x -l mine                                                 -d 'Checkout open PRs for the current user associated with the GitHub token'
 complete -c git-machete -n "__fish_seen_subcommand_from github; and __fish_seen_subcommand_from create-pr; and not __fish_seen_subcommand_from --draft"                   -f -l draft                                                -d 'Creates the new PR as a draft'
-complete -c git-machete -n "__fish_seen_subcommand_from github; and __fish_seen_subcommand_from retarget-pr; and not __fish_seen_subcommand_from --branch"                -f -l branch -s b -a '(__fish_git_local_branches)'         -d 'Specify the branch for which the associated PR base will be set to its upstream (parent) branch'
+complete -c git-machete -n "__fish_seen_subcommand_from github; and __fish_seen_subcommand_from retarget-pr; and not __fish_seen_subcommand_from --branch"                -x -l branch -s b       -a '(__machete_managed_branches)'  -d 'Specify the branch for which the associated PR base will be set to its upstream (parent) branch'
 complete -c git-machete -n "__fish_seen_subcommand_from github; and __fish_seen_subcommand_from retarget-pr; and not __fish_seen_subcommand_from --ignore-if-missing"     -f -l ignore-if-missing                                    -d 'Ignore errors and quietly terminate execution if there is no PR opened for current (or specified) branch'
 
 # git machete go
-complete -c git-machete -n "not __fish_seen_subcommand_from $__mcht_commands" -f -a go -d 'Check out the branch relative to the position of the current branch, accepts down/first/last/next/root/prev/up argument'
+complete -c git-machete -n "not __fish_seen_subcommand_from $__machete_commands"                                                                   -f -a go    -d 'Check out the branch relative to the position of the current branch, accepts down/first/last/next/root/prev/up argument'
 complete -c git-machete -n "__fish_seen_subcommand_from go g; and not __fish_seen_subcommand_from down d first f last l next n prev p root r up u" -f -a down  -d 'the direct children/downstream branch of the current branch'
 complete -c git-machete -n "__fish_seen_subcommand_from go g; and not __fish_seen_subcommand_from down d first f last l next n prev p root r up u" -f -a first -d 'the first downstream of the root branch of the current branch (like root followed by next), or the root branch itself if the root has no downstream branches'
 complete -c git-machete -n "__fish_seen_subcommand_from go g; and not __fish_seen_subcommand_from down d first f last l next n prev p root r up u" -f -a last  -d 'the last branch in the branch layout file that has the same root as the current branch; can be the root branch itself if the root has no downstream branches'
 complete -c git-machete -n "__fish_seen_subcommand_from go g; and not __fish_seen_subcommand_from down d first f last l next n prev p root r up u" -f -a next  -d 'the direct successor of the current branch in the branch layout file'
 complete -c git-machete -n "__fish_seen_subcommand_from go g; and not __fish_seen_subcommand_from down d first f last l next n prev p root r up u" -f -a prev  -d 'the direct predecessor of the current branch in the branch layout file'
-complete -c git-machete -n "__fish_seen_subcommand_from go g; and not __fish_seen_subcommand_from down d first f last l next n prev p root r up u" -f -a root  -d 'the root of the tree where the current branch is located. Note: this will typically be something like develop or master, since all branches are usually meant to be ultimately merged to one of those'
+complete -c git-machete -n "__fish_seen_subcommand_from go g; and not __fish_seen_subcommand_from down d first f last l next n prev p root r up u" -f -a root  -d 'the root of the tree where the current branch is located'
 complete -c git-machete -n "__fish_seen_subcommand_from go g; and not __fish_seen_subcommand_from down d first f last l next n prev p root r up u" -f -a up    -d 'the direct parent/upstream branch of the current branch'
 
 # git machete help
-complete -c git-machete -n "not __fish_seen_subcommand_from $__mcht_commands" -f -a help -d 'Display overview, or detailed help for a specified command'
-complete -c git-machete -n "__fish_seen_subcommand_from help" -f -a "$__mcht_help_topics"
-complete -c git-machete -n "__fish_seen_subcommand_from help" -f -a "(complete -C 'git machete ')"
+complete -c git-machete -n "not __fish_seen_subcommand_from $__machete_commands" -f -a help                           -d 'Display overview, or detailed help for a specified command'
+complete -c git-machete -n "__fish_seen_subcommand_from help"                    -f -a "$__machete_help_topics"
+complete -c git-machete -n "__fish_seen_subcommand_from help"                    -f -a "(complete -C 'git machete ')"
 
 # git machete is-managed
-complete -c git-machete -n "not __fish_seen_subcommand_from $__mcht_commands" -f -a is-managed -d 'Check if the current branch is managed by git machete (mostly for scripts)'
-complete -c git-machete -n "__fish_seen_subcommand_from is-managed"           -f -a '(__fish_git_local_branches)'
+complete -c git-machete -n "not __fish_seen_subcommand_from $__machete_commands" -f -a is-managed                     -d 'Check if the current branch is managed by git machete (mostly for scripts)'
+complete -c git-machete -n "__fish_seen_subcommand_from is-managed"              -f -a '(__fish_git_local_branches)'
 
 # git machete list
-complete -c git-machete -n "not __fish_seen_subcommand_from $__mcht_commands" -f -a list -d 'List all branches that fall into one of pre-defined categories (mostly for internal use)'
-complete -c git-machete -n "__fish_seen_subcommand_from list; and not __fish_seen_subcommand_from addable childless managed slidable slidable-after unmanaged with-overridden-fork-point" -f -a addable
-complete -c git-machete -n "__fish_seen_subcommand_from list; and not __fish_seen_subcommand_from addable childless managed slidable slidable-after unmanaged with-overridden-fork-point" -f -a childless                    -d 'all branches (local or remote) than can be added to the branch layout file'
-complete -c git-machete -n "__fish_seen_subcommand_from list; and not __fish_seen_subcommand_from addable childless managed slidable slidable-after unmanaged with-overridden-fork-point" -f -a managed                    -d 'all branches that appear in the branch layout file'
-complete -c git-machete -n "__fish_seen_subcommand_from list; and not __fish_seen_subcommand_from addable childless managed slidable slidable-after unmanaged with-overridden-fork-point" -f -a slidable                   -d 'all managed branches that have an upstream and can be slid out with slide-out command'
-complete -c git-machete -n "__fish_seen_subcommand_from list; and not __fish_seen_subcommand_from addable childless managed slidable slidable-after unmanaged with-overridden-fork-point" -f -a slidable-after             -d 'the downstream branch of the <branch>, if it exists and is the only downstream of <branch> (i.e. the one that can be slid out immediately following <branch>)'
-complete -c git-machete -n "__fish_seen_subcommand_from list; and __fish_seen_subcommand_from slidable-after" -f -a '(__fish_git_local_branches)'
-complete -c git-machete -n "__fish_seen_subcommand_from list; and not __fish_seen_subcommand_from addable childless managed slidable slidable-after unmanaged with-overridden-fork-point" -f -a unmanaged                  -d 'all local branches that don\'t appear in the branch layout file'
-complete -c git-machete -n "__fish_seen_subcommand_from list; and not __fish_seen_subcommand_from addable childless managed slidable slidable-after unmanaged with-overridden-fork-point" -f -a with-overridden-fork-point -d 'all local branches that have a fork point override set up (even if this override does not affect the location of their fork point anymore).'
+complete -c git-machete -n "not __fish_seen_subcommand_from $__machete_commands"                                                                                                          -f -a list                         -d 'List all branches that fall into one of pre-defined categories (mostly for internal use)'
+complete -c git-machete -n "__fish_seen_subcommand_from list; and not __fish_seen_subcommand_from addable childless managed slidable slidable-after unmanaged with-overridden-fork-point" -f -a addable                      -d 'all branches (local or remote) than can be added to the branch layout file'
+complete -c git-machete -n "__fish_seen_subcommand_from list; and not __fish_seen_subcommand_from addable childless managed slidable slidable-after unmanaged with-overridden-fork-point" -f -a childless                    -d' all managed branches that do not possess child branches'
+complete -c git-machete -n "__fish_seen_subcommand_from list; and not __fish_seen_subcommand_from addable childless managed slidable slidable-after unmanaged with-overridden-fork-point" -f -a managed                      -d 'all branches that appear in the branch layout file'
+complete -c git-machete -n "__fish_seen_subcommand_from list; and not __fish_seen_subcommand_from addable childless managed slidable slidable-after unmanaged with-overridden-fork-point" -f -a slidable                     -d 'all managed branches that have an upstream and can be slid out with slide-out command'
+complete -c git-machete -n "__fish_seen_subcommand_from list; and not __fish_seen_subcommand_from addable childless managed slidable slidable-after unmanaged with-overridden-fork-point" -f -a slidable-after               -d 'the downstream branch of the <branch>, if it exists and is the only downstream of <branch>'
+complete -c git-machete -n "__fish_seen_subcommand_from list; and __fish_seen_subcommand_from slidable-after"                                                                             -f -a '(__machete_slidable_branches)'
+complete -c git-machete -n "__fish_seen_subcommand_from list; and not __fish_seen_subcommand_from addable childless managed slidable slidable-after unmanaged with-overridden-fork-point" -f -a unmanaged                    -d 'all local branches that do not appear in the branch layout file'
+complete -c git-machete -n "__fish_seen_subcommand_from list; and not __fish_seen_subcommand_from addable childless managed slidable slidable-after unmanaged with-overridden-fork-point" -f -a with-overridden-fork-point   -d 'all local branches that have a fork point override set up (even if this override does not affect the location of their fork point anymore).'
 
 # git machete log
-complete -c git-machete -n "not __fish_seen_subcommand_from $__mcht_commands" -f -a log -d 'Log the part of history specific to the given branch'
-complete -c git-machete -n "__fish_seen_subcommand_from log" -f -a '(__fish_git_local_branches)'
+complete -c git-machete -n "not __fish_seen_subcommand_from $__machete_commands" -f -a log                           -d 'Log the part of history specific to the given branch'
+complete -c git-machete -n "__fish_seen_subcommand_from log l"                   -f -a '(__fish_git_local_branches)'
 
 # git machete reapply
-complete -c git-machete -n "not __fish_seen_subcommand_from $__mcht_commands" -f -a reapply -d 'Rebase the current branch onto its computed fork point'
-complete -c git-machete -n "__fish_seen_subcommand_from reapply" -x -l fork-point -s f -a '(__fish_git_local_branches)' -d 'Specifies the alternative fork point commit after which the rebased part of history is meant to start'
+complete -c git-machete -n "not __fish_seen_subcommand_from $__machete_commands" -f                    -a reapply             -d 'Rebase the current branch onto its computed fork point'
+complete -c git-machete -n "__fish_seen_subcommand_from reapply"                 -x -l fork-point -s f -a '(__fish_git_refs)' -d 'Specifies the alternative fork point commit after which the rebased part of history is meant to start'
 
 # git machete show
-complete -c git-machete -n "not __fish_seen_subcommand_from $__mcht_commands" -f -a show -d 'Show name(s) of the branch(es) relative to the position of a branch, accepts down/first/last/next/root/prev/up argument'
-complete -c git-machete -n "__fish_seen_subcommand_from show; and not __fish_seen_subcommand_from current c down d first f last l next n prev p root r up u" -f -a 'current c' -d 'the current branch; exits with a non-zero status if none (detached HEAD)'
-complete -c git-machete -n "__fish_seen_subcommand_from show; and not __fish_seen_subcommand_from current c down d first f last l next n prev p root r up u" -f -a 'down d'    -d 'the direct children/downstream branch of the given branch'
-complete -c git-machete -n "__fish_seen_subcommand_from show; and not __fish_seen_subcommand_from current c down d first f last l next n prev p root r up u" -f -a 'first f'   -d 'the first downstream of the root branch of the given branch (like root followed by next), or the root branch itself if the root has no downstream branches'
-complete -c git-machete -n "__fish_seen_subcommand_from show; and not __fish_seen_subcommand_from current c down d first f last l next n prev p root r up u" -f -a 'last l'    -d 'the last branch in the branch layout file that has the same root as the given branch; can be the root branch itself if the root has no downstream branches'
-complete -c git-machete -n "__fish_seen_subcommand_from show; and not __fish_seen_subcommand_from current c down d first f last l next n prev p root r up u" -f -a 'next n'    -d 'the direct successor of the given branch in the branch layout file'
-complete -c git-machete -n "__fish_seen_subcommand_from show; and not __fish_seen_subcommand_from current c down d first f last l next n prev p root r up u" -f -a 'prev p'    -d 'the direct predecessor of the given branch in the branch layout file'
-complete -c git-machete -n "__fish_seen_subcommand_from show; and not __fish_seen_subcommand_from current c down d first f last l next n prev p root r up u" -f -a 'root r'    -d 'the root of the tree where the given branch is located'
-complete -c git-machete -n "__fish_seen_subcommand_from show; and not __fish_seen_subcommand_from current c down d first f last l next n prev p root r up u" -f -a 'up u'      -d 'the direct parent/upstream branch of the given branch'
+complete -c git-machete -n "not __fish_seen_subcommand_from $__machete_commands"                                                                             -f -a show    -d 'Show name(s) of the branch(es) relative to the position of a branch, accepts down/first/last/next/root/prev/up argument'
+complete -c git-machete -n "__fish_seen_subcommand_from show; and not __fish_seen_subcommand_from current c down d first f last l next n prev p root r up u" -f -a current -d 'the current branch; exits with a non-zero status if none (detached HEAD)'
+complete -c git-machete -n "__fish_seen_subcommand_from show; and not __fish_seen_subcommand_from current c down d first f last l next n prev p root r up u" -f -a down    -d 'the direct children/downstream branch of the given branch'
+complete -c git-machete -n "__fish_seen_subcommand_from show; and not __fish_seen_subcommand_from current c down d first f last l next n prev p root r up u" -f -a first   -d 'the first downstream of the root branch of the given branch (like root followed by next), or the root branch itself if the root has no downstream branches'
+complete -c git-machete -n "__fish_seen_subcommand_from show; and not __fish_seen_subcommand_from current c down d first f last l next n prev p root r up u" -f -a last    -d 'the last branch in the branch layout file that has the same root as the given branch; can be the root branch itself if the root has no downstream branches'
+complete -c git-machete -n "__fish_seen_subcommand_from show; and not __fish_seen_subcommand_from current c down d first f last l next n prev p root r up u" -f -a next    -d 'the direct successor of the given branch in the branch layout file'
+complete -c git-machete -n "__fish_seen_subcommand_from show; and not __fish_seen_subcommand_from current c down d first f last l next n prev p root r up u" -f -a prev    -d 'the direct predecessor of the given branch in the branch layout file'
+complete -c git-machete -n "__fish_seen_subcommand_from show; and not __fish_seen_subcommand_from current c down d first f last l next n prev p root r up u" -f -a root    -d 'the root of the tree where the given branch is located'
+complete -c git-machete -n "__fish_seen_subcommand_from show; and not __fish_seen_subcommand_from current c down d first f last l next n prev p root r up u" -f -a up      -d 'the direct parent/upstream branch of the given branch'
 
 # git machete slide-out
-complete -c git-machete -n "not __fish_seen_subcommand_from $__mcht_commands" -f -a slide-out -d 'Slide out the current branch and sync its downstream (child) branches with its upstream (parent) branch via rebase or merge'
-complete -c git-machete -n "__fish_seen_subcommand_from slide-out; and not __fish_seen_subcommand_from --merge -M --down-fork-point -d"                                                        -x -l down-fork-point -s d  -d 'If updating by rebase, specifies the alternative fork point for downstream branches for the operation. git machete fork-point overrides for downstream branches are recommended over use of this option. See also doc for --fork-point option in git machete help reapply and git machete help update. Not allowed if updating by merge'
-complete -c git-machete -n "__fish_seen_subcommand_from slide-out; and not __fish_seen_subcommand_from --delete"                                                                               -f -l delete                -d 'Delete branches from git'
-complete -c git-machete -n "__fish_seen_subcommand_from slide-out; and not __fish_seen_subcommand_from --merge -M --down-fork-point -d"                                                        -f -l merge           -s M  -d 'Update the downstream branch by merge rather than by rebase'
-complete -c git-machete -n "__fish_seen_subcommand_from slide-out; and not __fish_seen_subcommand_from -n --no-edit-merge --no-interactive-rebase"                                             -f                    -s n  -d 'If updating by rebase, equivalent to --no-interactive-rebase. If updating by merge, equivalent to --no-edit-merge'
-complete -c git-machete -n "__fish_seen_subcommand_from slide-out; and not __fish_seen_subcommand_from -n --no-edit-merge --no-interactive-rebase; and __fish_seen_subcommand_from --merge -M" -f -l no-edit-merge         -d 'If updating by merge, skip opening the editor for merge commit message while doing git merge (i.e. pass --no-edit flag to underlying git merge). Not allowed if updating by rebase'
-complete -c git-machete -n "__fish_seen_subcommand_from slide-out; and not __fish_seen_subcommand_from -n --no-edit-merge --no-interactive-rebase -M --merge"                                  -f -l no-interactive-rebase -d 'If updating by rebase, run git rebase in non-interactive mode (without -i/--interactive flag). Not allowed if updating by merge'
+complete -c git-machete -n "not __fish_seen_subcommand_from $__machete_commands" -f -a slide-out -d 'Slide out the current branch and sync its downstream (child) branches with its upstream (parent) branch via rebase or merge'
+complete -c git-machete -n "__fish_seen_subcommand_from slide-out"               -f -a '(__machete_slidable_branches)'
+complete -c git-machete -n "__fish_seen_subcommand_from slide-out; and not __fish_seen_subcommand_from --merge -M --down-fork-point -d"                                                        -x -l down-fork-point -s d -a '(__fish_git_refs)' -d 'If updating by rebase, specifies the alternative fork point for downstream branches for the operation. Not allowed if updating by merge'
+complete -c git-machete -n "__fish_seen_subcommand_from slide-out; and not __fish_seen_subcommand_from --delete"                                                                               -f -l delete                                      -d 'Delete branches from git'
+complete -c git-machete -n "__fish_seen_subcommand_from slide-out; and not __fish_seen_subcommand_from --merge -M --down-fork-point -d"                                                        -f -l merge           -s M                        -d 'Update the downstream branch by merge rather than by rebase'
+complete -c git-machete -n "__fish_seen_subcommand_from slide-out; and not __fish_seen_subcommand_from -n --no-edit-merge --no-interactive-rebase"                                             -f                    -s n                        -d 'If updating by rebase, equivalent to --no-interactive-rebase. If updating by merge, equivalent to --no-edit-merge'
+complete -c git-machete -n "__fish_seen_subcommand_from slide-out; and not __fish_seen_subcommand_from -n --no-edit-merge --no-interactive-rebase; and __fish_seen_subcommand_from --merge -M" -f -l no-edit-merge                               -d 'If updating by merge, skip opening the editor for merge commit message while doing git merge (i.e. pass --no-edit flag to underlying git merge). Not allowed if updating by rebase'
+complete -c git-machete -n "__fish_seen_subcommand_from slide-out; and not __fish_seen_subcommand_from -n --no-edit-merge --no-interactive-rebase -M --merge"                                  -f -l no-interactive-rebase                       -d 'If updating by rebase, run git rebase in non-interactive mode (without -i/--interactive flag). Not allowed if updating by merge'
 
 # git machete squash
-complete -c git-machete -n "not __fish_seen_subcommand_from $__mcht_commands" -f -a squash -d 'Squash the unique history of the current branch into a single commit'
-complete -c git-machete -n "__fish_seen_subcommand_from squash" -x -l fork-point -s f -a '(__fish_git_local_branches)' -d 'Specifies the alternative fork point commit after which the squashed part of history is meant to start'
+complete -c git-machete -n "not __fish_seen_subcommand_from $__machete_commands" -f -a squash -d 'Squash the unique history of the current branch into a single commit'
+complete -c git-machete -n "__fish_seen_subcommand_from squash" -x -l fork-point -s f -a '(__fish_git_refs)' -d 'Specifies the alternative fork point commit after which the squashed part of history is meant to start'
 
 # git machete status
-complete -c git-machete -n "not __fish_seen_subcommand_from $__mcht_commands" -f -a status -d 'Display formatted tree of branch dependencies, including info on their sync with upstream branch and with remote'
-complete -c git-machete -n "__fish_seen_subcommand_from status s; and not __fish_seen_subcommand_from --color"                       -f -l color                    -a "auto always never" -d 'Colorize the output (default: auto)'
+complete -c git-machete -n "not __fish_seen_subcommand_from $__machete_commands" -f -a status -d 'Display formatted tree of branch dependencies, including info on their sync with upstream branch and with remote'
+complete -c git-machete -n "__fish_seen_subcommand_from status s; and not __fish_seen_subcommand_from --color"                       -x -l color                    -a "auto always never" -d 'Colorize the output (default: auto)'
 complete -c git-machete -n "__fish_seen_subcommand_from status s; and not __fish_seen_subcommand_from --list-commits -l"             -f -l list-commits             -s l                   -d 'Additionally list the commits introduced on each branch'
 complete -c git-machete -n "__fish_seen_subcommand_from status s; and not __fish_seen_subcommand_from --list-commits-with-hashes -L" -f -l list-commits-with-hashes -s L                   -d 'Additionally list the short hashes and messages of commits introduced on each branch'
 complete -c git-machete -n "__fish_seen_subcommand_from status s; and not __fish_seen_subcommand_from --no-detect-squash-merges"     -f -l no-detect-squash-merges                         -d 'Only consider "strict" (fast-forward or 2-parent) merges, rather than rebase/squash merges, when detecting if a branch is merged into its upstream (parent)'
 
 # git machete traverse
-complete -c git-machete -n "not __fish_seen_subcommand_from $__mcht_commands" -f -a traverse -d 'Walk through the tree of branch dependencies and rebase, merge, slide out, push and/or pull each branch one by one'
-complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --fetch -F"                -f -l fetch        -s F -d 'Fetch the remotes of all managed branches at the beginning of traversal (no git pull involved, only git fetch)'
-complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --list-commits -l"         -f -l list-commits -s l -d 'When printing the status, additionally list the messages of commits introduced on each branch'
-complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --merge -M"                -f -l merge        -s M -d 'Update by merge rather than by rebase'
-complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --yes -y"                  -f                 -s n -d 'If updating by rebase, equivalent to --no-interactive-rebase. If updating by merge, equivalent to --no-edit-merge'
-complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --no-detect-squash-merges" -f -l no-detect-squash-merges -d 'Only consider "strict" (fast-forward or 2-parent) merges, rather than rebase/squash merges, when detecting if a branch is merged into its upstream (parent)'
-complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --no-edit-merge; and __fish_seen_subcommand_from --merge -M"             -f -l no-edit-merge         -d 'If updating by merge, skip opening the editor for merge commit message while doing git merge (i.e. pass --no-edit flag to underlying git merge). Not allowed if updating by rebase'
-complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --no-interactive-rebase; and not __fish_seen_subcommand_from --merge -M" -f -l no-interactive-rebase -d 'If updating by rebase, run git rebase in non-interactive mode (without -i/--interactive flag). Not allowed if updating by merge'
-complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --no-push"           -f -l no-push           -d 'Do not push any (neither tracked nor untracked) branches to remote, re-enable via --push'
-complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --no-push-untracked" -f -l no-push-untracked -d 'Do not push untracked branches to remote, re-enable via --push-untracked'
-complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --push"              -f -l push              -d 'Push all (both tracked and untracked) branches to remote - default behavior'
-complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --push-untracked"    -f -l push-untracked    -d 'Push untracked branches to remote - default behavior'
-complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --return-to"         -x -l return-to  -a 'stay here nearest-remaining' -d 'Specifies the branch to return after traversal is successfully completed; WHERE can be here (the current branch at the moment when traversal starts), nearest-remaining (nearest remaining branch in case the here branch has been slid out by the traversal) or stay (the default - just stay wherever the traversal stops). Note: when user quits by q/yq or when traversal is stopped because one of git actions fails, the behavior is always stay'
-complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --start-from"        -x -l start-from -a 'here root first-root'        -d 'Specifies the branch to start the traversal from; WHERE can be here (the default - current branch, must be managed by git-machete), root (root branch of the current branch, as in git machete show root) or first-root (first listed managed branch)'
-complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --whole -w"          -f -l whole      -s w                             -d 'Equivalent to -n --start-from=first-root --return-to=nearest-remaining; useful for quickly traversing & syncing all branches (rather than doing more fine-grained operations on the local section of the branch tree)'
-complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from -W"                  -f               -s W                             -d 'Equivalent to --fetch --whole; useful for even more automated traversal of all branches'
-complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --yes -y"            -f -l yes        -s y                             -d 'Don\'t ask for any interactive input, including confirmation of rebase/push/pull. Implies -n'
+complete -c git-machete -n "not __fish_seen_subcommand_from $__machete_commands" -f -a traverse -d 'Walk through the tree of branch dependencies and rebase, merge, slide out, push and/or pull each branch one by one'
+complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --fetch -F"                -f -l fetch        -s F                             -d 'Fetch the remotes of all managed branches at the beginning of traversal (no git pull involved, only git fetch)'
+complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --list-commits -l"         -f -l list-commits -s l                             -d 'When printing the status, additionally list the messages of commits introduced on each branch'
+complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --merge -M"                -f -l merge        -s M                             -d 'Update by merge rather than by rebase'
+complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --yes -y"                  -f                 -s n                             -d 'If updating by rebase, equivalent to --no-interactive-rebase. If updating by merge, equivalent to --no-edit-merge'
+complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --no-detect-squash-merges" -f -l no-detect-squash-merges                       -d 'Only consider "strict" (fast-forward or 2-parent) merges, rather than rebase/squash merges, when detecting if a branch is merged into its upstream (parent)'
+complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --no-edit-merge"           -f -l no-edit-merge                                 -d 'If updating by merge, skip opening the editor for merge commit message while doing git merge (i.e. pass --no-edit flag to underlying git merge). Not allowed if updating by rebase'
+complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --no-interactive-rebase"   -f -l no-interactive-rebase                         -d 'If updating by rebase, run git rebase in non-interactive mode (without -i/--interactive flag). Not allowed if updating by merge'
+complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --no-push"                 -f -l no-push                                       -d 'Do not push any (neither tracked nor untracked) branches to remote, re-enable via --push'
+complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --no-push-untracked"       -f -l no-push-untracked                             -d 'Do not push untracked branches to remote, re-enable via --push-untracked'
+complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --push"                    -f -l push                                          -d 'Push all (both tracked and untracked) branches to remote - default behavior'
+complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --push-untracked"          -f -l push-untracked                                -d 'Push untracked branches to remote - default behavior'
+complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --return-to"               -x -l return-to    -a 'stay here nearest-remaining' -d 'Specifies the branch to return after traversal is successfully completed'
+complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --start-from"              -x -l start-from   -a 'here root first-root'        -d 'Specifies the branch to start the traversal from'
+complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --whole -w"                -f -l whole        -s w                             -d 'Equivalent to -n --start-from=first-root --return-to=nearest-remaining'
+complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from -W"                        -f                 -s W                             -d 'Equivalent to --fetch --whole; useful for even more automated traversal of all branches'
+complete -c git-machete -n "__fish_seen_subcommand_from traverse t; and not __fish_seen_subcommand_from --yes -y"                  -f -l yes          -s y                             -d 'Do not ask for any interactive input, including confirmation of rebase/push/pull. Implies -n'
 
 # git machete update
-complete -c git-machete -n "not __fish_seen_subcommand_from $__mcht_commands" -f -a update -d 'Sync the current branch with its upstream (parent) branch via rebase or merge'
-complete -c git-machete -n "__fish_seen_subcommand_from update; and not __fish_seen_subcommand_from --merge -M"                                            -x -l fork-point -s f -a '(__fish_git_local_branches)' -d 'If updating by rebase, specifies the alternative fork point commit after which the rebased part of history is meant to start. Not allowed if updating by merge'
-complete -c git-machete -n "__fish_seen_subcommand_from update; and not __fish_seen_subcommand_from -n --no-edit-merge --no-interactive-rebase"            -f               -s n                                  -d 'If updating by rebase, equivalent to --no-interactive-rebase. If updating by merge, equivalent to --no-edit-merge'
-complete -c git-machete -n "__fish_seen_subcommand_from update; and not __fish_seen_subcommand_from -n --no-edit-merge --no-interactive-rebase"            -f -l no-edit-merge                                    -d 'If updating by merge, skip opening the editor for merge commit message while doing git merge (i.e. pass --no-edit flag to underlying git merge)'
-complete -c git-machete -n "__fish_seen_subcommand_from update; and not __fish_seen_subcommand_from -n --no-edit-merge --no-interactive-rebase -M --merge" -f -l no-interactive-rebase                            -d 'If updating by rebase, run git rebase in non-interactive mode (without -i/--interactive flag). Not allowed if updating by merge'
-complete -c git-machete -n "__fish_seen_subcommand_from update; and not __fish_seen_subcommand_from --merge -M"                                            -f -l merge      -s M                                  -d 'Update by merge rather than by rebase'
+complete -c git-machete -n "not __fish_seen_subcommand_from $__machete_commands" -f -a update -d 'Sync the current branch with its upstream (parent) branch via rebase or merge'
+complete -c git-machete -n "__fish_seen_subcommand_from update; and not __fish_seen_subcommand_from --merge -M"                                            -x -l fork-point -s f -a '(__fish_git_refs)' -d 'If updating by rebase, specifies the alternative fork point commit after which the rebased part of history is meant to start. Not allowed if updating by merge'
+complete -c git-machete -n "__fish_seen_subcommand_from update; and not __fish_seen_subcommand_from -n --no-edit-merge --no-interactive-rebase"            -f               -s n                        -d 'If updating by rebase, equivalent to --no-interactive-rebase. If updating by merge, equivalent to --no-edit-merge'
+complete -c git-machete -n "__fish_seen_subcommand_from update; and not __fish_seen_subcommand_from -n --no-edit-merge --no-interactive-rebase"            -f -l no-edit-merge                          -d 'If updating by merge, skip opening the editor for merge commit message while doing git merge (i.e. pass --no-edit flag to underlying git merge)'
+complete -c git-machete -n "__fish_seen_subcommand_from update; and not __fish_seen_subcommand_from -n --no-edit-merge --no-interactive-rebase -M --merge" -f -l no-interactive-rebase                  -d 'If updating by rebase, run git rebase in non-interactive mode (without -i/--interactive flag). Not allowed if updating by merge'
+complete -c git-machete -n "__fish_seen_subcommand_from update; and not __fish_seen_subcommand_from --merge -M"                                            -f -l merge      -s M                        -d 'Update by merge rather than by rebase'
 
 # git machete version
-complete -c git-machete -n "not __fish_seen_subcommand_from $__mcht_commands" -f -a version -d 'Display the version and exit'
+complete -c git-machete -n "not __fish_seen_subcommand_from $__machete_commands" -f -a version -d 'Display the version and exit'
diff --git a/docs/man/git-machete.1 b/docs/man/git-machete.1
index ceb29fe..5ef0c67 100644
--- a/docs/man/git-machete.1
+++ b/docs/man/git-machete.1
@@ -27,9 +27,9 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
 .in \\n[rst2man-indent\\n[rst2man-indent-level]]u
 ..
-.TH "GIT-MACHETE" "1" "Aug 09, 2023" "" "git-machete"
+.TH "GIT-MACHETE" "1" "Aug 11, 2023" "" "git-machete"
 .SH NAME
-git-machete \- git-machete 3.17.9
+git-machete \- git-machete 3.18.0
 .sp
 git machete is a robust tool that \fBsimplifies your git workflows\fP\&.
 .sp
@@ -66,6 +66,8 @@ git machete commands and help topics:
 .IP \(bu 2
 \fI\%clean\fP            \-\- Delete untracked and unmanaged branches and also optionally check out user\(aqs open GitHub PRs
 .IP \(bu 2
+\fI\%completion\fP       \-\- Print out completion script for bash/fish/zsh
+.IP \(bu 2
 \fI\%config\fP           \-\- Display docs for the git machete configuration keys and environment variables
 .IP \(bu 2
 \fI\%delete\-unmanaged\fP \-\- Delete local branches that are not present in the branch layout file
@@ -623,6 +625,69 @@ Don\(aqt ask for confirmation when deleting branches from git.
 .B \fBGITHUB_TOKEN\fP
 GitHub API token.
 .UNINDENT
+.SH COMPLETION
+.sp
+\fBUsage:\fP
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+git machete completion <shell>
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+where \fB<shell>\fP is one of: \fBbash\fP, \fBfish\fP, \fBzsh\fP\&.
+.sp
+Prints out completion scripts.
+.sp
+\fBSupported shells:\fP
+.sp
+\fBbash\fP
+.sp
+Put the following into \fB~/.bashrc\fP or \fB~/.bash_profile\fP:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+eval \(dq$(git machete completion bash)\(dq  # or, if it doesn\(aqt work:
+source <(git machete completion bash)
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+\fBfish\fP
+.sp
+Put the following into \fB~/.config/fish/config.fish\fP:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+git machete completion fish | source
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+\fBzsh\fP
+.sp
+Put the following into \fB~/.zshrc\fP:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+eval \(dq$(git machete completion zsh)\(dq  # or, if it doesn\(aqt work:
+source <(git machete completion zsh)
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
 .SH DELETE-UNMANAGED
 .sp
 \fBUsage:\fP
diff --git a/docs/source/cli/completion.rst b/docs/source/cli/completion.rst
new file mode 100644
index 0000000..8cef4a2
--- /dev/null
+++ b/docs/source/cli/completion.rst
@@ -0,0 +1,41 @@
+.. _completion:
+
+completion
+==========
+**Usage:**
+
+.. code-block:: shell
+
+    git machete completion <shell>
+
+where ``<shell>`` is one of: ``bash``, ``fish``, ``zsh``.
+
+Prints out completion scripts.
+
+**Supported shells:**
+
+**bash**
+
+Put the following into ``~/.bashrc`` or ``~/.bash_profile``:
+
+.. code-block:: shell
+
+    eval "$(git machete completion bash)"  # or, if it doesn't work:
+    source <(git machete completion bash)
+
+**fish**
+
+Put the following into ``~/.config/fish/config.fish``:
+
+.. code-block:: shell
+
+    git machete completion fish | source
+
+**zsh**
+
+Put the following into ``~/.zshrc``:
+
+.. code-block:: shell
+
+    eval "$(git machete completion zsh)"  # or, if it doesn't work:
+    source <(git machete completion zsh)
diff --git a/docs/source/index.rst b/docs/source/index.rst
index 8a1a227..af03fa6 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -27,14 +27,15 @@ git machete commands and help topics:
 
 .. include:: learning_materials.rst
 
-Commands
-------------------
+Commands & help topics
+----------------------
 
 .. include:: cli/add.rst
 .. include:: cli/advance.rst
 .. include:: cli/anno.rst
 .. include:: cli/config.rst
 .. include:: cli/clean.rst
+.. include:: cli/completion.rst
 .. include:: cli/delete-unmanaged.rst
 .. include:: cli/diff.rst
 .. include:: cli/discover.rst
diff --git a/docs/source/short_docs.rst b/docs/source/short_docs.rst
index 480b479..3367ef7 100644
--- a/docs/source/short_docs.rst
+++ b/docs/source/short_docs.rst
@@ -4,6 +4,7 @@
 * :ref:`advance`          -- Fast-forward merge one of children to the current branch, push it and then slide out the child
 * :ref:`anno`             -- Manage custom annotations
 * :ref:`clean`            -- Delete untracked and unmanaged branches and also optionally check out user's open GitHub PRs
+* :ref:`completion`       -- Print out completion script for bash/fish/zsh
 * :ref:`config`           -- Display docs for the git machete configuration keys and environment variables
 * :ref:`delete-unmanaged` -- Delete local branches that are not present in the branch layout file
 * :ref:`diff`             -- Diff current working directory or a given branch against its computed fork point
diff --git a/git_machete/__init__.py b/git_machete/__init__.py
index 057450a..c754d32 100644
--- a/git_machete/__init__.py
+++ b/git_machete/__init__.py
@@ -1,1 +1,1 @@
-__version__ = '3.17.9'
+__version__ = '3.18.0'
diff --git a/git_machete/cli.py b/git_machete/cli.py
index 364592f..4e7422b 100644
--- a/git_machete/cli.py
+++ b/git_machete/cli.py
@@ -2,6 +2,7 @@
 
 import argparse
 import os
+import pkgutil
 import re
 import sys
 import textwrap
@@ -16,7 +17,8 @@ from .exceptions import (ExitCode, InteractionStopped, MacheteException,
 from .generated_docs import long_docs, short_docs
 from .git_operations import (AnyBranchName, AnyRevision, GitContext,
                              LocalBranchShortName, RemoteBranchShortName)
-from .utils import bold, excluding, fmt, underline, warn
+from .utils import (GITHUB_NEW_ISSUE_MESSAGE, bold, excluding, fmt, underline,
+                    warn)
 
 T = TypeVar('T')
 
@@ -33,11 +35,11 @@ command_by_alias: Dict[str, str] = {v: k for k, v in alias_by_command.items()}
 
 command_groups: List[Tuple[str, List[str]]] = [
     ("General topics",
-     ["config", "file", "format", "help", "hooks", "version"]),
+     ["completion", "config", "file", "format", "help", "hooks", "version"]),
     ("Build, display and modify the tree of branch dependencies",
      ["add", "anno", "discover", "edit", "status"]),
     ("List, check out and delete branches",
-     ["clean", "delete-unmanaged", "go", "is-managed", "list", "show"]),
+     ["delete-unmanaged", "go", "is-managed", "list", "show"]),
     ("Determine changes specific to the given branch",
      ["diff", "fork-point", "log"]),
     ("Update git history in accordance with the tree of branch dependencies",
@@ -119,19 +121,19 @@ class MacheteHelpAction(argparse.Action):
 
 def create_cli_parser() -> argparse.ArgumentParser:
     common_args_parser = argparse.ArgumentParser(
-        prog='git machete', argument_default=argparse.SUPPRESS, add_help=False)
+        prog='git machete',
+        argument_default=argparse.SUPPRESS,
+        add_help=False)
     common_args_parser.add_argument('--debug', action='store_true')
     common_args_parser.add_argument('-h', '--help', action=MacheteHelpAction)
-    common_args_parser.add_argument(
-        '--version', action='version', version=f'git-machete version {__version__}')
+    common_args_parser.add_argument('--version', action='version', version=f'git-machete version {__version__}')
     common_args_parser.add_argument('-v', '--verbose', action='store_true')
 
     cli_parser = argparse.ArgumentParser(
         prog='git machete',
         argument_default=argparse.SUPPRESS,
         add_help=False,
-        parents=[common_args_parser]
-    )
+        parents=[common_args_parser])
 
     subparsers = cli_parser.add_subparsers(dest='command')
 
@@ -147,28 +149,44 @@ def create_cli_parser() -> argparse.ArgumentParser:
     add_parser.add_argument('-y', '--yes', action='store_true')
 
     advance_parser = subparsers.add_parser(
-        'advance', usage=argparse.SUPPRESS, add_help=False, parents=[common_args_parser])
+        'advance',
+        usage=argparse.SUPPRESS,
+        add_help=False,
+        parents=[common_args_parser])
     advance_parser.add_argument('-y', '--yes', action='store_true', default=argparse.SUPPRESS)
 
     anno_parser = subparsers.add_parser(
-        'anno', usage=argparse.SUPPRESS, add_help=False, parents=[common_args_parser])
+        'anno',
+        usage=argparse.SUPPRESS,
+        add_help=False,
+        parents=[common_args_parser])
     # possible values of 'annotation_text' include: [], [''], ['some_val'], ['text_1', 'text_2']
     anno_parser.add_argument('annotation_text', nargs='*')
     anno_parser.add_argument('-b', '--branch', default=argparse.SUPPRESS)
-    anno_parser.add_argument(
-        '-H', '--sync-github-prs', action='store_true', default=argparse.SUPPRESS)
+    anno_parser.add_argument('-H', '--sync-github-prs', action='store_true', default=argparse.SUPPRESS)
 
-    clean_parser = subparsers.add_parser('clean', usage=argparse.SUPPRESS, add_help=False, parents=[common_args_parser])
+    clean_parser = subparsers.add_parser(
+        'clean',
+        usage=argparse.SUPPRESS,
+        add_help=False,
+        parents=[common_args_parser])
     clean_parser.add_argument('-H', '--checkout-my-github-prs', action='store_true', default=argparse.SUPPRESS)
     clean_parser.add_argument('-y', '--yes', action='store_true', default=argparse.SUPPRESS)
 
+    completion_parser = subparsers.add_parser(
+        'completion',
+        argument_default=argparse.SUPPRESS,
+        usage=argparse.SUPPRESS,
+        add_help=False,
+        parents=[common_args_parser])
+    completion_parser.add_argument('shell', choices=['bash', 'fish', 'zsh'])
+
     delete_unmanaged_parser = subparsers.add_parser(
         'delete-unmanaged',
         usage=argparse.SUPPRESS,
         add_help=False,
         parents=[common_args_parser])
-    delete_unmanaged_parser.add_argument(
-        '-y', '--yes', action='store_true', default=argparse.SUPPRESS)
+    delete_unmanaged_parser.add_argument('-y', '--yes', action='store_true', default=argparse.SUPPRESS)
 
     diff_full_parser = subparsers.add_parser(
         'diff',
@@ -177,8 +195,7 @@ def create_cli_parser() -> argparse.ArgumentParser:
         add_help=False,
         parents=[common_args_parser])
     diff_full_parser.add_argument('branch', nargs='?')
-    diff_full_parser.add_argument(
-        '-s', '--stat', action='store_true', default=argparse.SUPPRESS)
+    diff_full_parser.add_argument('-s', '--stat', action='store_true', default=argparse.SUPPRESS)
 
     discover_parser = subparsers.add_parser(
         'discover',
@@ -230,8 +247,8 @@ def create_cli_parser() -> argparse.ArgumentParser:
     github_parser.add_argument('--all', action='store_true')
     github_parser.add_argument('--by')
     github_parser.add_argument('--draft', action='store_true')
-    github_parser.add_argument('--mine', action='store_true')
     github_parser.add_argument('--ignore-if-missing', action='store_true')
+    github_parser.add_argument('--mine', action='store_true')
 
     go_parser = subparsers.add_parser(
         'go',
@@ -246,7 +263,10 @@ def create_cli_parser() -> argparse.ArgumentParser:
     )
 
     help_parser = subparsers.add_parser(
-        'help', add_help=False, usage=argparse.SUPPRESS, parents=[common_args_parser])
+        'help',
+        add_help=False,
+        usage=argparse.SUPPRESS,
+        parents=[common_args_parser])
     help_parser.add_argument('topic_or_cmd', nargs='?', choices=commands_and_aliases)
 
     is_managed_parser = subparsers.add_parser(
@@ -257,7 +277,10 @@ def create_cli_parser() -> argparse.ArgumentParser:
     is_managed_parser.add_argument('branch', nargs='?')
 
     list_parser = subparsers.add_parser(
-        'list', usage=argparse.SUPPRESS, add_help=False, parents=[common_args_parser])
+        'list',
+        usage=argparse.SUPPRESS,
+        add_help=False,
+        parents=[common_args_parser])
     list_parser.add_argument(
         'category',
         choices=[
@@ -275,11 +298,17 @@ def create_cli_parser() -> argparse.ArgumentParser:
     log_parser.add_argument('branch', nargs='?', default=argparse.SUPPRESS)
 
     reapply_parser = subparsers.add_parser(
-        'reapply', usage=argparse.SUPPRESS, add_help=False, parents=[common_args_parser])
+        'reapply',
+        usage=argparse.SUPPRESS,
+        add_help=False,
+        parents=[common_args_parser])
     reapply_parser.add_argument('-f', '--fork-point', default=argparse.SUPPRESS)
 
     show_parser = subparsers.add_parser(
-        'show', usage=argparse.SUPPRESS, add_help=False, parents=[common_args_parser])
+        'show',
+        usage=argparse.SUPPRESS,
+        add_help=False,
+        parents=[common_args_parser])
     show_parser.add_argument(
         'direction',
         choices=['c', 'current', 'd', 'down', 'f', 'first', 'l', 'last',
@@ -302,7 +331,10 @@ def create_cli_parser() -> argparse.ArgumentParser:
     slide_out_parser.add_argument('--no-interactive-rebase', action='store_true')
 
     squash_parser = subparsers.add_parser(
-        'squash', usage=argparse.SUPPRESS, add_help=False, parents=[common_args_parser])
+        'squash',
+        usage=argparse.SUPPRESS,
+        add_help=False,
+        parents=[common_args_parser])
     squash_parser.add_argument('-f', '--fork-point', default=argparse.SUPPRESS)
 
     status_parser = subparsers.add_parser(
@@ -354,7 +386,10 @@ def create_cli_parser() -> argparse.ArgumentParser:
     update_parser.add_argument('--no-interactive-rebase', action='store_true')
 
     subparsers.add_parser(
-        'version', usage=argparse.SUPPRESS, add_help=False, parents=[common_args_parser])
+        'version',
+        usage=argparse.SUPPRESS,
+        add_help=False,
+        parents=[common_args_parser])
 
     return cli_parser
 
@@ -366,8 +401,8 @@ def update_cli_options_using_parsed_args(
     # Since argparse is not typed, everything that comes from argparse.Namespace will be taken as Any :(
     # Even if we add type=LocalBranchShortName into argument parser for branch,
     # python debugger will see branch as LocalBranchShortName but mypy always will see it as Any,
-    # until you specifically tell mypy what is the exact type by casting (right now it's done this way below, but casting does not solve all
-    # of the problems).
+    # until you specifically tell mypy what is the exact type by casting
+    # (right now it's done this way below, but casting does not solve all of the problems).
     #
     # The reasonable solution here would be to use Typed Argument Parser
     # which is a wrapper over argparse with modernised solution for typing.
@@ -510,7 +545,24 @@ def launch(orig_args: List[str]) -> None:
 
         cmd = parsed_cli.command
 
-        if cmd == "help":
+        if cmd == "completion":
+            completion_shell = parsed_cli.shell
+
+            def print_completion_resource(name: str) -> None:
+                data = pkgutil.get_data("completion", name)
+                if not data:
+                    raise MacheteException(f"Completion file `{name}` not found. " + GITHUB_NEW_ISSUE_MESSAGE)  # pragma: no cover
+                print(data.decode())
+
+            # Deliberately using if/else instead of a dict - to measure coverage more accurately.
+            if completion_shell == "bash":
+                print_completion_resource("git-machete.completion.bash")
+            elif completion_shell == "fish":
+                print_completion_resource("git-machete.fish")
+            elif completion_shell == "zsh":  # pragma: no branch; an unknown shell is handled by argparse
+                print_completion_resource("git-machete.completion.zsh")
+            return
+        elif cmd == "help":
             print(get_help_description(display_help_topics=True, command=parsed_cli.topic_or_cmd))
             return
         elif cmd == "version":
@@ -688,7 +740,7 @@ def launch(orig_args: List[str]) -> None:
                 res = excluding(git.get_local_branches(), machete_client.managed_branches) + list(
                     map(strip_remote_name, qualifying_remote_branches))
             elif category == "childless":
-                res = machete_client.get_childless_branches()
+                res = machete_client.get_childless_managed_branches()
             elif category == "managed":
                 res = machete_client.managed_branches
             elif category == "slidable":
diff --git a/git_machete/client.py b/git_machete/client.py
index 9d71151..794d2f5 100644
--- a/git_machete/client.py
+++ b/git_machete/client.py
@@ -68,7 +68,7 @@ class MacheteClient:
     def up_branch_for(self, branch: LocalBranchShortName) -> Optional[LocalBranchShortName]:
         return self.__up_branch.get(branch)
 
-    def get_childless_branches(self) -> List[LocalBranchShortName]:
+    def get_childless_managed_branches(self) -> List[LocalBranchShortName]:
         parent_branches = [parent_branch for parent_branch, child_branches in self.__down_branches.items() if child_branches]
         return excluding(self.managed_branches, parent_branches)
 
diff --git a/git_machete/generated_docs.py b/git_machete/generated_docs.py
index a023073..efb16e7 100644
--- a/git_machete/generated_docs.py
+++ b/git_machete/generated_docs.py
@@ -9,6 +9,7 @@ short_docs: Dict[str, str] = {
     "advance": "Fast-forward merge one of children to the current branch, push it and then slide out the child",
     "anno": "Manage custom annotations",
     "clean": "Delete untracked and unmanaged branches and also optionally check out user's open GitHub PRs",
+    "completion": "Print out completion script for bash/fish/zsh",
     "config": "Display docs for the git machete configuration keys and environment variables",
     "delete-unmanaged": "Delete local branches that are not present in the branch layout file",
     "diff": "Diff current working directory or a given branch against its computed fork point",
@@ -243,6 +244,33 @@ long_docs: Dict[str, str] = {
            `GITHUB_TOKEN`
               GitHub API token.
 
+   """,
+    "completion": """
+        <b>Usage:</b><b>
+           git machete completion <shell></b>
+
+        where `<shell>` is one of: `bash`, `fish`, `zsh`.
+
+        Prints out completion scripts.
+
+        <b>Supported shells:</b>
+
+        <b>bash</b>
+
+        Put the following into `~/.bashrc` or `~/.bash_profile`:<b>
+           eval "$(git machete completion bash)"  # or, if it doesn't work:
+           source <(git machete completion bash)</b>
+
+        <b>fish</b>
+
+        Put the following into `~/.config/fish/config.fish`:<b>
+           git machete completion fish | source</b>
+
+        <b>zsh</b>
+
+        Put the following into `~/.zshrc`:<b>
+           eval "$(git machete completion zsh)"  # or, if it doesn't work:
+           source <(git machete completion zsh)</b>
    """,
     "config": """
         Documentation about available `git machete` git config keys and environment variables that change the command's default behavior.
diff --git a/requirements/coverage.py36-txt b/requirements/coverage.py36-txt
deleted file mode 100644
index 27468d1..0000000
--- a/requirements/coverage.py36-txt
+++ /dev/null
@@ -1,5 +0,0 @@
-# Deliberately using a non-standard file extension so that the GitHub dependabot doesn't try to update this file.
-# Version marks like `python_version=="3.6"` aren't supported by GitHub dependabot.
-coverage==6.2
-pytest==7.0.1
-pytest-mock==3.6.1
diff --git a/requirements/coverage.txt b/requirements/coverage.txt
index 50692e1..5e7d98e 100644
--- a/requirements/coverage.txt
+++ b/requirements/coverage.txt
@@ -1,3 +1,8 @@
-coverage==7.2.7
-pytest==7.4.0
-pytest-mock==3.11.1
+coverage==6.2; python_version <= '3.6'
+coverage==7.2.7; python_version >= '3.7'
+
+pytest==7.0.1; python_version <= '3.6'
+pytest==7.4.0; python_version >= '3.7'
+
+pytest-mock==3.6.1; python_version <= '3.6'
+pytest-mock==3.11.1; python_version >= '3.7'
diff --git a/requirements/mypy.py36-txt b/requirements/mypy.py36-txt
deleted file mode 100644
index abbd571..0000000
--- a/requirements/mypy.py36-txt
+++ /dev/null
@@ -1,5 +0,0 @@
-# Deliberately using a non-standard file extension so that the GitHub dependabot doesn't try to update this file.
-# Version marks like `python_version=="3.6"` aren't supported by GitHub dependabot.
-mypy==0.971
-pytest==7.0.1
-pytest-mock==3.6.1
diff --git a/requirements/mypy.txt b/requirements/mypy.txt
index 9a9c563..7ad91e5 100644
--- a/requirements/mypy.txt
+++ b/requirements/mypy.txt
@@ -1,3 +1,9 @@
-mypy==1.4.1
-pytest==7.4.0
-pytest-mock==3.11.1
+mypy==0.971; python_version <= '3.6'
+mypy==1.4.1; python_version >= '3.7' and python_version < '3.8'
+mypy==1.5.0; python_version >= '3.8'
+
+pytest==7.0.1; python_version <= '3.6'
+pytest==7.4.0; python_version >= '3.7'
+
+pytest-mock==3.6.1; python_version <= '3.6'
+pytest-mock==3.11.1; python_version >= '3.7'
diff --git a/requirements/vulture-check.txt b/requirements/vulture-check.txt
index a8398d1..7a6f752 100644
--- a/requirements/vulture-check.txt
+++ b/requirements/vulture-check.txt
@@ -1,1 +1,1 @@
-vulture==2.7
+vulture==2.8
diff --git a/tox.ini b/tox.ini
index 83340a3..9f64b9d 100644
--- a/tox.ini
+++ b/tox.ini
@@ -3,7 +3,7 @@
 # Let's still include pep8-check despite a preceding autopep8 run to filter out certain errors
 # (like W293 blank line in multiline strings contains whitespace)
 # that don't seem to ever get fixed by autopep8.
-envlist = pep8,isort,pep8-check,mypy-py311,vulture-check,sphinx-man,py-docs
+envlist = pep8,isort,pep8-check,mypy,vulture-check,sphinx-man,py-docs
 minversion = 2.3.2
 skipsdist = True
 
@@ -19,11 +19,19 @@ deps =
 allowlist_externals = mkdir
 commands =
   mkdir -p test-results/
-  pytest --numprocesses=auto --junitxml=test-results/testenv-{envname}.xml {posargs}
+  pytest --numprocesses=auto --junitxml=test-results/testenv-{envname}.xml -m "not completion_e2e" {posargs}
 
-[testenv:py36]
-deps =
-  -r{[requirements]dir}/testenv.py36-txt
+[testenv:test-completions]
+description = "Test shell completions"
+setenv =
+  PYTHONWARNINGS=ignore:easy_install command is deprecated,ignore:setup.py install is deprecated
+commands =
+  python setup.py develop  # for some reason, `usedevelop = True` seems to be ignored
+  mkdir -p test-results/
+  pytest --numprocesses=auto --junitxml=test-results/testenv-{envname}.xml -m completion_e2e {posargs}
+
+[pytest]
+markers = completion_e2e
 
 [testenv:pep8]
 description = "Apply PEP8 formatting"
@@ -72,10 +80,10 @@ commands = vulture git_machete/ tests/
 [testenv:venv]
 commands = {posargs}
 
-[testenv:coverage-py36]
+[testenv:coverage]
 description = "Check the test coverage of the code"
 deps =
-  -r{[requirements]dir}/coverage.py36-txt
+  -r{[requirements]dir}/coverage.txt
 passenv = PYTHON_VERSION
 allowlist_externals = cp
 commands =
@@ -83,22 +91,13 @@ commands =
   # used as the source of truth for the subsequent reports.
   # Note that coverage (as of v7.2.3) doesn't support parallel running (`--numprocesses=...`) via python-xdist.
   coverage run --append --branch --omit='tests/*' \
-    -m pytest --junitxml=test-results/testenv-{envname}.xml {posargs}
+    -m pytest --junitxml=test-results/testenv-{envname}.xml -m "not completion_e2e" {posargs}
   # Prints a report to console
   coverage report
   # Saves a report to htmlcov/
   coverage html --show-contexts
   cp .coverage .coverage.{env:PYTHON_VERSION:bin}
 
-[testenv:coverage-{py37,py38,py39,py310,py311}]
-description = "Check the test coverage of the code"
-deps =
-  -r{[requirements]dir}/coverage.txt
-passenv = PYTHON_VERSION
-allowlist_externals = cp
-commands =
-  {[testenv:coverage-py36]commands}
-
 [testenv:coverage-erase]
 description = "Erase test coverage data"
 deps =
@@ -172,14 +171,8 @@ deps =
 commands =
   bash docs/enforce-py-docs-up-to-date.sh
 
-[testenv:mypy-py36]
-deps =
-  -r{[requirements]dir}/mypy.py36-txt
-commands =
-  mypy --config-file mypy.ini git_machete tests
-
-[testenv:mypy-py{37,38,39,310,311}]
+[testenv:mypy]
 deps =
   -r{[requirements]dir}/mypy.txt
 commands =
-  {[testenv:mypy-py36]commands}
+  mypy --config-file mypy.ini git_machete tests


Test patch:
diff --git a/requirements/testenv.py36-txt b/requirements/testenv.py36-txt
deleted file mode 100644
index 4306cca..0000000
--- a/requirements/testenv.py36-txt
+++ /dev/null
@@ -1,5 +0,0 @@
-# Deliberately using a non-standard file extension so that the GitHub dependabot doesn't try to update this file.
-# Version marks like `python_version=="3.6"` aren't supported by GitHub dependabot.
-pytest==7.0.1
-pytest-mock==3.6.1
-pytest-xdist==2.4.0
diff --git a/requirements/testenv.txt b/requirements/testenv.txt
index 06a05b1..6a605ff 100644
--- a/requirements/testenv.txt
+++ b/requirements/testenv.txt
@@ -1,3 +1,8 @@
-pytest==7.4.0
-pytest-mock==3.11.1
-pytest-xdist==3.3.1
+pytest==7.0.1; python_version <= '3.6'
+pytest==7.4.0; python_version >= '3.7'
+
+pytest-mock==3.6.1; python_version <= '3.6'
+pytest-mock==3.11.1; python_version >= '3.7'
+
+pytest-xdist==2.4.0; python_version <= '3.6'
+pytest-xdist==3.3.1; python_version >= '3.7'
diff --git a/tests/base_test.py b/tests/base_test.py
index 9d79b75..1be3636 100644
--- a/tests/base_test.py
+++ b/tests/base_test.py
@@ -47,8 +47,7 @@ class GitRepositorySandbox:
         self.file_counter = 0
 
     def popen(self, command: str) -> str:
-        with os.popen(command) as process:
-            return process.read().strip()
+        return subprocess.check_output(command, shell=True, timeout=5).decode("utf-8").strip()
 
     def execute(self, command: str) -> "GitRepositorySandbox":
         subprocess.check_call(command, shell=True)
diff --git a/tests/completion_e2e/__init__.py b/tests/completion_e2e/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/completion_e2e/complete-bash.sh b/tests/completion_e2e/complete-bash.sh
new file mode 100755
index 0000000..592f012
--- /dev/null
+++ b/tests/completion_e2e/complete-bash.sh
@@ -0,0 +1,38 @@
+#!/usr/bin/env bash
+
+set -e -o pipefail -u
+
+eval "$(git machete completion bash)"
+
+if ! declare -f __git_complete &>/dev/null; then
+  # __git_complete (defined in https://github.com/git/git/blob/master/contrib/completion/git-completion.bash#L3496-L3505)
+  # is not public and is loaded by bash_completion dynamically on demand.
+  # The solution is to source git completions (from one of these common locations).
+  if [ -e /usr/share/bash-completion/completions/git ]; then
+    source /usr/share/bash-completion/completions/git
+  elif [ -f /usr/local/share/bash-completion/completions/git ]; then
+    source /usr/local/share/bash-completion/completions/git
+  elif [ -e /etc/bash_completion.d/git ]; then
+    source /etc/bash_completion.d/git
+  elif [ -e "$(brew --prefix)/etc/bash_completion.d/git-completion.bash" ]; then
+    source "$(brew --prefix)/etc/bash_completion.d/git-completion.bash"
+  else
+    exit 1
+  fi
+fi
+
+input="$1"
+if [[ $input == *" " ]]; then
+  input="$input%"
+fi
+read -r -a COMP_WORDS <<< "$input"
+COMPREPLY=()
+COMP_CWORD=${#COMP_WORDS[@]}
+COMP_CWORD=$((COMP_CWORD-1))
+if [[ ${COMP_WORDS[$COMP_CWORD]} == "%" ]]; then
+  COMP_WORDS[$COMP_CWORD]=""
+fi
+
+_git_machete
+
+echo "${COMPREPLY[*]}" | sed 's/= / /g;  s/=$//'
diff --git a/tests/completion_e2e/complete-fish.fish b/tests/completion_e2e/complete-fish.fish
new file mode 100755
index 0000000..32ba989
--- /dev/null
+++ b/tests/completion_e2e/complete-fish.fish
@@ -0,0 +1,6 @@
+#!/usr/bin/env fish
+
+git machete completion fish | source
+
+set input "$argv[1]"
+complete -C "$input" | cut -f1 | sed -E 's/^--.+=(.+)$/\1/'
diff --git a/tests/completion_e2e/complete-zsh.zsh b/tests/completion_e2e/complete-zsh.zsh
new file mode 100755
index 0000000..712adc0
--- /dev/null
+++ b/tests/completion_e2e/complete-zsh.zsh
@@ -0,0 +1,65 @@
+#!/usr/bin/env zsh
+
+set -e -o pipefail -u
+
+# Adapted from https://unix.stackexchange.com/a/668827
+
+comptest () {
+  eval "$(git machete completion zsh)"
+
+  # Gather all matching completions in this array.
+  # -U discards duplicates.
+  typeset -aU completions=()
+
+  # Override the builtin compadd command.
+  compadd () {
+    # Gather all matching completions for this call in $reply.
+    # Note that this call overwrites the specified array.
+    # Therefore we cannot use $completions directly.
+    builtin compadd -O reply "$@"
+
+    completions+=("$reply[@]") # Collect them.
+    builtin compadd "$@"       # Run the actual command.
+  }
+
+  # Bind a custom widget to TAB.
+  bindkey "^I" complete-word
+  zle -C {,,}complete-word
+  complete-word () {
+    # Make the completion system believe we're on a normal
+    # command line, not in vared.
+    unset 'compstate[vared]'
+
+    _main_complete "$@"  # Generate completions.
+
+    # Print out our completions.
+    # Use of ^B and ^C as delimiters here is arbitrary.
+    # Just use something that won't normally be printed.
+    print -n $'\C-B'
+    print -nlr -- "$completions[@]"  # Print one per line.
+    print -n $'\C-C'
+    exit
+  }
+
+  vared -c tmp
+}
+
+autoload -Uz compinit && compinit
+# Load the pseudo terminal module.
+zmodload zsh/zpty
+
+input=$1
+
+# Create a new pty and run our function in it.
+zpty {,}comptest
+# Simulate a command being typed, ending with TAB to get completions.
+zpty -w comptest "$input"$'\t'
+# Read up to the first delimiter. Discard all of this.
+zpty -r comptest REPLY $'*\C-B'
+# Read up to the second delimiter.
+zpty -r comptest REPLY $'*\C-C'
+# Delete the pty.
+zpty -d comptest
+
+# Extract the results; trim off the ^C, just in case.
+echo "${REPLY%$'\C-C'}"
diff --git a/tests/completion_e2e/test_completion_e2e.py b/tests/completion_e2e/test_completion_e2e.py
new file mode 100644
index 0000000..070831f
--- /dev/null
+++ b/tests/completion_e2e/test_completion_e2e.py
@@ -0,0 +1,152 @@
+import pathlib
+from typing import Dict
+
+import pytest
+
+from tests.base_test import GitRepositorySandbox
+from tests.mockers import rewrite_branch_layout_file
+
+test_cases: Dict[str, str] = {
+    "git machete ":
+        "add advance anno clean completion delete-unmanaged diff discover edit file fork-point github go "
+        "help is-managed list log reapply show slide-out squash status traverse update version",
+    "git machete -":
+        "--debug -h --help -v --verbose --version",
+    "git machete a":
+        "add advance anno",
+    "git machete add ":
+        "feature",
+    "git machete add -o ":
+        "develop master",
+    "git machete add --onto ":
+        "develop master",
+    "git machete add --onto=":
+        "develop master",
+    "git machete advance -":
+        "--debug -h --help -v --verbose -y --yes",
+    "git machete anno -b ":
+        "develop master",
+    "git machete anno --branch ":
+        "develop master",
+    "git machete anno --branch=":
+        "develop master",
+    "git machete clean -":
+        "-H --checkout-my-github-prs --debug -h --help -v --verbose -y --yes",
+    "git machete completion ":
+        "bash fish zsh",
+    "git machete delete-unmanaged -":
+        "--debug -h --help -v --verbose -y --yes",
+    "git machete d ":
+        "develop feature master",
+    "git machete diff ":
+        "develop feature master",
+    "git machete discover -":
+        "-C --checked-out-since --debug -h --help -l --list-commits -r --roots -v --verbose -y --yes",
+    "git machete e":
+        "edit",
+    "git machete f":
+        "file fork-point",
+    "git machete fork-point ":
+        "develop feature master",
+    "git machete fork-point -":
+        "--debug -h --help --inferred --override-to --override-to-inferred --override-to-parent --unset-override -v --verbose",
+    "git machete fork-point --inferred ":
+        "develop feature master",
+    "git machete fork-point --override-to=":
+        "HEAD develop feature master",
+    "git machete fork-point --unset-override ":
+        "develop",
+    "git machete github ":
+        "anno-prs checkout-prs create-pr retarget-pr sync",
+    "git machete github checkout-prs -":
+        "--all --by --debug -h --help --mine -v --verbose",
+    "git machete github create-pr -":
+        "--debug --draft -h --help -v --verbose",
+    "git machete github retarget-pr --":
+        "--branch --debug --help --ignore-if-missing --verbose",
+    "git machete github retarget-pr -b ":
+        "develop master",
+    "git machete g ":
+        "down first last next prev root up",
+    "git machete go ":
+        "down first last next prev root up",
+    "git machete help ":
+        "add advance anno clean completion config delete-unmanaged diff discover edit file fork-point format github go "
+        "help hooks is-managed list log reapply show slide-out squash status traverse update version",
+    "git machete is-managed ":
+        "develop feature master",
+    "git machete list ":
+        "addable childless managed slidable slidable-after unmanaged with-overridden-fork-point",
+    "git machete l ":
+        "develop feature master",
+    "git machete log ":
+        "develop feature master",
+    "git machete reapply -":
+        "--debug -f --fork-point -h --help -v --verbose",
+    "git machete reapply --fork-point ":
+        "HEAD develop feature master",
+    "git machete s -":
+        "-L --color --debug -h --help -l --list-commits --list-commits-with-hashes --no-detect-squash-merges -v --verbose",
+    "git machete show ":
+        "current down first last next prev root up",
+    "git machete slide-out ":
+        "develop",
+    "git machete slide-out --down-fork-point=":
+        "HEAD develop feature master",
+    "git machete squash -":
+        "--debug -f --fork-point -h --help -v --verbose",
+    "git machete squash --fork-point=":
+        "HEAD develop feature master",
+    "git machete status -":
+        "-L --color --debug -h --help -l --list-commits --list-commits-with-hashes --no-detect-squash-merges -v --verbose",
+    "git machete status --color=":
+        "always auto never",
+    "git machete t -":
+        "-F -M -W --debug --fetch -h --help -l --list-commits --merge -n --no-detect-squash-merges --no-edit-merge --no-interactive-rebase "
+        "--no-push --no-push-untracked --push --push-untracked --return-to --start-from -v --verbose -w --whole -y --yes",
+    "git machete traverse -":
+        "-F -M -W --debug --fetch -h --help -l --list-commits --merge -n --no-detect-squash-merges --no-edit-merge --no-interactive-rebase "
+        "--no-push --no-push-untracked --push --push-untracked --return-to --start-from -v --verbose -w --whole -y --yes",
+    "git machete update -":
+        "-M --debug -f --fork-point -h --help --merge -n --no-edit-merge --no-interactive-rebase -v --verbose",
+    "git machete update -f ":
+        "HEAD develop feature master",
+    "git machete version ":
+        ""
+}
+
+
+@pytest.mark.completion_e2e
+class TestCompletionEndToEnd:
+
+    repo_sandbox = GitRepositorySandbox()
+
+    @classmethod
+    def setup_class(cls) -> None:
+        (
+            cls.repo_sandbox
+            # Create the remote and sandbox repos, chdir into sandbox repo
+            .new_repo(cls.repo_sandbox.remote_path, bare=True)
+            .new_repo(cls.repo_sandbox.local_path, bare=False)
+            .add_remote("origin", cls.repo_sandbox.remote_path)
+            .set_git_config_key("user.email", "tester@test.com")
+            .set_git_config_key("user.name", "Tester Test")
+            .new_branch("master")
+            .commit()
+            .new_branch("develop")
+            .commit()
+            .set_git_config_key("machete.overrideForkPoint.develop.to", cls.repo_sandbox.get_current_commit_hash())
+            .commit()
+            .new_branch("feature")
+            .commit()
+            .check_out("master")
+        )
+        rewrite_branch_layout_file("master\n\tdevelop")
+
+    @pytest.mark.parametrize("input,expected_result", test_cases.items(), ids=lambda x: x if x.startswith('git machete') else '')
+    @pytest.mark.parametrize("script_name", ["complete-bash.sh", "complete-fish.fish", "complete-zsh.zsh"])
+    def test_completion(self, input: str, expected_result: str, script_name: str) -> None:
+        script = pathlib.Path(__file__).parent.joinpath(script_name).absolute()
+        output_tokens = self.repo_sandbox.popen(f"'{script}' '{input}'").split()
+        result = " ".join(sorted(output_tokens, key=lambda s: ''.join([c for c in s if c.isalpha()])))
+        assert result == expected_result, f"for '{input}'"
diff --git a/tests/test_completion.py b/tests/test_completion.py
new file mode 100644
index 0000000..42ee427
--- /dev/null
+++ b/tests/test_completion.py
@@ -0,0 +1,20 @@
+
+from .base_test import BaseTest
+from .mockers import launch_command
+
+
+# These are mostly smoke-tests which also provide coverage.
+# The actual end-to-end tests (which run the corresponding shells) are located in completion_e2e/.
+class TestCompletion(BaseTest):
+
+    def test_bash_completion(self) -> None:
+        output = launch_command("completion", "bash")
+        assert '_git_machete()' in output
+
+    def test_fish_completion(self) -> None:
+        output = launch_command("completion", "fish")
+        assert 'complete -c git-machete' in output
+
+    def test_zsh_completion(self) -> None:
+        output = launch_command("completion", "zsh")
+        assert '_git-machete()' in output
diff --git a/tests/test_diff.py b/tests/test_diff.py
index 863f66e..d1dc022 100644
--- a/tests/test_diff.py
+++ b/tests/test_diff.py
@@ -7,10 +7,7 @@ from .mockers import assert_success, mock__run_cmd_and_forward_stdout
 class TestDiff(BaseTest):
 
     def test_diff(self, mocker: MockerFixture) -> None:
-        """
-        Verify behaviour of a 'git machete diff' command.
-        """
-        self.patch_symbol(mocker, 'git_machete.utils._run_cmd', mock__run_cmd_and_forward_stdout)  # to hide git outputs in tests
+        self.patch_symbol(mocker, 'git_machete.utils._run_cmd', mock__run_cmd_and_forward_stdout)  # to capture `git diff` outputs
         (
             self.repo_sandbox.new_branch("master")
                 .add_file_and_commit(message='master commit1')