##// END OF EJS Templates
merge with stable
Kevin Bullock -
r35187:aef2b98d merge default
parent child Browse files
Show More
@@ -155,3 +155,4 b' 2f427b57bf9019c6dc3750baa539dc22c1be50f6'
155 155 1e2454b60e5936f5e77498cab2648db469504487 0 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
@@ -168,3 +168,4 b' 2f427b57bf9019c6dc3750baa539dc22c1be50f6'
168 168 1e2454b60e5936f5e77498cab2648db469504487 4.4-rc
169 169 0ccb43d4cf01d013ae05917ec4f305509f851b2d 4.4
170 170 cabc840ffdee8a72f3689fb77dd74d04fdc2bc04 4.4.1
171 a92b9f8e11ba330614cdfd6af0e03b15c1ff3797 4.4.2
@@ -455,6 +455,7 b' def updatelfiles(ui, repo, filelist=None'
455 455 lfiles = [f for f in lfiles if f in filelist]
456 456
457 457 update = {}
458 dropped = set()
458 459 updated, removed = 0, 0
459 460 wvfs = repo.wvfs
460 461 wctx = repo[None]
@@ -476,7 +477,11 b' def updatelfiles(ui, repo, filelist=None'
476 477 expecthash = lfutil.readasstandin(wctx[relstandin])
477 478 if expecthash != '':
478 479 if lfile not in wctx: # not switched to normal file
479 wvfs.unlinkpath(rellfile, ignoremissing=True)
480 if repo.dirstate[relstandin] != '?':
481 wvfs.unlinkpath(rellfile, ignoremissing=True)
482 else:
483 dropped.add(rellfile)
484
480 485 # use normallookup() to allocate an entry in largefiles
481 486 # dirstate to prevent lfilesrepo.status() from reporting
482 487 # missing files as removed.
@@ -496,6 +501,15 b' def updatelfiles(ui, repo, filelist=None'
496 501 lfdirstate.write()
497 502
498 503 if lfiles:
504 lfiles = [f for f in lfiles if f not in dropped]
505
506 for f in dropped:
507 repo.wvfs.unlinkpath(lfutil.standin(f))
508
509 # This needs to happen for dropped files, otherwise they stay in
510 # the M state.
511 lfutil.synclfdirstate(repo, lfdirstate, f, normallookup)
512
499 513 statuswriter(_('getting changed largefiles\n'))
500 514 cachelfiles(ui, repo, None, lfiles)
501 515
@@ -220,8 +220,17 b' def _loadnewui(srcui, args):'
220 220 newui._csystem = srcui._csystem
221 221
222 222 # command line args
223 args = args[:]
224 dispatch._parseconfig(newui, dispatch._earlygetopt(['--config'], args))
223 options = {}
224 if srcui.plain('strictflags'):
225 options.update(dispatch._earlyparseopts(args))
226 else:
227 args = args[:]
228 options['config'] = dispatch._earlygetopt(['--config'], args)
229 cwds = dispatch._earlygetopt(['--cwd'], args)
230 options['cwd'] = cwds and cwds[-1] or ''
231 rpath = dispatch._earlygetopt(["-R", "--repository", "--repo"], args)
232 options['repository'] = rpath and rpath[-1] or ''
233 dispatch._parseconfig(newui, options['config'])
225 234
226 235 # stolen from tortoisehg.util.copydynamicconfig()
227 236 for section, name, value in srcui.walkconfig():
@@ -232,10 +241,9 b' def _loadnewui(srcui, args):'
232 241 newui.setconfig(section, name, value, source)
233 242
234 243 # load wd and repo config, copied from dispatch.py
235 cwds = dispatch._earlygetopt(['--cwd'], args)
236 cwd = cwds and os.path.realpath(cwds[-1]) or None
237 rpath = dispatch._earlygetopt(["-R", "--repository", "--repo"], args)
238 rpath = rpath and rpath[-1] or ''
244 cwd = options['cwd']
245 cwd = cwd and os.path.realpath(cwd) or None
246 rpath = options['repository']
239 247 path, newlui = dispatch._getlocal(newui, rpath, wd=cwd)
240 248
241 249 return (newui, newlui)
@@ -150,6 +150,8 b' def dispatch(req):'
150 150 try:
151 151 if not req.ui:
152 152 req.ui = uimod.ui.load()
153 if req.ui.plain('strictflags'):
154 req.earlyoptions.update(_earlyparseopts(req.args))
153 155 if _earlyreqoptbool(req, 'traceback', ['--traceback']):
154 156 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
155 157
@@ -644,6 +646,12 b' def _parseconfig(ui, config):'
644 646
645 647 return configs
646 648
649 def _earlyparseopts(args):
650 options = {}
651 fancyopts.fancyopts(args, commands.globalopts, options,
652 gnu=False, early=True)
653 return options
654
647 655 def _earlygetopt(aliases, args, strip=True):
648 656 """Return list of values for an option (or aliases).
649 657
@@ -732,12 +740,16 b' def _earlygetopt(aliases, args, strip=Tr'
732 740
733 741 def _earlyreqopt(req, name, aliases):
734 742 """Peek a list option without using a full options table"""
743 if req.ui.plain('strictflags'):
744 return req.earlyoptions[name]
735 745 values = _earlygetopt(aliases, req.args, strip=False)
736 746 req.earlyoptions[name] = values
737 747 return values
738 748
739 749 def _earlyreqoptstr(req, name, aliases):
740 750 """Peek a string option without using a full options table"""
751 if req.ui.plain('strictflags'):
752 return req.earlyoptions[name]
741 753 value = (_earlygetopt(aliases, req.args, strip=False) or [''])[-1]
742 754 req.earlyoptions[name] = value
743 755 return value
@@ -745,13 +757,15 b' def _earlyreqoptstr(req, name, aliases):'
745 757 def _earlyreqoptbool(req, name, aliases):
746 758 """Peek a boolean option without using a full options table
747 759
748 >>> req = request([b'x', b'--debugger'])
760 >>> req = request([b'x', b'--debugger'], uimod.ui())
749 761 >>> _earlyreqoptbool(req, b'debugger', [b'--debugger'])
750 762 True
751 763
752 >>> req = request([b'x', b'--', b'--debugger'])
764 >>> req = request([b'x', b'--', b'--debugger'], uimod.ui())
753 765 >>> _earlyreqoptbool(req, b'debugger', [b'--debugger'])
754 766 """
767 if req.ui.plain('strictflags'):
768 return req.earlyoptions[name]
755 769 try:
756 770 argcount = req.args.index("--")
757 771 except ValueError:
@@ -7,6 +7,8 b''
7 7
8 8 from __future__ import absolute_import
9 9
10 import functools
11
10 12 from .i18n import _
11 13 from . import (
12 14 error,
@@ -24,6 +26,179 b' nevernegate = {'
24 26 'version',
25 27 }
26 28
29 def _earlyoptarg(arg, shortlist, namelist):
30 """Check if the given arg is a valid unabbreviated option
31
32 Returns (flag_str, has_embedded_value?, embedded_value, takes_value?)
33
34 >>> def opt(arg):
35 ... return _earlyoptarg(arg, b'R:q', [b'cwd=', b'debugger'])
36
37 long form:
38
39 >>> opt(b'--cwd')
40 ('--cwd', False, '', True)
41 >>> opt(b'--cwd=')
42 ('--cwd', True, '', True)
43 >>> opt(b'--cwd=foo')
44 ('--cwd', True, 'foo', True)
45 >>> opt(b'--debugger')
46 ('--debugger', False, '', False)
47 >>> opt(b'--debugger=') # invalid but parsable
48 ('--debugger', True, '', False)
49
50 short form:
51
52 >>> opt(b'-R')
53 ('-R', False, '', True)
54 >>> opt(b'-Rfoo')
55 ('-R', True, 'foo', True)
56 >>> opt(b'-q')
57 ('-q', False, '', False)
58 >>> opt(b'-qfoo') # invalid but parsable
59 ('-q', True, 'foo', False)
60
61 unknown or invalid:
62
63 >>> opt(b'--unknown')
64 ('', False, '', False)
65 >>> opt(b'-u')
66 ('', False, '', False)
67 >>> opt(b'-ufoo')
68 ('', False, '', False)
69 >>> opt(b'--')
70 ('', False, '', False)
71 >>> opt(b'-')
72 ('', False, '', False)
73 >>> opt(b'-:')
74 ('', False, '', False)
75 >>> opt(b'-:foo')
76 ('', False, '', False)
77 """
78 if arg.startswith('--'):
79 flag, eq, val = arg.partition('=')
80 if flag[2:] in namelist:
81 return flag, bool(eq), val, False
82 if flag[2:] + '=' in namelist:
83 return flag, bool(eq), val, True
84 elif arg.startswith('-') and arg != '-' and not arg.startswith('-:'):
85 flag, val = arg[:2], arg[2:]
86 i = shortlist.find(flag[1:])
87 if i >= 0:
88 return flag, bool(val), val, shortlist.startswith(':', i + 1)
89 return '', False, '', False
90
91 def earlygetopt(args, shortlist, namelist, gnu=False, keepsep=False):
92 """Parse options like getopt, but ignores unknown options and abbreviated
93 forms
94
95 If gnu=False, this stops processing options as soon as a non/unknown-option
96 argument is encountered. Otherwise, option and non-option arguments may be
97 intermixed, and unknown-option arguments are taken as non-option.
98
99 If keepsep=True, '--' won't be removed from the list of arguments left.
100 This is useful for stripping early options from a full command arguments.
101
102 >>> def get(args, gnu=False, keepsep=False):
103 ... return earlygetopt(args, b'R:q', [b'cwd=', b'debugger'],
104 ... gnu=gnu, keepsep=keepsep)
105
106 default parsing rules for early options:
107
108 >>> get([b'x', b'--cwd', b'foo', b'-Rbar', b'-q', b'y'], gnu=True)
109 ([('--cwd', 'foo'), ('-R', 'bar'), ('-q', '')], ['x', 'y'])
110 >>> get([b'x', b'--cwd=foo', b'y', b'-R', b'bar', b'--debugger'], gnu=True)
111 ([('--cwd', 'foo'), ('-R', 'bar'), ('--debugger', '')], ['x', 'y'])
112 >>> get([b'--unknown', b'--cwd=foo', b'--', '--debugger'], gnu=True)
113 ([('--cwd', 'foo')], ['--unknown', '--debugger'])
114
115 restricted parsing rules (early options must come first):
116
117 >>> get([b'--cwd', b'foo', b'-Rbar', b'x', b'-q', b'y'], gnu=False)
118 ([('--cwd', 'foo'), ('-R', 'bar')], ['x', '-q', 'y'])
119 >>> get([b'--cwd=foo', b'x', b'y', b'-R', b'bar', b'--debugger'], gnu=False)
120 ([('--cwd', 'foo')], ['x', 'y', '-R', 'bar', '--debugger'])
121 >>> get([b'--unknown', b'--cwd=foo', b'--', '--debugger'], gnu=False)
122 ([], ['--unknown', '--cwd=foo', '--debugger'])
123
124 stripping early options (without loosing '--'):
125
126 >>> get([b'x', b'-Rbar', b'--', '--debugger'], gnu=True, keepsep=True)[1]
127 ['x', '--', '--debugger']
128
129 last argument:
130
131 >>> get([b'--cwd'])
132 ([], ['--cwd'])
133 >>> get([b'--cwd=foo'])
134 ([('--cwd', 'foo')], [])
135 >>> get([b'-R'])
136 ([], ['-R'])
137 >>> get([b'-Rbar'])
138 ([('-R', 'bar')], [])
139 >>> get([b'-q'])
140 ([('-q', '')], [])
141 >>> get([b'-q', b'--'])
142 ([('-q', '')], [])
143
144 value passed to bool options:
145
146 >>> get([b'--debugger=foo', b'x'])
147 ([], ['--debugger=foo', 'x'])
148 >>> get([b'-qfoo', b'x'])
149 ([], ['-qfoo', 'x'])
150
151 short option isn't separated with '=':
152
153 >>> get([b'-R=bar'])
154 ([('-R', '=bar')], [])
155
156 ':' may be in shortlist, but shouldn't be taken as an option letter:
157
158 >>> get([b'-:', b'y'])
159 ([], ['-:', 'y'])
160
161 '-' is a valid non-option argument:
162
163 >>> get([b'-', b'y'])
164 ([], ['-', 'y'])
165 """
166 # ignoring everything just after '--' isn't correct as '--' may be an
167 # option value (e.g. ['-R', '--']), but we do that consistently.
168 try:
169 argcount = args.index('--')
170 except ValueError:
171 argcount = len(args)
172
173 parsedopts = []
174 parsedargs = []
175 pos = 0
176 while pos < argcount:
177 arg = args[pos]
178 flag, hasval, val, takeval = _earlyoptarg(arg, shortlist, namelist)
179 if not hasval and takeval and pos + 1 >= argcount:
180 # missing last argument
181 break
182 if not flag or hasval and not takeval:
183 # non-option argument or -b/--bool=INVALID_VALUE
184 if gnu:
185 parsedargs.append(arg)
186 pos += 1
187 else:
188 break
189 elif hasval == takeval:
190 # -b/--bool or -s/--str=VALUE
191 parsedopts.append((flag, val))
192 pos += 1
193 else:
194 # -s/--str VALUE
195 parsedopts.append((flag, args[pos + 1]))
196 pos += 2
197
198 parsedargs.extend(args[pos:argcount])
199 parsedargs.extend(args[argcount + (not keepsep):])
200 return parsedopts, parsedargs
201
27 202 def gnugetopt(args, options, longoptions):
28 203 """Parse options mostly like getopt.gnu_getopt.
29 204
@@ -51,7 +226,7 b' def gnugetopt(args, options, longoptions'
51 226 return opts, args
52 227
53 228
54 def fancyopts(args, options, state, gnu=False):
229 def fancyopts(args, options, state, gnu=False, early=False):
55 230 """
56 231 read args, parse options, and store options in state
57 232
@@ -124,7 +299,9 b' def fancyopts(args, options, state, gnu='
124 299 namelist.append(oname)
125 300
126 301 # parse arguments
127 if gnu:
302 if early:
303 parse = functools.partial(earlygetopt, gnu=gnu)
304 elif gnu:
128 305 parse = gnugetopt
129 306 else:
130 307 parse = pycompat.getoptb
@@ -56,9 +56,17 b' HGPLAIN'
56 56 localization. This can be useful when scripting against Mercurial
57 57 in the face of existing user configuration.
58 58
59 In addition to the features disabled by ``HGPLAIN=``, the following
60 values can be specified to adjust behavior:
61
62 ``+strictflags``
63 Restrict parsing of command line flags.
64
59 65 Equivalent options set via command line flags or environment
60 66 variables are not overridden.
61 67
68 See :hg:`help scripting` for details.
69
62 70 HGPLAINEXCEPT
63 71 This is a comma-separated list of features to preserve when
64 72 HGPLAIN is enabled. Currently the following values are supported:
@@ -74,6 +74,32 b' HGRCPATH'
74 74 like the username and extensions that may be required to interface
75 75 with a repository.
76 76
77 Command-line Flags
78 ==================
79
80 Mercurial's default command-line parser is designed for humans, and is not
81 robust against malicious input. For instance, you can start a debugger by
82 passing ``--debugger`` as an option value::
83
84 $ REV=--debugger sh -c 'hg log -r "$REV"'
85
86 This happens because several command-line flags need to be scanned without
87 using a concrete command table, which may be modified while loading repository
88 settings and extensions.
89
90 Since Mercurial 4.4.2, the parsing of such flags may be restricted by setting
91 ``HGPLAIN=+strictflags``. When this feature is enabled, all early options
92 (e.g. ``-R/--repository``, ``--cwd``, ``--config``) must be specified first
93 amongst the other global options, and cannot be injected to an arbitrary
94 location::
95
96 $ HGPLAIN=+strictflags hg -R "$REPO" log -r "$REV"
97
98 In earlier Mercurial versions where ``+strictflags`` isn't available, you
99 can mitigate the issue by concatenating an option value with its flag::
100
101 $ hg log -r"$REV" --keyword="$KEYWORD"
102
77 103 Consuming Command Output
78 104 ========================
79 105
@@ -90,7 +90,7 b' Interaction with Mercurial Commands'
90 90 :archive: archive does not recurse in subrepositories unless
91 91 -S/--subrepos is specified.
92 92
93 :cat: cat currently only handles exact file matches in subrepos.
93 :cat: Git subrepositories only support exact file matches.
94 94 Subversion subrepositories are currently ignored.
95 95
96 96 :commit: commit creates a consistent snapshot of the state of the
@@ -653,7 +653,7 b' def _checkunknownfile(repo, wctx, mctx, '
653 653 and repo.dirstate.normalize(f) not in repo.dirstate
654 654 and mctx[f2].cmp(wctx[f]))
655 655
656 def _checkunknowndirs(repo, f):
656 class _unknowndirschecker(object):
657 657 """
658 658 Look for any unknown files or directories that may have a path conflict
659 659 with a file. If any path prefix of the file exists as a file or link,
@@ -663,23 +663,42 b' def _checkunknowndirs(repo, f):'
663 663 Returns the shortest path at which a conflict occurs, or None if there is
664 664 no conflict.
665 665 """
666 def __init__(self):
667 # A set of paths known to be good. This prevents repeated checking of
668 # dirs. It will be updated with any new dirs that are checked and found
669 # to be safe.
670 self._unknowndircache = set()
666 671
667 # Check for path prefixes that exist as unknown files.
668 for p in reversed(list(util.finddirs(f))):
669 if (repo.wvfs.audit.check(p)
670 and repo.wvfs.isfileorlink(p)
671 and repo.dirstate.normalize(p) not in repo.dirstate):
672 return p
672 # A set of paths that are known to be absent. This prevents repeated
673 # checking of subdirectories that are known not to exist. It will be
674 # updated with any new dirs that are checked and found to be absent.
675 self._missingdircache = set()
673 676
674 # Check if the file conflicts with a directory containing unknown files.
675 if repo.wvfs.audit.check(f) and repo.wvfs.isdir(f):
676 # Does the directory contain any files that are not in the dirstate?
677 for p, dirs, files in repo.wvfs.walk(f):
678 for fn in files:
679 relf = repo.dirstate.normalize(repo.wvfs.reljoin(p, fn))
680 if relf not in repo.dirstate:
681 return f
682 return None
677 def __call__(self, repo, f):
678 # Check for path prefixes that exist as unknown files.
679 for p in reversed(list(util.finddirs(f))):
680 if p in self._missingdircache:
681 return
682 if p in self._unknowndircache:
683 continue
684 if repo.wvfs.audit.check(p):
685 if (repo.wvfs.isfileorlink(p)
686 and repo.dirstate.normalize(p) not in repo.dirstate):
687 return p
688 if not repo.wvfs.lexists(p):
689 self._missingdircache.add(p)
690 return
691 self._unknowndircache.add(p)
692
693 # Check if the file conflicts with a directory containing unknown files.
694 if repo.wvfs.audit.check(f) and repo.wvfs.isdir(f):
695 # Does the directory contain any files that are not in the dirstate?
696 for p, dirs, files in repo.wvfs.walk(f):
697 for fn in files:
698 relf = repo.dirstate.normalize(repo.wvfs.reljoin(p, fn))
699 if relf not in repo.dirstate:
700 return f
701 return None
683 702
684 703 def _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce):
685 704 """
@@ -701,12 +720,13 b' def _checkunknownfiles(repo, wctx, mctx,'
701 720 elif config == 'warn':
702 721 warnconflicts.update(conflicts)
703 722
723 checkunknowndirs = _unknowndirschecker()
704 724 for f, (m, args, msg) in actions.iteritems():
705 725 if m in ('c', 'dc'):
706 726 if _checkunknownfile(repo, wctx, mctx, f):
707 727 fileconflicts.add(f)
708 728 elif pathconfig and f not in wctx:
709 path = _checkunknowndirs(repo, f)
729 path = checkunknowndirs(repo, f)
710 730 if path is not None:
711 731 pathconflicts.add(path)
712 732 elif m == 'dg':
@@ -895,34 +915,21 b' def checkpathconflicts(repo, wctx, mctx,'
895 915 # can't be updated to cleanly.
896 916 invalidconflicts = set()
897 917
918 # The set of directories that contain files that are being created.
919 createdfiledirs = set()
920
898 921 # The set of files deleted by all the actions.
899 922 deletedfiles = set()
900 923
901 924 for f, (m, args, msg) in actions.items():
902 925 if m in ('c', 'dc', 'm', 'cm'):
903 926 # This action may create a new local file.
927 createdfiledirs.update(util.finddirs(f))
904 928 if mf.hasdir(f):
905 929 # The file aliases a local directory. This might be ok if all
906 930 # the files in the local directory are being deleted. This
907 931 # will be checked once we know what all the deleted files are.
908 932 remoteconflicts.add(f)
909 for p in util.finddirs(f):
910 if p in mf:
911 if p in mctx:
912 # The file is in a directory which aliases both a local
913 # and a remote file. This is an internal inconsistency
914 # within the remote manifest.
915 invalidconflicts.add(p)
916 else:
917 # The file is in a directory which aliases a local file.
918 # We will need to rename the local file.
919 localconflicts.add(p)
920 if p in actions and actions[p][0] in ('c', 'dc', 'm', 'cm'):
921 # The file is in a directory which aliases a remote file.
922 # This is an internal inconsistency within the remote
923 # manifest.
924 invalidconflicts.add(p)
925
926 933 # Track the names of all deleted files.
927 934 if m == 'r':
928 935 deletedfiles.add(f)
@@ -934,6 +941,24 b' def checkpathconflicts(repo, wctx, mctx,'
934 941 f2, flags = args
935 942 deletedfiles.add(f2)
936 943
944 # Check all directories that contain created files for path conflicts.
945 for p in createdfiledirs:
946 if p in mf:
947 if p in mctx:
948 # A file is in a directory which aliases both a local
949 # and a remote file. This is an internal inconsistency
950 # within the remote manifest.
951 invalidconflicts.add(p)
952 else:
953 # A file is in a directory which aliases a local file.
954 # We will need to rename the local file.
955 localconflicts.add(p)
956 if p in actions and actions[p][0] in ('c', 'dc', 'm', 'cm'):
957 # The file is in a directory which aliases a remote file.
958 # This is an internal inconsistency within the remote
959 # manifest.
960 invalidconflicts.add(p)
961
937 962 # Rename all local conflicting files that have not been deleted.
938 963 for p in localconflicts:
939 964 if p not in deletedfiles:
@@ -766,6 +766,7 b' class ui(object):'
766 766
767 767 The return value can either be
768 768 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
769 - False if feature is disabled by default and not included in HGPLAIN
769 770 - True otherwise
770 771 '''
771 772 if ('HGPLAIN' not in encoding.environ and
@@ -773,6 +774,9 b' class ui(object):'
773 774 return False
774 775 exceptions = encoding.environ.get('HGPLAINEXCEPT',
775 776 '').strip().split(',')
777 # TODO: add support for HGPLAIN=+feature,-feature syntax
778 if '+strictflags' not in encoding.environ.get('HGPLAIN', '').split(','):
779 exceptions.append('strictflags')
776 780 if feature and exceptions:
777 781 return feature not in exceptions
778 782 return True
@@ -58,7 +58,7 b' amend with dirty subrepo'
58 58
59 59 $ echo a >> s/a
60 60 $ hg add -R s
61 adding s/a
61 adding s/a (glob)
62 62 $ hg amend
63 63 abort: uncommitted changes in subrepository "s"
64 64 (use --subrepos for recursive commit)
@@ -9,7 +9,7 b' on commit:'
9 9 $ hg init sub/.hg
10 10 $ echo 'sub/.hg = sub/.hg' >> .hgsub
11 11 $ hg ci -qAm 'add subrepo "sub/.hg"'
12 abort: path 'sub/.hg' is inside nested repo 'sub'
12 abort: path 'sub/.hg' is inside nested repo 'sub' (glob)
13 13 [255]
14 14
15 15 prepare tampered repo (including the commit above):
@@ -33,7 +33,7 b' prepare tampered repo (including the com'
33 33 on clone (and update):
34 34
35 35 $ hg clone -q hgname hgname2
36 abort: path 'sub/.hg' is inside nested repo 'sub'
36 abort: path 'sub/.hg' is inside nested repo 'sub' (glob)
37 37 [255]
38 38
39 39 Test direct symlink traversal
@@ -137,6 +137,20 b' typical client does not want echo-back m'
137 137 summary: 1
138 138
139 139
140 check strict parsing of early options:
141
142 >>> import os
143 >>> from hgclient import check, readchannel, runcommand
144 >>> os.environ['HGPLAIN'] = '+strictflags'
145 >>> @check
146 ... def cwd(server):
147 ... readchannel(server)
148 ... runcommand(server, ['log', '-b', '--config=alias.log=!echo pwned',
149 ... 'default'])
150 *** runcommand log -b --config=alias.log=!echo pwned default
151 abort: unknown revision '--config=alias.log=!echo pwned'!
152 [255]
153
140 154 check that "histedit --commands=-" can read rules from the input channel:
141 155
142 156 >>> import cStringIO
@@ -113,6 +113,51 b' Shell aliases bypass any command parsing'
113 113 $ hg log -b '--config=alias.log=!echo howdy'
114 114 howdy
115 115
116 Early options must come first if HGPLAIN=+strictflags is specified:
117 (BUG: chg cherry-picks early options to pass them as a server command)
118
119 #if no-chg
120 $ HGPLAIN=+strictflags hg log -b --config='hooks.pre-log=false' default
121 abort: unknown revision '--config=hooks.pre-log=false'!
122 [255]
123 $ HGPLAIN=+strictflags hg log -b -R. default
124 abort: unknown revision '-R.'!
125 [255]
126 $ HGPLAIN=+strictflags hg log -b --cwd=. default
127 abort: unknown revision '--cwd=.'!
128 [255]
129 #endif
130 $ HGPLAIN=+strictflags hg log -b --debugger default
131 abort: unknown revision '--debugger'!
132 [255]
133 $ HGPLAIN=+strictflags hg log -b --config='alias.log=!echo pwned' default
134 abort: unknown revision '--config=alias.log=!echo pwned'!
135 [255]
136
137 $ HGPLAIN=+strictflags hg log --config='hooks.pre-log=false' -b default
138 abort: option --config may not be abbreviated!
139 [255]
140 $ HGPLAIN=+strictflags hg log -q --cwd=.. -b default
141 abort: option --cwd may not be abbreviated!
142 [255]
143 $ HGPLAIN=+strictflags hg log -q -R . -b default
144 abort: option -R has to be separated from other options (e.g. not -qR) and --repository may only be abbreviated as --repo!
145 [255]
146
147 $ HGPLAIN=+strictflags hg --config='hooks.pre-log=false' log -b default
148 abort: pre-log hook exited with status 1
149 [255]
150 $ HGPLAIN=+strictflags hg --cwd .. -q -Ra log -b default
151 0:cb9a9f314b8b
152
153 For compatibility reasons, HGPLAIN=+strictflags is not enabled by plain HGPLAIN:
154
155 $ HGPLAIN= hg log --config='hooks.pre-log=false' -b default
156 abort: pre-log hook exited with status 1
157 [255]
158 $ HGPLAINEXCEPT= hg log --cwd .. -q -Ra -b default
159 0:cb9a9f314b8b
160
116 161 [defaults]
117 162
118 163 $ hg cat a
@@ -48,6 +48,7 b" testmod('mercurial.context')"
48 48 testmod('mercurial.dagparser', optionflags=doctest.NORMALIZE_WHITESPACE)
49 49 testmod('mercurial.dispatch')
50 50 testmod('mercurial.encoding')
51 testmod('mercurial.fancyopts')
51 52 testmod('mercurial.formatter')
52 53 testmod('mercurial.hg')
53 54 testmod('mercurial.hgweb.hgwebdir_mod')
@@ -972,6 +972,7 b' test import with similarity and git and '
972 972 adding b
973 973 recording removal of a as rename to b (88% similar)
974 974 applied to working directory
975 $ echo 'mod b' > b
975 976 $ hg st -C
976 977 A b
977 978 a
@@ -979,6 +980,8 b' test import with similarity and git and '
979 980 $ hg revert -a
980 981 undeleting a
981 982 forgetting b
983 $ cat b
984 mod b
982 985 $ rm b
983 986 $ hg import --no-commit -v -s 100 ../rename.diff -p2
984 987 applying ../rename.diff
@@ -390,8 +390,12 b' Test update with subrepos.'
390 390 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
391 391 $ hg status -S
392 392
393 Forget doesn't change the content of the file
394 $ echo 'pre-forget content' > subrepo/large.txt
393 395 $ hg forget -v subrepo/large.txt
394 396 removing subrepo/large.txt (glob)
397 $ cat subrepo/large.txt
398 pre-forget content
395 399
396 400 Test reverting a forgotten file
397 401 $ hg revert -R subrepo subrepo/large.txt
@@ -1060,7 +1064,9 b' largefiles (issue4547)'
1060 1064 > largefiles=
1061 1065 > EOF
1062 1066 $ echo large > subrepo-root/large
1063 $ hg -R subrepo-root add --large subrepo-root/large
1067 $ mkdir -p subrepo-root/dir/subdir
1068 $ echo large2 > subrepo-root/dir/subdir/large.bin
1069 $ hg -R subrepo-root add --large subrepo-root/large subrepo-root/dir/subdir/large.bin
1064 1070 $ hg clone -q no-largefiles subrepo-root/no-largefiles
1065 1071 $ cat > subrepo-root/.hgsub <<EOF
1066 1072 > no-largefiles = no-largefiles
@@ -1069,6 +1075,7 b' largefiles (issue4547)'
1069 1075 $ hg -R subrepo-root commit -m '#0'
1070 1076 Invoking status precommit hook
1071 1077 A .hgsub
1078 A dir/subdir/large.bin
1072 1079 A large
1073 1080 ? .hgsubstate
1074 1081 $ echo dirty >> subrepo-root/large
@@ -1085,8 +1092,155 b' largefiles (issue4547)'
1085 1092 reverting subrepo no-largefiles
1086 1093 reverting subrepo-root/no-largefiles/normal1 (glob)
1087 1094
1088 $ cd ..
1095 Move (and then undo) a directory move with only largefiles.
1096
1097 $ listtree() {
1098 > find $@ \( -type d -printf "%p/\n" -o -type f -printf "%p\n" \) \
1099 > -a -name .hg -prune | sort
1100 > }
1101
1102 $ cd subrepo-root
1103 $ listtree .hglf dir* large*
1104 .hglf/
1105 .hglf/dir/
1106 .hglf/dir/subdir/
1107 .hglf/dir/subdir/large.bin
1108 .hglf/large
1109 dir/
1110 dir/subdir/
1111 dir/subdir/large.bin
1112 large
1113 large.orig
1114
1115 $ hg mv dir/subdir dir/subdir2
1116 moving .hglf/dir/subdir/large.bin to .hglf/dir/subdir2/large.bin (glob)
1117
1118 $ listtree .hglf dir* large*
1119 .hglf/
1120 .hglf/dir/
1121 .hglf/dir/subdir2/
1122 .hglf/dir/subdir2/large.bin
1123 .hglf/large
1124 dir/
1125 dir/subdir2/
1126 dir/subdir2/large.bin
1127 large
1128 large.orig
1129 $ hg status -C
1130 A dir/subdir2/large.bin
1131 dir/subdir/large.bin
1132 R dir/subdir/large.bin
1133 ? large.orig
1134
1135 $ echo 'modified' > dir/subdir2/large.bin
1136 $ hg status -C
1137 A dir/subdir2/large.bin
1138 dir/subdir/large.bin
1139 R dir/subdir/large.bin
1140 ? large.orig
1141
1142 $ hg revert --all
1143 undeleting .hglf/dir/subdir/large.bin (glob)
1144 forgetting .hglf/dir/subdir2/large.bin (glob)
1145 reverting subrepo no-largefiles
1146
1147 $ hg status -C
1148 ? dir/subdir2/large.bin
1149 ? large.orig
1150
1151 The content of the forgotten file shouldn't be clobbered
1152
1153 $ cat dir/subdir2/large.bin
1154 modified
1155
1156 The standin for subdir2 should be deleted, not just dropped
1089 1157
1158 $ listtree .hglf dir* large*
1159 .hglf/
1160 .hglf/dir/
1161 .hglf/dir/subdir/
1162 .hglf/dir/subdir/large.bin
1163 .hglf/large
1164 dir/
1165 dir/subdir/
1166 dir/subdir/large.bin
1167 dir/subdir2/
1168 dir/subdir2/large.bin
1169 large
1170 large.orig
1171
1172 $ rm -r dir/subdir2
1173
1174 'subdir' should not be in the destination. It would be if the subdir2 directory
1175 existed under .hglf/.
1176 $ hg mv dir/subdir dir/subdir2
1177 moving .hglf/dir/subdir/large.bin to .hglf/dir/subdir2/large.bin (glob)
1178
1179 $ hg status -C
1180 A dir/subdir2/large.bin
1181 dir/subdir/large.bin
1182 R dir/subdir/large.bin
1183 ? large.orig
1184
1185 $ listtree .hglf dir* large*
1186 .hglf/
1187 .hglf/dir/
1188 .hglf/dir/subdir2/
1189 .hglf/dir/subdir2/large.bin
1190 .hglf/large
1191 dir/
1192 dir/subdir2/
1193 dir/subdir2/large.bin
1194 large
1195 large.orig
1196
1197 Start from scratch, and rename something other than the final path component.
1198
1199 $ hg up -qC .
1200 $ hg --config extensions.purge= purge
1201
1202 $ hg mv dir/subdir dir2/subdir
1203 moving .hglf/dir/subdir/large.bin to .hglf/dir2/subdir/large.bin (glob)
1204
1205 $ hg status -C
1206 A dir2/subdir/large.bin
1207 dir/subdir/large.bin
1208 R dir/subdir/large.bin
1209
1210 $ listtree .hglf dir* large*
1211 .hglf/
1212 .hglf/dir2/
1213 .hglf/dir2/subdir/
1214 .hglf/dir2/subdir/large.bin
1215 .hglf/large
1216 dir2/
1217 dir2/subdir/
1218 dir2/subdir/large.bin
1219 large
1220
1221 $ hg revert --all
1222 undeleting .hglf/dir/subdir/large.bin (glob)
1223 forgetting .hglf/dir2/subdir/large.bin (glob)
1224 reverting subrepo no-largefiles
1225
1226 $ hg status -C
1227 ? dir2/subdir/large.bin
1228
1229 $ listtree .hglf dir* large*
1230 .hglf/
1231 .hglf/dir/
1232 .hglf/dir/subdir/
1233 .hglf/dir/subdir/large.bin
1234 .hglf/large
1235 dir/
1236 dir/subdir/
1237 dir/subdir/large.bin
1238 dir2/
1239 dir2/subdir/
1240 dir2/subdir/large.bin
1241 large
1242
1243 $ cd ../..
1090 1244
1091 1245 Test "pull --rebase" when rebase is enabled before largefiles (issue3861)
1092 1246 =========================================================================
@@ -1071,6 +1071,18 b' Prepare a repo with subrepo'
1071 1071 "path": "sub/repo/foo"
1072 1072 }
1073 1073 ]
1074
1075 non-exact match:
1076
1077 $ hg cat -T '{path}\n' 'glob:**'
1078 .hgsub
1079 .hgsubstate
1080 sub/repo/foo (glob)
1081 $ hg cat -T '{path}\n' 're:^sub'
1082 sub/repo/foo (glob)
1083
1084 missing subrepos in working directory:
1085
1074 1086 $ mkdir -p tmp/sub/repo
1075 1087 $ hg cat -r 0 --output tmp/%p_p sub/repo/foo
1076 1088 $ cat tmp/sub/repo/foo_p
General Comments 0
You need to be logged in to leave comments. Login now