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
./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] <repo> |
clone a repository (or archive) from the server |
list [-a] [pattern] |
list repositories on the server |
show <repo> |
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 <name> |
remove a command alias |
rescan |
refresh the cached server repository list |
!<command> |
run <command> in the shell |
Aliases
alias <name> '<command>' 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 <name> shows one, and
unalias <name> 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:
remoteurl = https://git.example.com # base URL of the server
remotekey = <personal-access-token> # 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 <description>) 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:
# --- 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.