mud-git 1.1.7__tar.gz → 1.1.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. mud_git-1.1.9/.github/requirements-binary.txt +2 -0
  2. mud_git-1.1.9/.github/workflows/publish-aur.yaml +169 -0
  3. mud_git-1.1.9/AGENTS.md +89 -0
  4. {mud_git-1.1.7 → mud_git-1.1.9}/PKG-INFO +40 -19
  5. mud_git-1.1.9/PKGBUILD +21 -0
  6. {mud_git-1.1.7 → mud_git-1.1.9}/README.md +39 -18
  7. {mud_git-1.1.7 → mud_git-1.1.9}/src/mud/__init__.py +9 -1
  8. {mud_git-1.1.7 → mud_git-1.1.9}/src/mud/app.py +20 -8
  9. {mud_git-1.1.7 → mud_git-1.1.9}/src/mud/commands.py +3 -1
  10. mud_git-1.1.9/src/mud/completion.py +143 -0
  11. {mud_git-1.1.7 → mud_git-1.1.9}/src/mud/runner.py +7 -0
  12. {mud_git-1.1.7 → mud_git-1.1.9}/src/mud/settings.py +14 -8
  13. {mud_git-1.1.7 → mud_git-1.1.9}/src/mud/utils.py +0 -1
  14. {mud_git-1.1.7 → mud_git-1.1.9}/src/mud_git.egg-info/PKG-INFO +40 -19
  15. {mud_git-1.1.7 → mud_git-1.1.9}/src/mud_git.egg-info/SOURCES.txt +4 -0
  16. {mud_git-1.1.7 → mud_git-1.1.9}/src/mud_git.egg-info/scm_file_list.json +4 -0
  17. mud_git-1.1.9/src/mud_git.egg-info/scm_version.json +8 -0
  18. {mud_git-1.1.7 → mud_git-1.1.9}/tests/helpers.py +8 -2
  19. mud_git-1.1.9/tests/test_completion.py +255 -0
  20. {mud_git-1.1.7 → mud_git-1.1.9}/tests/test_filters.py +36 -0
  21. {mud_git-1.1.7 → mud_git-1.1.9}/tests/test_run.py +19 -0
  22. mud_git-1.1.9/tests/test_settings.py +79 -0
  23. mud_git-1.1.7/.github/workflows/publish-aur.yaml +0 -60
  24. mud_git-1.1.7/AGENTS.md +0 -70
  25. mud_git-1.1.7/PKGBUILD +0 -37
  26. mud_git-1.1.7/src/mud_git.egg-info/scm_version.json +0 -8
  27. {mud_git-1.1.7 → mud_git-1.1.9}/.editorconfig +0 -0
  28. {mud_git-1.1.7 → mud_git-1.1.9}/.github/workflows/publish-pypi.yaml +0 -0
  29. {mud_git-1.1.7 → mud_git-1.1.9}/.github/workflows/test.yaml +0 -0
  30. {mud_git-1.1.7 → mud_git-1.1.9}/.gitignore +0 -0
  31. {mud_git-1.1.7 → mud_git-1.1.9}/LICENSE +0 -0
  32. {mud_git-1.1.7 → mud_git-1.1.9}/img.png +0 -0
  33. {mud_git-1.1.7 → mud_git-1.1.9}/pyproject.toml +0 -0
  34. {mud_git-1.1.7 → mud_git-1.1.9}/requirements.txt +0 -0
  35. {mud_git-1.1.7 → mud_git-1.1.9}/setup.cfg +0 -0
  36. {mud_git-1.1.7 → mud_git-1.1.9}/src/mud/__main__.py +0 -0
  37. {mud_git-1.1.7 → mud_git-1.1.9}/src/mud/config.py +0 -0
  38. {mud_git-1.1.7 → mud_git-1.1.9}/src/mud/styles.py +0 -0
  39. {mud_git-1.1.7 → mud_git-1.1.9}/src/mud_git.egg-info/dependency_links.txt +0 -0
  40. {mud_git-1.1.7 → mud_git-1.1.9}/src/mud_git.egg-info/entry_points.txt +0 -0
  41. {mud_git-1.1.7 → mud_git-1.1.9}/src/mud_git.egg-info/requires.txt +0 -0
  42. {mud_git-1.1.7 → mud_git-1.1.9}/src/mud_git.egg-info/top_level.txt +0 -0
  43. {mud_git-1.1.7 → mud_git-1.1.9}/tests/conftest.py +0 -0
  44. {mud_git-1.1.7 → mud_git-1.1.9}/tests/test_config.py +0 -0
  45. {mud_git-1.1.7 → mud_git-1.1.9}/tests/test_display.py +0 -0
  46. {mud_git-1.1.7 → mud_git-1.1.9}/tests/test_states.py +0 -0
@@ -0,0 +1,2 @@
1
+ pyinstaller==6.22.3
2
+ pyinstaller-hooks-contrib==2026.7
@@ -0,0 +1,169 @@
1
+ name: Publish mud binary to AUR
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+ inputs:
8
+ tag:
9
+ description: "Published stable release tag (vX.Y.Z)"
10
+ required: true
11
+ type: string
12
+
13
+ permissions:
14
+ contents: read
15
+
16
+ concurrency:
17
+ group: publish-aur
18
+ cancel-in-progress: false
19
+
20
+ env:
21
+ RELEASE_TAG: ${{ github.event.release.tag_name || inputs.tag }}
22
+
23
+ jobs:
24
+ build:
25
+ if: github.event_name == 'workflow_dispatch' || !github.event.release.prerelease
26
+ runs-on: ubuntu-22.04
27
+ outputs:
28
+ version: ${{ steps.release.outputs.version }}
29
+ steps:
30
+ - name: Validate release
31
+ id: release
32
+ env:
33
+ GH_TOKEN: ${{ github.token }}
34
+ run: |
35
+ [[ "$RELEASE_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]
36
+ stable=$(gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" \
37
+ --json isDraft,isPrerelease --jq '(.isDraft or .isPrerelease) | not')
38
+ [[ "$stable" == true ]]
39
+ echo "version=${RELEASE_TAG#v}" >> "$GITHUB_OUTPUT"
40
+
41
+ - name: Checkout release tag
42
+ uses: actions/checkout@v4
43
+ with:
44
+ ref: ${{ env.RELEASE_TAG }}
45
+ fetch-depth: 0
46
+
47
+ - name: Set up Python 3.13
48
+ uses: actions/setup-python@v5
49
+ with:
50
+ python-version: "3.13"
51
+
52
+ - name: Configure git
53
+ run: |
54
+ git config --global user.name "Test User"
55
+ git config --global user.email "test@example.com"
56
+
57
+ - name: Install dependencies and build wheel
58
+ run: |
59
+ python -m pip install build pytest -r .github/requirements-binary.txt
60
+ python -m build
61
+ python -m pip install dist/*.whl
62
+
63
+ - name: Test wheel
64
+ run: python -m pytest
65
+
66
+ - name: Build standalone executable
67
+ run: |
68
+ python -m PyInstaller --noconfirm --clean --onedir --name mud \
69
+ --distpath dist/binary --workpath build/pyinstaller --specpath build \
70
+ --collect-all pygit2 --hidden-import _cffi_backend \
71
+ --recursive-copy-metadata mud-git src/mud/__main__.py
72
+
73
+ - name: Test standalone executable
74
+ env:
75
+ MUD_EXECUTABLE: ${{ github.workspace }}/dist/binary/mud/mud
76
+ run: python -m pytest
77
+
78
+ - name: Prepare release archive and PKGBUILD
79
+ env:
80
+ VERSION: ${{ steps.release.outputs.version }}
81
+ run: |
82
+ mkdir -p dist/aur
83
+ install -m644 LICENSE dist/binary/LICENSE
84
+ archive="mud-${VERSION}-linux-x86_64.tar.gz"
85
+ tar -czf "dist/aur/$archive" -C dist/binary mud LICENSE
86
+ checksum=$(sha256sum "dist/aur/$archive" | cut -d ' ' -f 1)
87
+ sed -e "s/@VERSION@/$VERSION/" -e "s/@SHA256@/$checksum/" \
88
+ PKGBUILD > dist/aur/PKGBUILD
89
+
90
+ - uses: actions/upload-artifact@v4
91
+ with:
92
+ name: mud-aur
93
+ path: dist/aur/
94
+ if-no-files-found: error
95
+
96
+ test-package:
97
+ needs: build
98
+ runs-on: ubuntu-latest
99
+ container: archlinux:base-devel
100
+ steps:
101
+ - name: Install runtime dependencies
102
+ run: pacman -Syu --noconfirm git
103
+
104
+ - uses: actions/download-artifact@v4
105
+ with:
106
+ name: mud-aur
107
+ path: package
108
+
109
+ - name: Build and install Arch package
110
+ working-directory: package
111
+ run: |
112
+ useradd --create-home builder
113
+ chown -R builder:builder .
114
+ runuser -u builder -- makepkg --noconfirm
115
+ pacman -U --noconfirm mud-*.pkg.tar.zst
116
+
117
+ - name: Smoke test installed binary
118
+ shell: bash
119
+ run: |
120
+ HOME=$(mktemp -d)
121
+ export HOME
122
+ cd "$HOME"
123
+ mud completion carapace | grep -F 'mud'
124
+ test ! -e "$HOME/.config/mud/settings.ini"
125
+ mud --help
126
+ git init repo-a
127
+ git -C repo-a -c user.name=Test -c user.email=test@example.com \
128
+ commit --allow-empty -m Initial
129
+ mud init
130
+ mud status | grep -F 'repo-a'
131
+ mud -a -c='git rev-parse --is-inside-work-tree' | grep -F 'true'
132
+
133
+ publish:
134
+ needs: [build, test-package]
135
+ runs-on: ubuntu-latest
136
+ permissions:
137
+ contents: write
138
+ steps:
139
+ - uses: actions/download-artifact@v4
140
+ with:
141
+ name: mud-aur
142
+ path: package
143
+
144
+ - name: Upload binary to GitHub release
145
+ env:
146
+ GH_TOKEN: ${{ github.token }}
147
+ VERSION: ${{ needs.build.outputs.version }}
148
+ run: |
149
+ archive="mud-${VERSION}-linux-x86_64.tar.gz"
150
+ if gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" \
151
+ --json assets --jq '.assets[].name' | grep -Fxq "$archive"; then
152
+ gh release download "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" \
153
+ --pattern "$archive" --dir "$RUNNER_TEMP/published"
154
+ cmp "package/$archive" "$RUNNER_TEMP/published/$archive"
155
+ else
156
+ gh release upload "$RELEASE_TAG" "package/$archive" --repo "$GITHUB_REPOSITORY"
157
+ fi
158
+
159
+ - name: Publish AUR package
160
+ uses: KSXGitHub/github-actions-deploy-aur@v4.1.2
161
+ with:
162
+ pkgname: mud
163
+ pkgbuild: ./package/PKGBUILD
164
+ test: true
165
+ commit_username: ${{ secrets.AUR_USERNAME }}
166
+ commit_email: ${{ secrets.AUR_EMAIL }}
167
+ ssh_private_key: ${{ secrets.AUR_SSH_KEY }}
168
+ commit_message: "Update mud to version ${{ needs.build.outputs.version }}"
169
+ ssh_keyscan_types: rsa,ecdsa,ed25519
@@ -0,0 +1,89 @@
1
+ # AI Agent Instructions
2
+
3
+ ## What `mud` does
4
+
5
+ `mud` runs git commands (and arbitrary shell commands) across multiple repositories simultaneously. It reads a `.mudconfig` TSV file listing repo paths and optional labels, then dispatches to every matching repo with filtering, async execution, and rich terminal output.
6
+
7
+ For commands, flags, and settings refer to `README.md` — it is the source of truth for user-facing behaviour. For runtime dependencies refer to `requirements.txt`.
8
+
9
+ ## Build and test
10
+
11
+ ```sh
12
+ pip install -e ".[dev]" # install with dev deps
13
+ python -m build # build wheel
14
+ pytest # run tests (verbose by default)
15
+ pytest tests/test_run.py # run a single file
16
+ ```
17
+
18
+ ## Binary releases and AUR
19
+
20
+ `.github/workflows/publish-aur.yaml` builds stable `vX.Y.Z` releases, with an explicit published tag required for manual runs. It checks out that exact tag, tests the wheel, freezes an x86_64 executable with PyInstaller on Ubuntu 22.04, and reruns the CLI tests against the executable. Build tools are pinned in `.github/requirements-binary.txt`.
21
+
22
+ The workflow fills `@VERSION@` and `@SHA256@` in the root `PKGBUILD` template, tests the package in Arch Linux, uploads `mud-X.Y.Z-linux-x86_64.tar.gz` to the release, then publishes `mud` and generated `.SRCINFO` to AUR using `AUR_USERNAME`, `AUR_EMAIL`, and `AUR_SSH_KEY`. Published assets are immutable; retry failed jobs with the original artifact. The package installs the bundled runtime under `/opt/mud` and links `/usr/bin/mud`, with no end-user Python build. `mud-git` is a provided/conflicting legacy AUR package; its listing is retired by requesting an AUR merge after `mud` is published. The PyPI distribution is still named `mud-git`.
23
+
24
+ ## Entry point
25
+
26
+ `mud` CLI → `mud:run` in `src/mud/__init__.py` → `App` in `src/mud/app.py`; `mud completion` is dispatched to `completion.complete()` before writable settings are initialised.
27
+
28
+ ## Module map
29
+
30
+ | File | Purpose |
31
+ |---|---|
32
+ | `src/mud/__init__.py` | Entry point; creates `App`, dispatches completion or initialises `Settings` and calls `run()` |
33
+ | `src/mud/app.py` | CLI dispatch; parses args, applies filters, calls `Runner` |
34
+ | `src/mud/completion.py` | Exports the Carapace spec and provides read-only dynamic completion via `C_ARG<n>` / `C_VALUE` |
35
+ | `src/mud/runner.py` | All display commands and execution modes |
36
+ | `src/mud/config.py` | `.mudconfig` TSV read/write; `init`, `add`, `remove`, `prune` |
37
+ | `src/mud/settings.py` | `~/.config/mud/settings.ini` read/write; legacy settings migration; read-only initialisation for completion |
38
+ | `src/mud/commands.py` | Constants for every command name and filter flag prefix |
39
+ | `src/mud/styles.py` | ANSI escape codes and Nerd Font glyphs |
40
+ | `src/mud/utils.py` | Shared helpers: table creation, error printing, configure wizard, help banner (independent of source-file paths for frozen builds) |
41
+
42
+ ## Architecture notes
43
+
44
+ **Global settings instance** — `utils.settings` is a single `Settings` object created in `__init__.run()` and accessed across all modules.
45
+
46
+ **Settings location** — All settings writes target `~/.config/mud/settings.ini`; mud never creates `~/.mudsettings`. An existing modern file takes precedence and leaves the legacy file untouched; otherwise normal startup moves the legacy file to the modern location, preserving its contents, or creates defaults at the modern location if neither file exists. The legacy file is removed only after a successful move. Completion can read legacy settings as a fallback but never migrates them. Ordinary package imports and builds do not initialise settings.
47
+
48
+ **Command dispatch** — `App.run()` routes to either a native `Runner` method (matched against constants in `commands.py`) or a shell pass-through. The `--` separator and `-c=<cmd>` flag both reach the shell path.
49
+
50
+ **Carapace completion** — `mud completion carapace` exports a shell-independent spec; its fixed `mud completion values` callback handles mud's equals-only flags without executing user input. Command/flag descriptions come from the argparse definitions, aliases from settings, and label/path/branch values from the discovered config. Local and remote branch names reuse `Runner._get_unique_branch_names()`. Nothing is completed after `--`, inside `-c=`, or in arbitrary shell/alias arguments. Filters remain available after a completed `-c=` token. Neither spec export nor completion creates settings files.
51
+
52
+ **Argument metadata** — argparse registers value-taking options without trailing `=` and toggles as booleans, so empty values and `-t` cannot consume native commands. The execution scanner still requires `-flag=value`; completion inserts the equals sign.
53
+
54
+ **Execution modes** — three modes controlled by `run_async` + `run_table` settings (toggled by `-a` / `-t` flags): sequential, async streamed, async live-table.
55
+
56
+ **Frozen subprocesses** — `Runner` restores the original `LD_LIBRARY_PATH` (or removes it if originally unset) in the child-command environment when running under PyInstaller, so Git and arbitrary shell commands use system libraries rather than the bundled runtime.
57
+
58
+ **Filter chain** — `App._filter_with_arguments()` applies up to nine filters in sequence (ignore label, include/exclude label, include/exclude branch, include/exclude name substring, modified, diverged). Each step removes non-matching repos. Repeated `-N=` / `--not-name=` values exclude any matching path substring; empty exclusions are ignored.
59
+
60
+ **Nerd Fonts** — every glyph in `styles.GLYPHS` has an ASCII fallback. `utils.glyphs(key)` selects between them based on the `nerd_fonts` setting, so mud works with or without a patched font.
61
+
62
+ ## Tests
63
+
64
+ Tests are black-box CLI tests — each runs `python -m mud` as a subprocess against real git repos in a temporary directory. Set `MUD_EXECUTABLE` to an absolute executable path to run CLI and completion tests against a frozen binary instead; import-only checks still use the Python environment.
65
+
66
+ | File | Covers |
67
+ |---|---|
68
+ | `tests/test_config.py` | `init`, `add`, `remove`, `prune` |
69
+ | `tests/test_settings.py` | Import side effects, first-launch settings location, legacy moves and migration failures, modern-file precedence, and settings save destination |
70
+ | `tests/test_display.py` | `status`, `info`, `log`, `labels`, `branches`, `tags` |
71
+ | `tests/test_run.py` | Execution modes, flags, and preservation of the external-command library path in frozen builds |
72
+ | `tests/test_filters.py` | `-l=`, `-L=`, `-b=`, `-B=`, `-n=`, `-N=` filter flags and native-command parsing regressions |
73
+ | `tests/test_completion.py` | Completion callback, dynamic values, command boundaries, read-only settings precedence/discovery, and optional real Carapace/Nushell integration |
74
+ | `tests/test_states.py` | Edge-case repo states (unborn, detached, rebasing) |
75
+
76
+ Carapace integration tests run when `carapace` is on `PATH`; the Nushell round-trip test also requires `nu`. Tests isolate `HOME`, Carapace config/cache directories, and use `MUD_EXECUTABLE` or the current Python environment's `mud` executable.
77
+
78
+ ## Knowledge base update rule
79
+ After editing any file under `src/mud/`, update the affected sections of this file before finishing the task.
80
+ `README.md` is the source of truth for user-facing behaviour — if commands, flags, or settings changed, update the relevant table rows there. Table edits only — no new prose.
81
+
82
+ # Git
83
+
84
+ ## Branching
85
+ - All new features should have `feature/` prefix
86
+ - Bugfixes are using `bugfix/` prefix
87
+
88
+ ## Tags
89
+ Tags format is vX.Y.Z
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mud-git
3
- Version: 1.1.7
3
+ Version: 1.1.9
4
4
  Summary: Multi repository git utility. Manage multiple git-repositories simultaneously.
5
5
  Author-email: Jasur Sadikov <jasur@sadikoff.com>
6
6
  License-Expression: MIT
@@ -18,7 +18,7 @@ Dynamic: license-file
18
18
  # mud
19
19
 
20
20
  ![Version](https://img.shields.io/pypi/v/mud-git?logo=python)
21
- ![AUR Version](https://img.shields.io/aur/version/mud-git?logo=archlinux)
21
+ ![AUR Version](https://img.shields.io/aur/version/mud?logo=archlinux)
22
22
  [![Test projects integrity](https://github.com/jasursadikov/mud/actions/workflows/test.yaml/badge.svg)](https://github.com/jasursadikov/mud/actions/workflows/test.yaml)
23
23
  [![Publish Python Package](https://github.com/jasursadikov/mud/actions/workflows/publish-pypi.yaml/badge.svg)](https://github.com/jasursadikov/mud/actions/workflows/publish-pypi.yaml)
24
24
  [![Publish to AUR](https://github.com/jasursadikov/mud/actions/workflows/publish-aur.yaml/badge.svg)](https://github.com/jasursadikov/mud/actions/workflows/publish-aur.yaml)
@@ -28,14 +28,16 @@ Dynamic: license-file
28
28
  mud is CLI utility that allows you to run git commands in multiple repositories. It has multiple powerful filtering tools, native commands with an informative terminal output and support of aliasing. This tool is not limited to git commands only; you can run any commands you wish. However, this tool was primarily designed to be used with git, so each referenced directory should have a `.git` directory.
29
29
 
30
30
  ## Installing
31
- **PyPI**
32
- ```bash
33
- pip install mud-git
34
- ```
35
- **ArchLinux**
36
- ```bash
37
- yay -S mud-git
38
- ```
31
+ | Distribution | Installation |
32
+ |--------------|--------------|
33
+ | PyPI | `pip install mud-git` |
34
+ | Arch Linux (x86_64) | `yay -S mud` installs the prebuilt release executable with its bundled Python runtime and libraries. |
35
+ | Migrate from AUR `mud-git` | Run `yay -S mud` and accept removal of the conflicting `mud-git` package. Existing mud settings and repository configurations are preserved. |
36
+ | Linux binary (x86_64) | Download `mud-X.Y.Z-linux-x86_64.tar.gz` from [GitHub Releases](https://github.com/jasursadikov/mud/releases), extract it, and run `mud/mud`; keep its `_internal` directory beside the executable. Requires Git, glibc 2.35 or newer, and zlib. |
37
+ | AUR release automation | Publish a stable `vX.Y.Z` GitHub release, or run **Publish mud binary to AUR** with its published release tag. CI tests the wheel and frozen executable, tests installation on Arch Linux, uploads the binary, and publishes `mud` with a SHA-256 checksum. |
38
+ | AUR credentials | Configure repository secrets `AUR_USERNAME`, `AUR_EMAIL`, and `AUR_SSH_KEY`; the key must belong to an AUR account allowed to publish `mud`. GitHub release uploads use the workflow's automatic token. |
39
+ | Retire the old AUR listing | After the first successful `mud` publication, request that AUR merge `mud-git` into `mud`. New releases publish only to `mud`. |
40
+ | Retry AUR publication | Re-run failed jobs to reuse the tested artifact. Existing release binaries are never overwritten; rebuilding a published binary requires a new release version. |
39
41
 
40
42
  For requirements check [requirements.txt](requirements.txt).
41
43
 
@@ -65,18 +67,23 @@ mud --diverged git pull
65
67
  ### Commands
66
68
  | Command | Description |
67
69
  |---------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|
68
- | `mud set-global` | sets the current `.mudconfig` as a global configuration so it will be used as a fallback configuration to run from any directory. |
70
+ | `mud init` | creates `.mudconfig` and adds repositories beneath the current directory. |
71
+ | `mud configure`/`mud config` | runs the interactive settings wizard, saving only to `~/.config/mud/settings.ini`. If the modern file is missing, normal startup moves `~/.mudsettings` there or creates defaults if neither exists. An existing modern file takes precedence and leaves the legacy file untouched; mud never creates `~/.mudsettings`. |
72
+ | `mud help`/`mud --help`/`mud -h` | displays available commands and flags. |
73
+ | `mud set-global [path]` | sets the current `.mudconfig`, or the specified configuration path, as the fallback configuration to run from any directory; saves to `~/.config/mud/settings.ini`. |
69
74
  | `mud get-config` | prints the current `.mudconfig` location. |
70
75
  | `mud prune` | removes all invalid repositories from the `.mudconfig`. |
71
76
  | `mud info`/`mud i` | displays branch divergence and working directory changes. |
72
77
  | `mud status`/`mud st` | displays working directory changes. |
73
78
  | `mud log`/`mud l` | displays the latest commit message, its time, and its author. |
74
79
  | `mud labels`/`mud lb` | displays mud labels across repositories. |
75
- | `mud branches`/`mud br` | displays all branches in repositories. |
76
- | `mud remote-branches`/`mud rbr` | displays all remote branches in repositories. |
80
+ | `mud branch`/`mud branches`/`mud br` | displays all branches in repositories. |
81
+ | `mud remote-branch`/`mud remote-branches`/`mud rbr` | displays all remote branches in repositories. |
77
82
  | `mud complete-branch` | prints unique current branch names across repositories for shell completion. |
78
83
  | `mud complete-branch-all` | prints unique local and remote branch names across repositories for shell completion. |
79
- | `mud tags`/`mud t` | displays git tags in repositories. |
84
+ | `mud completion carapace` | exports a Carapace spec for all commands, aliases, flags, and native arguments; dynamically completes labels, local/remote branch names, and configured repository paths. |
85
+ | `mud completion values` | internal read-only Carapace callback; reads completion context from `C_ARG<n>` and `C_VALUE`, never executing command text. |
86
+ | `mud tags`/`mud tag`/`mud t` | displays git tags in repositories. |
80
87
 
81
88
  `--` format is also supported. An example would be `mud -- git status`.
82
89
 
@@ -87,11 +94,13 @@ mud --diverged git pull
87
94
  | Flag | Description |
88
95
  |------------------------------------------|--------------------------------------------------------------------------------------|
89
96
  | `-n=<str>` or `--name=<str>` | includes repositories that contains provided string. |
97
+ | `-N=<str>` or `--not-name=<str>` | excludes repositories whose path contains the provided string; repeat to exclude multiple substrings. |
90
98
  | `-l=<label>` or `--label=<label>` | includes repositories with the provided label. |
91
99
  | `-L=<label>` or `--not-label=<label>` | excludes repositories with the provided label. |
92
100
  | `-b=<branch>` or `--branch=<branch>` | includes repositories with the provided branch. |
93
101
  | `-B=<branch>` or `--not-branch=<branch>` | excludes repositories with the provided branch. |
94
- | `-c` or `--command` | explicit command argument. Use this whenever you're trying to run a complex command. |
102
+ | `-c="<command>"` or `--command="<command>"` | explicit shell command; no completion inside its value, but mud filters remain available in following arguments. |
103
+ | `--` | starts an opaque shell command; no mud completion or filtering after this separator. |
95
104
  | `-m` or `--modified` | filters out modified repositories. |
96
105
  | `-d` or `--diverged` | filters repositories with diverged branches. |
97
106
  | `-t` or `--table` | toggles the default table view setting for execution. |
@@ -117,6 +126,18 @@ mud complete-branch
117
126
  mud complete-branch-all
118
127
  ```
119
128
 
129
+ | Carapace Setup | Instructions |
130
+ |----------------|--------------|
131
+ | Requirements | Install `mud` and [Carapace](https://carapace-sh.github.io/carapace-bin/install.html) on `PATH`; the same spec works across Carapace-supported shells. |
132
+ | Install Spec (Nushell, Linux) | `let specs = (($env.XDG_CONFIG_HOME? \| default ($nu.home-path \| path join .config)) \| path join carapace specs)`; `mkdir $specs`; `mud completion carapace \| save --force ($specs \| path join mud.yaml)` |
133
+ | Install Spec (Bash/Zsh, Linux) | `mkdir -p "${XDG_CONFIG_HOME:-$HOME/.config}/carapace/specs"`; `mud completion carapace > "${XDG_CONFIG_HOME:-$HOME/.config}/carapace/specs/mud.yaml"` |
134
+ | Nushell Hook | Follow [Carapace's Nushell setup](https://carapace-sh.github.io/carapace-bin/setup.html#nushell), or use `{\|spans\| carapace $spans.0 nushell ...$spans \| from json }` as your external completer; preserve empty results for mud rather than falling back to file or command completion. |
135
+ | Bash/Zsh Hook | `source <(carapace _carapace)`; Zsh also requires `autoload -U compinit && compinit`. |
136
+ | Fish Hook | `carapace _carapace fish \| source` |
137
+ | Other Platforms/Shells | Install `mud.yaml` in [Carapace's user spec directory](https://carapace-sh.github.io/carapace-bin/spec/user.html), then follow the appropriate [shell setup](https://carapace-sh.github.io/carapace-bin/setup.html). Restart the shell after first installing the spec. |
138
+ | Dynamic Values | Labels and paths come from the nearest ancestor `.mudconfig` or global fallback; branches include unique local and remote names with remote prefixes removed. Branch filters still match the current branch. No settings or repositories are written during completion. |
139
+ | Command Boundaries | Complete mud filters before a command or `--`, or after a completed `-c="..."` argument. Arbitrary shell commands and alias arguments are not completed. |
140
+
120
141
  ## Settings
121
142
 
122
143
  Settings are stored at `~/.config/mud/settings.ini`.
@@ -150,7 +171,7 @@ You can modify your `.mudconfig` file using the following commands:
150
171
 
151
172
  | Command | Description |
152
173
  |-----------------------------|------------------------------------------------|
153
- | `mud add <path>` | adds a path without a label. |
154
- | `mud add <path> <label>` | adds a path with an optional label. |
155
- | `mud remove <path>` | removes the directory with the specified path. |
156
- | `mud remove <path> <label>` | removes the label from a directory. |
174
+ | `mud add <path>`/`mud a <path>` | adds a path without a label. |
175
+ | `mud add <path> <label>`/`mud a <path> <label>` | adds a path with an optional label. |
176
+ | `mud remove <path>`/`mud rm <path>` | removes the directory with the specified path. |
177
+ | `mud remove <path> <label>`/`mud rm <path> <label>` | removes the label from a directory. |
mud_git-1.1.9/PKGBUILD ADDED
@@ -0,0 +1,21 @@
1
+ # Maintainer: Jasur Sadikov <jasur@sadikoff.com>
2
+ pkgname=mud
3
+ pkgver=@VERSION@
4
+ pkgrel=1
5
+ pkgdesc="Multi repository git utility. Manage multiple git-repositories simultaneously."
6
+ arch=('x86_64')
7
+ url="https://github.com/jasursadikov/mud"
8
+ license=('MIT')
9
+ provides=("mud-git=$pkgver")
10
+ conflicts=('mud-git')
11
+ depends=('git' 'glibc' 'zlib')
12
+ options=('!strip')
13
+ source=("${url}/releases/download/v${pkgver}/mud-${pkgver}-linux-x86_64.tar.gz")
14
+ sha256sums=('@SHA256@')
15
+
16
+ package() {
17
+ install -d "$pkgdir/opt" "$pkgdir/usr/bin"
18
+ cp -a "$srcdir/mud" "$pkgdir/opt/mud"
19
+ ln -s /opt/mud/mud "$pkgdir/usr/bin/mud"
20
+ install -Dm644 "$srcdir/LICENSE" "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
21
+ }
@@ -1,7 +1,7 @@
1
1
  # mud
2
2
 
3
3
  ![Version](https://img.shields.io/pypi/v/mud-git?logo=python)
4
- ![AUR Version](https://img.shields.io/aur/version/mud-git?logo=archlinux)
4
+ ![AUR Version](https://img.shields.io/aur/version/mud?logo=archlinux)
5
5
  [![Test projects integrity](https://github.com/jasursadikov/mud/actions/workflows/test.yaml/badge.svg)](https://github.com/jasursadikov/mud/actions/workflows/test.yaml)
6
6
  [![Publish Python Package](https://github.com/jasursadikov/mud/actions/workflows/publish-pypi.yaml/badge.svg)](https://github.com/jasursadikov/mud/actions/workflows/publish-pypi.yaml)
7
7
  [![Publish to AUR](https://github.com/jasursadikov/mud/actions/workflows/publish-aur.yaml/badge.svg)](https://github.com/jasursadikov/mud/actions/workflows/publish-aur.yaml)
@@ -11,14 +11,16 @@
11
11
  mud is CLI utility that allows you to run git commands in multiple repositories. It has multiple powerful filtering tools, native commands with an informative terminal output and support of aliasing. This tool is not limited to git commands only; you can run any commands you wish. However, this tool was primarily designed to be used with git, so each referenced directory should have a `.git` directory.
12
12
 
13
13
  ## Installing
14
- **PyPI**
15
- ```bash
16
- pip install mud-git
17
- ```
18
- **ArchLinux**
19
- ```bash
20
- yay -S mud-git
21
- ```
14
+ | Distribution | Installation |
15
+ |--------------|--------------|
16
+ | PyPI | `pip install mud-git` |
17
+ | Arch Linux (x86_64) | `yay -S mud` installs the prebuilt release executable with its bundled Python runtime and libraries. |
18
+ | Migrate from AUR `mud-git` | Run `yay -S mud` and accept removal of the conflicting `mud-git` package. Existing mud settings and repository configurations are preserved. |
19
+ | Linux binary (x86_64) | Download `mud-X.Y.Z-linux-x86_64.tar.gz` from [GitHub Releases](https://github.com/jasursadikov/mud/releases), extract it, and run `mud/mud`; keep its `_internal` directory beside the executable. Requires Git, glibc 2.35 or newer, and zlib. |
20
+ | AUR release automation | Publish a stable `vX.Y.Z` GitHub release, or run **Publish mud binary to AUR** with its published release tag. CI tests the wheel and frozen executable, tests installation on Arch Linux, uploads the binary, and publishes `mud` with a SHA-256 checksum. |
21
+ | AUR credentials | Configure repository secrets `AUR_USERNAME`, `AUR_EMAIL`, and `AUR_SSH_KEY`; the key must belong to an AUR account allowed to publish `mud`. GitHub release uploads use the workflow's automatic token. |
22
+ | Retire the old AUR listing | After the first successful `mud` publication, request that AUR merge `mud-git` into `mud`. New releases publish only to `mud`. |
23
+ | Retry AUR publication | Re-run failed jobs to reuse the tested artifact. Existing release binaries are never overwritten; rebuilding a published binary requires a new release version. |
22
24
 
23
25
  For requirements check [requirements.txt](requirements.txt).
24
26
 
@@ -48,18 +50,23 @@ mud --diverged git pull
48
50
  ### Commands
49
51
  | Command | Description |
50
52
  |---------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|
51
- | `mud set-global` | sets the current `.mudconfig` as a global configuration so it will be used as a fallback configuration to run from any directory. |
53
+ | `mud init` | creates `.mudconfig` and adds repositories beneath the current directory. |
54
+ | `mud configure`/`mud config` | runs the interactive settings wizard, saving only to `~/.config/mud/settings.ini`. If the modern file is missing, normal startup moves `~/.mudsettings` there or creates defaults if neither exists. An existing modern file takes precedence and leaves the legacy file untouched; mud never creates `~/.mudsettings`. |
55
+ | `mud help`/`mud --help`/`mud -h` | displays available commands and flags. |
56
+ | `mud set-global [path]` | sets the current `.mudconfig`, or the specified configuration path, as the fallback configuration to run from any directory; saves to `~/.config/mud/settings.ini`. |
52
57
  | `mud get-config` | prints the current `.mudconfig` location. |
53
58
  | `mud prune` | removes all invalid repositories from the `.mudconfig`. |
54
59
  | `mud info`/`mud i` | displays branch divergence and working directory changes. |
55
60
  | `mud status`/`mud st` | displays working directory changes. |
56
61
  | `mud log`/`mud l` | displays the latest commit message, its time, and its author. |
57
62
  | `mud labels`/`mud lb` | displays mud labels across repositories. |
58
- | `mud branches`/`mud br` | displays all branches in repositories. |
59
- | `mud remote-branches`/`mud rbr` | displays all remote branches in repositories. |
63
+ | `mud branch`/`mud branches`/`mud br` | displays all branches in repositories. |
64
+ | `mud remote-branch`/`mud remote-branches`/`mud rbr` | displays all remote branches in repositories. |
60
65
  | `mud complete-branch` | prints unique current branch names across repositories for shell completion. |
61
66
  | `mud complete-branch-all` | prints unique local and remote branch names across repositories for shell completion. |
62
- | `mud tags`/`mud t` | displays git tags in repositories. |
67
+ | `mud completion carapace` | exports a Carapace spec for all commands, aliases, flags, and native arguments; dynamically completes labels, local/remote branch names, and configured repository paths. |
68
+ | `mud completion values` | internal read-only Carapace callback; reads completion context from `C_ARG<n>` and `C_VALUE`, never executing command text. |
69
+ | `mud tags`/`mud tag`/`mud t` | displays git tags in repositories. |
63
70
 
64
71
  `--` format is also supported. An example would be `mud -- git status`.
65
72
 
@@ -70,11 +77,13 @@ mud --diverged git pull
70
77
  | Flag | Description |
71
78
  |------------------------------------------|--------------------------------------------------------------------------------------|
72
79
  | `-n=<str>` or `--name=<str>` | includes repositories that contains provided string. |
80
+ | `-N=<str>` or `--not-name=<str>` | excludes repositories whose path contains the provided string; repeat to exclude multiple substrings. |
73
81
  | `-l=<label>` or `--label=<label>` | includes repositories with the provided label. |
74
82
  | `-L=<label>` or `--not-label=<label>` | excludes repositories with the provided label. |
75
83
  | `-b=<branch>` or `--branch=<branch>` | includes repositories with the provided branch. |
76
84
  | `-B=<branch>` or `--not-branch=<branch>` | excludes repositories with the provided branch. |
77
- | `-c` or `--command` | explicit command argument. Use this whenever you're trying to run a complex command. |
85
+ | `-c="<command>"` or `--command="<command>"` | explicit shell command; no completion inside its value, but mud filters remain available in following arguments. |
86
+ | `--` | starts an opaque shell command; no mud completion or filtering after this separator. |
78
87
  | `-m` or `--modified` | filters out modified repositories. |
79
88
  | `-d` or `--diverged` | filters repositories with diverged branches. |
80
89
  | `-t` or `--table` | toggles the default table view setting for execution. |
@@ -100,6 +109,18 @@ mud complete-branch
100
109
  mud complete-branch-all
101
110
  ```
102
111
 
112
+ | Carapace Setup | Instructions |
113
+ |----------------|--------------|
114
+ | Requirements | Install `mud` and [Carapace](https://carapace-sh.github.io/carapace-bin/install.html) on `PATH`; the same spec works across Carapace-supported shells. |
115
+ | Install Spec (Nushell, Linux) | `let specs = (($env.XDG_CONFIG_HOME? \| default ($nu.home-path \| path join .config)) \| path join carapace specs)`; `mkdir $specs`; `mud completion carapace \| save --force ($specs \| path join mud.yaml)` |
116
+ | Install Spec (Bash/Zsh, Linux) | `mkdir -p "${XDG_CONFIG_HOME:-$HOME/.config}/carapace/specs"`; `mud completion carapace > "${XDG_CONFIG_HOME:-$HOME/.config}/carapace/specs/mud.yaml"` |
117
+ | Nushell Hook | Follow [Carapace's Nushell setup](https://carapace-sh.github.io/carapace-bin/setup.html#nushell), or use `{\|spans\| carapace $spans.0 nushell ...$spans \| from json }` as your external completer; preserve empty results for mud rather than falling back to file or command completion. |
118
+ | Bash/Zsh Hook | `source <(carapace _carapace)`; Zsh also requires `autoload -U compinit && compinit`. |
119
+ | Fish Hook | `carapace _carapace fish \| source` |
120
+ | Other Platforms/Shells | Install `mud.yaml` in [Carapace's user spec directory](https://carapace-sh.github.io/carapace-bin/spec/user.html), then follow the appropriate [shell setup](https://carapace-sh.github.io/carapace-bin/setup.html). Restart the shell after first installing the spec. |
121
+ | Dynamic Values | Labels and paths come from the nearest ancestor `.mudconfig` or global fallback; branches include unique local and remote names with remote prefixes removed. Branch filters still match the current branch. No settings or repositories are written during completion. |
122
+ | Command Boundaries | Complete mud filters before a command or `--`, or after a completed `-c="..."` argument. Arbitrary shell commands and alias arguments are not completed. |
123
+
103
124
  ## Settings
104
125
 
105
126
  Settings are stored at `~/.config/mud/settings.ini`.
@@ -133,7 +154,7 @@ You can modify your `.mudconfig` file using the following commands:
133
154
 
134
155
  | Command | Description |
135
156
  |-----------------------------|------------------------------------------------|
136
- | `mud add <path>` | adds a path without a label. |
137
- | `mud add <path> <label>` | adds a path with an optional label. |
138
- | `mud remove <path>` | removes the directory with the specified path. |
139
- | `mud remove <path> <label>` | removes the label from a directory. |
157
+ | `mud add <path>`/`mud a <path>` | adds a path without a label. |
158
+ | `mud add <path> <label>`/`mud a <path> <label>` | adds a path with an optional label. |
159
+ | `mud remove <path>`/`mud rm <path>` | removes the directory with the specified path. |
160
+ | `mud remove <path> <label>`/`mud rm <path> <label>` | removes the label from a directory. |
@@ -2,15 +2,23 @@
2
2
 
3
3
  import mud.utils
4
4
  import mud.settings
5
+ import sys
5
6
 
6
7
  from mud.app import App
8
+ from mud.commands import COMPLETION
7
9
 
8
10
 
9
11
  def run():
10
12
  try:
13
+ app: App = App()
14
+ if sys.argv[1:2] == COMPLETION:
15
+ from mud.completion import complete
16
+
17
+ complete(app.parser)
18
+ return
19
+
11
20
  utils.settings = settings.Settings(utils.SETTINGS_FILE_NAME, utils.OLD_SETTINGS_FILE_NAME)
12
21
 
13
- app: App = App()
14
22
  app.run()
15
23
  except KeyboardInterrupt:
16
24
  utils.print_error(0)
@@ -51,6 +51,8 @@ class App:
51
51
  subparsers.add_parser(REMOTE_BRANCHES[0], aliases=REMOTE_BRANCHES[1:], help='Displays all remote branches in repositories.')
52
52
  subparsers.add_parser(COMPLETE_BRANCH[0], help='Prints unique current branch names across repositories for shell completion.')
53
53
  subparsers.add_parser(COMPLETE_BRANCH_ALL[0], help='Prints unique local and remote branch names across repositories for shell completion.')
54
+ completion_parser = subparsers.add_parser(COMPLETION[0], help='Prints shell completion support.')
55
+ completion_parser.add_argument('shell', choices=['carapace', 'values'], help='Completion format (values is the Carapace callback).')
54
56
  subparsers.add_parser(CONFIGURE[0], aliases=CONFIGURE[1:], help='Runs the interactive configuration wizard.')
55
57
  subparsers.add_parser(GET_CONFIG[0], aliases=GET_CONFIG[1:], help='Prints current .mudconfig path.')
56
58
  subparsers.add_parser(SET_GLOBAL[0], aliases=SET_GLOBAL[1:], help='Sets .mudconfig in the current repository as your fallback .mudconfig.')
@@ -65,13 +67,17 @@ class App:
65
67
 
66
68
  subparsers.add_parser(PRUNE[0], help='Removes invalid paths from .mudconfig.')
67
69
 
68
- parser.add_argument(*COMMAND_ATTR, metavar='COMMAND', help=f'Explicit command argument. Use this when you want to run a command that has a special characters.', nargs='?', default='', type=str)
69
- parser.add_argument(*TABLE_ATTR, metavar='TABLE', help=f'Switches table view, runs in table view it is disabled in .mudsettings.', nargs='?', default='', type=str)
70
- parser.add_argument(*NAME_PREFIX, metavar='NAME', help='Includes repositories where name contains provided value.', nargs='?', default='', type=str)
71
- parser.add_argument(*LABEL_PREFIX, metavar='LABEL', help='Includes repositories with provided label.', nargs='?', default='', type=str)
72
- parser.add_argument(*NOT_LABEL_PREFIX, metavar='NOT_LABEL', help=f'Excludes repositories with provided label.', nargs='?', default='', type=str)
73
- parser.add_argument(*BRANCH_PREFIX, metavar='BRANCH', help='Includes repositories on a provided branch.', nargs='?', default='', type=str)
74
- parser.add_argument(*NOT_BRANCH_PREFIX, metavar='NOT_BRANCH', help='Excludes repositories on a provided branch.', nargs='?', default='', type=str)
70
+ for prefixes, metavar, description in [
71
+ (COMMAND_ATTR, 'COMMAND', 'Explicit shell command; use for complex commands.'),
72
+ (NAME_PREFIX, 'NAME', 'Includes repositories where name contains provided value.'),
73
+ (NOT_NAME_PREFIX, 'NOT_NAME', 'Excludes repositories where name contains provided value.'),
74
+ (LABEL_PREFIX, 'LABEL', 'Includes repositories with provided label.'),
75
+ (NOT_LABEL_PREFIX, 'NOT_LABEL', 'Excludes repositories with provided label.'),
76
+ (BRANCH_PREFIX, 'BRANCH', 'Includes repositories on a provided branch.'),
77
+ (NOT_BRANCH_PREFIX, 'NOT_BRANCH', 'Excludes repositories on a provided branch.'),
78
+ ]:
79
+ parser.add_argument(*(prefix.rstrip('=') for prefix in prefixes), metavar=metavar, help=description, default='', type=str)
80
+ parser.add_argument(*TABLE_ATTR, action='store_true', help='Toggles the table view setting for execution.')
75
81
  parser.add_argument(*MODIFIED_ATTR, action='store_true', help='Filters modified repositories.')
76
82
  parser.add_argument(*DIVERGED_ATTR, action='store_true', help='Filters repositories with diverged branches.')
77
83
  parser.add_argument(*ASYNC_ATTR, action='store_true', help='Switches asynchronous run feature.')
@@ -85,7 +91,6 @@ class App:
85
91
  print()
86
92
  self.parser.print_help()
87
93
  return
88
- # Sets global repository in .mudsettings
89
94
  if sys.argv[1] in SET_GLOBAL:
90
95
  if len(sys.argv) > 2:
91
96
  config_path = sys.argv[2]
@@ -229,6 +234,7 @@ class App:
229
234
  include_labels = []
230
235
  exclude_labels = []
231
236
  contains_strings = []
237
+ not_contains_strings = []
232
238
  include_branches = []
233
239
  exclude_branches = []
234
240
  modified = False
@@ -253,6 +259,10 @@ class App:
253
259
  exclude_branches.append(arg.split('=', 1)[1])
254
260
  elif any(arg.startswith(prefix) for prefix in NAME_PREFIX):
255
261
  contains_strings.append(arg.split('=', 1)[1])
262
+ elif any(arg.startswith(prefix) for prefix in NOT_NAME_PREFIX):
263
+ value = arg.split('=', 1)[1]
264
+ if value:
265
+ not_contains_strings.append(value)
256
266
  elif arg in MODIFIED_ATTR:
257
267
  modified = True
258
268
  elif arg in DIVERGED_ATTR:
@@ -297,6 +307,8 @@ class App:
297
307
  delete = True
298
308
  if any(contains_strings) and not any(substr in path for substr in contains_strings):
299
309
  delete = True
310
+ if any(not_contains_strings) and any(substr in path for substr in not_contains_strings):
311
+ delete = True
300
312
 
301
313
  if not delete and not repo.head_is_unborn and (any(include_branches) or any(exclude_branches)):
302
314
  if any(include_branches) and repo.head.shorthand not in include_branches: