Show More
@@ -1,98 +1,99 b'' | |||||
1 | # rcutil.py - utilities about config paths, special config sections etc. |
|
1 | # rcutil.py - utilities about config paths, special config sections etc. | |
2 | # |
|
2 | # | |
3 | # Copyright Mercurial Contributors |
|
3 | # Copyright Mercurial Contributors | |
4 | # |
|
4 | # | |
5 | # This software may be used and distributed according to the terms of the |
|
5 | # This software may be used and distributed according to the terms of the | |
6 | # GNU General Public License version 2 or any later version. |
|
6 | # GNU General Public License version 2 or any later version. | |
7 |
|
7 | |||
8 | from __future__ import absolute_import |
|
8 | from __future__ import absolute_import | |
9 |
|
9 | |||
10 | import os |
|
10 | import os | |
11 |
|
11 | |||
12 | from . import ( |
|
12 | from . import ( | |
13 | encoding, |
|
13 | encoding, | |
14 | pycompat, |
|
14 | pycompat, | |
15 | util, |
|
15 | util, | |
16 | ) |
|
16 | ) | |
17 |
|
17 | |||
18 | if pycompat.iswindows: |
|
18 | if pycompat.iswindows: | |
19 | from . import scmwindows as scmplatform |
|
19 | from . import scmwindows as scmplatform | |
20 | else: |
|
20 | else: | |
21 | from . import scmposix as scmplatform |
|
21 | from . import scmposix as scmplatform | |
22 |
|
22 | |||
23 | fallbackpager = scmplatform.fallbackpager |
|
23 | fallbackpager = scmplatform.fallbackpager | |
24 | systemrcpath = scmplatform.systemrcpath |
|
24 | systemrcpath = scmplatform.systemrcpath | |
25 | userrcpath = scmplatform.userrcpath |
|
25 | userrcpath = scmplatform.userrcpath | |
26 |
|
26 | |||
27 | def _expandrcpath(path): |
|
27 | def _expandrcpath(path): | |
28 | '''path could be a file or a directory. return a list of file paths''' |
|
28 | '''path could be a file or a directory. return a list of file paths''' | |
29 | p = util.expandpath(path) |
|
29 | p = util.expandpath(path) | |
30 | if os.path.isdir(p): |
|
30 | if os.path.isdir(p): | |
31 | join = os.path.join |
|
31 | join = os.path.join | |
32 |
return |
|
32 | return sorted(join(p, f) for f, k in util.listdir(p) | |
|
33 | if f.endswith('.rc')) | |||
33 | return [p] |
|
34 | return [p] | |
34 |
|
35 | |||
35 | def envrcitems(env=None): |
|
36 | def envrcitems(env=None): | |
36 | '''Return [(section, name, value, source)] config items. |
|
37 | '''Return [(section, name, value, source)] config items. | |
37 |
|
38 | |||
38 | The config items are extracted from environment variables specified by env, |
|
39 | The config items are extracted from environment variables specified by env, | |
39 | used to override systemrc, but not userrc. |
|
40 | used to override systemrc, but not userrc. | |
40 |
|
41 | |||
41 | If env is not provided, encoding.environ will be used. |
|
42 | If env is not provided, encoding.environ will be used. | |
42 | ''' |
|
43 | ''' | |
43 | if env is None: |
|
44 | if env is None: | |
44 | env = encoding.environ |
|
45 | env = encoding.environ | |
45 | checklist = [ |
|
46 | checklist = [ | |
46 | ('EDITOR', 'ui', 'editor'), |
|
47 | ('EDITOR', 'ui', 'editor'), | |
47 | ('VISUAL', 'ui', 'editor'), |
|
48 | ('VISUAL', 'ui', 'editor'), | |
48 | ('PAGER', 'pager', 'pager'), |
|
49 | ('PAGER', 'pager', 'pager'), | |
49 | ] |
|
50 | ] | |
50 | result = [] |
|
51 | result = [] | |
51 | for envname, section, configname in checklist: |
|
52 | for envname, section, configname in checklist: | |
52 | if envname not in env: |
|
53 | if envname not in env: | |
53 | continue |
|
54 | continue | |
54 | result.append((section, configname, env[envname], '$%s' % envname)) |
|
55 | result.append((section, configname, env[envname], '$%s' % envname)) | |
55 | return result |
|
56 | return result | |
56 |
|
57 | |||
57 | def defaultrcpath(): |
|
58 | def defaultrcpath(): | |
58 | '''return rc paths in default.d''' |
|
59 | '''return rc paths in default.d''' | |
59 | path = [] |
|
60 | path = [] | |
60 | defaultpath = os.path.join(util.datapath, 'default.d') |
|
61 | defaultpath = os.path.join(util.datapath, 'default.d') | |
61 | if os.path.isdir(defaultpath): |
|
62 | if os.path.isdir(defaultpath): | |
62 | path = _expandrcpath(defaultpath) |
|
63 | path = _expandrcpath(defaultpath) | |
63 | return path |
|
64 | return path | |
64 |
|
65 | |||
65 | def rccomponents(): |
|
66 | def rccomponents(): | |
66 | '''return an ordered [(type, obj)] about where to load configs. |
|
67 | '''return an ordered [(type, obj)] about where to load configs. | |
67 |
|
68 | |||
68 | respect $HGRCPATH. if $HGRCPATH is empty, only .hg/hgrc of current repo is |
|
69 | respect $HGRCPATH. if $HGRCPATH is empty, only .hg/hgrc of current repo is | |
69 | used. if $HGRCPATH is not set, the platform default will be used. |
|
70 | used. if $HGRCPATH is not set, the platform default will be used. | |
70 |
|
71 | |||
71 | if a directory is provided, *.rc files under it will be used. |
|
72 | if a directory is provided, *.rc files under it will be used. | |
72 |
|
73 | |||
73 | type could be either 'path' or 'items', if type is 'path', obj is a string, |
|
74 | type could be either 'path' or 'items', if type is 'path', obj is a string, | |
74 | and is the config file path. if type is 'items', obj is a list of (section, |
|
75 | and is the config file path. if type is 'items', obj is a list of (section, | |
75 | name, value, source) that should fill the config directly. |
|
76 | name, value, source) that should fill the config directly. | |
76 | ''' |
|
77 | ''' | |
77 | envrc = ('items', envrcitems()) |
|
78 | envrc = ('items', envrcitems()) | |
78 |
|
79 | |||
79 | if 'HGRCPATH' in encoding.environ: |
|
80 | if 'HGRCPATH' in encoding.environ: | |
80 | # assume HGRCPATH is all about user configs so environments can be |
|
81 | # assume HGRCPATH is all about user configs so environments can be | |
81 | # overridden. |
|
82 | # overridden. | |
82 | _rccomponents = [envrc] |
|
83 | _rccomponents = [envrc] | |
83 | for p in encoding.environ['HGRCPATH'].split(pycompat.ospathsep): |
|
84 | for p in encoding.environ['HGRCPATH'].split(pycompat.ospathsep): | |
84 | if not p: |
|
85 | if not p: | |
85 | continue |
|
86 | continue | |
86 | _rccomponents.extend(('path', p) for p in _expandrcpath(p)) |
|
87 | _rccomponents.extend(('path', p) for p in _expandrcpath(p)) | |
87 | else: |
|
88 | else: | |
88 | normpaths = lambda paths: [('path', os.path.normpath(p)) for p in paths] |
|
89 | normpaths = lambda paths: [('path', os.path.normpath(p)) for p in paths] | |
89 | _rccomponents = normpaths(defaultrcpath() + systemrcpath()) |
|
90 | _rccomponents = normpaths(defaultrcpath() + systemrcpath()) | |
90 | _rccomponents.append(envrc) |
|
91 | _rccomponents.append(envrc) | |
91 | _rccomponents.extend(normpaths(userrcpath())) |
|
92 | _rccomponents.extend(normpaths(userrcpath())) | |
92 | return _rccomponents |
|
93 | return _rccomponents | |
93 |
|
94 | |||
94 | def defaultpagerenv(): |
|
95 | def defaultpagerenv(): | |
95 | '''return a dict of default environment variables and their values, |
|
96 | '''return a dict of default environment variables and their values, | |
96 | intended to be set before starting a pager. |
|
97 | intended to be set before starting a pager. | |
97 | ''' |
|
98 | ''' | |
98 | return {'LESS': 'FRX', 'LV': '-c'} |
|
99 | return {'LESS': 'FRX', 'LV': '-c'} |
@@ -1,213 +1,222 b'' | |||||
1 | hide outer repo |
|
1 | hide outer repo | |
2 | $ hg init |
|
2 | $ hg init | |
3 |
|
3 | |||
4 | Invalid syntax: no value |
|
4 | Invalid syntax: no value | |
5 |
|
5 | |||
6 | $ cat > .hg/hgrc << EOF |
|
6 | $ cat > .hg/hgrc << EOF | |
7 | > novaluekey |
|
7 | > novaluekey | |
8 | > EOF |
|
8 | > EOF | |
9 | $ hg showconfig |
|
9 | $ hg showconfig | |
10 | hg: parse error at $TESTTMP/.hg/hgrc:1: novaluekey |
|
10 | hg: parse error at $TESTTMP/.hg/hgrc:1: novaluekey | |
11 | [255] |
|
11 | [255] | |
12 |
|
12 | |||
13 | Invalid syntax: no key |
|
13 | Invalid syntax: no key | |
14 |
|
14 | |||
15 | $ cat > .hg/hgrc << EOF |
|
15 | $ cat > .hg/hgrc << EOF | |
16 | > =nokeyvalue |
|
16 | > =nokeyvalue | |
17 | > EOF |
|
17 | > EOF | |
18 | $ hg showconfig |
|
18 | $ hg showconfig | |
19 | hg: parse error at $TESTTMP/.hg/hgrc:1: =nokeyvalue |
|
19 | hg: parse error at $TESTTMP/.hg/hgrc:1: =nokeyvalue | |
20 | [255] |
|
20 | [255] | |
21 |
|
21 | |||
22 | Test hint about invalid syntax from leading white space |
|
22 | Test hint about invalid syntax from leading white space | |
23 |
|
23 | |||
24 | $ cat > .hg/hgrc << EOF |
|
24 | $ cat > .hg/hgrc << EOF | |
25 | > key=value |
|
25 | > key=value | |
26 | > EOF |
|
26 | > EOF | |
27 | $ hg showconfig |
|
27 | $ hg showconfig | |
28 | hg: parse error at $TESTTMP/.hg/hgrc:1: key=value |
|
28 | hg: parse error at $TESTTMP/.hg/hgrc:1: key=value | |
29 | unexpected leading whitespace |
|
29 | unexpected leading whitespace | |
30 | [255] |
|
30 | [255] | |
31 |
|
31 | |||
32 | $ cat > .hg/hgrc << EOF |
|
32 | $ cat > .hg/hgrc << EOF | |
33 | > [section] |
|
33 | > [section] | |
34 | > key=value |
|
34 | > key=value | |
35 | > EOF |
|
35 | > EOF | |
36 | $ hg showconfig |
|
36 | $ hg showconfig | |
37 | hg: parse error at $TESTTMP/.hg/hgrc:1: [section] |
|
37 | hg: parse error at $TESTTMP/.hg/hgrc:1: [section] | |
38 | unexpected leading whitespace |
|
38 | unexpected leading whitespace | |
39 | [255] |
|
39 | [255] | |
40 |
|
40 | |||
41 | Reset hgrc |
|
41 | Reset hgrc | |
42 |
|
42 | |||
43 | $ echo > .hg/hgrc |
|
43 | $ echo > .hg/hgrc | |
44 |
|
44 | |||
45 | Test case sensitive configuration |
|
45 | Test case sensitive configuration | |
46 |
|
46 | |||
47 | $ cat <<EOF >> $HGRCPATH |
|
47 | $ cat <<EOF >> $HGRCPATH | |
48 | > [Section] |
|
48 | > [Section] | |
49 | > KeY = Case Sensitive |
|
49 | > KeY = Case Sensitive | |
50 | > key = lower case |
|
50 | > key = lower case | |
51 | > EOF |
|
51 | > EOF | |
52 |
|
52 | |||
53 | $ hg showconfig Section |
|
53 | $ hg showconfig Section | |
54 | Section.KeY=Case Sensitive |
|
54 | Section.KeY=Case Sensitive | |
55 | Section.key=lower case |
|
55 | Section.key=lower case | |
56 |
|
56 | |||
57 | $ hg showconfig Section -Tjson |
|
57 | $ hg showconfig Section -Tjson | |
58 | [ |
|
58 | [ | |
59 | { |
|
59 | { | |
60 | "name": "Section.KeY", |
|
60 | "name": "Section.KeY", | |
61 | "source": "*.hgrc:*", (glob) |
|
61 | "source": "*.hgrc:*", (glob) | |
62 | "value": "Case Sensitive" |
|
62 | "value": "Case Sensitive" | |
63 | }, |
|
63 | }, | |
64 | { |
|
64 | { | |
65 | "name": "Section.key", |
|
65 | "name": "Section.key", | |
66 | "source": "*.hgrc:*", (glob) |
|
66 | "source": "*.hgrc:*", (glob) | |
67 | "value": "lower case" |
|
67 | "value": "lower case" | |
68 | } |
|
68 | } | |
69 | ] |
|
69 | ] | |
70 | $ hg showconfig Section.KeY -Tjson |
|
70 | $ hg showconfig Section.KeY -Tjson | |
71 | [ |
|
71 | [ | |
72 | { |
|
72 | { | |
73 | "name": "Section.KeY", |
|
73 | "name": "Section.KeY", | |
74 | "source": "*.hgrc:*", (glob) |
|
74 | "source": "*.hgrc:*", (glob) | |
75 | "value": "Case Sensitive" |
|
75 | "value": "Case Sensitive" | |
76 | } |
|
76 | } | |
77 | ] |
|
77 | ] | |
78 | $ hg showconfig -Tjson | tail -7 |
|
78 | $ hg showconfig -Tjson | tail -7 | |
79 | }, |
|
79 | }, | |
80 | { |
|
80 | { | |
81 | "name": "*", (glob) |
|
81 | "name": "*", (glob) | |
82 | "source": "*", (glob) |
|
82 | "source": "*", (glob) | |
83 | "value": "*" (glob) |
|
83 | "value": "*" (glob) | |
84 | } |
|
84 | } | |
85 | ] |
|
85 | ] | |
86 |
|
86 | |||
87 | Test empty config source: |
|
87 | Test empty config source: | |
88 |
|
88 | |||
89 | $ cat <<EOF > emptysource.py |
|
89 | $ cat <<EOF > emptysource.py | |
90 | > def reposetup(ui, repo): |
|
90 | > def reposetup(ui, repo): | |
91 | > ui.setconfig(b'empty', b'source', b'value') |
|
91 | > ui.setconfig(b'empty', b'source', b'value') | |
92 | > EOF |
|
92 | > EOF | |
93 | $ cp .hg/hgrc .hg/hgrc.orig |
|
93 | $ cp .hg/hgrc .hg/hgrc.orig | |
94 | $ cat <<EOF >> .hg/hgrc |
|
94 | $ cat <<EOF >> .hg/hgrc | |
95 | > [extensions] |
|
95 | > [extensions] | |
96 | > emptysource = `pwd`/emptysource.py |
|
96 | > emptysource = `pwd`/emptysource.py | |
97 | > EOF |
|
97 | > EOF | |
98 |
|
98 | |||
99 | $ hg config --debug empty.source |
|
99 | $ hg config --debug empty.source | |
100 | read config from: * (glob) |
|
100 | read config from: * (glob) | |
101 | none: value |
|
101 | none: value | |
102 | $ hg config empty.source -Tjson |
|
102 | $ hg config empty.source -Tjson | |
103 | [ |
|
103 | [ | |
104 | { |
|
104 | { | |
105 | "name": "empty.source", |
|
105 | "name": "empty.source", | |
106 | "source": "", |
|
106 | "source": "", | |
107 | "value": "value" |
|
107 | "value": "value" | |
108 | } |
|
108 | } | |
109 | ] |
|
109 | ] | |
110 |
|
110 | |||
111 | $ cp .hg/hgrc.orig .hg/hgrc |
|
111 | $ cp .hg/hgrc.orig .hg/hgrc | |
112 |
|
112 | |||
113 | Test "%unset" |
|
113 | Test "%unset" | |
114 |
|
114 | |||
115 | $ cat >> $HGRCPATH <<EOF |
|
115 | $ cat >> $HGRCPATH <<EOF | |
116 | > [unsettest] |
|
116 | > [unsettest] | |
117 | > local-hgrcpath = should be unset (HGRCPATH) |
|
117 | > local-hgrcpath = should be unset (HGRCPATH) | |
118 | > %unset local-hgrcpath |
|
118 | > %unset local-hgrcpath | |
119 | > |
|
119 | > | |
120 | > global = should be unset (HGRCPATH) |
|
120 | > global = should be unset (HGRCPATH) | |
121 | > |
|
121 | > | |
122 | > both = should be unset (HGRCPATH) |
|
122 | > both = should be unset (HGRCPATH) | |
123 | > |
|
123 | > | |
124 | > set-after-unset = should be unset (HGRCPATH) |
|
124 | > set-after-unset = should be unset (HGRCPATH) | |
125 | > EOF |
|
125 | > EOF | |
126 |
|
126 | |||
127 | $ cat >> .hg/hgrc <<EOF |
|
127 | $ cat >> .hg/hgrc <<EOF | |
128 | > [unsettest] |
|
128 | > [unsettest] | |
129 | > local-hgrc = should be unset (.hg/hgrc) |
|
129 | > local-hgrc = should be unset (.hg/hgrc) | |
130 | > %unset local-hgrc |
|
130 | > %unset local-hgrc | |
131 | > |
|
131 | > | |
132 | > %unset global |
|
132 | > %unset global | |
133 | > |
|
133 | > | |
134 | > both = should be unset (.hg/hgrc) |
|
134 | > both = should be unset (.hg/hgrc) | |
135 | > %unset both |
|
135 | > %unset both | |
136 | > |
|
136 | > | |
137 | > set-after-unset = should be unset (.hg/hgrc) |
|
137 | > set-after-unset = should be unset (.hg/hgrc) | |
138 | > %unset set-after-unset |
|
138 | > %unset set-after-unset | |
139 | > set-after-unset = should be set (.hg/hgrc) |
|
139 | > set-after-unset = should be set (.hg/hgrc) | |
140 | > EOF |
|
140 | > EOF | |
141 |
|
141 | |||
142 | $ hg showconfig unsettest |
|
142 | $ hg showconfig unsettest | |
143 | unsettest.set-after-unset=should be set (.hg/hgrc) |
|
143 | unsettest.set-after-unset=should be set (.hg/hgrc) | |
144 |
|
144 | |||
145 | Test exit code when no config matches |
|
145 | Test exit code when no config matches | |
146 |
|
146 | |||
147 | $ hg config Section.idontexist |
|
147 | $ hg config Section.idontexist | |
148 | [1] |
|
148 | [1] | |
149 |
|
149 | |||
150 | sub-options in [paths] aren't expanded |
|
150 | sub-options in [paths] aren't expanded | |
151 |
|
151 | |||
152 | $ cat > .hg/hgrc << EOF |
|
152 | $ cat > .hg/hgrc << EOF | |
153 | > [paths] |
|
153 | > [paths] | |
154 | > foo = ~/foo |
|
154 | > foo = ~/foo | |
155 | > foo:suboption = ~/foo |
|
155 | > foo:suboption = ~/foo | |
156 | > EOF |
|
156 | > EOF | |
157 |
|
157 | |||
158 | $ hg showconfig paths |
|
158 | $ hg showconfig paths | |
159 | paths.foo:suboption=~/foo |
|
159 | paths.foo:suboption=~/foo | |
160 | paths.foo=$TESTTMP/foo |
|
160 | paths.foo=$TESTTMP/foo | |
161 |
|
161 | |||
162 | edit failure |
|
162 | edit failure | |
163 |
|
163 | |||
164 | $ HGEDITOR=false hg config --edit |
|
164 | $ HGEDITOR=false hg config --edit | |
165 | abort: edit failed: false exited with status 1 |
|
165 | abort: edit failed: false exited with status 1 | |
166 | [255] |
|
166 | [255] | |
167 |
|
167 | |||
168 | config affected by environment variables |
|
168 | config affected by environment variables | |
169 |
|
169 | |||
170 | $ EDITOR=e1 VISUAL=e2 hg config --debug | grep 'ui\.editor' |
|
170 | $ EDITOR=e1 VISUAL=e2 hg config --debug | grep 'ui\.editor' | |
171 | $VISUAL: ui.editor=e2 |
|
171 | $VISUAL: ui.editor=e2 | |
172 |
|
172 | |||
173 | $ VISUAL=e2 hg config --debug --config ui.editor=e3 | grep 'ui\.editor' |
|
173 | $ VISUAL=e2 hg config --debug --config ui.editor=e3 | grep 'ui\.editor' | |
174 | --config: ui.editor=e3 |
|
174 | --config: ui.editor=e3 | |
175 |
|
175 | |||
176 | $ PAGER=p1 hg config --debug | grep 'pager\.pager' |
|
176 | $ PAGER=p1 hg config --debug | grep 'pager\.pager' | |
177 | $PAGER: pager.pager=p1 |
|
177 | $PAGER: pager.pager=p1 | |
178 |
|
178 | |||
179 | $ PAGER=p1 hg config --debug --config pager.pager=p2 | grep 'pager\.pager' |
|
179 | $ PAGER=p1 hg config --debug --config pager.pager=p2 | grep 'pager\.pager' | |
180 | --config: pager.pager=p2 |
|
180 | --config: pager.pager=p2 | |
181 |
|
181 | |||
182 | verify that aliases are evaluated as well |
|
182 | verify that aliases are evaluated as well | |
183 |
|
183 | |||
184 | $ hg init aliastest |
|
184 | $ hg init aliastest | |
185 | $ cd aliastest |
|
185 | $ cd aliastest | |
186 | $ cat > .hg/hgrc << EOF |
|
186 | $ cat > .hg/hgrc << EOF | |
187 | > [ui] |
|
187 | > [ui] | |
188 | > user = repo user |
|
188 | > user = repo user | |
189 | > EOF |
|
189 | > EOF | |
190 | $ touch index |
|
190 | $ touch index | |
191 | $ unset HGUSER |
|
191 | $ unset HGUSER | |
192 | $ hg ci -Am test |
|
192 | $ hg ci -Am test | |
193 | adding index |
|
193 | adding index | |
194 | $ hg log --template '{author}\n' |
|
194 | $ hg log --template '{author}\n' | |
195 | repo user |
|
195 | repo user | |
196 | $ cd .. |
|
196 | $ cd .. | |
197 |
|
197 | |||
198 | alias has lower priority |
|
198 | alias has lower priority | |
199 |
|
199 | |||
200 | $ hg init aliaspriority |
|
200 | $ hg init aliaspriority | |
201 | $ cd aliaspriority |
|
201 | $ cd aliaspriority | |
202 | $ cat > .hg/hgrc << EOF |
|
202 | $ cat > .hg/hgrc << EOF | |
203 | > [ui] |
|
203 | > [ui] | |
204 | > user = alias user |
|
204 | > user = alias user | |
205 | > username = repo user |
|
205 | > username = repo user | |
206 | > EOF |
|
206 | > EOF | |
207 | $ touch index |
|
207 | $ touch index | |
208 | $ unset HGUSER |
|
208 | $ unset HGUSER | |
209 | $ hg ci -Am test |
|
209 | $ hg ci -Am test | |
210 | adding index |
|
210 | adding index | |
211 | $ hg log --template '{author}\n' |
|
211 | $ hg log --template '{author}\n' | |
212 | repo user |
|
212 | repo user | |
213 | $ cd .. |
|
213 | $ cd .. | |
|
214 | ||||
|
215 | configs should be read in lexicographical order | |||
|
216 | ||||
|
217 | $ mkdir configs | |||
|
218 | $ for i in `$TESTDIR/seq.py 10 99`; do | |||
|
219 | > printf "[section]\nkey=$i" > configs/$i.rc | |||
|
220 | > done | |||
|
221 | $ HGRCPATH=configs hg config section.key | |||
|
222 | 99 |
General Comments 0
You need to be logged in to leave comments.
Login now