Remove the view command and the editor setting with it

`view` was the last user of `editor`, so leaving the setting behind would
have made it exactly what `gitkey` was until recently: documented,
parsed, and doing nothing. It is gone from the struct, the template, the
environment, `config -k` and the settings table.

Both names are free for aliases now, as `open` already was.

The tests that used `editor` as their example of a project-overridable
setting use `gitkey` instead, which is the same kind of thing and still
exists.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-26 19:11:06 +02:00
co-authored by Claude Opus 5
parent 65342bcd7c
commit cd5ab1a2bd
10 changed files with 17 additions and 64 deletions
+3 -7
View File
@@ -40,7 +40,7 @@ stand in. Outside `base` no project is selected. `mgsh <project>` starts the
interactive shell with that project preselected. interactive shell with that project preselected.
The commands available directly from the shell are `clone`, `init`, `log`, The commands available directly from the shell are `clone`, `init`, `log`,
`push`, `pushremote`, `release`, `list`, `tag`, `archive`, `show`, `view`, `push`, `pushremote`, `release`, `list`, `tag`, `archive`, `show`,
`pull`, `fetch`, `status`, `diff`, `overview`, `config`, `count`, `login` and `pull`, `fetch`, `status`, `diff`, `overview`, `config`, `count`, `login` and
`cloneall`; every other command is interactive-only. `cloneall`; every other command is interactive-only.
@@ -52,7 +52,7 @@ project, its git branch and a `*` dirty marker:
``` ```
Features: command history (`~/.mgsh_history`), Tab completion (commands, local Features: command history (`~/.mgsh_history`), Tab completion (commands, local
projects for `cd`/`view`, server repos for `clone`/`show`, branches/tags for projects for `cd`, server repos for `clone`/`show`, branches/tags for
`checkout`/`tag`, mirror targets for `pushremote`/`release`, filesystem paths for `checkout`/`tag`, mirror targets for `pushremote`/`release`, filesystem paths for
`dist`, and shell-style completion after `!` and for aliases that expand to `dist`, and shell-style completion after `!` and for aliases that expand to
one), and colored `list`/`log`/error output. one), and colored `list`/`log`/error output.
@@ -106,7 +106,6 @@ Run `help` for the full list. Highlights:
| command | description | | command | description |
|---------------------------|------------------------------------------------| |---------------------------|------------------------------------------------|
| `cd [project]` | change project (no argument: back to the base) | | `cd [project]` | change project (no argument: back to the base) |
| `view [project]` | open a project in Xcode / the configured editor |
| `push [comment]` | commit everything and push to the server | | `push [comment]` | commit everything and push to the server |
| `pushremote [desc]` | mirror the repo to a public server (gitea/github/gitlab) | | `pushremote [desc]` | mirror the repo to a public server (gitea/github/gitlab) |
| `pull` / `fetch` | pull / fetch from the server | | `pull` / `fetch` | pull / fetch from the server |
@@ -145,7 +144,6 @@ project /Users/me/src/myproject/.mgshrc
gitport 22 gitport 22
gituser git gituser git
gitpath /home/git gitpath /home/git
editor code (.mgshrc)
remotes hub (.mgshrc) remotes hub (.mgshrc)
clone url ssh://git@git.example.com:22/home/git clone url ssh://git@git.example.com:22/home/git
@@ -410,7 +408,6 @@ gitpath = /home/git
gitname = Your Name gitname = Your Name
gitemail = you@example.com gitemail = you@example.com
pushdefault = matching pushdefault = matching
editor = code # fallback opener for `view`
alias co 'checkout $1' alias co 'checkout $1'
``` ```
@@ -436,7 +433,6 @@ setting.
| `gitname` | global | `user.name` written to the **global** git config at startup | | `gitname` | global | `user.name` written to the **global** git config at startup |
| `gitemail` | global | `user.email` written to the global git config | | `gitemail` | global | `user.email` written to the global git config |
| `pushdefault` | global | `push.default` written to the global git config | | `pushdefault` | global | `push.default` written to the global git config |
| `editor` | project | opener used by `view` when the project has no Xcode workspace (default `coda`) |
| `remote.<name>.url` | project | base URL of the mirror target `<name>` | | `remote.<name>.url` | project | base URL of the mirror target `<name>` |
| `remote.<name>.key` | project | API token for that target | | `remote.<name>.key` | project | API token for that target |
| `remote.<name>.type` | project | `gitea`\|`github`\|`gitlab`; auto-detected from the url when unset | | `remote.<name>.type` | project | `gitea`\|`github`\|`gitlab`; auto-detected from the url when unset |
@@ -453,7 +449,7 @@ only when they actually differ, so a plain `mgsh status` does not rewrite
A project may carry its own `.mgshrc`, which overrides the global settings while A project may carry its own `.mgshrc`, which overrides the global settings while
that project is active — a project on a different git server, with a different that project is active — a project on a different git server, with a different
editor, or mirrored to a different place: ssh identity, or mirrored to a different place:
```ini ```ini
# ~/src/myproject/.mgshrc # ~/src/myproject/.mgshrc
+1 -1
View File
@@ -46,7 +46,7 @@ var builtinCmds = map[string]bool{
"diff": true, "pull": true, "fetch": true, "push": true, "edit": true, "diff": true, "pull": true, "fetch": true, "push": true, "edit": true,
"pushremote": true, "overview": true, "archive": true, "init": true, "pushremote": true, "overview": true, "archive": true, "init": true,
"login": true, "cd": true, "checkout": true, "clone": true, "cloneall": true, "login": true, "cd": true, "checkout": true, "clone": true, "cloneall": true,
"view": true, "count": true, "tag": true, "alias": true, "count": true, "tag": true, "alias": true,
"unalias": true, "config": true, "release": true, "unalias": true, "config": true, "release": true,
} }
-37
View File
@@ -573,42 +573,6 @@ func runCommandDepth(line string, depth int) bool {
} }
} }
case "view": // open a project in Xcode / the configured editor
prj := PRJ
if w := word(words, 1); w != "" {
prj = w
}
d := BASE + "/" + prj
if !validProject(prj) || !isDir(d) {
errorln("not found")
break
}
xws, xprj := "", ""
if entries, err := os.ReadDir(d); err == nil {
for _, e := range entries {
if strings.HasSuffix(e.Name(), ".xcworkspace") {
xws = e.Name()
}
if strings.HasSuffix(e.Name(), ".xcodeproj") {
xprj = e.Name()
}
}
}
// prefer the workspace over the project; fall back to the editor unless
// one of them is really openable (a name match on a plain file is not).
switch {
case xws != "" && isDir(d+"/"+xws):
runInDir(d, "open", xws)
case xprj != "" && isDir(d+"/"+xprj):
runInDir(d, "open", xprj)
default:
editor := cfg.Editor
if editor == "" {
editor = "coda"
}
runInDir(d, editor, d)
}
case "count": // count source lines in the project case "count": // count source lines in the project
if !requireProject() { if !requireProject() {
break break
@@ -806,7 +770,6 @@ const gitignore = `.DS_Store
var helpItems = []struct{ cmd, desc string }{ var helpItems = []struct{ cmd, desc string }{
{"cd [project]", "change project (no argument: back to the base)"}, {"cd [project]", "change project (no argument: back to the base)"},
{"view [project]", "open project in Xcode / the editor"},
{"init", "make new repository from current directory"}, {"init", "make new repository from current directory"},
{"push [comment]", "push changes to git server"}, {"push [comment]", "push changes to git server"},
{"pushremote [@name] [desc]", "mirror repo to the public server(s) (gitea/github/gitlab)"}, {"pushremote [@name] [desc]", "mirror repo to the public server(s) (gitea/github/gitlab)"},
+1 -2
View File
@@ -45,14 +45,13 @@ func runeSuffixes(cands []string, prefix string) ([][]rune, int) {
} }
// builtinCompleter is the command tree. Command names complete at the start of // builtinCompleter is the command tree. Command names complete at the start of
// the line; cd/view complete local project names; clone/show complete // the line; cd completes local project names; clone/show complete
// repository names cached from the git server; checkout/tag complete branch and // repository names cached from the git server; checkout/tag complete branch and
// tag names; pushremote/release complete mirror targets; dist completes // tag names; pushremote/release complete mirror targets; dist completes
// filesystem paths. // filesystem paths.
func builtinCompleter() *readline.PrefixCompleter { func builtinCompleter() *readline.PrefixCompleter {
return readline.NewPrefixCompleter( return readline.NewPrefixCompleter(
readline.PcItem("cd", readline.PcItemDynamic(dynLocalProjects)), readline.PcItem("cd", readline.PcItemDynamic(dynLocalProjects)),
readline.PcItem("view", readline.PcItemDynamic(dynLocalProjects)),
readline.PcItem("clone", readline.PcItem("clone",
readline.PcItem("-a", readline.PcItemDynamic(dynServerArchives)), readline.PcItem("-a", readline.PcItemDynamic(dynServerArchives)),
readline.PcItemDynamic(dynServerRepos), readline.PcItemDynamic(dynServerRepos),
+1 -4
View File
@@ -33,7 +33,6 @@ type Config struct {
GitName string // git user.name to set globally ("" = leave alone) GitName string // git user.name to set globally ("" = leave alone)
GitEmail string // git user.email to set globally ("" = leave alone) GitEmail string // git user.email to set globally ("" = leave alone)
PushDefault string // git push.default to set globally ("" = leave alone) PushDefault string // git push.default to set globally ("" = leave alone)
Editor string // editor/opener used as fallback by `view` ("" = coda)
Mirror string // truthy -> `push` also mirrors via `pushremote` Mirror string // truthy -> `push` also mirrors via `pushremote`
SecretScan string // falsy -> `push` skips the credential scan SecretScan string // falsy -> `push` skips the credential scan
Remotes []RemoteTarget Remotes []RemoteTarget
@@ -320,7 +319,7 @@ func writeConfigTemplate(path string) {
b.WriteString("# gitname = Your Name\n") b.WriteString("# gitname = Your Name\n")
b.WriteString("# gitemail = you@example.com\n") b.WriteString("# gitemail = you@example.com\n")
b.WriteString("# pushdefault = matching\n") b.WriteString("# pushdefault = matching\n")
b.WriteString("# editor = code\n\n") b.WriteString("\n")
b.WriteString("# --- public mirrors for `pushremote` ---\n") b.WriteString("# --- public mirrors for `pushremote` ---\n")
b.WriteString("# One 'remote.<name>.*' block per server. `pushremote` pushes to all\n") b.WriteString("# One 'remote.<name>.*' block per server. `pushremote` pushes to all\n")
b.WriteString("# of them, `pushremote @gitlab` to a single one. <name> is also the\n") b.WriteString("# of them, `pushremote @gitlab` to a single one. <name> is also the\n")
@@ -409,7 +408,6 @@ func applyConfig(c *Config, m map[string]string) {
set("gitname", &c.GitName) set("gitname", &c.GitName)
set("gitemail", &c.GitEmail) set("gitemail", &c.GitEmail)
set("pushdefault", &c.PushDefault) set("pushdefault", &c.PushDefault)
set("editor", &c.Editor)
set("remotes", &c.RemoteNames) set("remotes", &c.RemoteNames)
set("mirror", &c.Mirror) set("mirror", &c.Mirror)
set("secretscan", &c.SecretScan) set("secretscan", &c.SecretScan)
@@ -505,7 +503,6 @@ func applyEnv(c *Config) {
env("MGSH_GITNAME", &c.GitName) env("MGSH_GITNAME", &c.GitName)
env("MGSH_GITEMAIL", &c.GitEmail) env("MGSH_GITEMAIL", &c.GitEmail)
env("MGSH_PUSHDEFAULT", &c.PushDefault) env("MGSH_PUSHDEFAULT", &c.PushDefault)
env("MGSH_EDITOR", &c.Editor)
env("MGSH_REMOTES", &c.RemoteNames) env("MGSH_REMOTES", &c.RemoteNames)
env("MGSH_MIRROR", &c.Mirror) env("MGSH_MIRROR", &c.Mirror)
env("MGSH_SECRETSCAN", &c.SecretScan) env("MGSH_SECRETSCAN", &c.SecretScan)
+1 -1
View File
@@ -145,7 +145,7 @@ func parseArgs() (int, string, bool) {
} }
cls := map[string]int{ cls := map[string]int{
"clone": 2, "init": 2, "log": 2, "clone": 2, "init": 2, "log": 2,
"push": 1, "pushremote": 1, "list": 1, "tag": 1, "archive": 1, "show": 1, "view": 1, "push": 1, "pushremote": 1, "list": 1, "tag": 1, "archive": 1, "show": 1,
"pull": 1, "fetch": 1, "status": 1, "diff": 1, "overview": 1, "pull": 1, "fetch": 1, "status": 1, "diff": 1, "overview": 1,
"config": 1, "count": 1, "login": 1, "cloneall": 1, "release": 1, "config": 1, "count": 1, "login": 1, "cloneall": 1, "release": 1,
} }
+8 -8
View File
@@ -142,7 +142,7 @@ func TestParseConfig(t *testing.T) {
githost = 10.0.0.1 githost = 10.0.0.1
GitPort: 22 GitPort: 22
gituser = "deploy" gituser = "deploy"
editor = 'code' gitkey = 'mgit_rsa'
ignored line without separator ignored line without separator
base=/tmp/src base=/tmp/src
` `
@@ -151,7 +151,7 @@ base=/tmp/src
"githost": "10.0.0.1", "githost": "10.0.0.1",
"gitport": "22", "gitport": "22",
"gituser": "deploy", "gituser": "deploy",
"editor": "code", "gitkey": "mgit_rsa",
"base": "/tmp/src", "base": "/tmp/src",
} }
for k, want := range checks { for k, want := range checks {
@@ -166,7 +166,7 @@ base=/tmp/src
func TestParseConfigInlineComments(t *testing.T) { func TestParseConfigInlineComments(t *testing.T) {
rc := ` rc := `
editor = code # fallback opener for ` + "`open`" + ` gitkey = mgit_rsa # fallback opener comment
mirror = true # ` + "`push`" + ` also mirrors via pushremote mirror = true # ` + "`push`" + ` also mirrors via pushremote
gitport = 22 # ssh port gitport = 22 # ssh port
remotekey = abc#123 remotekey = abc#123
@@ -176,7 +176,7 @@ gitemail = # value is only a comment
` `
m := parseConfig(rc) m := parseConfig(rc)
checks := map[string]string{ checks := map[string]string{
"editor": "code", "gitkey": "mgit_rsa",
"mirror": "true", "mirror": "true",
"gitport": "22", "gitport": "22",
"remotekey": "abc#123", // '#' not preceded by space stays part of the value "remotekey": "abc#123", // '#' not preceded by space stays part of the value
@@ -539,7 +539,7 @@ func TestResolveProjectConfig(t *testing.T) {
dir := t.TempDir() dir := t.TempDir()
global := Config{ global := Config{
Base: "/base", GitHost: "global.example", GitPort: "22", GitUser: "git", Base: "/base", GitHost: "global.example", GitPort: "22", GitUser: "git",
GitPath: "/home/git", GitName: "Global Name", Editor: "vi", GitPath: "/home/git", GitName: "Global Name", GitKey: "global_rsa",
Remotes: []RemoteTarget{{Name: "gitea", URL: "https://gitea.example", Key: "tok"}}, Remotes: []RemoteTarget{{Name: "gitea", URL: "https://gitea.example", Key: "tok"}},
} }
@@ -550,7 +550,7 @@ func TestResolveProjectConfig(t *testing.T) {
rc := ` rc := `
githost = project.example githost = project.example
editor = code gitkey = project_rsa
base = /somewhere/else base = /somewhere/else
gitname = Project Name gitname = Project Name
remote.hub.url = https://github.com remote.hub.url = https://github.com
@@ -562,8 +562,8 @@ remote.gitea.visibility = public
} }
got := resolveConfig(global, dir) got := resolveConfig(global, dir)
if got.GitHost != "project.example" || got.Editor != "code" { if got.GitHost != "project.example" || got.GitKey != "project_rsa" {
t.Errorf("project overrides not applied: host=%q editor=%q", got.GitHost, got.Editor) t.Errorf("project overrides not applied: host=%q gitkey=%q", got.GitHost, got.GitKey)
} }
// base and the git identity stay global // base and the git identity stay global
if got.Base != "/base" { if got.Base != "/base" {
-1
View File
@@ -20,7 +20,6 @@ gitpath = /home/git
# gitname = Your Name # gitname = Your Name
# gitemail = you@example.com # gitemail = you@example.com
# pushdefault = matching # pushdefault = matching
# editor = code
# --- pushremote: mirror to public servers (gitea/github/gitlab) via their API --- # --- pushremote: mirror to public servers (gitea/github/gitlab) via their API ---
# One "remote.<name>.<field>" block per server, with the fields url, key, type # One "remote.<name>.<field>" block per server, with the fields url, key, type
+1 -2
View File
@@ -42,7 +42,6 @@ func showConfig() {
{"gitname", cfg.GitName}, {"gitname", cfg.GitName},
{"gitemail", cfg.GitEmail}, {"gitemail", cfg.GitEmail},
{"pushdefault", cfg.PushDefault}, {"pushdefault", cfg.PushDefault},
{"editor", cfg.Editor},
{"mirror", cfg.Mirror}, {"mirror", cfg.Mirror},
{"secretscan", cfg.SecretScan}, {"secretscan", cfg.SecretScan},
{"remotes", cfg.RemoteNames}, {"remotes", cfg.RemoteNames},
@@ -146,7 +145,7 @@ func envName(key string) string { return "MGSH_" + strings.ToUpper(key) }
func configKeys() []string { func configKeys() []string {
keys := []string{ keys := []string{
"base", "githost", "gitport", "gituser", "gitpath", "gitkey", "base", "githost", "gitport", "gituser", "gitpath", "gitkey",
"gitname", "gitemail", "pushdefault", "editor", "gitname", "gitemail", "pushdefault",
"remotes", "mirror", "secretscan", "remotes", "mirror", "secretscan",
} }
sort.Strings(keys) sort.Strings(keys)
+1 -1
View File
@@ -1 +1 @@
4.0.48 4.0.50