# mgsh — git shell A small interactive shell / command-line wrapper around a self-hosted bare git server reachable over ssh. It manages a flat set of projects living under a base directory (default `$HOME/src`, or `/db/src` on Linux). Go port of the original Perl `mgsh` (`mgsh.perl`). ## Build ```sh ./build.sh # builds ./mgsh and bumps the patch version by 0.0.1 go build -o mgsh . # plain build, keeps the default version ``` `build.sh` reads `version.txt`, increments the patch component, injects it via `-ldflags -X main.VERSION`, and writes it back — so `version.txt` always holds the version of the binary just built. Dependencies are fetched via Go modules (`go.mod` / `go.sum`) on first build. Run the tests with `go test ./...`. ## Usage Launch `mgsh` for the interactive shell, or run a single command directly from a project directory, e.g. `mgsh push "message"`, `mgsh status`, `mgsh log`. The interactive prompt is colored (Catppuccin Mocha) and shows the active project, its git branch and a `*` dirty marker: ``` < src/myproject (master*) > ``` Features: command history (`~/.mgsh_history`), Tab completion (commands, local projects for `cd`/`open`, server repos for `clone`/`show`, branches/tags for `checkout`/`tag`, filesystem paths for `dist`), and colored `list`/`log`/error output. Exit with `quit`, `exit`, `Ctrl-D`, or `Ctrl-C` on an empty line. ### Shell escape Unknown commands are **not** forwarded to a shell. To run a shell command, prefix it with `!`: ``` < src/myproject > !ls -la ``` ### Commands Run `help` for the full list. Highlights: | command | description | |---------------------------|------------------------------------------------| | `cd [project]` | change project | | `push [comment]` | commit everything and push to the server | | `pushremote [desc]` | mirror the repo to a public server (gitea/github/gitlab) | | `pull` / `fetch` | pull / fetch from the server | | `status [-a]` / `diff` | short git status (`-a`: overview of all projects) | | `overview` | dirty / ahead-behind summary of all projects | | `log` | show the project log | | `edit [n]` | interactive rebase of the last n commits | | `clone [-a] ` | clone a repository (or archive) from the server | | `list [-a] [pattern]` | list repositories on the server | | `show ` | show a repository log directly on the server | | `archive [comment]` | snapshot the server-side repo into `./archive` | | `init` | make a new repository from the current directory | | `tag [add/checkout/delete]` | manage tags | | `alias [name [cmd]]` | list, show or define a command alias | | `unalias ` | remove a command alias | | `rescan` | refresh the cached server repository list | | `!` | run `` in the shell | ### Aliases `alias ''` defines a reusable shortcut, persisted to `~/.mgshrc` and reloaded on every start. The expansion is itself a mgsh command line and may reference the alias arguments: | placeholder | meaning | |-------------|----------------------------------| | `$1` … `$N` | the Nth argument (empty if unset)| | `$*` / `$@` | all arguments, space-joined | When the expansion contains no placeholder, the arguments are appended (classic shell-alias behaviour). Because unknown commands are **not** forwarded to a shell, a shell command inside an alias needs the `!` prefix: ``` alias co 'checkout $1' # co v2 -> checkout v2 (builtin) alias p 'push $*' # p fixed bug -> push fixed bug (builtin) alias ec '!echo $1' # ec hello -> echo hello (shell) ``` `alias` with no arguments lists all aliases, `alias ` shows one, and `unalias ` removes it. Aliases cannot shadow builtin commands. ### Public mirror (`pushremote`) Besides the internal ssh git server, `pushremote` mirrors the active project to a public hosting server (Gitea, GitHub or GitLab) over its REST API. It reads two settings from `~/.mgshrc`: ```ini remoteurl = https://git.example.com # base URL of the server remotekey = # API token # remotetype = gitea # optional; auto-detected from remoteurl # remotevisibility = private # visibility of created repos (default private) # mirror = true # `push` also mirrors via pushremote ``` `pushremote` authenticates with the token, creates the repository (named after the current project) if it does not exist yet, adds a credential-free remote named `public`, and pushes all branches and tags. New repositories are **private** unless `remotevisibility = public`; any words after the command (`pushremote `) are set as the repository description on creation. The token is sent as a one-shot HTTP auth header, never written into the repo's git config. The provider is auto-detected from `remoteurl` (`github.com` → GitHub, `gitlab*` → GitLab, otherwise Gitea) and can be forced with `remotetype`. Set `mirror = true` to have every `push` mirror automatically. ## Configuration mgsh has **no built-in defaults**. Configuration comes entirely from `~/.mgshrc` (overlaid with `MGSH_*` environment variables). On first run mgsh writes a blank, annotated `~/.mgshrc` template (migrating any aliases from a pre-4.x `~/.mgsh_aliases`) and then exits with an error until the required settings — `base`, `githost`, `gitport`, `gituser`, `gitpath` — are filled in. It uses simple `key = value` (or `key: value`) lines (`#` comments allowed); alias definitions live in the same file: ```ini # --- required --- base = /Users/me/src githost = git.example.com gitport = 22 gituser = git gitpath = /home/git # --- optional --- gitname = Your Name gitemail = you@example.com pushdefault = matching editor = code # fallback opener for `open` alias co 'checkout $1' ``` Environment overrides: `MGSH_BASE`, `MGSH_GITHOST`, `MGSH_GITPORT`, `MGSH_GITUSER`, `MGSH_GITPATH`, `MGSH_GITKEY`, `MGSH_GITNAME`, `MGSH_GITEMAIL`, `MGSH_PUSHDEFAULT`, `MGSH_EDITOR`, `MGSH_REMOTEURL`, `MGSH_REMOTEKEY`, `MGSH_REMOTETYPE`, `MGSH_REMOTEVISIBILITY`, `MGSH_MIRROR`. See `mgshrc.example` for an annotated template.