Allow quoted keys for --git-config
This allows keys to contain literal ':' which would previously confuse the parser.
This commit is contained in:
parent
b07dba4026
commit
25295dd0de
|
|
@ -127,7 +127,7 @@ var flAskPassURL = flag.String("askpass-url", envString("GIT_ASKPASS_URL", ""),
|
|||
var flGitCmd = flag.String("git", envString("GIT_SYNC_GIT", "git"),
|
||||
"the git command to run (subject to PATH search, mostly for testing)")
|
||||
var flGitConfig = flag.String("git-config", envString("GIT_SYNC_GIT_CONFIG", ""),
|
||||
"additional git config options in 'key1:val1,key2:val2' format")
|
||||
"additional git config options in 'section.var1:val1,\"section.sub.var2\":\"val2\"' format")
|
||||
var flGitGC = flag.String("git-gc", envString("GIT_SYNC_GIT_GC", "auto"),
|
||||
"git garbage collection behavior: one of 'auto', 'always', 'aggressive', or 'off'")
|
||||
|
||||
|
|
@ -1293,9 +1293,19 @@ func parseGitConfigs(configsFlag string) ([]keyVal, error) {
|
|||
if r, ok := <-ch; !ok {
|
||||
break
|
||||
} else {
|
||||
cur.key, err = parseGitConfigKey(r, ch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// This can accumulate things that git doesn't allow, but we'll
|
||||
// just let git handle it, rather than try to pre-validate to their
|
||||
// spec.
|
||||
if r == '"' {
|
||||
cur.key, err = parseGitConfigQKey(ch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
cur.key, err = parseGitConfigKey(r, ch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1322,6 +1332,23 @@ func parseGitConfigs(configsFlag string) ([]keyVal, error) {
|
|||
return result, nil
|
||||
}
|
||||
|
||||
func parseGitConfigQKey(ch <-chan rune) (string, error) {
|
||||
str, err := parseQString(ch)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// The next character must be a colon.
|
||||
r, ok := <-ch
|
||||
if !ok {
|
||||
return "", fmt.Errorf("unexpected end of key: %q", str)
|
||||
}
|
||||
if r != ':' {
|
||||
return "", fmt.Errorf("unexpected character after quoted key: %q%c", str, r)
|
||||
}
|
||||
return str, nil
|
||||
}
|
||||
|
||||
func parseGitConfigKey(r rune, ch <-chan rune) (string, error) {
|
||||
buf := make([]rune, 0, 64)
|
||||
buf = append(buf, r)
|
||||
|
|
@ -1331,9 +1358,6 @@ func parseGitConfigKey(r rune, ch <-chan rune) (string, error) {
|
|||
case r == ':':
|
||||
return string(buf), nil
|
||||
default:
|
||||
// This can accumulate things that git doesn't allow, but we'll
|
||||
// just let git handle it, rather than try to pre-validate to their
|
||||
// spec.
|
||||
buf = append(buf, r)
|
||||
}
|
||||
}
|
||||
|
|
@ -1341,30 +1365,17 @@ func parseGitConfigKey(r rune, ch <-chan rune) (string, error) {
|
|||
}
|
||||
|
||||
func parseGitConfigQVal(ch <-chan rune) (string, error) {
|
||||
buf := make([]rune, 0, 64)
|
||||
|
||||
for r := range ch {
|
||||
switch r {
|
||||
case '\\':
|
||||
if e, err := unescape(ch); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
buf = append(buf, e)
|
||||
}
|
||||
case '"':
|
||||
// Once we have a closing quote, the next must be either a comma or
|
||||
// end-of-string. This helps reset the state for the next key, if
|
||||
// there is one.
|
||||
r, ok := <-ch
|
||||
if ok && r != ',' {
|
||||
return "", fmt.Errorf("unexpected trailing character '%c'", r)
|
||||
}
|
||||
return string(buf), nil
|
||||
default:
|
||||
buf = append(buf, r)
|
||||
}
|
||||
str, err := parseQString(ch)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "", fmt.Errorf("unexpected end of value: %q", string(buf))
|
||||
|
||||
// If there is a next character, it must be a comma.
|
||||
r, ok := <-ch
|
||||
if ok && r != ',' {
|
||||
return "", fmt.Errorf("unexpected character after quoted value %q%c", str, r)
|
||||
}
|
||||
return str, nil
|
||||
}
|
||||
|
||||
func parseGitConfigVal(r rune, ch <-chan rune) (string, error) {
|
||||
|
|
@ -1389,6 +1400,26 @@ func parseGitConfigVal(r rune, ch <-chan rune) (string, error) {
|
|||
return string(buf), nil
|
||||
}
|
||||
|
||||
func parseQString(ch <-chan rune) (string, error) {
|
||||
buf := make([]rune, 0, 64)
|
||||
|
||||
for r := range ch {
|
||||
switch r {
|
||||
case '\\':
|
||||
if e, err := unescape(ch); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
buf = append(buf, e)
|
||||
}
|
||||
case '"':
|
||||
return string(buf), nil
|
||||
default:
|
||||
buf = append(buf, r)
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("unexpected end of quoted string: %q", string(buf))
|
||||
}
|
||||
|
||||
// unescape processes most of the documented escapes that git config supports.
|
||||
func unescape(ch <-chan rune) (rune, error) {
|
||||
r, ok := <-ch
|
||||
|
|
|
|||
|
|
@ -114,26 +114,66 @@ func TestParseGitConfigs(t *testing.T) {
|
|||
name: "one-pair",
|
||||
input: `k:v`,
|
||||
expect: []keyVal{keyVal{"k", "v"}},
|
||||
}, {
|
||||
name: "one-pair-qkey",
|
||||
input: `"k":v`,
|
||||
expect: []keyVal{keyVal{"k", "v"}},
|
||||
}, {
|
||||
name: "one-pair-qval",
|
||||
input: `k:"v"`,
|
||||
expect: []keyVal{keyVal{"k", "v"}},
|
||||
}, {
|
||||
name: "one-pair-qkey-qval",
|
||||
input: `"k":"v"`,
|
||||
expect: []keyVal{keyVal{"k", "v"}},
|
||||
}, {
|
||||
name: "multi-pair",
|
||||
input: `k1:v1,"k2":v2,k3:"v3","k4":"v4"`,
|
||||
expect: []keyVal{{"k1", "v1"}, {"k2", "v2"}, {"k3", "v3"}, {"k4", "v4"}},
|
||||
}, {
|
||||
name: "garbage",
|
||||
input: `abc123`,
|
||||
fail: true,
|
||||
}, {
|
||||
name: "invalid-val",
|
||||
input: `k:v\xv`,
|
||||
fail: true,
|
||||
name: "key-section-var",
|
||||
input: `sect.var:v`,
|
||||
expect: []keyVal{keyVal{"sect.var", "v"}},
|
||||
}, {
|
||||
name: "invalid-qval",
|
||||
input: `k:"v\xv"`,
|
||||
fail: true,
|
||||
name: "key-section-subsection-var",
|
||||
input: `sect.sub.var:v`,
|
||||
expect: []keyVal{keyVal{"sect.sub.var", "v"}},
|
||||
}, {
|
||||
name: "two-pair",
|
||||
input: `k1:v1,k2:v2`,
|
||||
expect: []keyVal{{"k1", "v1"}, {"k2", "v2"}},
|
||||
name: "key-subsection-with-space",
|
||||
input: `k.sect.sub section:v`,
|
||||
expect: []keyVal{keyVal{"k.sect.sub section", "v"}},
|
||||
}, {
|
||||
name: "key-subsection-with-escape",
|
||||
input: `k.sect.sub\tsection:v`,
|
||||
expect: []keyVal{keyVal{"k.sect.sub\\tsection", "v"}},
|
||||
}, {
|
||||
name: "key-subsection-with-comma",
|
||||
input: `k.sect.sub,section:v`,
|
||||
expect: []keyVal{keyVal{"k.sect.sub,section", "v"}},
|
||||
}, {
|
||||
name: "qkey-subsection-with-space",
|
||||
input: `"k.sect.sub section":v`,
|
||||
expect: []keyVal{keyVal{"k.sect.sub section", "v"}},
|
||||
}, {
|
||||
name: "qkey-subsection-with-escapes",
|
||||
input: `"k.sect.sub\t\n\\section":v`,
|
||||
expect: []keyVal{keyVal{"k.sect.sub\t\n\\section", "v"}},
|
||||
}, {
|
||||
name: "qkey-subsection-with-comma",
|
||||
input: `"k.sect.sub,section":v`,
|
||||
expect: []keyVal{keyVal{"k.sect.sub,section", "v"}},
|
||||
}, {
|
||||
name: "qkey-subsection-with-colon",
|
||||
input: `"k.sect.sub:section":v`,
|
||||
expect: []keyVal{keyVal{"k.sect.sub:section", "v"}},
|
||||
}, {
|
||||
name: "invalid-qkey",
|
||||
input: `"k\xk":v"`,
|
||||
fail: true,
|
||||
}, {
|
||||
name: "val-spaces",
|
||||
input: `k1:v 1,k2:v 2`,
|
||||
|
|
@ -155,17 +195,21 @@ func TestParseGitConfigs(t *testing.T) {
|
|||
input: `k1:"v1",k2:"v2",`,
|
||||
expect: []keyVal{{"k1", "v1"}, {"k2", "v2"}},
|
||||
}, {
|
||||
name: "val-escapes",
|
||||
name: "val-with-escapes",
|
||||
input: `k1:v\n\t\\\"\,1`,
|
||||
expect: []keyVal{{"k1", "v\n\t\\\",1"}},
|
||||
}, {
|
||||
name: "qval-escapes",
|
||||
name: "qval-with-escapes",
|
||||
input: `k1:"v\n\t\\\"\,1"`,
|
||||
expect: []keyVal{{"k1", "v\n\t\\\",1"}},
|
||||
}, {
|
||||
name: "qval-comma",
|
||||
name: "qval-with-comma",
|
||||
input: `k1:"v,1"`,
|
||||
expect: []keyVal{{"k1", "v,1"}},
|
||||
}, {
|
||||
name: "qkey-missing-close",
|
||||
input: `"k1:v1`,
|
||||
fail: true,
|
||||
}, {
|
||||
name: "qval-missing-close",
|
||||
input: `k1:"v1`,
|
||||
|
|
@ -182,7 +226,7 @@ func TestParseGitConfigs(t *testing.T) {
|
|||
t.Errorf("unexpected success")
|
||||
}
|
||||
if !reflect.DeepEqual(kvs, tc.expect) {
|
||||
t.Errorf("bad result: expected %v, got %v", tc.expect, kvs)
|
||||
t.Errorf("bad result:\n\texpected: %#v\n\t got: %#v", tc.expect, kvs)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue