##// END OF EJS Templates
pathauditor: check for codepoints ignored on OS X
Augie Fackler -
r23598:c02a05cc stable
parent child Browse files
Show More
@@ -1,166 +1,170 b''
1 import os, errno, stat
1 import os, errno, stat
2
2
3 import encoding
3 import util
4 import util
4 from i18n import _
5 from i18n import _
5
6
7 def _lowerclean(s):
8 return encoding.hfsignoreclean(s.lower())
9
6 class pathauditor(object):
10 class pathauditor(object):
7 '''ensure that a filesystem path contains no banned components.
11 '''ensure that a filesystem path contains no banned components.
8 the following properties of a path are checked:
12 the following properties of a path are checked:
9
13
10 - ends with a directory separator
14 - ends with a directory separator
11 - under top-level .hg
15 - under top-level .hg
12 - starts at the root of a windows drive
16 - starts at the root of a windows drive
13 - contains ".."
17 - contains ".."
14 - traverses a symlink (e.g. a/symlink_here/b)
18 - traverses a symlink (e.g. a/symlink_here/b)
15 - inside a nested repository (a callback can be used to approve
19 - inside a nested repository (a callback can be used to approve
16 some nested repositories, e.g., subrepositories)
20 some nested repositories, e.g., subrepositories)
17 '''
21 '''
18
22
19 def __init__(self, root, callback=None):
23 def __init__(self, root, callback=None):
20 self.audited = set()
24 self.audited = set()
21 self.auditeddir = set()
25 self.auditeddir = set()
22 self.root = root
26 self.root = root
23 self.callback = callback
27 self.callback = callback
24 if os.path.lexists(root) and not util.checkcase(root):
28 if os.path.lexists(root) and not util.checkcase(root):
25 self.normcase = util.normcase
29 self.normcase = util.normcase
26 else:
30 else:
27 self.normcase = lambda x: x
31 self.normcase = lambda x: x
28
32
29 def __call__(self, path):
33 def __call__(self, path):
30 '''Check the relative path.
34 '''Check the relative path.
31 path may contain a pattern (e.g. foodir/**.txt)'''
35 path may contain a pattern (e.g. foodir/**.txt)'''
32
36
33 path = util.localpath(path)
37 path = util.localpath(path)
34 normpath = self.normcase(path)
38 normpath = self.normcase(path)
35 if normpath in self.audited:
39 if normpath in self.audited:
36 return
40 return
37 # AIX ignores "/" at end of path, others raise EISDIR.
41 # AIX ignores "/" at end of path, others raise EISDIR.
38 if util.endswithsep(path):
42 if util.endswithsep(path):
39 raise util.Abort(_("path ends in directory separator: %s") % path)
43 raise util.Abort(_("path ends in directory separator: %s") % path)
40 parts = util.splitpath(path)
44 parts = util.splitpath(path)
41 if (os.path.splitdrive(path)[0]
45 if (os.path.splitdrive(path)[0]
42 or parts[0].lower() in ('.hg', '.hg.', '')
46 or _lowerclean(parts[0]) in ('.hg', '.hg.', '')
43 or os.pardir in parts):
47 or os.pardir in parts):
44 raise util.Abort(_("path contains illegal component: %s") % path)
48 raise util.Abort(_("path contains illegal component: %s") % path)
45 if '.hg' in path.lower():
49 if '.hg' in _lowerclean(path):
46 lparts = [p.lower() for p in parts]
50 lparts = [_lowerclean(p.lower()) for p in parts]
47 for p in '.hg', '.hg.':
51 for p in '.hg', '.hg.':
48 if p in lparts[1:]:
52 if p in lparts[1:]:
49 pos = lparts.index(p)
53 pos = lparts.index(p)
50 base = os.path.join(*parts[:pos])
54 base = os.path.join(*parts[:pos])
51 raise util.Abort(_("path '%s' is inside nested repo %r")
55 raise util.Abort(_("path '%s' is inside nested repo %r")
52 % (path, base))
56 % (path, base))
53
57
54 normparts = util.splitpath(normpath)
58 normparts = util.splitpath(normpath)
55 assert len(parts) == len(normparts)
59 assert len(parts) == len(normparts)
56
60
57 parts.pop()
61 parts.pop()
58 normparts.pop()
62 normparts.pop()
59 prefixes = []
63 prefixes = []
60 while parts:
64 while parts:
61 prefix = os.sep.join(parts)
65 prefix = os.sep.join(parts)
62 normprefix = os.sep.join(normparts)
66 normprefix = os.sep.join(normparts)
63 if normprefix in self.auditeddir:
67 if normprefix in self.auditeddir:
64 break
68 break
65 curpath = os.path.join(self.root, prefix)
69 curpath = os.path.join(self.root, prefix)
66 try:
70 try:
67 st = os.lstat(curpath)
71 st = os.lstat(curpath)
68 except OSError, err:
72 except OSError, err:
69 # EINVAL can be raised as invalid path syntax under win32.
73 # EINVAL can be raised as invalid path syntax under win32.
70 # They must be ignored for patterns can be checked too.
74 # They must be ignored for patterns can be checked too.
71 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
75 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
72 raise
76 raise
73 else:
77 else:
74 if stat.S_ISLNK(st.st_mode):
78 if stat.S_ISLNK(st.st_mode):
75 raise util.Abort(
79 raise util.Abort(
76 _('path %r traverses symbolic link %r')
80 _('path %r traverses symbolic link %r')
77 % (path, prefix))
81 % (path, prefix))
78 elif (stat.S_ISDIR(st.st_mode) and
82 elif (stat.S_ISDIR(st.st_mode) and
79 os.path.isdir(os.path.join(curpath, '.hg'))):
83 os.path.isdir(os.path.join(curpath, '.hg'))):
80 if not self.callback or not self.callback(curpath):
84 if not self.callback or not self.callback(curpath):
81 raise util.Abort(_("path '%s' is inside nested "
85 raise util.Abort(_("path '%s' is inside nested "
82 "repo %r")
86 "repo %r")
83 % (path, prefix))
87 % (path, prefix))
84 prefixes.append(normprefix)
88 prefixes.append(normprefix)
85 parts.pop()
89 parts.pop()
86 normparts.pop()
90 normparts.pop()
87
91
88 self.audited.add(normpath)
92 self.audited.add(normpath)
89 # only add prefixes to the cache after checking everything: we don't
93 # only add prefixes to the cache after checking everything: we don't
90 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
94 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
91 self.auditeddir.update(prefixes)
95 self.auditeddir.update(prefixes)
92
96
93 def check(self, path):
97 def check(self, path):
94 try:
98 try:
95 self(path)
99 self(path)
96 return True
100 return True
97 except (OSError, util.Abort):
101 except (OSError, util.Abort):
98 return False
102 return False
99
103
100 def canonpath(root, cwd, myname, auditor=None):
104 def canonpath(root, cwd, myname, auditor=None):
101 '''return the canonical path of myname, given cwd and root'''
105 '''return the canonical path of myname, given cwd and root'''
102 if util.endswithsep(root):
106 if util.endswithsep(root):
103 rootsep = root
107 rootsep = root
104 else:
108 else:
105 rootsep = root + os.sep
109 rootsep = root + os.sep
106 name = myname
110 name = myname
107 if not os.path.isabs(name):
111 if not os.path.isabs(name):
108 name = os.path.join(root, cwd, name)
112 name = os.path.join(root, cwd, name)
109 name = os.path.normpath(name)
113 name = os.path.normpath(name)
110 if auditor is None:
114 if auditor is None:
111 auditor = pathauditor(root)
115 auditor = pathauditor(root)
112 if name != rootsep and name.startswith(rootsep):
116 if name != rootsep and name.startswith(rootsep):
113 name = name[len(rootsep):]
117 name = name[len(rootsep):]
114 auditor(name)
118 auditor(name)
115 return util.pconvert(name)
119 return util.pconvert(name)
116 elif name == root:
120 elif name == root:
117 return ''
121 return ''
118 else:
122 else:
119 # Determine whether `name' is in the hierarchy at or beneath `root',
123 # Determine whether `name' is in the hierarchy at or beneath `root',
120 # by iterating name=dirname(name) until that causes no change (can't
124 # by iterating name=dirname(name) until that causes no change (can't
121 # check name == '/', because that doesn't work on windows). The list
125 # check name == '/', because that doesn't work on windows). The list
122 # `rel' holds the reversed list of components making up the relative
126 # `rel' holds the reversed list of components making up the relative
123 # file name we want.
127 # file name we want.
124 rel = []
128 rel = []
125 while True:
129 while True:
126 try:
130 try:
127 s = util.samefile(name, root)
131 s = util.samefile(name, root)
128 except OSError:
132 except OSError:
129 s = False
133 s = False
130 if s:
134 if s:
131 if not rel:
135 if not rel:
132 # name was actually the same as root (maybe a symlink)
136 # name was actually the same as root (maybe a symlink)
133 return ''
137 return ''
134 rel.reverse()
138 rel.reverse()
135 name = os.path.join(*rel)
139 name = os.path.join(*rel)
136 auditor(name)
140 auditor(name)
137 return util.pconvert(name)
141 return util.pconvert(name)
138 dirname, basename = util.split(name)
142 dirname, basename = util.split(name)
139 rel.append(basename)
143 rel.append(basename)
140 if dirname == name:
144 if dirname == name:
141 break
145 break
142 name = dirname
146 name = dirname
143
147
144 raise util.Abort(_("%s not under root '%s'") % (myname, root))
148 raise util.Abort(_("%s not under root '%s'") % (myname, root))
145
149
146 def normasprefix(path):
150 def normasprefix(path):
147 '''normalize the specified path as path prefix
151 '''normalize the specified path as path prefix
148
152
149 Returned vaule can be used safely for "p.startswith(prefix)",
153 Returned vaule can be used safely for "p.startswith(prefix)",
150 "p[len(prefix):]", and so on.
154 "p[len(prefix):]", and so on.
151
155
152 For efficiency, this expects "path" argument to be already
156 For efficiency, this expects "path" argument to be already
153 normalized by "os.path.normpath", "os.path.realpath", and so on.
157 normalized by "os.path.normpath", "os.path.realpath", and so on.
154
158
155 See also issue3033 for detail about need of this function.
159 See also issue3033 for detail about need of this function.
156
160
157 >>> normasprefix('/foo/bar').replace(os.sep, '/')
161 >>> normasprefix('/foo/bar').replace(os.sep, '/')
158 '/foo/bar/'
162 '/foo/bar/'
159 >>> normasprefix('/').replace(os.sep, '/')
163 >>> normasprefix('/').replace(os.sep, '/')
160 '/'
164 '/'
161 '''
165 '''
162 d, p = os.path.splitdrive(path)
166 d, p = os.path.splitdrive(path)
163 if len(p) != len(os.sep):
167 if len(p) != len(os.sep):
164 return path + os.sep
168 return path + os.sep
165 else:
169 else:
166 return path
170 return path
@@ -1,460 +1,477 b''
1 commit date test
1 commit date test
2
2
3 $ hg init test
3 $ hg init test
4 $ cd test
4 $ cd test
5 $ echo foo > foo
5 $ echo foo > foo
6 $ hg add foo
6 $ hg add foo
7 $ cat > $TESTTMP/checkeditform.sh <<EOF
7 $ cat > $TESTTMP/checkeditform.sh <<EOF
8 > env | grep HGEDITFORM
8 > env | grep HGEDITFORM
9 > true
9 > true
10 > EOF
10 > EOF
11 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg commit -m ""
11 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg commit -m ""
12 HGEDITFORM=commit.normal.normal
12 HGEDITFORM=commit.normal.normal
13 abort: empty commit message
13 abort: empty commit message
14 [255]
14 [255]
15 $ hg commit -d '0 0' -m commit-1
15 $ hg commit -d '0 0' -m commit-1
16 $ echo foo >> foo
16 $ echo foo >> foo
17 $ hg commit -d '1 4444444' -m commit-3
17 $ hg commit -d '1 4444444' -m commit-3
18 abort: impossible time zone offset: 4444444
18 abort: impossible time zone offset: 4444444
19 [255]
19 [255]
20 $ hg commit -d '1 15.1' -m commit-4
20 $ hg commit -d '1 15.1' -m commit-4
21 abort: invalid date: '1\t15.1'
21 abort: invalid date: '1\t15.1'
22 [255]
22 [255]
23 $ hg commit -d 'foo bar' -m commit-5
23 $ hg commit -d 'foo bar' -m commit-5
24 abort: invalid date: 'foo bar'
24 abort: invalid date: 'foo bar'
25 [255]
25 [255]
26 $ hg commit -d ' 1 4444' -m commit-6
26 $ hg commit -d ' 1 4444' -m commit-6
27 $ hg commit -d '111111111111 0' -m commit-7
27 $ hg commit -d '111111111111 0' -m commit-7
28 abort: date exceeds 32 bits: 111111111111
28 abort: date exceeds 32 bits: 111111111111
29 [255]
29 [255]
30 $ hg commit -d '-7654321 3600' -m commit-7
30 $ hg commit -d '-7654321 3600' -m commit-7
31 abort: negative date value: -7654321
31 abort: negative date value: -7654321
32 [255]
32 [255]
33
33
34 commit added file that has been deleted
34 commit added file that has been deleted
35
35
36 $ echo bar > bar
36 $ echo bar > bar
37 $ hg add bar
37 $ hg add bar
38 $ rm bar
38 $ rm bar
39 $ hg commit -m commit-8
39 $ hg commit -m commit-8
40 nothing changed (1 missing files, see 'hg status')
40 nothing changed (1 missing files, see 'hg status')
41 [1]
41 [1]
42 $ hg commit -m commit-8-2 bar
42 $ hg commit -m commit-8-2 bar
43 abort: bar: file not found!
43 abort: bar: file not found!
44 [255]
44 [255]
45
45
46 $ hg -q revert -a --no-backup
46 $ hg -q revert -a --no-backup
47
47
48 $ mkdir dir
48 $ mkdir dir
49 $ echo boo > dir/file
49 $ echo boo > dir/file
50 $ hg add
50 $ hg add
51 adding dir/file (glob)
51 adding dir/file (glob)
52 $ hg -v commit -m commit-9 dir
52 $ hg -v commit -m commit-9 dir
53 dir/file
53 dir/file
54 committed changeset 2:d2a76177cb42
54 committed changeset 2:d2a76177cb42
55
55
56 $ echo > dir.file
56 $ echo > dir.file
57 $ hg add
57 $ hg add
58 adding dir.file
58 adding dir.file
59 $ hg commit -m commit-10 dir dir.file
59 $ hg commit -m commit-10 dir dir.file
60 abort: dir: no match under directory!
60 abort: dir: no match under directory!
61 [255]
61 [255]
62
62
63 $ echo >> dir/file
63 $ echo >> dir/file
64 $ mkdir bleh
64 $ mkdir bleh
65 $ mkdir dir2
65 $ mkdir dir2
66 $ cd bleh
66 $ cd bleh
67 $ hg commit -m commit-11 .
67 $ hg commit -m commit-11 .
68 abort: bleh: no match under directory!
68 abort: bleh: no match under directory!
69 [255]
69 [255]
70 $ hg commit -m commit-12 ../dir ../dir2
70 $ hg commit -m commit-12 ../dir ../dir2
71 abort: dir2: no match under directory!
71 abort: dir2: no match under directory!
72 [255]
72 [255]
73 $ hg -v commit -m commit-13 ../dir
73 $ hg -v commit -m commit-13 ../dir
74 dir/file
74 dir/file
75 committed changeset 3:1cd62a2d8db5
75 committed changeset 3:1cd62a2d8db5
76 $ cd ..
76 $ cd ..
77
77
78 $ hg commit -m commit-14 does-not-exist
78 $ hg commit -m commit-14 does-not-exist
79 abort: does-not-exist: * (glob)
79 abort: does-not-exist: * (glob)
80 [255]
80 [255]
81
81
82 #if symlink
82 #if symlink
83 $ ln -s foo baz
83 $ ln -s foo baz
84 $ hg commit -m commit-15 baz
84 $ hg commit -m commit-15 baz
85 abort: baz: file not tracked!
85 abort: baz: file not tracked!
86 [255]
86 [255]
87 #endif
87 #endif
88
88
89 $ touch quux
89 $ touch quux
90 $ hg commit -m commit-16 quux
90 $ hg commit -m commit-16 quux
91 abort: quux: file not tracked!
91 abort: quux: file not tracked!
92 [255]
92 [255]
93 $ echo >> dir/file
93 $ echo >> dir/file
94 $ hg -v commit -m commit-17 dir/file
94 $ hg -v commit -m commit-17 dir/file
95 dir/file
95 dir/file
96 committed changeset 4:49176991390e
96 committed changeset 4:49176991390e
97
97
98 An empty date was interpreted as epoch origin
98 An empty date was interpreted as epoch origin
99
99
100 $ echo foo >> foo
100 $ echo foo >> foo
101 $ hg commit -d '' -m commit-no-date
101 $ hg commit -d '' -m commit-no-date
102 $ hg tip --template '{date|isodate}\n' | grep '1970'
102 $ hg tip --template '{date|isodate}\n' | grep '1970'
103 [1]
103 [1]
104
104
105 Make sure we do not obscure unknown requires file entries (issue2649)
105 Make sure we do not obscure unknown requires file entries (issue2649)
106
106
107 $ echo foo >> foo
107 $ echo foo >> foo
108 $ echo fake >> .hg/requires
108 $ echo fake >> .hg/requires
109 $ hg commit -m bla
109 $ hg commit -m bla
110 abort: repository requires features unknown to this Mercurial: fake!
110 abort: repository requires features unknown to this Mercurial: fake!
111 (see http://mercurial.selenic.com/wiki/MissingRequirement for more information)
111 (see http://mercurial.selenic.com/wiki/MissingRequirement for more information)
112 [255]
112 [255]
113
113
114 $ cd ..
114 $ cd ..
115
115
116
116
117 partial subdir commit test
117 partial subdir commit test
118
118
119 $ hg init test2
119 $ hg init test2
120 $ cd test2
120 $ cd test2
121 $ mkdir foo
121 $ mkdir foo
122 $ echo foo > foo/foo
122 $ echo foo > foo/foo
123 $ mkdir bar
123 $ mkdir bar
124 $ echo bar > bar/bar
124 $ echo bar > bar/bar
125 $ hg add
125 $ hg add
126 adding bar/bar (glob)
126 adding bar/bar (glob)
127 adding foo/foo (glob)
127 adding foo/foo (glob)
128 $ HGEDITOR=cat hg ci -e -m commit-subdir-1 foo
128 $ HGEDITOR=cat hg ci -e -m commit-subdir-1 foo
129 commit-subdir-1
129 commit-subdir-1
130
130
131
131
132 HG: Enter commit message. Lines beginning with 'HG:' are removed.
132 HG: Enter commit message. Lines beginning with 'HG:' are removed.
133 HG: Leave message empty to abort commit.
133 HG: Leave message empty to abort commit.
134 HG: --
134 HG: --
135 HG: user: test
135 HG: user: test
136 HG: branch 'default'
136 HG: branch 'default'
137 HG: added foo/foo
137 HG: added foo/foo
138
138
139
139
140 $ hg ci -m commit-subdir-2 bar
140 $ hg ci -m commit-subdir-2 bar
141
141
142 subdir log 1
142 subdir log 1
143
143
144 $ hg log -v foo
144 $ hg log -v foo
145 changeset: 0:f97e73a25882
145 changeset: 0:f97e73a25882
146 user: test
146 user: test
147 date: Thu Jan 01 00:00:00 1970 +0000
147 date: Thu Jan 01 00:00:00 1970 +0000
148 files: foo/foo
148 files: foo/foo
149 description:
149 description:
150 commit-subdir-1
150 commit-subdir-1
151
151
152
152
153
153
154 subdir log 2
154 subdir log 2
155
155
156 $ hg log -v bar
156 $ hg log -v bar
157 changeset: 1:aa809156d50d
157 changeset: 1:aa809156d50d
158 tag: tip
158 tag: tip
159 user: test
159 user: test
160 date: Thu Jan 01 00:00:00 1970 +0000
160 date: Thu Jan 01 00:00:00 1970 +0000
161 files: bar/bar
161 files: bar/bar
162 description:
162 description:
163 commit-subdir-2
163 commit-subdir-2
164
164
165
165
166
166
167 full log
167 full log
168
168
169 $ hg log -v
169 $ hg log -v
170 changeset: 1:aa809156d50d
170 changeset: 1:aa809156d50d
171 tag: tip
171 tag: tip
172 user: test
172 user: test
173 date: Thu Jan 01 00:00:00 1970 +0000
173 date: Thu Jan 01 00:00:00 1970 +0000
174 files: bar/bar
174 files: bar/bar
175 description:
175 description:
176 commit-subdir-2
176 commit-subdir-2
177
177
178
178
179 changeset: 0:f97e73a25882
179 changeset: 0:f97e73a25882
180 user: test
180 user: test
181 date: Thu Jan 01 00:00:00 1970 +0000
181 date: Thu Jan 01 00:00:00 1970 +0000
182 files: foo/foo
182 files: foo/foo
183 description:
183 description:
184 commit-subdir-1
184 commit-subdir-1
185
185
186
186
187 $ cd ..
187 $ cd ..
188
188
189
189
190 dot and subdir commit test
190 dot and subdir commit test
191
191
192 $ hg init test3
192 $ hg init test3
193 $ echo commit-foo-subdir > commit-log-test
193 $ echo commit-foo-subdir > commit-log-test
194 $ cd test3
194 $ cd test3
195 $ mkdir foo
195 $ mkdir foo
196 $ echo foo content > foo/plain-file
196 $ echo foo content > foo/plain-file
197 $ hg add foo/plain-file
197 $ hg add foo/plain-file
198 $ HGEDITOR=cat hg ci --edit -l ../commit-log-test foo
198 $ HGEDITOR=cat hg ci --edit -l ../commit-log-test foo
199 commit-foo-subdir
199 commit-foo-subdir
200
200
201
201
202 HG: Enter commit message. Lines beginning with 'HG:' are removed.
202 HG: Enter commit message. Lines beginning with 'HG:' are removed.
203 HG: Leave message empty to abort commit.
203 HG: Leave message empty to abort commit.
204 HG: --
204 HG: --
205 HG: user: test
205 HG: user: test
206 HG: branch 'default'
206 HG: branch 'default'
207 HG: added foo/plain-file
207 HG: added foo/plain-file
208
208
209
209
210 $ echo modified foo content > foo/plain-file
210 $ echo modified foo content > foo/plain-file
211 $ hg ci -m commit-foo-dot .
211 $ hg ci -m commit-foo-dot .
212
212
213 full log
213 full log
214
214
215 $ hg log -v
215 $ hg log -v
216 changeset: 1:95b38e3a5b2e
216 changeset: 1:95b38e3a5b2e
217 tag: tip
217 tag: tip
218 user: test
218 user: test
219 date: Thu Jan 01 00:00:00 1970 +0000
219 date: Thu Jan 01 00:00:00 1970 +0000
220 files: foo/plain-file
220 files: foo/plain-file
221 description:
221 description:
222 commit-foo-dot
222 commit-foo-dot
223
223
224
224
225 changeset: 0:65d4e9386227
225 changeset: 0:65d4e9386227
226 user: test
226 user: test
227 date: Thu Jan 01 00:00:00 1970 +0000
227 date: Thu Jan 01 00:00:00 1970 +0000
228 files: foo/plain-file
228 files: foo/plain-file
229 description:
229 description:
230 commit-foo-subdir
230 commit-foo-subdir
231
231
232
232
233
233
234 subdir log
234 subdir log
235
235
236 $ cd foo
236 $ cd foo
237 $ hg log .
237 $ hg log .
238 changeset: 1:95b38e3a5b2e
238 changeset: 1:95b38e3a5b2e
239 tag: tip
239 tag: tip
240 user: test
240 user: test
241 date: Thu Jan 01 00:00:00 1970 +0000
241 date: Thu Jan 01 00:00:00 1970 +0000
242 summary: commit-foo-dot
242 summary: commit-foo-dot
243
243
244 changeset: 0:65d4e9386227
244 changeset: 0:65d4e9386227
245 user: test
245 user: test
246 date: Thu Jan 01 00:00:00 1970 +0000
246 date: Thu Jan 01 00:00:00 1970 +0000
247 summary: commit-foo-subdir
247 summary: commit-foo-subdir
248
248
249 $ cd ..
249 $ cd ..
250 $ cd ..
250 $ cd ..
251
251
252 Issue1049: Hg permits partial commit of merge without warning
252 Issue1049: Hg permits partial commit of merge without warning
253
253
254 $ hg init issue1049
254 $ hg init issue1049
255 $ cd issue1049
255 $ cd issue1049
256 $ echo a > a
256 $ echo a > a
257 $ hg ci -Ama
257 $ hg ci -Ama
258 adding a
258 adding a
259 $ echo a >> a
259 $ echo a >> a
260 $ hg ci -mb
260 $ hg ci -mb
261 $ hg up 0
261 $ hg up 0
262 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
262 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
263 $ echo b >> a
263 $ echo b >> a
264 $ hg ci -mc
264 $ hg ci -mc
265 created new head
265 created new head
266 $ HGMERGE=true hg merge
266 $ HGMERGE=true hg merge
267 merging a
267 merging a
268 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
268 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
269 (branch merge, don't forget to commit)
269 (branch merge, don't forget to commit)
270
270
271 should fail because we are specifying a file name
271 should fail because we are specifying a file name
272
272
273 $ hg ci -mmerge a
273 $ hg ci -mmerge a
274 abort: cannot partially commit a merge (do not specify files or patterns)
274 abort: cannot partially commit a merge (do not specify files or patterns)
275 [255]
275 [255]
276
276
277 should fail because we are specifying a pattern
277 should fail because we are specifying a pattern
278
278
279 $ hg ci -mmerge -I a
279 $ hg ci -mmerge -I a
280 abort: cannot partially commit a merge (do not specify files or patterns)
280 abort: cannot partially commit a merge (do not specify files or patterns)
281 [255]
281 [255]
282
282
283 should succeed
283 should succeed
284
284
285 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg ci -mmerge --edit
285 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg ci -mmerge --edit
286 HGEDITFORM=commit.normal.merge
286 HGEDITFORM=commit.normal.merge
287 $ cd ..
287 $ cd ..
288
288
289
289
290 test commit message content
290 test commit message content
291
291
292 $ hg init commitmsg
292 $ hg init commitmsg
293 $ cd commitmsg
293 $ cd commitmsg
294 $ echo changed > changed
294 $ echo changed > changed
295 $ echo removed > removed
295 $ echo removed > removed
296 $ hg book currentbookmark
296 $ hg book currentbookmark
297 $ hg ci -qAm init
297 $ hg ci -qAm init
298
298
299 $ hg rm removed
299 $ hg rm removed
300 $ echo changed >> changed
300 $ echo changed >> changed
301 $ echo added > added
301 $ echo added > added
302 $ hg add added
302 $ hg add added
303 $ HGEDITOR=cat hg ci -A
303 $ HGEDITOR=cat hg ci -A
304
304
305
305
306 HG: Enter commit message. Lines beginning with 'HG:' are removed.
306 HG: Enter commit message. Lines beginning with 'HG:' are removed.
307 HG: Leave message empty to abort commit.
307 HG: Leave message empty to abort commit.
308 HG: --
308 HG: --
309 HG: user: test
309 HG: user: test
310 HG: branch 'default'
310 HG: branch 'default'
311 HG: bookmark 'currentbookmark'
311 HG: bookmark 'currentbookmark'
312 HG: added added
312 HG: added added
313 HG: changed changed
313 HG: changed changed
314 HG: removed removed
314 HG: removed removed
315 abort: empty commit message
315 abort: empty commit message
316 [255]
316 [255]
317
317
318 test saving last-message.txt
318 test saving last-message.txt
319
319
320 $ hg init sub
320 $ hg init sub
321 $ echo a > sub/a
321 $ echo a > sub/a
322 $ hg -R sub add sub/a
322 $ hg -R sub add sub/a
323 $ cat > sub/.hg/hgrc <<EOF
323 $ cat > sub/.hg/hgrc <<EOF
324 > [hooks]
324 > [hooks]
325 > precommit.test-saving-last-message = false
325 > precommit.test-saving-last-message = false
326 > EOF
326 > EOF
327
327
328 $ echo 'sub = sub' > .hgsub
328 $ echo 'sub = sub' > .hgsub
329 $ hg add .hgsub
329 $ hg add .hgsub
330
330
331 $ cat > $TESTTMP/editor.sh <<EOF
331 $ cat > $TESTTMP/editor.sh <<EOF
332 > echo "==== before editing:"
332 > echo "==== before editing:"
333 > cat \$1
333 > cat \$1
334 > echo "===="
334 > echo "===="
335 > echo "test saving last-message.txt" >> \$1
335 > echo "test saving last-message.txt" >> \$1
336 > EOF
336 > EOF
337
337
338 $ rm -f .hg/last-message.txt
338 $ rm -f .hg/last-message.txt
339 $ HGEDITOR="sh $TESTTMP/editor.sh" hg commit -S -q
339 $ HGEDITOR="sh $TESTTMP/editor.sh" hg commit -S -q
340 ==== before editing:
340 ==== before editing:
341
341
342
342
343 HG: Enter commit message. Lines beginning with 'HG:' are removed.
343 HG: Enter commit message. Lines beginning with 'HG:' are removed.
344 HG: Leave message empty to abort commit.
344 HG: Leave message empty to abort commit.
345 HG: --
345 HG: --
346 HG: user: test
346 HG: user: test
347 HG: branch 'default'
347 HG: branch 'default'
348 HG: bookmark 'currentbookmark'
348 HG: bookmark 'currentbookmark'
349 HG: subrepo sub
349 HG: subrepo sub
350 HG: added .hgsub
350 HG: added .hgsub
351 HG: added added
351 HG: added added
352 HG: changed .hgsubstate
352 HG: changed .hgsubstate
353 HG: changed changed
353 HG: changed changed
354 HG: removed removed
354 HG: removed removed
355 ====
355 ====
356 abort: precommit.test-saving-last-message hook exited with status 1 (in subrepo sub)
356 abort: precommit.test-saving-last-message hook exited with status 1 (in subrepo sub)
357 [255]
357 [255]
358 $ cat .hg/last-message.txt
358 $ cat .hg/last-message.txt
359
359
360
360
361 test saving last-message.txt
361 test saving last-message.txt
362
362
363 test that '[committemplate] changeset' definition and commit log
363 test that '[committemplate] changeset' definition and commit log
364 specific template keywords work well
364 specific template keywords work well
365
365
366 $ cat >> .hg/hgrc <<EOF
366 $ cat >> .hg/hgrc <<EOF
367 > [committemplate]
367 > [committemplate]
368 > changeset.commit.normal = HG: this is "commit.normal" template
368 > changeset.commit.normal = HG: this is "commit.normal" template
369 > HG: {extramsg}
369 > HG: {extramsg}
370 > {if(currentbookmark,
370 > {if(currentbookmark,
371 > "HG: bookmark '{currentbookmark}' is activated\n",
371 > "HG: bookmark '{currentbookmark}' is activated\n",
372 > "HG: no bookmark is activated\n")}{subrepos %
372 > "HG: no bookmark is activated\n")}{subrepos %
373 > "HG: subrepo '{subrepo}' is changed\n"}
373 > "HG: subrepo '{subrepo}' is changed\n"}
374 >
374 >
375 > changeset.commit = HG: this is "commit" template
375 > changeset.commit = HG: this is "commit" template
376 > HG: {extramsg}
376 > HG: {extramsg}
377 > {if(currentbookmark,
377 > {if(currentbookmark,
378 > "HG: bookmark '{currentbookmark}' is activated\n",
378 > "HG: bookmark '{currentbookmark}' is activated\n",
379 > "HG: no bookmark is activated\n")}{subrepos %
379 > "HG: no bookmark is activated\n")}{subrepos %
380 > "HG: subrepo '{subrepo}' is changed\n"}
380 > "HG: subrepo '{subrepo}' is changed\n"}
381 >
381 >
382 > changeset = HG: this is customized commit template
382 > changeset = HG: this is customized commit template
383 > HG: {extramsg}
383 > HG: {extramsg}
384 > {if(currentbookmark,
384 > {if(currentbookmark,
385 > "HG: bookmark '{currentbookmark}' is activated\n",
385 > "HG: bookmark '{currentbookmark}' is activated\n",
386 > "HG: no bookmark is activated\n")}{subrepos %
386 > "HG: no bookmark is activated\n")}{subrepos %
387 > "HG: subrepo '{subrepo}' is changed\n"}
387 > "HG: subrepo '{subrepo}' is changed\n"}
388 > EOF
388 > EOF
389
389
390 $ hg init sub2
390 $ hg init sub2
391 $ echo a > sub2/a
391 $ echo a > sub2/a
392 $ hg -R sub2 add sub2/a
392 $ hg -R sub2 add sub2/a
393 $ echo 'sub2 = sub2' >> .hgsub
393 $ echo 'sub2 = sub2' >> .hgsub
394
394
395 $ HGEDITOR=cat hg commit -S -q
395 $ HGEDITOR=cat hg commit -S -q
396 HG: this is "commit.normal" template
396 HG: this is "commit.normal" template
397 HG: Leave message empty to abort commit.
397 HG: Leave message empty to abort commit.
398 HG: bookmark 'currentbookmark' is activated
398 HG: bookmark 'currentbookmark' is activated
399 HG: subrepo 'sub' is changed
399 HG: subrepo 'sub' is changed
400 HG: subrepo 'sub2' is changed
400 HG: subrepo 'sub2' is changed
401 abort: empty commit message
401 abort: empty commit message
402 [255]
402 [255]
403
403
404 $ cat >> .hg/hgrc <<EOF
404 $ cat >> .hg/hgrc <<EOF
405 > [committemplate]
405 > [committemplate]
406 > changeset.commit.normal =
406 > changeset.commit.normal =
407 > # now, "changeset.commit" should be chosen for "hg commit"
407 > # now, "changeset.commit" should be chosen for "hg commit"
408 > EOF
408 > EOF
409
409
410 $ hg bookmark --inactive currentbookmark
410 $ hg bookmark --inactive currentbookmark
411 $ hg forget .hgsub
411 $ hg forget .hgsub
412 $ HGEDITOR=cat hg commit -q
412 $ HGEDITOR=cat hg commit -q
413 HG: this is "commit" template
413 HG: this is "commit" template
414 HG: Leave message empty to abort commit.
414 HG: Leave message empty to abort commit.
415 HG: no bookmark is activated
415 HG: no bookmark is activated
416 abort: empty commit message
416 abort: empty commit message
417 [255]
417 [255]
418
418
419 $ cat >> .hg/hgrc <<EOF
419 $ cat >> .hg/hgrc <<EOF
420 > [committemplate]
420 > [committemplate]
421 > changeset.commit =
421 > changeset.commit =
422 > # now, "changeset" should be chosen for "hg commit"
422 > # now, "changeset" should be chosen for "hg commit"
423 > EOF
423 > EOF
424
424
425 $ HGEDITOR=cat hg commit -q
425 $ HGEDITOR=cat hg commit -q
426 HG: this is customized commit template
426 HG: this is customized commit template
427 HG: Leave message empty to abort commit.
427 HG: Leave message empty to abort commit.
428 HG: no bookmark is activated
428 HG: no bookmark is activated
429 abort: empty commit message
429 abort: empty commit message
430 [255]
430 [255]
431
431
432 $ cat >> .hg/hgrc <<EOF
432 $ cat >> .hg/hgrc <<EOF
433 > # disable customizing for subsequent tests
433 > # disable customizing for subsequent tests
434 > [committemplate]
434 > [committemplate]
435 > changeset =
435 > changeset =
436 > EOF
436 > EOF
437
437
438 $ cd ..
438 $ cd ..
439
439
440
440
441 commit copy
441 commit copy
442
442
443 $ hg init dir2
443 $ hg init dir2
444 $ cd dir2
444 $ cd dir2
445 $ echo bleh > bar
445 $ echo bleh > bar
446 $ hg add bar
446 $ hg add bar
447 $ hg ci -m 'add bar'
447 $ hg ci -m 'add bar'
448
448
449 $ hg cp bar foo
449 $ hg cp bar foo
450 $ echo >> bar
450 $ echo >> bar
451 $ hg ci -m 'cp bar foo; change bar'
451 $ hg ci -m 'cp bar foo; change bar'
452
452
453 $ hg debugrename foo
453 $ hg debugrename foo
454 foo renamed from bar:26d3ca0dfd18e44d796b564e38dd173c9668d3a9
454 foo renamed from bar:26d3ca0dfd18e44d796b564e38dd173c9668d3a9
455 $ hg debugindex bar
455 $ hg debugindex bar
456 rev offset length ..... linkrev nodeid p1 p2 (re)
456 rev offset length ..... linkrev nodeid p1 p2 (re)
457 0 0 6 ..... 0 26d3ca0dfd18 000000000000 000000000000 (re)
457 0 0 6 ..... 0 26d3ca0dfd18 000000000000 000000000000 (re)
458 1 6 7 ..... 1 d267bddd54f7 26d3ca0dfd18 000000000000 (re)
458 1 6 7 ..... 1 d267bddd54f7 26d3ca0dfd18 000000000000 (re)
459
459
460 verify pathauditor blocks evil filepaths
461 $ cat > evil-commit.py <<EOF
462 > from mercurial import ui, hg, context, node
463 > notrc = u".h\u200cg".encode('utf-8') + '/hgrc'
464 > u = ui.ui()
465 > r = hg.repository(u, '.')
466 > def filectxfn(repo, memctx, path):
467 > return context.memfilectx(repo, path, '[hooks]\nupdate = echo owned')
468 > c = context.memctx(r, [r['tip'].node(), node.nullid],
469 > 'evil', [notrc], filectxfn, 0)
470 > r.commitctx(c)
471 > EOF
472 $ $PYTHON evil-commit.py
473 $ hg co --clean tip
474 abort: path contains illegal component: .h\xe2\x80\x8cg/hgrc (esc)
475 [255]
476
460 $ cd ..
477 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now