Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd5ab1a2bd | ||
|
|
65342bcd7c |
@@ -40,7 +40,7 @@ stand in. Outside `base` no project is selected. `mgsh <project>` starts the
|
||||
interactive shell with that project preselected.
|
||||
|
||||
The commands available directly from the shell are `clone`, `init`, `log`,
|
||||
`push`, `pushremote`, `release`, `list`, `tag`, `archive`, `show`, `open`,
|
||||
`push`, `pushremote`, `release`, `list`, `tag`, `archive`, `show`,
|
||||
`pull`, `fetch`, `status`, `diff`, `overview`, `config`, `count`, `login` and
|
||||
`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
|
||||
projects for `cd`/`open`, 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
|
||||
`dist`, and shell-style completion after `!` and for aliases that expand to
|
||||
one), and colored `list`/`log`/error output.
|
||||
@@ -144,7 +144,6 @@ project /Users/me/src/myproject/.mgshrc
|
||||
gitport 22
|
||||
gituser git
|
||||
gitpath /home/git
|
||||
editor code (.mgshrc)
|
||||
remotes hub (.mgshrc)
|
||||
clone url ssh://git@git.example.com:22/home/git
|
||||
|
||||
@@ -409,7 +408,6 @@ gitpath = /home/git
|
||||
gitname = Your Name
|
||||
gitemail = you@example.com
|
||||
pushdefault = matching
|
||||
editor = code # fallback opener for `open`
|
||||
|
||||
alias co 'checkout $1'
|
||||
```
|
||||
@@ -435,7 +433,6 @@ setting.
|
||||
| `gitname` | global | `user.name` written to the **global** git config at startup |
|
||||
| `gitemail` | global | `user.email` written to the global git config |
|
||||
| `pushdefault` | global | `push.default` written to the global git config |
|
||||
| `editor` | project | opener used by `open`/`view` when the project has no Xcode workspace (default `coda`) |
|
||||
| `remote.<name>.url` | project | base URL of the mirror target `<name>` |
|
||||
| `remote.<name>.key` | project | API token for that target |
|
||||
| `remote.<name>.type` | project | `gitea`\|`github`\|`gitlab`; auto-detected from the url when unset |
|
||||
@@ -452,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
|
||||
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
|
||||
# ~/src/myproject/.mgshrc
|
||||
|
||||
@@ -46,7 +46,7 @@ var builtinCmds = map[string]bool{
|
||||
"diff": true, "pull": true, "fetch": true, "push": true, "edit": true,
|
||||
"pushremote": true, "overview": true, "archive": true, "init": true,
|
||||
"login": true, "cd": true, "checkout": true, "clone": true, "cloneall": true,
|
||||
"open": true, "view": true, "count": true, "tag": true, "alias": true,
|
||||
"count": true, "tag": true, "alias": true,
|
||||
"unalias": true, "config": true, "release": true,
|
||||
}
|
||||
|
||||
|
||||
-40
@@ -573,45 +573,6 @@ func runCommandDepth(line string, depth int) bool {
|
||||
}
|
||||
}
|
||||
|
||||
case "open", "view": // open project in Xcode / 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)
|
||||
}
|
||||
if words[0] == "open" {
|
||||
PRJ = prj
|
||||
}
|
||||
|
||||
case "count": // count source lines in the project
|
||||
if !requireProject() {
|
||||
break
|
||||
@@ -809,7 +770,6 @@ const gitignore = `.DS_Store
|
||||
|
||||
var helpItems = []struct{ cmd, desc string }{
|
||||
{"cd [project]", "change project (no argument: back to the base)"},
|
||||
{"open [project]", "open project"},
|
||||
{"init", "make new repository from current directory"},
|
||||
{"push [comment]", "push changes to git server"},
|
||||
{"pushremote [@name] [desc]", "mirror repo to the public server(s) (gitea/github/gitlab)"},
|
||||
|
||||
+1
-3
@@ -45,15 +45,13 @@ func runeSuffixes(cands []string, prefix string) ([][]rune, int) {
|
||||
}
|
||||
|
||||
// builtinCompleter is the command tree. Command names complete at the start of
|
||||
// the line; cd/open/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
|
||||
// tag names; pushremote/release complete mirror targets; dist completes
|
||||
// filesystem paths.
|
||||
func builtinCompleter() *readline.PrefixCompleter {
|
||||
return readline.NewPrefixCompleter(
|
||||
readline.PcItem("cd", readline.PcItemDynamic(dynLocalProjects)),
|
||||
readline.PcItem("open", readline.PcItemDynamic(dynLocalProjects)),
|
||||
readline.PcItem("view", readline.PcItemDynamic(dynLocalProjects)),
|
||||
readline.PcItem("clone",
|
||||
readline.PcItem("-a", readline.PcItemDynamic(dynServerArchives)),
|
||||
readline.PcItemDynamic(dynServerRepos),
|
||||
|
||||
@@ -33,7 +33,6 @@ type Config struct {
|
||||
GitName string // git user.name 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)
|
||||
Editor string // editor/opener used as fallback by `open` ("" = coda)
|
||||
Mirror string // truthy -> `push` also mirrors via `pushremote`
|
||||
SecretScan string // falsy -> `push` skips the credential scan
|
||||
Remotes []RemoteTarget
|
||||
@@ -320,7 +319,7 @@ func writeConfigTemplate(path string) {
|
||||
b.WriteString("# gitname = Your Name\n")
|
||||
b.WriteString("# gitemail = you@example.com\n")
|
||||
b.WriteString("# pushdefault = matching\n")
|
||||
b.WriteString("# editor = code\n\n")
|
||||
b.WriteString("\n")
|
||||
b.WriteString("# --- public mirrors for `pushremote` ---\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")
|
||||
@@ -409,7 +408,6 @@ func applyConfig(c *Config, m map[string]string) {
|
||||
set("gitname", &c.GitName)
|
||||
set("gitemail", &c.GitEmail)
|
||||
set("pushdefault", &c.PushDefault)
|
||||
set("editor", &c.Editor)
|
||||
set("remotes", &c.RemoteNames)
|
||||
set("mirror", &c.Mirror)
|
||||
set("secretscan", &c.SecretScan)
|
||||
@@ -505,7 +503,6 @@ func applyEnv(c *Config) {
|
||||
env("MGSH_GITNAME", &c.GitName)
|
||||
env("MGSH_GITEMAIL", &c.GitEmail)
|
||||
env("MGSH_PUSHDEFAULT", &c.PushDefault)
|
||||
env("MGSH_EDITOR", &c.Editor)
|
||||
env("MGSH_REMOTES", &c.RemoteNames)
|
||||
env("MGSH_MIRROR", &c.Mirror)
|
||||
env("MGSH_SECRETSCAN", &c.SecretScan)
|
||||
|
||||
@@ -145,7 +145,7 @@ func parseArgs() (int, string, bool) {
|
||||
}
|
||||
cls := map[string]int{
|
||||
"clone": 2, "init": 2, "log": 2,
|
||||
"push": 1, "pushremote": 1, "list": 1, "tag": 1, "archive": 1, "show": 1, "open": 1,
|
||||
"push": 1, "pushremote": 1, "list": 1, "tag": 1, "archive": 1, "show": 1,
|
||||
"pull": 1, "fetch": 1, "status": 1, "diff": 1, "overview": 1,
|
||||
"config": 1, "count": 1, "login": 1, "cloneall": 1, "release": 1,
|
||||
}
|
||||
|
||||
+8
-8
@@ -142,7 +142,7 @@ func TestParseConfig(t *testing.T) {
|
||||
githost = 10.0.0.1
|
||||
GitPort: 22
|
||||
gituser = "deploy"
|
||||
editor = 'code'
|
||||
gitkey = 'mgit_rsa'
|
||||
ignored line without separator
|
||||
base=/tmp/src
|
||||
`
|
||||
@@ -151,7 +151,7 @@ base=/tmp/src
|
||||
"githost": "10.0.0.1",
|
||||
"gitport": "22",
|
||||
"gituser": "deploy",
|
||||
"editor": "code",
|
||||
"gitkey": "mgit_rsa",
|
||||
"base": "/tmp/src",
|
||||
}
|
||||
for k, want := range checks {
|
||||
@@ -166,7 +166,7 @@ base=/tmp/src
|
||||
|
||||
func TestParseConfigInlineComments(t *testing.T) {
|
||||
rc := `
|
||||
editor = code # fallback opener for ` + "`open`" + `
|
||||
gitkey = mgit_rsa # fallback opener comment
|
||||
mirror = true # ` + "`push`" + ` also mirrors via pushremote
|
||||
gitport = 22 # ssh port
|
||||
remotekey = abc#123
|
||||
@@ -176,7 +176,7 @@ gitemail = # value is only a comment
|
||||
`
|
||||
m := parseConfig(rc)
|
||||
checks := map[string]string{
|
||||
"editor": "code",
|
||||
"gitkey": "mgit_rsa",
|
||||
"mirror": "true",
|
||||
"gitport": "22",
|
||||
"remotekey": "abc#123", // '#' not preceded by space stays part of the value
|
||||
@@ -539,7 +539,7 @@ func TestResolveProjectConfig(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
global := Config{
|
||||
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"}},
|
||||
}
|
||||
|
||||
@@ -550,7 +550,7 @@ func TestResolveProjectConfig(t *testing.T) {
|
||||
|
||||
rc := `
|
||||
githost = project.example
|
||||
editor = code
|
||||
gitkey = project_rsa
|
||||
base = /somewhere/else
|
||||
gitname = Project Name
|
||||
remote.hub.url = https://github.com
|
||||
@@ -562,8 +562,8 @@ remote.gitea.visibility = public
|
||||
}
|
||||
got := resolveConfig(global, dir)
|
||||
|
||||
if got.GitHost != "project.example" || got.Editor != "code" {
|
||||
t.Errorf("project overrides not applied: host=%q editor=%q", got.GitHost, got.Editor)
|
||||
if got.GitHost != "project.example" || got.GitKey != "project_rsa" {
|
||||
t.Errorf("project overrides not applied: host=%q gitkey=%q", got.GitHost, got.GitKey)
|
||||
}
|
||||
// base and the git identity stay global
|
||||
if got.Base != "/base" {
|
||||
|
||||
@@ -20,7 +20,6 @@ gitpath = /home/git
|
||||
# gitname = Your Name
|
||||
# gitemail = you@example.com
|
||||
# pushdefault = matching
|
||||
# editor = code
|
||||
|
||||
# --- pushremote: mirror to public servers (gitea/github/gitlab) via their API ---
|
||||
# One "remote.<name>.<field>" block per server, with the fields url, key, type
|
||||
|
||||
@@ -214,10 +214,10 @@ func TestShellCandidatesThroughAlias(t *testing.T) {
|
||||
shellTree(t)
|
||||
fakeCommands(t, "vi", "ls")
|
||||
withAliases(t, map[string]string{
|
||||
"ll": "!ls -la",
|
||||
"e": "!vi $1",
|
||||
"co": "checkout $1", // a builtin, not a shell command
|
||||
"open": "!xdg-open $1", // shadows a builtin: must not count
|
||||
"ll": "!ls -la",
|
||||
"e": "!vi $1",
|
||||
"co": "checkout $1", // expands to a builtin, not a shell command
|
||||
"status": "!git status", // shadows a builtin: must not count
|
||||
})
|
||||
|
||||
// arguments of a shell alias complete against the filesystem
|
||||
@@ -246,7 +246,7 @@ func TestShellCandidatesThroughAlias(t *testing.T) {
|
||||
t.Error("an alias expanding to a builtin was treated as a shell line")
|
||||
}
|
||||
// nor is a name that a builtin owns, since runCommand never expands those
|
||||
if _, _, ok := shellLine("open ma"); ok {
|
||||
if _, _, ok := shellLine("status ma"); ok {
|
||||
t.Error("a builtin name was resolved through an alias")
|
||||
}
|
||||
// nor an undefined name
|
||||
|
||||
+1
-2
@@ -42,7 +42,6 @@ func showConfig() {
|
||||
{"gitname", cfg.GitName},
|
||||
{"gitemail", cfg.GitEmail},
|
||||
{"pushdefault", cfg.PushDefault},
|
||||
{"editor", cfg.Editor},
|
||||
{"mirror", cfg.Mirror},
|
||||
{"secretscan", cfg.SecretScan},
|
||||
{"remotes", cfg.RemoteNames},
|
||||
@@ -146,7 +145,7 @@ func envName(key string) string { return "MGSH_" + strings.ToUpper(key) }
|
||||
func configKeys() []string {
|
||||
keys := []string{
|
||||
"base", "githost", "gitport", "gituser", "gitpath", "gitkey",
|
||||
"gitname", "gitemail", "pushdefault", "editor",
|
||||
"gitname", "gitemail", "pushdefault",
|
||||
"remotes", "mirror", "secretscan",
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
4.0.46
|
||||
4.0.50
|
||||
|
||||
Reference in New Issue
Block a user