Define mirror targets one way: remote.<name>.<field>

There were two spellings for the same thing -- a flat
remoteurl/remotekey/remotetype/remotevisibility set for a single server,
and remote.<name>.* blocks for several. The flat one is gone; every
target, including a lone one, is now a named block with the fields url,
key, type and visibility.

An existing ~/.mgshrc is converted on the next start. Only the key is
rewritten, so values, comments, alignment, commented-out lines and the
file's 0600 mode survive untouched, and mgsh prints each rename rather
than doing it quietly. The target is named "public", which is what the
old settings called the git remote they created, so a converted setup
keeps pushing to the same place under the same remote name. A file that
carries both spellings keeps what the new one says.

The environment follows the same shape: MGSH_REMOTEURL and friends are
replaced by MGSH_REMOTE_<NAME>_<FIELD>, so MGSH_REMOTE_GITLAB_KEY sets
remote.gitlab.key. The field is read from the end of the variable name,
which leaves target names free to contain underscores.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-26 18:00:26 +02:00
co-authored by Claude Opus 5
parent 61a7059f61
commit 2a622046f2
7 changed files with 301 additions and 99 deletions
+11 -16
View File
@@ -4,23 +4,18 @@ package main
// git hosting server (Gitea, GitHub or GitLab), creating the repository via the
// server's REST API when it does not exist yet.
//
// Configuration (in ~/.mgshrc, a project .mgshrc, or MGSH_* env) — either a
// single flat target:
// Configuration (in ~/.mgshrc, a project .mgshrc, or MGSH_* env): one
// remote.<name>.<field> block per server, which `pushremote` mirrors to in turn.
//
// remoteurl = https://git.example.com base URL of the server
// remotekey = <api-token> personal access token
// remotetype = gitea|github|gitlab optional; auto-detected from the URL
// remote.gitlab.url = https://gitlab.example.com
// remote.gitlab.key = <api-token>
// remote.gitlab.type = gitlab optional; detected from the url
// remote.gitlab.visibility = public or private (the default)
// remotes = gitlab optional: restrict/order the set
//
// or any number of named ones, which `pushremote` mirrors to in turn:
//
// remote.gitea.url = https://git.example.com
// remote.gitea.key = <api-token>
// remote.hub.url = https://github.com
// remote.hub.key = <api-token>
// remote.hub.visibility = public
// remotes = gitea, hub optional: restrict/order the set
//
// Each target owns a git remote of the same name in the repository.
// Each target owns a git remote of its own name in the repository. There is no
// second spelling: the pre-4.1 flat remoteurl/remotekey pair is migrated to
// remote.public.* on load.
//
// The token is used for the API calls and, via an HTTP Basic auth header, for
// the git push. It is never written into the repository's git config, and it is
@@ -287,7 +282,7 @@ func handlePushRemote(args string) {
targets = pickRemotes(targets, names)
if len(targets) == 0 {
if len(names) == 0 { // an unknown @name already reported itself
errorln("pushremote needs 'remoteurl'/'remotekey' or a 'remote.<name>.*' block in " + configFile())
errorln("pushremote needs a 'remote.<name>.url' and 'remote.<name>.key' in " + configFile())
}
return
}