##// END OF EJS Templates
debugcommands: use temporary dict for its command table...
Yuya Nishihara -
r32377:c942c83a default
parent child Browse files
Show More
@@ -1,210 +1,204 b''
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 '''setup for largefiles extension: uisetup'''
9 '''setup for largefiles extension: uisetup'''
10 from __future__ import absolute_import
10 from __future__ import absolute_import
11
11
12 from mercurial.i18n import _
12 from mercurial.i18n import _
13
13
14 from mercurial.hgweb import (
14 from mercurial.hgweb import (
15 hgweb_mod,
15 hgweb_mod,
16 webcommands,
16 webcommands,
17 )
17 )
18
18
19 from mercurial import (
19 from mercurial import (
20 archival,
20 archival,
21 cmdutil,
21 cmdutil,
22 commands,
22 commands,
23 copies,
23 copies,
24 debugcommands,
25 exchange,
24 exchange,
26 extensions,
25 extensions,
27 filemerge,
26 filemerge,
28 hg,
27 hg,
29 httppeer,
28 httppeer,
30 merge,
29 merge,
31 scmutil,
30 scmutil,
32 sshpeer,
31 sshpeer,
33 subrepo,
32 subrepo,
34 wireproto,
33 wireproto,
35 )
34 )
36
35
37 from . import (
36 from . import (
38 overrides,
37 overrides,
39 proto,
38 proto,
40 )
39 )
41
40
42 def uisetup(ui):
41 def uisetup(ui):
43 # TODO: debugcommands should use a separate command table
44 # Side-effect of accessing is debugcommands module is guaranteed to be
45 # imported and commands.table is populated.
46 debugcommands.command
47
48 # Disable auto-status for some commands which assume that all
42 # Disable auto-status for some commands which assume that all
49 # files in the result are under Mercurial's control
43 # files in the result are under Mercurial's control
50
44
51 entry = extensions.wrapcommand(commands.table, 'add',
45 entry = extensions.wrapcommand(commands.table, 'add',
52 overrides.overrideadd)
46 overrides.overrideadd)
53 addopt = [('', 'large', None, _('add as largefile')),
47 addopt = [('', 'large', None, _('add as largefile')),
54 ('', 'normal', None, _('add as normal file')),
48 ('', 'normal', None, _('add as normal file')),
55 ('', 'lfsize', '', _('add all files above this size '
49 ('', 'lfsize', '', _('add all files above this size '
56 '(in megabytes) as largefiles '
50 '(in megabytes) as largefiles '
57 '(default: 10)'))]
51 '(default: 10)'))]
58 entry[1].extend(addopt)
52 entry[1].extend(addopt)
59
53
60 # The scmutil function is called both by the (trivial) addremove command,
54 # The scmutil function is called both by the (trivial) addremove command,
61 # and in the process of handling commit -A (issue3542)
55 # and in the process of handling commit -A (issue3542)
62 entry = extensions.wrapfunction(scmutil, 'addremove',
56 entry = extensions.wrapfunction(scmutil, 'addremove',
63 overrides.scmutiladdremove)
57 overrides.scmutiladdremove)
64 extensions.wrapfunction(cmdutil, 'add', overrides.cmdutiladd)
58 extensions.wrapfunction(cmdutil, 'add', overrides.cmdutiladd)
65 extensions.wrapfunction(cmdutil, 'remove', overrides.cmdutilremove)
59 extensions.wrapfunction(cmdutil, 'remove', overrides.cmdutilremove)
66 extensions.wrapfunction(cmdutil, 'forget', overrides.cmdutilforget)
60 extensions.wrapfunction(cmdutil, 'forget', overrides.cmdutilforget)
67
61
68 extensions.wrapfunction(copies, 'pathcopies', overrides.copiespathcopies)
62 extensions.wrapfunction(copies, 'pathcopies', overrides.copiespathcopies)
69
63
70 # Subrepos call status function
64 # Subrepos call status function
71 entry = extensions.wrapcommand(commands.table, 'status',
65 entry = extensions.wrapcommand(commands.table, 'status',
72 overrides.overridestatus)
66 overrides.overridestatus)
73 entry = extensions.wrapfunction(subrepo.hgsubrepo, 'status',
67 entry = extensions.wrapfunction(subrepo.hgsubrepo, 'status',
74 overrides.overridestatusfn)
68 overrides.overridestatusfn)
75
69
76 entry = extensions.wrapcommand(commands.table, 'log',
70 entry = extensions.wrapcommand(commands.table, 'log',
77 overrides.overridelog)
71 overrides.overridelog)
78 entry = extensions.wrapcommand(commands.table, 'rollback',
72 entry = extensions.wrapcommand(commands.table, 'rollback',
79 overrides.overriderollback)
73 overrides.overriderollback)
80 entry = extensions.wrapcommand(commands.table, 'verify',
74 entry = extensions.wrapcommand(commands.table, 'verify',
81 overrides.overrideverify)
75 overrides.overrideverify)
82
76
83 verifyopt = [('', 'large', None,
77 verifyopt = [('', 'large', None,
84 _('verify that all largefiles in current revision exists')),
78 _('verify that all largefiles in current revision exists')),
85 ('', 'lfa', None,
79 ('', 'lfa', None,
86 _('verify largefiles in all revisions, not just current')),
80 _('verify largefiles in all revisions, not just current')),
87 ('', 'lfc', None,
81 ('', 'lfc', None,
88 _('verify local largefile contents, not just existence'))]
82 _('verify local largefile contents, not just existence'))]
89 entry[1].extend(verifyopt)
83 entry[1].extend(verifyopt)
90
84
91 entry = extensions.wrapcommand(commands.table, 'debugstate',
85 entry = extensions.wrapcommand(commands.table, 'debugstate',
92 overrides.overridedebugstate)
86 overrides.overridedebugstate)
93 debugstateopt = [('', 'large', None, _('display largefiles dirstate'))]
87 debugstateopt = [('', 'large', None, _('display largefiles dirstate'))]
94 entry[1].extend(debugstateopt)
88 entry[1].extend(debugstateopt)
95
89
96 outgoing = lambda orgfunc, *arg, **kwargs: orgfunc(*arg, **kwargs)
90 outgoing = lambda orgfunc, *arg, **kwargs: orgfunc(*arg, **kwargs)
97 entry = extensions.wrapcommand(commands.table, 'outgoing', outgoing)
91 entry = extensions.wrapcommand(commands.table, 'outgoing', outgoing)
98 outgoingopt = [('', 'large', None, _('display outgoing largefiles'))]
92 outgoingopt = [('', 'large', None, _('display outgoing largefiles'))]
99 entry[1].extend(outgoingopt)
93 entry[1].extend(outgoingopt)
100 cmdutil.outgoinghooks.add('largefiles', overrides.outgoinghook)
94 cmdutil.outgoinghooks.add('largefiles', overrides.outgoinghook)
101 entry = extensions.wrapcommand(commands.table, 'summary',
95 entry = extensions.wrapcommand(commands.table, 'summary',
102 overrides.overridesummary)
96 overrides.overridesummary)
103 summaryopt = [('', 'large', None, _('display outgoing largefiles'))]
97 summaryopt = [('', 'large', None, _('display outgoing largefiles'))]
104 entry[1].extend(summaryopt)
98 entry[1].extend(summaryopt)
105 cmdutil.summaryremotehooks.add('largefiles', overrides.summaryremotehook)
99 cmdutil.summaryremotehooks.add('largefiles', overrides.summaryremotehook)
106
100
107 entry = extensions.wrapcommand(commands.table, 'pull',
101 entry = extensions.wrapcommand(commands.table, 'pull',
108 overrides.overridepull)
102 overrides.overridepull)
109 pullopt = [('', 'all-largefiles', None,
103 pullopt = [('', 'all-largefiles', None,
110 _('download all pulled versions of largefiles (DEPRECATED)')),
104 _('download all pulled versions of largefiles (DEPRECATED)')),
111 ('', 'lfrev', [],
105 ('', 'lfrev', [],
112 _('download largefiles for these revisions'), _('REV'))]
106 _('download largefiles for these revisions'), _('REV'))]
113 entry[1].extend(pullopt)
107 entry[1].extend(pullopt)
114
108
115 entry = extensions.wrapcommand(commands.table, 'push',
109 entry = extensions.wrapcommand(commands.table, 'push',
116 overrides.overridepush)
110 overrides.overridepush)
117 pushopt = [('', 'lfrev', [],
111 pushopt = [('', 'lfrev', [],
118 _('upload largefiles for these revisions'), _('REV'))]
112 _('upload largefiles for these revisions'), _('REV'))]
119 entry[1].extend(pushopt)
113 entry[1].extend(pushopt)
120 entry = extensions.wrapfunction(exchange, 'pushoperation',
114 entry = extensions.wrapfunction(exchange, 'pushoperation',
121 overrides.exchangepushoperation)
115 overrides.exchangepushoperation)
122
116
123 entry = extensions.wrapcommand(commands.table, 'clone',
117 entry = extensions.wrapcommand(commands.table, 'clone',
124 overrides.overrideclone)
118 overrides.overrideclone)
125 cloneopt = [('', 'all-largefiles', None,
119 cloneopt = [('', 'all-largefiles', None,
126 _('download all versions of all largefiles'))]
120 _('download all versions of all largefiles'))]
127 entry[1].extend(cloneopt)
121 entry[1].extend(cloneopt)
128 entry = extensions.wrapfunction(hg, 'clone', overrides.hgclone)
122 entry = extensions.wrapfunction(hg, 'clone', overrides.hgclone)
129 entry = extensions.wrapfunction(hg, 'postshare', overrides.hgpostshare)
123 entry = extensions.wrapfunction(hg, 'postshare', overrides.hgpostshare)
130
124
131 entry = extensions.wrapcommand(commands.table, 'cat',
125 entry = extensions.wrapcommand(commands.table, 'cat',
132 overrides.overridecat)
126 overrides.overridecat)
133 entry = extensions.wrapfunction(merge, '_checkunknownfile',
127 entry = extensions.wrapfunction(merge, '_checkunknownfile',
134 overrides.overridecheckunknownfile)
128 overrides.overridecheckunknownfile)
135 entry = extensions.wrapfunction(merge, 'calculateupdates',
129 entry = extensions.wrapfunction(merge, 'calculateupdates',
136 overrides.overridecalculateupdates)
130 overrides.overridecalculateupdates)
137 entry = extensions.wrapfunction(merge, 'recordupdates',
131 entry = extensions.wrapfunction(merge, 'recordupdates',
138 overrides.mergerecordupdates)
132 overrides.mergerecordupdates)
139 entry = extensions.wrapfunction(merge, 'update',
133 entry = extensions.wrapfunction(merge, 'update',
140 overrides.mergeupdate)
134 overrides.mergeupdate)
141 entry = extensions.wrapfunction(filemerge, '_filemerge',
135 entry = extensions.wrapfunction(filemerge, '_filemerge',
142 overrides.overridefilemerge)
136 overrides.overridefilemerge)
143 entry = extensions.wrapfunction(cmdutil, 'copy',
137 entry = extensions.wrapfunction(cmdutil, 'copy',
144 overrides.overridecopy)
138 overrides.overridecopy)
145
139
146 # Summary calls dirty on the subrepos
140 # Summary calls dirty on the subrepos
147 entry = extensions.wrapfunction(subrepo.hgsubrepo, 'dirty',
141 entry = extensions.wrapfunction(subrepo.hgsubrepo, 'dirty',
148 overrides.overridedirty)
142 overrides.overridedirty)
149
143
150 entry = extensions.wrapfunction(cmdutil, 'revert',
144 entry = extensions.wrapfunction(cmdutil, 'revert',
151 overrides.overriderevert)
145 overrides.overriderevert)
152
146
153 extensions.wrapcommand(commands.table, 'archive',
147 extensions.wrapcommand(commands.table, 'archive',
154 overrides.overridearchivecmd)
148 overrides.overridearchivecmd)
155 extensions.wrapfunction(archival, 'archive', overrides.overridearchive)
149 extensions.wrapfunction(archival, 'archive', overrides.overridearchive)
156 extensions.wrapfunction(subrepo.hgsubrepo, 'archive',
150 extensions.wrapfunction(subrepo.hgsubrepo, 'archive',
157 overrides.hgsubrepoarchive)
151 overrides.hgsubrepoarchive)
158 extensions.wrapfunction(webcommands, 'archive',
152 extensions.wrapfunction(webcommands, 'archive',
159 overrides.hgwebarchive)
153 overrides.hgwebarchive)
160 extensions.wrapfunction(cmdutil, 'bailifchanged',
154 extensions.wrapfunction(cmdutil, 'bailifchanged',
161 overrides.overridebailifchanged)
155 overrides.overridebailifchanged)
162
156
163 extensions.wrapfunction(cmdutil, 'postcommitstatus',
157 extensions.wrapfunction(cmdutil, 'postcommitstatus',
164 overrides.postcommitstatus)
158 overrides.postcommitstatus)
165 extensions.wrapfunction(scmutil, 'marktouched',
159 extensions.wrapfunction(scmutil, 'marktouched',
166 overrides.scmutilmarktouched)
160 overrides.scmutilmarktouched)
167
161
168 # create the new wireproto commands ...
162 # create the new wireproto commands ...
169 wireproto.commands['putlfile'] = (proto.putlfile, 'sha')
163 wireproto.commands['putlfile'] = (proto.putlfile, 'sha')
170 wireproto.commands['getlfile'] = (proto.getlfile, 'sha')
164 wireproto.commands['getlfile'] = (proto.getlfile, 'sha')
171 wireproto.commands['statlfile'] = (proto.statlfile, 'sha')
165 wireproto.commands['statlfile'] = (proto.statlfile, 'sha')
172
166
173 # ... and wrap some existing ones
167 # ... and wrap some existing ones
174 wireproto.commands['capabilities'] = (proto.capabilities, '')
168 wireproto.commands['capabilities'] = (proto.capabilities, '')
175 wireproto.commands['heads'] = (proto.heads, '')
169 wireproto.commands['heads'] = (proto.heads, '')
176 wireproto.commands['lheads'] = (wireproto.heads, '')
170 wireproto.commands['lheads'] = (wireproto.heads, '')
177
171
178 # make putlfile behave the same as push and {get,stat}lfile behave
172 # make putlfile behave the same as push and {get,stat}lfile behave
179 # the same as pull w.r.t. permissions checks
173 # the same as pull w.r.t. permissions checks
180 hgweb_mod.perms['putlfile'] = 'push'
174 hgweb_mod.perms['putlfile'] = 'push'
181 hgweb_mod.perms['getlfile'] = 'pull'
175 hgweb_mod.perms['getlfile'] = 'pull'
182 hgweb_mod.perms['statlfile'] = 'pull'
176 hgweb_mod.perms['statlfile'] = 'pull'
183
177
184 extensions.wrapfunction(webcommands, 'decodepath', overrides.decodepath)
178 extensions.wrapfunction(webcommands, 'decodepath', overrides.decodepath)
185
179
186 # the hello wireproto command uses wireproto.capabilities, so it won't see
180 # the hello wireproto command uses wireproto.capabilities, so it won't see
187 # our largefiles capability unless we replace the actual function as well.
181 # our largefiles capability unless we replace the actual function as well.
188 proto.capabilitiesorig = wireproto.capabilities
182 proto.capabilitiesorig = wireproto.capabilities
189 wireproto.capabilities = proto.capabilities
183 wireproto.capabilities = proto.capabilities
190
184
191 # can't do this in reposetup because it needs to have happened before
185 # can't do this in reposetup because it needs to have happened before
192 # wirerepo.__init__ is called
186 # wirerepo.__init__ is called
193 proto.ssholdcallstream = sshpeer.sshpeer._callstream
187 proto.ssholdcallstream = sshpeer.sshpeer._callstream
194 proto.httpoldcallstream = httppeer.httppeer._callstream
188 proto.httpoldcallstream = httppeer.httppeer._callstream
195 sshpeer.sshpeer._callstream = proto.sshrepocallstream
189 sshpeer.sshpeer._callstream = proto.sshrepocallstream
196 httppeer.httppeer._callstream = proto.httprepocallstream
190 httppeer.httppeer._callstream = proto.httprepocallstream
197
191
198 # override some extensions' stuff as well
192 # override some extensions' stuff as well
199 for name, module in extensions.extensions():
193 for name, module in extensions.extensions():
200 if name == 'purge':
194 if name == 'purge':
201 extensions.wrapcommand(getattr(module, 'cmdtable'), 'purge',
195 extensions.wrapcommand(getattr(module, 'cmdtable'), 'purge',
202 overrides.overridepurge)
196 overrides.overridepurge)
203 if name == 'rebase':
197 if name == 'rebase':
204 extensions.wrapcommand(getattr(module, 'cmdtable'), 'rebase',
198 extensions.wrapcommand(getattr(module, 'cmdtable'), 'rebase',
205 overrides.overriderebase)
199 overrides.overriderebase)
206 extensions.wrapfunction(module, 'rebase',
200 extensions.wrapfunction(module, 'rebase',
207 overrides.overriderebase)
201 overrides.overriderebase)
208 if name == 'transplant':
202 if name == 'transplant':
209 extensions.wrapcommand(getattr(module, 'cmdtable'), 'transplant',
203 extensions.wrapcommand(getattr(module, 'cmdtable'), 'transplant',
210 overrides.overridetransplant)
204 overrides.overridetransplant)
@@ -1,5470 +1,5472 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
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 difflib
10 import difflib
11 import errno
11 import errno
12 import os
12 import os
13 import re
13 import re
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import (
16 from .node import (
17 hex,
17 hex,
18 nullid,
18 nullid,
19 nullrev,
19 nullrev,
20 short,
20 short,
21 )
21 )
22 from . import (
22 from . import (
23 archival,
23 archival,
24 bookmarks,
24 bookmarks,
25 bundle2,
25 bundle2,
26 changegroup,
26 changegroup,
27 cmdutil,
27 cmdutil,
28 copies,
28 copies,
29 debugcommands as debugcommandsmod,
29 destutil,
30 destutil,
30 dirstateguard,
31 dirstateguard,
31 discovery,
32 discovery,
32 encoding,
33 encoding,
33 error,
34 error,
34 exchange,
35 exchange,
35 extensions,
36 extensions,
36 graphmod,
37 graphmod,
37 hbisect,
38 hbisect,
38 help,
39 help,
39 hg,
40 hg,
40 lock as lockmod,
41 lock as lockmod,
41 merge as mergemod,
42 merge as mergemod,
42 obsolete,
43 obsolete,
43 patch,
44 patch,
44 phases,
45 phases,
45 pycompat,
46 pycompat,
46 rcutil,
47 rcutil,
47 registrar,
48 registrar,
48 revsetlang,
49 revsetlang,
49 scmutil,
50 scmutil,
50 server,
51 server,
51 sshserver,
52 sshserver,
52 streamclone,
53 streamclone,
53 tags as tagsmod,
54 tags as tagsmod,
54 templatekw,
55 templatekw,
55 ui as uimod,
56 ui as uimod,
56 util,
57 util,
57 )
58 )
58
59
59 release = lockmod.release
60 release = lockmod.release
60
61
61 table = {}
62 table = {}
63 table.update(debugcommandsmod.command._table)
62
64
63 command = registrar.command(table)
65 command = registrar.command(table)
64
66
65 # label constants
67 # label constants
66 # until 3.5, bookmarks.current was the advertised name, not
68 # until 3.5, bookmarks.current was the advertised name, not
67 # bookmarks.active, so we must use both to avoid breaking old
69 # bookmarks.active, so we must use both to avoid breaking old
68 # custom styles
70 # custom styles
69 activebookmarklabel = 'bookmarks.active bookmarks.current'
71 activebookmarklabel = 'bookmarks.active bookmarks.current'
70
72
71 # common command options
73 # common command options
72
74
73 globalopts = [
75 globalopts = [
74 ('R', 'repository', '',
76 ('R', 'repository', '',
75 _('repository root directory or name of overlay bundle file'),
77 _('repository root directory or name of overlay bundle file'),
76 _('REPO')),
78 _('REPO')),
77 ('', 'cwd', '',
79 ('', 'cwd', '',
78 _('change working directory'), _('DIR')),
80 _('change working directory'), _('DIR')),
79 ('y', 'noninteractive', None,
81 ('y', 'noninteractive', None,
80 _('do not prompt, automatically pick the first choice for all prompts')),
82 _('do not prompt, automatically pick the first choice for all prompts')),
81 ('q', 'quiet', None, _('suppress output')),
83 ('q', 'quiet', None, _('suppress output')),
82 ('v', 'verbose', None, _('enable additional output')),
84 ('v', 'verbose', None, _('enable additional output')),
83 ('', 'color', '',
85 ('', 'color', '',
84 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
86 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
85 # and should not be translated
87 # and should not be translated
86 _("when to colorize (boolean, always, auto, never, or debug)"),
88 _("when to colorize (boolean, always, auto, never, or debug)"),
87 _('TYPE')),
89 _('TYPE')),
88 ('', 'config', [],
90 ('', 'config', [],
89 _('set/override config option (use \'section.name=value\')'),
91 _('set/override config option (use \'section.name=value\')'),
90 _('CONFIG')),
92 _('CONFIG')),
91 ('', 'debug', None, _('enable debugging output')),
93 ('', 'debug', None, _('enable debugging output')),
92 ('', 'debugger', None, _('start debugger')),
94 ('', 'debugger', None, _('start debugger')),
93 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
95 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
94 _('ENCODE')),
96 _('ENCODE')),
95 ('', 'encodingmode', encoding.encodingmode,
97 ('', 'encodingmode', encoding.encodingmode,
96 _('set the charset encoding mode'), _('MODE')),
98 _('set the charset encoding mode'), _('MODE')),
97 ('', 'traceback', None, _('always print a traceback on exception')),
99 ('', 'traceback', None, _('always print a traceback on exception')),
98 ('', 'time', None, _('time how long the command takes')),
100 ('', 'time', None, _('time how long the command takes')),
99 ('', 'profile', None, _('print command execution profile')),
101 ('', 'profile', None, _('print command execution profile')),
100 ('', 'version', None, _('output version information and exit')),
102 ('', 'version', None, _('output version information and exit')),
101 ('h', 'help', None, _('display help and exit')),
103 ('h', 'help', None, _('display help and exit')),
102 ('', 'hidden', False, _('consider hidden changesets')),
104 ('', 'hidden', False, _('consider hidden changesets')),
103 ('', 'pager', 'auto',
105 ('', 'pager', 'auto',
104 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
106 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
105 ]
107 ]
106
108
107 dryrunopts = cmdutil.dryrunopts
109 dryrunopts = cmdutil.dryrunopts
108 remoteopts = cmdutil.remoteopts
110 remoteopts = cmdutil.remoteopts
109 walkopts = cmdutil.walkopts
111 walkopts = cmdutil.walkopts
110 commitopts = cmdutil.commitopts
112 commitopts = cmdutil.commitopts
111 commitopts2 = cmdutil.commitopts2
113 commitopts2 = cmdutil.commitopts2
112 formatteropts = cmdutil.formatteropts
114 formatteropts = cmdutil.formatteropts
113 templateopts = cmdutil.templateopts
115 templateopts = cmdutil.templateopts
114 logopts = cmdutil.logopts
116 logopts = cmdutil.logopts
115 diffopts = cmdutil.diffopts
117 diffopts = cmdutil.diffopts
116 diffwsopts = cmdutil.diffwsopts
118 diffwsopts = cmdutil.diffwsopts
117 diffopts2 = cmdutil.diffopts2
119 diffopts2 = cmdutil.diffopts2
118 mergetoolopts = cmdutil.mergetoolopts
120 mergetoolopts = cmdutil.mergetoolopts
119 similarityopts = cmdutil.similarityopts
121 similarityopts = cmdutil.similarityopts
120 subrepoopts = cmdutil.subrepoopts
122 subrepoopts = cmdutil.subrepoopts
121 debugrevlogopts = cmdutil.debugrevlogopts
123 debugrevlogopts = cmdutil.debugrevlogopts
122
124
123 # Commands start here, listed alphabetically
125 # Commands start here, listed alphabetically
124
126
125 @command('^add',
127 @command('^add',
126 walkopts + subrepoopts + dryrunopts,
128 walkopts + subrepoopts + dryrunopts,
127 _('[OPTION]... [FILE]...'),
129 _('[OPTION]... [FILE]...'),
128 inferrepo=True)
130 inferrepo=True)
129 def add(ui, repo, *pats, **opts):
131 def add(ui, repo, *pats, **opts):
130 """add the specified files on the next commit
132 """add the specified files on the next commit
131
133
132 Schedule files to be version controlled and added to the
134 Schedule files to be version controlled and added to the
133 repository.
135 repository.
134
136
135 The files will be added to the repository at the next commit. To
137 The files will be added to the repository at the next commit. To
136 undo an add before that, see :hg:`forget`.
138 undo an add before that, see :hg:`forget`.
137
139
138 If no names are given, add all files to the repository (except
140 If no names are given, add all files to the repository (except
139 files matching ``.hgignore``).
141 files matching ``.hgignore``).
140
142
141 .. container:: verbose
143 .. container:: verbose
142
144
143 Examples:
145 Examples:
144
146
145 - New (unknown) files are added
147 - New (unknown) files are added
146 automatically by :hg:`add`::
148 automatically by :hg:`add`::
147
149
148 $ ls
150 $ ls
149 foo.c
151 foo.c
150 $ hg status
152 $ hg status
151 ? foo.c
153 ? foo.c
152 $ hg add
154 $ hg add
153 adding foo.c
155 adding foo.c
154 $ hg status
156 $ hg status
155 A foo.c
157 A foo.c
156
158
157 - Specific files to be added can be specified::
159 - Specific files to be added can be specified::
158
160
159 $ ls
161 $ ls
160 bar.c foo.c
162 bar.c foo.c
161 $ hg status
163 $ hg status
162 ? bar.c
164 ? bar.c
163 ? foo.c
165 ? foo.c
164 $ hg add bar.c
166 $ hg add bar.c
165 $ hg status
167 $ hg status
166 A bar.c
168 A bar.c
167 ? foo.c
169 ? foo.c
168
170
169 Returns 0 if all files are successfully added.
171 Returns 0 if all files are successfully added.
170 """
172 """
171
173
172 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
174 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
173 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
175 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
174 return rejected and 1 or 0
176 return rejected and 1 or 0
175
177
176 @command('addremove',
178 @command('addremove',
177 similarityopts + subrepoopts + walkopts + dryrunopts,
179 similarityopts + subrepoopts + walkopts + dryrunopts,
178 _('[OPTION]... [FILE]...'),
180 _('[OPTION]... [FILE]...'),
179 inferrepo=True)
181 inferrepo=True)
180 def addremove(ui, repo, *pats, **opts):
182 def addremove(ui, repo, *pats, **opts):
181 """add all new files, delete all missing files
183 """add all new files, delete all missing files
182
184
183 Add all new files and remove all missing files from the
185 Add all new files and remove all missing files from the
184 repository.
186 repository.
185
187
186 Unless names are given, new files are ignored if they match any of
188 Unless names are given, new files are ignored if they match any of
187 the patterns in ``.hgignore``. As with add, these changes take
189 the patterns in ``.hgignore``. As with add, these changes take
188 effect at the next commit.
190 effect at the next commit.
189
191
190 Use the -s/--similarity option to detect renamed files. This
192 Use the -s/--similarity option to detect renamed files. This
191 option takes a percentage between 0 (disabled) and 100 (files must
193 option takes a percentage between 0 (disabled) and 100 (files must
192 be identical) as its parameter. With a parameter greater than 0,
194 be identical) as its parameter. With a parameter greater than 0,
193 this compares every removed file with every added file and records
195 this compares every removed file with every added file and records
194 those similar enough as renames. Detecting renamed files this way
196 those similar enough as renames. Detecting renamed files this way
195 can be expensive. After using this option, :hg:`status -C` can be
197 can be expensive. After using this option, :hg:`status -C` can be
196 used to check which files were identified as moved or renamed. If
198 used to check which files were identified as moved or renamed. If
197 not specified, -s/--similarity defaults to 100 and only renames of
199 not specified, -s/--similarity defaults to 100 and only renames of
198 identical files are detected.
200 identical files are detected.
199
201
200 .. container:: verbose
202 .. container:: verbose
201
203
202 Examples:
204 Examples:
203
205
204 - A number of files (bar.c and foo.c) are new,
206 - A number of files (bar.c and foo.c) are new,
205 while foobar.c has been removed (without using :hg:`remove`)
207 while foobar.c has been removed (without using :hg:`remove`)
206 from the repository::
208 from the repository::
207
209
208 $ ls
210 $ ls
209 bar.c foo.c
211 bar.c foo.c
210 $ hg status
212 $ hg status
211 ! foobar.c
213 ! foobar.c
212 ? bar.c
214 ? bar.c
213 ? foo.c
215 ? foo.c
214 $ hg addremove
216 $ hg addremove
215 adding bar.c
217 adding bar.c
216 adding foo.c
218 adding foo.c
217 removing foobar.c
219 removing foobar.c
218 $ hg status
220 $ hg status
219 A bar.c
221 A bar.c
220 A foo.c
222 A foo.c
221 R foobar.c
223 R foobar.c
222
224
223 - A file foobar.c was moved to foo.c without using :hg:`rename`.
225 - A file foobar.c was moved to foo.c without using :hg:`rename`.
224 Afterwards, it was edited slightly::
226 Afterwards, it was edited slightly::
225
227
226 $ ls
228 $ ls
227 foo.c
229 foo.c
228 $ hg status
230 $ hg status
229 ! foobar.c
231 ! foobar.c
230 ? foo.c
232 ? foo.c
231 $ hg addremove --similarity 90
233 $ hg addremove --similarity 90
232 removing foobar.c
234 removing foobar.c
233 adding foo.c
235 adding foo.c
234 recording removal of foobar.c as rename to foo.c (94% similar)
236 recording removal of foobar.c as rename to foo.c (94% similar)
235 $ hg status -C
237 $ hg status -C
236 A foo.c
238 A foo.c
237 foobar.c
239 foobar.c
238 R foobar.c
240 R foobar.c
239
241
240 Returns 0 if all files are successfully added.
242 Returns 0 if all files are successfully added.
241 """
243 """
242 opts = pycompat.byteskwargs(opts)
244 opts = pycompat.byteskwargs(opts)
243 try:
245 try:
244 sim = float(opts.get('similarity') or 100)
246 sim = float(opts.get('similarity') or 100)
245 except ValueError:
247 except ValueError:
246 raise error.Abort(_('similarity must be a number'))
248 raise error.Abort(_('similarity must be a number'))
247 if sim < 0 or sim > 100:
249 if sim < 0 or sim > 100:
248 raise error.Abort(_('similarity must be between 0 and 100'))
250 raise error.Abort(_('similarity must be between 0 and 100'))
249 matcher = scmutil.match(repo[None], pats, opts)
251 matcher = scmutil.match(repo[None], pats, opts)
250 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
252 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
251
253
252 @command('^annotate|blame',
254 @command('^annotate|blame',
253 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
255 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
254 ('', 'follow', None,
256 ('', 'follow', None,
255 _('follow copies/renames and list the filename (DEPRECATED)')),
257 _('follow copies/renames and list the filename (DEPRECATED)')),
256 ('', 'no-follow', None, _("don't follow copies and renames")),
258 ('', 'no-follow', None, _("don't follow copies and renames")),
257 ('a', 'text', None, _('treat all files as text')),
259 ('a', 'text', None, _('treat all files as text')),
258 ('u', 'user', None, _('list the author (long with -v)')),
260 ('u', 'user', None, _('list the author (long with -v)')),
259 ('f', 'file', None, _('list the filename')),
261 ('f', 'file', None, _('list the filename')),
260 ('d', 'date', None, _('list the date (short with -q)')),
262 ('d', 'date', None, _('list the date (short with -q)')),
261 ('n', 'number', None, _('list the revision number (default)')),
263 ('n', 'number', None, _('list the revision number (default)')),
262 ('c', 'changeset', None, _('list the changeset')),
264 ('c', 'changeset', None, _('list the changeset')),
263 ('l', 'line-number', None, _('show line number at the first appearance'))
265 ('l', 'line-number', None, _('show line number at the first appearance'))
264 ] + diffwsopts + walkopts + formatteropts,
266 ] + diffwsopts + walkopts + formatteropts,
265 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
267 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
266 inferrepo=True)
268 inferrepo=True)
267 def annotate(ui, repo, *pats, **opts):
269 def annotate(ui, repo, *pats, **opts):
268 """show changeset information by line for each file
270 """show changeset information by line for each file
269
271
270 List changes in files, showing the revision id responsible for
272 List changes in files, showing the revision id responsible for
271 each line.
273 each line.
272
274
273 This command is useful for discovering when a change was made and
275 This command is useful for discovering when a change was made and
274 by whom.
276 by whom.
275
277
276 If you include --file, --user, or --date, the revision number is
278 If you include --file, --user, or --date, the revision number is
277 suppressed unless you also include --number.
279 suppressed unless you also include --number.
278
280
279 Without the -a/--text option, annotate will avoid processing files
281 Without the -a/--text option, annotate will avoid processing files
280 it detects as binary. With -a, annotate will annotate the file
282 it detects as binary. With -a, annotate will annotate the file
281 anyway, although the results will probably be neither useful
283 anyway, although the results will probably be neither useful
282 nor desirable.
284 nor desirable.
283
285
284 Returns 0 on success.
286 Returns 0 on success.
285 """
287 """
286 opts = pycompat.byteskwargs(opts)
288 opts = pycompat.byteskwargs(opts)
287 if not pats:
289 if not pats:
288 raise error.Abort(_('at least one filename or pattern is required'))
290 raise error.Abort(_('at least one filename or pattern is required'))
289
291
290 if opts.get('follow'):
292 if opts.get('follow'):
291 # --follow is deprecated and now just an alias for -f/--file
293 # --follow is deprecated and now just an alias for -f/--file
292 # to mimic the behavior of Mercurial before version 1.5
294 # to mimic the behavior of Mercurial before version 1.5
293 opts['file'] = True
295 opts['file'] = True
294
296
295 ctx = scmutil.revsingle(repo, opts.get('rev'))
297 ctx = scmutil.revsingle(repo, opts.get('rev'))
296
298
297 fm = ui.formatter('annotate', opts)
299 fm = ui.formatter('annotate', opts)
298 if ui.quiet:
300 if ui.quiet:
299 datefunc = util.shortdate
301 datefunc = util.shortdate
300 else:
302 else:
301 datefunc = util.datestr
303 datefunc = util.datestr
302 if ctx.rev() is None:
304 if ctx.rev() is None:
303 def hexfn(node):
305 def hexfn(node):
304 if node is None:
306 if node is None:
305 return None
307 return None
306 else:
308 else:
307 return fm.hexfunc(node)
309 return fm.hexfunc(node)
308 if opts.get('changeset'):
310 if opts.get('changeset'):
309 # omit "+" suffix which is appended to node hex
311 # omit "+" suffix which is appended to node hex
310 def formatrev(rev):
312 def formatrev(rev):
311 if rev is None:
313 if rev is None:
312 return '%d' % ctx.p1().rev()
314 return '%d' % ctx.p1().rev()
313 else:
315 else:
314 return '%d' % rev
316 return '%d' % rev
315 else:
317 else:
316 def formatrev(rev):
318 def formatrev(rev):
317 if rev is None:
319 if rev is None:
318 return '%d+' % ctx.p1().rev()
320 return '%d+' % ctx.p1().rev()
319 else:
321 else:
320 return '%d ' % rev
322 return '%d ' % rev
321 def formathex(hex):
323 def formathex(hex):
322 if hex is None:
324 if hex is None:
323 return '%s+' % fm.hexfunc(ctx.p1().node())
325 return '%s+' % fm.hexfunc(ctx.p1().node())
324 else:
326 else:
325 return '%s ' % hex
327 return '%s ' % hex
326 else:
328 else:
327 hexfn = fm.hexfunc
329 hexfn = fm.hexfunc
328 formatrev = formathex = str
330 formatrev = formathex = str
329
331
330 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
332 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
331 ('number', ' ', lambda x: x[0].rev(), formatrev),
333 ('number', ' ', lambda x: x[0].rev(), formatrev),
332 ('changeset', ' ', lambda x: hexfn(x[0].node()), formathex),
334 ('changeset', ' ', lambda x: hexfn(x[0].node()), formathex),
333 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
335 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
334 ('file', ' ', lambda x: x[0].path(), str),
336 ('file', ' ', lambda x: x[0].path(), str),
335 ('line_number', ':', lambda x: x[1], str),
337 ('line_number', ':', lambda x: x[1], str),
336 ]
338 ]
337 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
339 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
338
340
339 if (not opts.get('user') and not opts.get('changeset')
341 if (not opts.get('user') and not opts.get('changeset')
340 and not opts.get('date') and not opts.get('file')):
342 and not opts.get('date') and not opts.get('file')):
341 opts['number'] = True
343 opts['number'] = True
342
344
343 linenumber = opts.get('line_number') is not None
345 linenumber = opts.get('line_number') is not None
344 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
346 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
345 raise error.Abort(_('at least one of -n/-c is required for -l'))
347 raise error.Abort(_('at least one of -n/-c is required for -l'))
346
348
347 ui.pager('annotate')
349 ui.pager('annotate')
348
350
349 if fm.isplain():
351 if fm.isplain():
350 def makefunc(get, fmt):
352 def makefunc(get, fmt):
351 return lambda x: fmt(get(x))
353 return lambda x: fmt(get(x))
352 else:
354 else:
353 def makefunc(get, fmt):
355 def makefunc(get, fmt):
354 return get
356 return get
355 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
357 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
356 if opts.get(op)]
358 if opts.get(op)]
357 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
359 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
358 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
360 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
359 if opts.get(op))
361 if opts.get(op))
360
362
361 def bad(x, y):
363 def bad(x, y):
362 raise error.Abort("%s: %s" % (x, y))
364 raise error.Abort("%s: %s" % (x, y))
363
365
364 m = scmutil.match(ctx, pats, opts, badfn=bad)
366 m = scmutil.match(ctx, pats, opts, badfn=bad)
365
367
366 follow = not opts.get('no_follow')
368 follow = not opts.get('no_follow')
367 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
369 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
368 whitespace=True)
370 whitespace=True)
369 for abs in ctx.walk(m):
371 for abs in ctx.walk(m):
370 fctx = ctx[abs]
372 fctx = ctx[abs]
371 if not opts.get('text') and fctx.isbinary():
373 if not opts.get('text') and fctx.isbinary():
372 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
374 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
373 continue
375 continue
374
376
375 lines = fctx.annotate(follow=follow, linenumber=linenumber,
377 lines = fctx.annotate(follow=follow, linenumber=linenumber,
376 diffopts=diffopts)
378 diffopts=diffopts)
377 if not lines:
379 if not lines:
378 continue
380 continue
379 formats = []
381 formats = []
380 pieces = []
382 pieces = []
381
383
382 for f, sep in funcmap:
384 for f, sep in funcmap:
383 l = [f(n) for n, dummy in lines]
385 l = [f(n) for n, dummy in lines]
384 if fm.isplain():
386 if fm.isplain():
385 sizes = [encoding.colwidth(x) for x in l]
387 sizes = [encoding.colwidth(x) for x in l]
386 ml = max(sizes)
388 ml = max(sizes)
387 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
389 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
388 else:
390 else:
389 formats.append(['%s' for x in l])
391 formats.append(['%s' for x in l])
390 pieces.append(l)
392 pieces.append(l)
391
393
392 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
394 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
393 fm.startitem()
395 fm.startitem()
394 fm.write(fields, "".join(f), *p)
396 fm.write(fields, "".join(f), *p)
395 fm.write('line', ": %s", l[1])
397 fm.write('line', ": %s", l[1])
396
398
397 if not lines[-1][1].endswith('\n'):
399 if not lines[-1][1].endswith('\n'):
398 fm.plain('\n')
400 fm.plain('\n')
399
401
400 fm.end()
402 fm.end()
401
403
402 @command('archive',
404 @command('archive',
403 [('', 'no-decode', None, _('do not pass files through decoders')),
405 [('', 'no-decode', None, _('do not pass files through decoders')),
404 ('p', 'prefix', '', _('directory prefix for files in archive'),
406 ('p', 'prefix', '', _('directory prefix for files in archive'),
405 _('PREFIX')),
407 _('PREFIX')),
406 ('r', 'rev', '', _('revision to distribute'), _('REV')),
408 ('r', 'rev', '', _('revision to distribute'), _('REV')),
407 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
409 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
408 ] + subrepoopts + walkopts,
410 ] + subrepoopts + walkopts,
409 _('[OPTION]... DEST'))
411 _('[OPTION]... DEST'))
410 def archive(ui, repo, dest, **opts):
412 def archive(ui, repo, dest, **opts):
411 '''create an unversioned archive of a repository revision
413 '''create an unversioned archive of a repository revision
412
414
413 By default, the revision used is the parent of the working
415 By default, the revision used is the parent of the working
414 directory; use -r/--rev to specify a different revision.
416 directory; use -r/--rev to specify a different revision.
415
417
416 The archive type is automatically detected based on file
418 The archive type is automatically detected based on file
417 extension (to override, use -t/--type).
419 extension (to override, use -t/--type).
418
420
419 .. container:: verbose
421 .. container:: verbose
420
422
421 Examples:
423 Examples:
422
424
423 - create a zip file containing the 1.0 release::
425 - create a zip file containing the 1.0 release::
424
426
425 hg archive -r 1.0 project-1.0.zip
427 hg archive -r 1.0 project-1.0.zip
426
428
427 - create a tarball excluding .hg files::
429 - create a tarball excluding .hg files::
428
430
429 hg archive project.tar.gz -X ".hg*"
431 hg archive project.tar.gz -X ".hg*"
430
432
431 Valid types are:
433 Valid types are:
432
434
433 :``files``: a directory full of files (default)
435 :``files``: a directory full of files (default)
434 :``tar``: tar archive, uncompressed
436 :``tar``: tar archive, uncompressed
435 :``tbz2``: tar archive, compressed using bzip2
437 :``tbz2``: tar archive, compressed using bzip2
436 :``tgz``: tar archive, compressed using gzip
438 :``tgz``: tar archive, compressed using gzip
437 :``uzip``: zip archive, uncompressed
439 :``uzip``: zip archive, uncompressed
438 :``zip``: zip archive, compressed using deflate
440 :``zip``: zip archive, compressed using deflate
439
441
440 The exact name of the destination archive or directory is given
442 The exact name of the destination archive or directory is given
441 using a format string; see :hg:`help export` for details.
443 using a format string; see :hg:`help export` for details.
442
444
443 Each member added to an archive file has a directory prefix
445 Each member added to an archive file has a directory prefix
444 prepended. Use -p/--prefix to specify a format string for the
446 prepended. Use -p/--prefix to specify a format string for the
445 prefix. The default is the basename of the archive, with suffixes
447 prefix. The default is the basename of the archive, with suffixes
446 removed.
448 removed.
447
449
448 Returns 0 on success.
450 Returns 0 on success.
449 '''
451 '''
450
452
451 opts = pycompat.byteskwargs(opts)
453 opts = pycompat.byteskwargs(opts)
452 ctx = scmutil.revsingle(repo, opts.get('rev'))
454 ctx = scmutil.revsingle(repo, opts.get('rev'))
453 if not ctx:
455 if not ctx:
454 raise error.Abort(_('no working directory: please specify a revision'))
456 raise error.Abort(_('no working directory: please specify a revision'))
455 node = ctx.node()
457 node = ctx.node()
456 dest = cmdutil.makefilename(repo, dest, node)
458 dest = cmdutil.makefilename(repo, dest, node)
457 if os.path.realpath(dest) == repo.root:
459 if os.path.realpath(dest) == repo.root:
458 raise error.Abort(_('repository root cannot be destination'))
460 raise error.Abort(_('repository root cannot be destination'))
459
461
460 kind = opts.get('type') or archival.guesskind(dest) or 'files'
462 kind = opts.get('type') or archival.guesskind(dest) or 'files'
461 prefix = opts.get('prefix')
463 prefix = opts.get('prefix')
462
464
463 if dest == '-':
465 if dest == '-':
464 if kind == 'files':
466 if kind == 'files':
465 raise error.Abort(_('cannot archive plain files to stdout'))
467 raise error.Abort(_('cannot archive plain files to stdout'))
466 dest = cmdutil.makefileobj(repo, dest)
468 dest = cmdutil.makefileobj(repo, dest)
467 if not prefix:
469 if not prefix:
468 prefix = os.path.basename(repo.root) + '-%h'
470 prefix = os.path.basename(repo.root) + '-%h'
469
471
470 prefix = cmdutil.makefilename(repo, prefix, node)
472 prefix = cmdutil.makefilename(repo, prefix, node)
471 matchfn = scmutil.match(ctx, [], opts)
473 matchfn = scmutil.match(ctx, [], opts)
472 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
474 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
473 matchfn, prefix, subrepos=opts.get('subrepos'))
475 matchfn, prefix, subrepos=opts.get('subrepos'))
474
476
475 @command('backout',
477 @command('backout',
476 [('', 'merge', None, _('merge with old dirstate parent after backout')),
478 [('', 'merge', None, _('merge with old dirstate parent after backout')),
477 ('', 'commit', None,
479 ('', 'commit', None,
478 _('commit if no conflicts were encountered (DEPRECATED)')),
480 _('commit if no conflicts were encountered (DEPRECATED)')),
479 ('', 'no-commit', None, _('do not commit')),
481 ('', 'no-commit', None, _('do not commit')),
480 ('', 'parent', '',
482 ('', 'parent', '',
481 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
483 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
482 ('r', 'rev', '', _('revision to backout'), _('REV')),
484 ('r', 'rev', '', _('revision to backout'), _('REV')),
483 ('e', 'edit', False, _('invoke editor on commit messages')),
485 ('e', 'edit', False, _('invoke editor on commit messages')),
484 ] + mergetoolopts + walkopts + commitopts + commitopts2,
486 ] + mergetoolopts + walkopts + commitopts + commitopts2,
485 _('[OPTION]... [-r] REV'))
487 _('[OPTION]... [-r] REV'))
486 def backout(ui, repo, node=None, rev=None, **opts):
488 def backout(ui, repo, node=None, rev=None, **opts):
487 '''reverse effect of earlier changeset
489 '''reverse effect of earlier changeset
488
490
489 Prepare a new changeset with the effect of REV undone in the
491 Prepare a new changeset with the effect of REV undone in the
490 current working directory. If no conflicts were encountered,
492 current working directory. If no conflicts were encountered,
491 it will be committed immediately.
493 it will be committed immediately.
492
494
493 If REV is the parent of the working directory, then this new changeset
495 If REV is the parent of the working directory, then this new changeset
494 is committed automatically (unless --no-commit is specified).
496 is committed automatically (unless --no-commit is specified).
495
497
496 .. note::
498 .. note::
497
499
498 :hg:`backout` cannot be used to fix either an unwanted or
500 :hg:`backout` cannot be used to fix either an unwanted or
499 incorrect merge.
501 incorrect merge.
500
502
501 .. container:: verbose
503 .. container:: verbose
502
504
503 Examples:
505 Examples:
504
506
505 - Reverse the effect of the parent of the working directory.
507 - Reverse the effect of the parent of the working directory.
506 This backout will be committed immediately::
508 This backout will be committed immediately::
507
509
508 hg backout -r .
510 hg backout -r .
509
511
510 - Reverse the effect of previous bad revision 23::
512 - Reverse the effect of previous bad revision 23::
511
513
512 hg backout -r 23
514 hg backout -r 23
513
515
514 - Reverse the effect of previous bad revision 23 and
516 - Reverse the effect of previous bad revision 23 and
515 leave changes uncommitted::
517 leave changes uncommitted::
516
518
517 hg backout -r 23 --no-commit
519 hg backout -r 23 --no-commit
518 hg commit -m "Backout revision 23"
520 hg commit -m "Backout revision 23"
519
521
520 By default, the pending changeset will have one parent,
522 By default, the pending changeset will have one parent,
521 maintaining a linear history. With --merge, the pending
523 maintaining a linear history. With --merge, the pending
522 changeset will instead have two parents: the old parent of the
524 changeset will instead have two parents: the old parent of the
523 working directory and a new child of REV that simply undoes REV.
525 working directory and a new child of REV that simply undoes REV.
524
526
525 Before version 1.7, the behavior without --merge was equivalent
527 Before version 1.7, the behavior without --merge was equivalent
526 to specifying --merge followed by :hg:`update --clean .` to
528 to specifying --merge followed by :hg:`update --clean .` to
527 cancel the merge and leave the child of REV as a head to be
529 cancel the merge and leave the child of REV as a head to be
528 merged separately.
530 merged separately.
529
531
530 See :hg:`help dates` for a list of formats valid for -d/--date.
532 See :hg:`help dates` for a list of formats valid for -d/--date.
531
533
532 See :hg:`help revert` for a way to restore files to the state
534 See :hg:`help revert` for a way to restore files to the state
533 of another revision.
535 of another revision.
534
536
535 Returns 0 on success, 1 if nothing to backout or there are unresolved
537 Returns 0 on success, 1 if nothing to backout or there are unresolved
536 files.
538 files.
537 '''
539 '''
538 wlock = lock = None
540 wlock = lock = None
539 try:
541 try:
540 wlock = repo.wlock()
542 wlock = repo.wlock()
541 lock = repo.lock()
543 lock = repo.lock()
542 return _dobackout(ui, repo, node, rev, **opts)
544 return _dobackout(ui, repo, node, rev, **opts)
543 finally:
545 finally:
544 release(lock, wlock)
546 release(lock, wlock)
545
547
546 def _dobackout(ui, repo, node=None, rev=None, **opts):
548 def _dobackout(ui, repo, node=None, rev=None, **opts):
547 opts = pycompat.byteskwargs(opts)
549 opts = pycompat.byteskwargs(opts)
548 if opts.get('commit') and opts.get('no_commit'):
550 if opts.get('commit') and opts.get('no_commit'):
549 raise error.Abort(_("cannot use --commit with --no-commit"))
551 raise error.Abort(_("cannot use --commit with --no-commit"))
550 if opts.get('merge') and opts.get('no_commit'):
552 if opts.get('merge') and opts.get('no_commit'):
551 raise error.Abort(_("cannot use --merge with --no-commit"))
553 raise error.Abort(_("cannot use --merge with --no-commit"))
552
554
553 if rev and node:
555 if rev and node:
554 raise error.Abort(_("please specify just one revision"))
556 raise error.Abort(_("please specify just one revision"))
555
557
556 if not rev:
558 if not rev:
557 rev = node
559 rev = node
558
560
559 if not rev:
561 if not rev:
560 raise error.Abort(_("please specify a revision to backout"))
562 raise error.Abort(_("please specify a revision to backout"))
561
563
562 date = opts.get('date')
564 date = opts.get('date')
563 if date:
565 if date:
564 opts['date'] = util.parsedate(date)
566 opts['date'] = util.parsedate(date)
565
567
566 cmdutil.checkunfinished(repo)
568 cmdutil.checkunfinished(repo)
567 cmdutil.bailifchanged(repo)
569 cmdutil.bailifchanged(repo)
568 node = scmutil.revsingle(repo, rev).node()
570 node = scmutil.revsingle(repo, rev).node()
569
571
570 op1, op2 = repo.dirstate.parents()
572 op1, op2 = repo.dirstate.parents()
571 if not repo.changelog.isancestor(node, op1):
573 if not repo.changelog.isancestor(node, op1):
572 raise error.Abort(_('cannot backout change that is not an ancestor'))
574 raise error.Abort(_('cannot backout change that is not an ancestor'))
573
575
574 p1, p2 = repo.changelog.parents(node)
576 p1, p2 = repo.changelog.parents(node)
575 if p1 == nullid:
577 if p1 == nullid:
576 raise error.Abort(_('cannot backout a change with no parents'))
578 raise error.Abort(_('cannot backout a change with no parents'))
577 if p2 != nullid:
579 if p2 != nullid:
578 if not opts.get('parent'):
580 if not opts.get('parent'):
579 raise error.Abort(_('cannot backout a merge changeset'))
581 raise error.Abort(_('cannot backout a merge changeset'))
580 p = repo.lookup(opts['parent'])
582 p = repo.lookup(opts['parent'])
581 if p not in (p1, p2):
583 if p not in (p1, p2):
582 raise error.Abort(_('%s is not a parent of %s') %
584 raise error.Abort(_('%s is not a parent of %s') %
583 (short(p), short(node)))
585 (short(p), short(node)))
584 parent = p
586 parent = p
585 else:
587 else:
586 if opts.get('parent'):
588 if opts.get('parent'):
587 raise error.Abort(_('cannot use --parent on non-merge changeset'))
589 raise error.Abort(_('cannot use --parent on non-merge changeset'))
588 parent = p1
590 parent = p1
589
591
590 # the backout should appear on the same branch
592 # the backout should appear on the same branch
591 branch = repo.dirstate.branch()
593 branch = repo.dirstate.branch()
592 bheads = repo.branchheads(branch)
594 bheads = repo.branchheads(branch)
593 rctx = scmutil.revsingle(repo, hex(parent))
595 rctx = scmutil.revsingle(repo, hex(parent))
594 if not opts.get('merge') and op1 != node:
596 if not opts.get('merge') and op1 != node:
595 dsguard = dirstateguard.dirstateguard(repo, 'backout')
597 dsguard = dirstateguard.dirstateguard(repo, 'backout')
596 try:
598 try:
597 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
599 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
598 'backout')
600 'backout')
599 stats = mergemod.update(repo, parent, True, True, node, False)
601 stats = mergemod.update(repo, parent, True, True, node, False)
600 repo.setparents(op1, op2)
602 repo.setparents(op1, op2)
601 dsguard.close()
603 dsguard.close()
602 hg._showstats(repo, stats)
604 hg._showstats(repo, stats)
603 if stats[3]:
605 if stats[3]:
604 repo.ui.status(_("use 'hg resolve' to retry unresolved "
606 repo.ui.status(_("use 'hg resolve' to retry unresolved "
605 "file merges\n"))
607 "file merges\n"))
606 return 1
608 return 1
607 finally:
609 finally:
608 ui.setconfig('ui', 'forcemerge', '', '')
610 ui.setconfig('ui', 'forcemerge', '', '')
609 lockmod.release(dsguard)
611 lockmod.release(dsguard)
610 else:
612 else:
611 hg.clean(repo, node, show_stats=False)
613 hg.clean(repo, node, show_stats=False)
612 repo.dirstate.setbranch(branch)
614 repo.dirstate.setbranch(branch)
613 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
615 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
614
616
615 if opts.get('no_commit'):
617 if opts.get('no_commit'):
616 msg = _("changeset %s backed out, "
618 msg = _("changeset %s backed out, "
617 "don't forget to commit.\n")
619 "don't forget to commit.\n")
618 ui.status(msg % short(node))
620 ui.status(msg % short(node))
619 return 0
621 return 0
620
622
621 def commitfunc(ui, repo, message, match, opts):
623 def commitfunc(ui, repo, message, match, opts):
622 editform = 'backout'
624 editform = 'backout'
623 e = cmdutil.getcommiteditor(editform=editform,
625 e = cmdutil.getcommiteditor(editform=editform,
624 **pycompat.strkwargs(opts))
626 **pycompat.strkwargs(opts))
625 if not message:
627 if not message:
626 # we don't translate commit messages
628 # we don't translate commit messages
627 message = "Backed out changeset %s" % short(node)
629 message = "Backed out changeset %s" % short(node)
628 e = cmdutil.getcommiteditor(edit=True, editform=editform)
630 e = cmdutil.getcommiteditor(edit=True, editform=editform)
629 return repo.commit(message, opts.get('user'), opts.get('date'),
631 return repo.commit(message, opts.get('user'), opts.get('date'),
630 match, editor=e)
632 match, editor=e)
631 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
633 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
632 if not newnode:
634 if not newnode:
633 ui.status(_("nothing changed\n"))
635 ui.status(_("nothing changed\n"))
634 return 1
636 return 1
635 cmdutil.commitstatus(repo, newnode, branch, bheads)
637 cmdutil.commitstatus(repo, newnode, branch, bheads)
636
638
637 def nice(node):
639 def nice(node):
638 return '%d:%s' % (repo.changelog.rev(node), short(node))
640 return '%d:%s' % (repo.changelog.rev(node), short(node))
639 ui.status(_('changeset %s backs out changeset %s\n') %
641 ui.status(_('changeset %s backs out changeset %s\n') %
640 (nice(repo.changelog.tip()), nice(node)))
642 (nice(repo.changelog.tip()), nice(node)))
641 if opts.get('merge') and op1 != node:
643 if opts.get('merge') and op1 != node:
642 hg.clean(repo, op1, show_stats=False)
644 hg.clean(repo, op1, show_stats=False)
643 ui.status(_('merging with changeset %s\n')
645 ui.status(_('merging with changeset %s\n')
644 % nice(repo.changelog.tip()))
646 % nice(repo.changelog.tip()))
645 try:
647 try:
646 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
648 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
647 'backout')
649 'backout')
648 return hg.merge(repo, hex(repo.changelog.tip()))
650 return hg.merge(repo, hex(repo.changelog.tip()))
649 finally:
651 finally:
650 ui.setconfig('ui', 'forcemerge', '', '')
652 ui.setconfig('ui', 'forcemerge', '', '')
651 return 0
653 return 0
652
654
653 @command('bisect',
655 @command('bisect',
654 [('r', 'reset', False, _('reset bisect state')),
656 [('r', 'reset', False, _('reset bisect state')),
655 ('g', 'good', False, _('mark changeset good')),
657 ('g', 'good', False, _('mark changeset good')),
656 ('b', 'bad', False, _('mark changeset bad')),
658 ('b', 'bad', False, _('mark changeset bad')),
657 ('s', 'skip', False, _('skip testing changeset')),
659 ('s', 'skip', False, _('skip testing changeset')),
658 ('e', 'extend', False, _('extend the bisect range')),
660 ('e', 'extend', False, _('extend the bisect range')),
659 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
661 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
660 ('U', 'noupdate', False, _('do not update to target'))],
662 ('U', 'noupdate', False, _('do not update to target'))],
661 _("[-gbsr] [-U] [-c CMD] [REV]"))
663 _("[-gbsr] [-U] [-c CMD] [REV]"))
662 def bisect(ui, repo, rev=None, extra=None, command=None,
664 def bisect(ui, repo, rev=None, extra=None, command=None,
663 reset=None, good=None, bad=None, skip=None, extend=None,
665 reset=None, good=None, bad=None, skip=None, extend=None,
664 noupdate=None):
666 noupdate=None):
665 """subdivision search of changesets
667 """subdivision search of changesets
666
668
667 This command helps to find changesets which introduce problems. To
669 This command helps to find changesets which introduce problems. To
668 use, mark the earliest changeset you know exhibits the problem as
670 use, mark the earliest changeset you know exhibits the problem as
669 bad, then mark the latest changeset which is free from the problem
671 bad, then mark the latest changeset which is free from the problem
670 as good. Bisect will update your working directory to a revision
672 as good. Bisect will update your working directory to a revision
671 for testing (unless the -U/--noupdate option is specified). Once
673 for testing (unless the -U/--noupdate option is specified). Once
672 you have performed tests, mark the working directory as good or
674 you have performed tests, mark the working directory as good or
673 bad, and bisect will either update to another candidate changeset
675 bad, and bisect will either update to another candidate changeset
674 or announce that it has found the bad revision.
676 or announce that it has found the bad revision.
675
677
676 As a shortcut, you can also use the revision argument to mark a
678 As a shortcut, you can also use the revision argument to mark a
677 revision as good or bad without checking it out first.
679 revision as good or bad without checking it out first.
678
680
679 If you supply a command, it will be used for automatic bisection.
681 If you supply a command, it will be used for automatic bisection.
680 The environment variable HG_NODE will contain the ID of the
682 The environment variable HG_NODE will contain the ID of the
681 changeset being tested. The exit status of the command will be
683 changeset being tested. The exit status of the command will be
682 used to mark revisions as good or bad: status 0 means good, 125
684 used to mark revisions as good or bad: status 0 means good, 125
683 means to skip the revision, 127 (command not found) will abort the
685 means to skip the revision, 127 (command not found) will abort the
684 bisection, and any other non-zero exit status means the revision
686 bisection, and any other non-zero exit status means the revision
685 is bad.
687 is bad.
686
688
687 .. container:: verbose
689 .. container:: verbose
688
690
689 Some examples:
691 Some examples:
690
692
691 - start a bisection with known bad revision 34, and good revision 12::
693 - start a bisection with known bad revision 34, and good revision 12::
692
694
693 hg bisect --bad 34
695 hg bisect --bad 34
694 hg bisect --good 12
696 hg bisect --good 12
695
697
696 - advance the current bisection by marking current revision as good or
698 - advance the current bisection by marking current revision as good or
697 bad::
699 bad::
698
700
699 hg bisect --good
701 hg bisect --good
700 hg bisect --bad
702 hg bisect --bad
701
703
702 - mark the current revision, or a known revision, to be skipped (e.g. if
704 - mark the current revision, or a known revision, to be skipped (e.g. if
703 that revision is not usable because of another issue)::
705 that revision is not usable because of another issue)::
704
706
705 hg bisect --skip
707 hg bisect --skip
706 hg bisect --skip 23
708 hg bisect --skip 23
707
709
708 - skip all revisions that do not touch directories ``foo`` or ``bar``::
710 - skip all revisions that do not touch directories ``foo`` or ``bar``::
709
711
710 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
712 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
711
713
712 - forget the current bisection::
714 - forget the current bisection::
713
715
714 hg bisect --reset
716 hg bisect --reset
715
717
716 - use 'make && make tests' to automatically find the first broken
718 - use 'make && make tests' to automatically find the first broken
717 revision::
719 revision::
718
720
719 hg bisect --reset
721 hg bisect --reset
720 hg bisect --bad 34
722 hg bisect --bad 34
721 hg bisect --good 12
723 hg bisect --good 12
722 hg bisect --command "make && make tests"
724 hg bisect --command "make && make tests"
723
725
724 - see all changesets whose states are already known in the current
726 - see all changesets whose states are already known in the current
725 bisection::
727 bisection::
726
728
727 hg log -r "bisect(pruned)"
729 hg log -r "bisect(pruned)"
728
730
729 - see the changeset currently being bisected (especially useful
731 - see the changeset currently being bisected (especially useful
730 if running with -U/--noupdate)::
732 if running with -U/--noupdate)::
731
733
732 hg log -r "bisect(current)"
734 hg log -r "bisect(current)"
733
735
734 - see all changesets that took part in the current bisection::
736 - see all changesets that took part in the current bisection::
735
737
736 hg log -r "bisect(range)"
738 hg log -r "bisect(range)"
737
739
738 - you can even get a nice graph::
740 - you can even get a nice graph::
739
741
740 hg log --graph -r "bisect(range)"
742 hg log --graph -r "bisect(range)"
741
743
742 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
744 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
743
745
744 Returns 0 on success.
746 Returns 0 on success.
745 """
747 """
746 # backward compatibility
748 # backward compatibility
747 if rev in "good bad reset init".split():
749 if rev in "good bad reset init".split():
748 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
750 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
749 cmd, rev, extra = rev, extra, None
751 cmd, rev, extra = rev, extra, None
750 if cmd == "good":
752 if cmd == "good":
751 good = True
753 good = True
752 elif cmd == "bad":
754 elif cmd == "bad":
753 bad = True
755 bad = True
754 else:
756 else:
755 reset = True
757 reset = True
756 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
758 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
757 raise error.Abort(_('incompatible arguments'))
759 raise error.Abort(_('incompatible arguments'))
758
760
759 if reset:
761 if reset:
760 hbisect.resetstate(repo)
762 hbisect.resetstate(repo)
761 return
763 return
762
764
763 state = hbisect.load_state(repo)
765 state = hbisect.load_state(repo)
764
766
765 # update state
767 # update state
766 if good or bad or skip:
768 if good or bad or skip:
767 if rev:
769 if rev:
768 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
770 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
769 else:
771 else:
770 nodes = [repo.lookup('.')]
772 nodes = [repo.lookup('.')]
771 if good:
773 if good:
772 state['good'] += nodes
774 state['good'] += nodes
773 elif bad:
775 elif bad:
774 state['bad'] += nodes
776 state['bad'] += nodes
775 elif skip:
777 elif skip:
776 state['skip'] += nodes
778 state['skip'] += nodes
777 hbisect.save_state(repo, state)
779 hbisect.save_state(repo, state)
778 if not (state['good'] and state['bad']):
780 if not (state['good'] and state['bad']):
779 return
781 return
780
782
781 def mayupdate(repo, node, show_stats=True):
783 def mayupdate(repo, node, show_stats=True):
782 """common used update sequence"""
784 """common used update sequence"""
783 if noupdate:
785 if noupdate:
784 return
786 return
785 cmdutil.checkunfinished(repo)
787 cmdutil.checkunfinished(repo)
786 cmdutil.bailifchanged(repo)
788 cmdutil.bailifchanged(repo)
787 return hg.clean(repo, node, show_stats=show_stats)
789 return hg.clean(repo, node, show_stats=show_stats)
788
790
789 displayer = cmdutil.show_changeset(ui, repo, {})
791 displayer = cmdutil.show_changeset(ui, repo, {})
790
792
791 if command:
793 if command:
792 changesets = 1
794 changesets = 1
793 if noupdate:
795 if noupdate:
794 try:
796 try:
795 node = state['current'][0]
797 node = state['current'][0]
796 except LookupError:
798 except LookupError:
797 raise error.Abort(_('current bisect revision is unknown - '
799 raise error.Abort(_('current bisect revision is unknown - '
798 'start a new bisect to fix'))
800 'start a new bisect to fix'))
799 else:
801 else:
800 node, p2 = repo.dirstate.parents()
802 node, p2 = repo.dirstate.parents()
801 if p2 != nullid:
803 if p2 != nullid:
802 raise error.Abort(_('current bisect revision is a merge'))
804 raise error.Abort(_('current bisect revision is a merge'))
803 if rev:
805 if rev:
804 node = repo[scmutil.revsingle(repo, rev, node)].node()
806 node = repo[scmutil.revsingle(repo, rev, node)].node()
805 try:
807 try:
806 while changesets:
808 while changesets:
807 # update state
809 # update state
808 state['current'] = [node]
810 state['current'] = [node]
809 hbisect.save_state(repo, state)
811 hbisect.save_state(repo, state)
810 status = ui.system(command, environ={'HG_NODE': hex(node)},
812 status = ui.system(command, environ={'HG_NODE': hex(node)},
811 blockedtag='bisect_check')
813 blockedtag='bisect_check')
812 if status == 125:
814 if status == 125:
813 transition = "skip"
815 transition = "skip"
814 elif status == 0:
816 elif status == 0:
815 transition = "good"
817 transition = "good"
816 # status < 0 means process was killed
818 # status < 0 means process was killed
817 elif status == 127:
819 elif status == 127:
818 raise error.Abort(_("failed to execute %s") % command)
820 raise error.Abort(_("failed to execute %s") % command)
819 elif status < 0:
821 elif status < 0:
820 raise error.Abort(_("%s killed") % command)
822 raise error.Abort(_("%s killed") % command)
821 else:
823 else:
822 transition = "bad"
824 transition = "bad"
823 state[transition].append(node)
825 state[transition].append(node)
824 ctx = repo[node]
826 ctx = repo[node]
825 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
827 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
826 hbisect.checkstate(state)
828 hbisect.checkstate(state)
827 # bisect
829 # bisect
828 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
830 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
829 # update to next check
831 # update to next check
830 node = nodes[0]
832 node = nodes[0]
831 mayupdate(repo, node, show_stats=False)
833 mayupdate(repo, node, show_stats=False)
832 finally:
834 finally:
833 state['current'] = [node]
835 state['current'] = [node]
834 hbisect.save_state(repo, state)
836 hbisect.save_state(repo, state)
835 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
837 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
836 return
838 return
837
839
838 hbisect.checkstate(state)
840 hbisect.checkstate(state)
839
841
840 # actually bisect
842 # actually bisect
841 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
843 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
842 if extend:
844 if extend:
843 if not changesets:
845 if not changesets:
844 extendnode = hbisect.extendrange(repo, state, nodes, good)
846 extendnode = hbisect.extendrange(repo, state, nodes, good)
845 if extendnode is not None:
847 if extendnode is not None:
846 ui.write(_("Extending search to changeset %d:%s\n")
848 ui.write(_("Extending search to changeset %d:%s\n")
847 % (extendnode.rev(), extendnode))
849 % (extendnode.rev(), extendnode))
848 state['current'] = [extendnode.node()]
850 state['current'] = [extendnode.node()]
849 hbisect.save_state(repo, state)
851 hbisect.save_state(repo, state)
850 return mayupdate(repo, extendnode.node())
852 return mayupdate(repo, extendnode.node())
851 raise error.Abort(_("nothing to extend"))
853 raise error.Abort(_("nothing to extend"))
852
854
853 if changesets == 0:
855 if changesets == 0:
854 hbisect.printresult(ui, repo, state, displayer, nodes, good)
856 hbisect.printresult(ui, repo, state, displayer, nodes, good)
855 else:
857 else:
856 assert len(nodes) == 1 # only a single node can be tested next
858 assert len(nodes) == 1 # only a single node can be tested next
857 node = nodes[0]
859 node = nodes[0]
858 # compute the approximate number of remaining tests
860 # compute the approximate number of remaining tests
859 tests, size = 0, 2
861 tests, size = 0, 2
860 while size <= changesets:
862 while size <= changesets:
861 tests, size = tests + 1, size * 2
863 tests, size = tests + 1, size * 2
862 rev = repo.changelog.rev(node)
864 rev = repo.changelog.rev(node)
863 ui.write(_("Testing changeset %d:%s "
865 ui.write(_("Testing changeset %d:%s "
864 "(%d changesets remaining, ~%d tests)\n")
866 "(%d changesets remaining, ~%d tests)\n")
865 % (rev, short(node), changesets, tests))
867 % (rev, short(node), changesets, tests))
866 state['current'] = [node]
868 state['current'] = [node]
867 hbisect.save_state(repo, state)
869 hbisect.save_state(repo, state)
868 return mayupdate(repo, node)
870 return mayupdate(repo, node)
869
871
870 @command('bookmarks|bookmark',
872 @command('bookmarks|bookmark',
871 [('f', 'force', False, _('force')),
873 [('f', 'force', False, _('force')),
872 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
874 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
873 ('d', 'delete', False, _('delete a given bookmark')),
875 ('d', 'delete', False, _('delete a given bookmark')),
874 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
876 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
875 ('i', 'inactive', False, _('mark a bookmark inactive')),
877 ('i', 'inactive', False, _('mark a bookmark inactive')),
876 ] + formatteropts,
878 ] + formatteropts,
877 _('hg bookmarks [OPTIONS]... [NAME]...'))
879 _('hg bookmarks [OPTIONS]... [NAME]...'))
878 def bookmark(ui, repo, *names, **opts):
880 def bookmark(ui, repo, *names, **opts):
879 '''create a new bookmark or list existing bookmarks
881 '''create a new bookmark or list existing bookmarks
880
882
881 Bookmarks are labels on changesets to help track lines of development.
883 Bookmarks are labels on changesets to help track lines of development.
882 Bookmarks are unversioned and can be moved, renamed and deleted.
884 Bookmarks are unversioned and can be moved, renamed and deleted.
883 Deleting or moving a bookmark has no effect on the associated changesets.
885 Deleting or moving a bookmark has no effect on the associated changesets.
884
886
885 Creating or updating to a bookmark causes it to be marked as 'active'.
887 Creating or updating to a bookmark causes it to be marked as 'active'.
886 The active bookmark is indicated with a '*'.
888 The active bookmark is indicated with a '*'.
887 When a commit is made, the active bookmark will advance to the new commit.
889 When a commit is made, the active bookmark will advance to the new commit.
888 A plain :hg:`update` will also advance an active bookmark, if possible.
890 A plain :hg:`update` will also advance an active bookmark, if possible.
889 Updating away from a bookmark will cause it to be deactivated.
891 Updating away from a bookmark will cause it to be deactivated.
890
892
891 Bookmarks can be pushed and pulled between repositories (see
893 Bookmarks can be pushed and pulled between repositories (see
892 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
894 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
893 diverged, a new 'divergent bookmark' of the form 'name@path' will
895 diverged, a new 'divergent bookmark' of the form 'name@path' will
894 be created. Using :hg:`merge` will resolve the divergence.
896 be created. Using :hg:`merge` will resolve the divergence.
895
897
896 A bookmark named '@' has the special property that :hg:`clone` will
898 A bookmark named '@' has the special property that :hg:`clone` will
897 check it out by default if it exists.
899 check it out by default if it exists.
898
900
899 .. container:: verbose
901 .. container:: verbose
900
902
901 Examples:
903 Examples:
902
904
903 - create an active bookmark for a new line of development::
905 - create an active bookmark for a new line of development::
904
906
905 hg book new-feature
907 hg book new-feature
906
908
907 - create an inactive bookmark as a place marker::
909 - create an inactive bookmark as a place marker::
908
910
909 hg book -i reviewed
911 hg book -i reviewed
910
912
911 - create an inactive bookmark on another changeset::
913 - create an inactive bookmark on another changeset::
912
914
913 hg book -r .^ tested
915 hg book -r .^ tested
914
916
915 - rename bookmark turkey to dinner::
917 - rename bookmark turkey to dinner::
916
918
917 hg book -m turkey dinner
919 hg book -m turkey dinner
918
920
919 - move the '@' bookmark from another branch::
921 - move the '@' bookmark from another branch::
920
922
921 hg book -f @
923 hg book -f @
922 '''
924 '''
923 opts = pycompat.byteskwargs(opts)
925 opts = pycompat.byteskwargs(opts)
924 force = opts.get('force')
926 force = opts.get('force')
925 rev = opts.get('rev')
927 rev = opts.get('rev')
926 delete = opts.get('delete')
928 delete = opts.get('delete')
927 rename = opts.get('rename')
929 rename = opts.get('rename')
928 inactive = opts.get('inactive')
930 inactive = opts.get('inactive')
929
931
930 def checkformat(mark):
932 def checkformat(mark):
931 mark = mark.strip()
933 mark = mark.strip()
932 if not mark:
934 if not mark:
933 raise error.Abort(_("bookmark names cannot consist entirely of "
935 raise error.Abort(_("bookmark names cannot consist entirely of "
934 "whitespace"))
936 "whitespace"))
935 scmutil.checknewlabel(repo, mark, 'bookmark')
937 scmutil.checknewlabel(repo, mark, 'bookmark')
936 return mark
938 return mark
937
939
938 def checkconflict(repo, mark, cur, force=False, target=None):
940 def checkconflict(repo, mark, cur, force=False, target=None):
939 if mark in marks and not force:
941 if mark in marks and not force:
940 if target:
942 if target:
941 if marks[mark] == target and target == cur:
943 if marks[mark] == target and target == cur:
942 # re-activating a bookmark
944 # re-activating a bookmark
943 return
945 return
944 anc = repo.changelog.ancestors([repo[target].rev()])
946 anc = repo.changelog.ancestors([repo[target].rev()])
945 bmctx = repo[marks[mark]]
947 bmctx = repo[marks[mark]]
946 divs = [repo[b].node() for b in marks
948 divs = [repo[b].node() for b in marks
947 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
949 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
948
950
949 # allow resolving a single divergent bookmark even if moving
951 # allow resolving a single divergent bookmark even if moving
950 # the bookmark across branches when a revision is specified
952 # the bookmark across branches when a revision is specified
951 # that contains a divergent bookmark
953 # that contains a divergent bookmark
952 if bmctx.rev() not in anc and target in divs:
954 if bmctx.rev() not in anc and target in divs:
953 bookmarks.deletedivergent(repo, [target], mark)
955 bookmarks.deletedivergent(repo, [target], mark)
954 return
956 return
955
957
956 deletefrom = [b for b in divs
958 deletefrom = [b for b in divs
957 if repo[b].rev() in anc or b == target]
959 if repo[b].rev() in anc or b == target]
958 bookmarks.deletedivergent(repo, deletefrom, mark)
960 bookmarks.deletedivergent(repo, deletefrom, mark)
959 if bookmarks.validdest(repo, bmctx, repo[target]):
961 if bookmarks.validdest(repo, bmctx, repo[target]):
960 ui.status(_("moving bookmark '%s' forward from %s\n") %
962 ui.status(_("moving bookmark '%s' forward from %s\n") %
961 (mark, short(bmctx.node())))
963 (mark, short(bmctx.node())))
962 return
964 return
963 raise error.Abort(_("bookmark '%s' already exists "
965 raise error.Abort(_("bookmark '%s' already exists "
964 "(use -f to force)") % mark)
966 "(use -f to force)") % mark)
965 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
967 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
966 and not force):
968 and not force):
967 raise error.Abort(
969 raise error.Abort(
968 _("a bookmark cannot have the name of an existing branch"))
970 _("a bookmark cannot have the name of an existing branch"))
969
971
970 if delete and rename:
972 if delete and rename:
971 raise error.Abort(_("--delete and --rename are incompatible"))
973 raise error.Abort(_("--delete and --rename are incompatible"))
972 if delete and rev:
974 if delete and rev:
973 raise error.Abort(_("--rev is incompatible with --delete"))
975 raise error.Abort(_("--rev is incompatible with --delete"))
974 if rename and rev:
976 if rename and rev:
975 raise error.Abort(_("--rev is incompatible with --rename"))
977 raise error.Abort(_("--rev is incompatible with --rename"))
976 if not names and (delete or rev):
978 if not names and (delete or rev):
977 raise error.Abort(_("bookmark name required"))
979 raise error.Abort(_("bookmark name required"))
978
980
979 if delete or rename or names or inactive:
981 if delete or rename or names or inactive:
980 wlock = lock = tr = None
982 wlock = lock = tr = None
981 try:
983 try:
982 wlock = repo.wlock()
984 wlock = repo.wlock()
983 lock = repo.lock()
985 lock = repo.lock()
984 cur = repo.changectx('.').node()
986 cur = repo.changectx('.').node()
985 marks = repo._bookmarks
987 marks = repo._bookmarks
986 if delete:
988 if delete:
987 tr = repo.transaction('bookmark')
989 tr = repo.transaction('bookmark')
988 for mark in names:
990 for mark in names:
989 if mark not in marks:
991 if mark not in marks:
990 raise error.Abort(_("bookmark '%s' does not exist") %
992 raise error.Abort(_("bookmark '%s' does not exist") %
991 mark)
993 mark)
992 if mark == repo._activebookmark:
994 if mark == repo._activebookmark:
993 bookmarks.deactivate(repo)
995 bookmarks.deactivate(repo)
994 del marks[mark]
996 del marks[mark]
995
997
996 elif rename:
998 elif rename:
997 tr = repo.transaction('bookmark')
999 tr = repo.transaction('bookmark')
998 if not names:
1000 if not names:
999 raise error.Abort(_("new bookmark name required"))
1001 raise error.Abort(_("new bookmark name required"))
1000 elif len(names) > 1:
1002 elif len(names) > 1:
1001 raise error.Abort(_("only one new bookmark name allowed"))
1003 raise error.Abort(_("only one new bookmark name allowed"))
1002 mark = checkformat(names[0])
1004 mark = checkformat(names[0])
1003 if rename not in marks:
1005 if rename not in marks:
1004 raise error.Abort(_("bookmark '%s' does not exist")
1006 raise error.Abort(_("bookmark '%s' does not exist")
1005 % rename)
1007 % rename)
1006 checkconflict(repo, mark, cur, force)
1008 checkconflict(repo, mark, cur, force)
1007 marks[mark] = marks[rename]
1009 marks[mark] = marks[rename]
1008 if repo._activebookmark == rename and not inactive:
1010 if repo._activebookmark == rename and not inactive:
1009 bookmarks.activate(repo, mark)
1011 bookmarks.activate(repo, mark)
1010 del marks[rename]
1012 del marks[rename]
1011 elif names:
1013 elif names:
1012 tr = repo.transaction('bookmark')
1014 tr = repo.transaction('bookmark')
1013 newact = None
1015 newact = None
1014 for mark in names:
1016 for mark in names:
1015 mark = checkformat(mark)
1017 mark = checkformat(mark)
1016 if newact is None:
1018 if newact is None:
1017 newact = mark
1019 newact = mark
1018 if inactive and mark == repo._activebookmark:
1020 if inactive and mark == repo._activebookmark:
1019 bookmarks.deactivate(repo)
1021 bookmarks.deactivate(repo)
1020 return
1022 return
1021 tgt = cur
1023 tgt = cur
1022 if rev:
1024 if rev:
1023 tgt = scmutil.revsingle(repo, rev).node()
1025 tgt = scmutil.revsingle(repo, rev).node()
1024 checkconflict(repo, mark, cur, force, tgt)
1026 checkconflict(repo, mark, cur, force, tgt)
1025 marks[mark] = tgt
1027 marks[mark] = tgt
1026 if not inactive and cur == marks[newact] and not rev:
1028 if not inactive and cur == marks[newact] and not rev:
1027 bookmarks.activate(repo, newact)
1029 bookmarks.activate(repo, newact)
1028 elif cur != tgt and newact == repo._activebookmark:
1030 elif cur != tgt and newact == repo._activebookmark:
1029 bookmarks.deactivate(repo)
1031 bookmarks.deactivate(repo)
1030 elif inactive:
1032 elif inactive:
1031 if len(marks) == 0:
1033 if len(marks) == 0:
1032 ui.status(_("no bookmarks set\n"))
1034 ui.status(_("no bookmarks set\n"))
1033 elif not repo._activebookmark:
1035 elif not repo._activebookmark:
1034 ui.status(_("no active bookmark\n"))
1036 ui.status(_("no active bookmark\n"))
1035 else:
1037 else:
1036 bookmarks.deactivate(repo)
1038 bookmarks.deactivate(repo)
1037 if tr is not None:
1039 if tr is not None:
1038 marks.recordchange(tr)
1040 marks.recordchange(tr)
1039 tr.close()
1041 tr.close()
1040 finally:
1042 finally:
1041 lockmod.release(tr, lock, wlock)
1043 lockmod.release(tr, lock, wlock)
1042 else: # show bookmarks
1044 else: # show bookmarks
1043 fm = ui.formatter('bookmarks', opts)
1045 fm = ui.formatter('bookmarks', opts)
1044 hexfn = fm.hexfunc
1046 hexfn = fm.hexfunc
1045 marks = repo._bookmarks
1047 marks = repo._bookmarks
1046 if len(marks) == 0 and fm.isplain():
1048 if len(marks) == 0 and fm.isplain():
1047 ui.status(_("no bookmarks set\n"))
1049 ui.status(_("no bookmarks set\n"))
1048 for bmark, n in sorted(marks.iteritems()):
1050 for bmark, n in sorted(marks.iteritems()):
1049 active = repo._activebookmark
1051 active = repo._activebookmark
1050 if bmark == active:
1052 if bmark == active:
1051 prefix, label = '*', activebookmarklabel
1053 prefix, label = '*', activebookmarklabel
1052 else:
1054 else:
1053 prefix, label = ' ', ''
1055 prefix, label = ' ', ''
1054
1056
1055 fm.startitem()
1057 fm.startitem()
1056 if not ui.quiet:
1058 if not ui.quiet:
1057 fm.plain(' %s ' % prefix, label=label)
1059 fm.plain(' %s ' % prefix, label=label)
1058 fm.write('bookmark', '%s', bmark, label=label)
1060 fm.write('bookmark', '%s', bmark, label=label)
1059 pad = " " * (25 - encoding.colwidth(bmark))
1061 pad = " " * (25 - encoding.colwidth(bmark))
1060 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1062 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1061 repo.changelog.rev(n), hexfn(n), label=label)
1063 repo.changelog.rev(n), hexfn(n), label=label)
1062 fm.data(active=(bmark == active))
1064 fm.data(active=(bmark == active))
1063 fm.plain('\n')
1065 fm.plain('\n')
1064 fm.end()
1066 fm.end()
1065
1067
1066 @command('branch',
1068 @command('branch',
1067 [('f', 'force', None,
1069 [('f', 'force', None,
1068 _('set branch name even if it shadows an existing branch')),
1070 _('set branch name even if it shadows an existing branch')),
1069 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1071 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1070 _('[-fC] [NAME]'))
1072 _('[-fC] [NAME]'))
1071 def branch(ui, repo, label=None, **opts):
1073 def branch(ui, repo, label=None, **opts):
1072 """set or show the current branch name
1074 """set or show the current branch name
1073
1075
1074 .. note::
1076 .. note::
1075
1077
1076 Branch names are permanent and global. Use :hg:`bookmark` to create a
1078 Branch names are permanent and global. Use :hg:`bookmark` to create a
1077 light-weight bookmark instead. See :hg:`help glossary` for more
1079 light-weight bookmark instead. See :hg:`help glossary` for more
1078 information about named branches and bookmarks.
1080 information about named branches and bookmarks.
1079
1081
1080 With no argument, show the current branch name. With one argument,
1082 With no argument, show the current branch name. With one argument,
1081 set the working directory branch name (the branch will not exist
1083 set the working directory branch name (the branch will not exist
1082 in the repository until the next commit). Standard practice
1084 in the repository until the next commit). Standard practice
1083 recommends that primary development take place on the 'default'
1085 recommends that primary development take place on the 'default'
1084 branch.
1086 branch.
1085
1087
1086 Unless -f/--force is specified, branch will not let you set a
1088 Unless -f/--force is specified, branch will not let you set a
1087 branch name that already exists.
1089 branch name that already exists.
1088
1090
1089 Use -C/--clean to reset the working directory branch to that of
1091 Use -C/--clean to reset the working directory branch to that of
1090 the parent of the working directory, negating a previous branch
1092 the parent of the working directory, negating a previous branch
1091 change.
1093 change.
1092
1094
1093 Use the command :hg:`update` to switch to an existing branch. Use
1095 Use the command :hg:`update` to switch to an existing branch. Use
1094 :hg:`commit --close-branch` to mark this branch head as closed.
1096 :hg:`commit --close-branch` to mark this branch head as closed.
1095 When all heads of a branch are closed, the branch will be
1097 When all heads of a branch are closed, the branch will be
1096 considered closed.
1098 considered closed.
1097
1099
1098 Returns 0 on success.
1100 Returns 0 on success.
1099 """
1101 """
1100 opts = pycompat.byteskwargs(opts)
1102 opts = pycompat.byteskwargs(opts)
1101 if label:
1103 if label:
1102 label = label.strip()
1104 label = label.strip()
1103
1105
1104 if not opts.get('clean') and not label:
1106 if not opts.get('clean') and not label:
1105 ui.write("%s\n" % repo.dirstate.branch())
1107 ui.write("%s\n" % repo.dirstate.branch())
1106 return
1108 return
1107
1109
1108 with repo.wlock():
1110 with repo.wlock():
1109 if opts.get('clean'):
1111 if opts.get('clean'):
1110 label = repo[None].p1().branch()
1112 label = repo[None].p1().branch()
1111 repo.dirstate.setbranch(label)
1113 repo.dirstate.setbranch(label)
1112 ui.status(_('reset working directory to branch %s\n') % label)
1114 ui.status(_('reset working directory to branch %s\n') % label)
1113 elif label:
1115 elif label:
1114 if not opts.get('force') and label in repo.branchmap():
1116 if not opts.get('force') and label in repo.branchmap():
1115 if label not in [p.branch() for p in repo[None].parents()]:
1117 if label not in [p.branch() for p in repo[None].parents()]:
1116 raise error.Abort(_('a branch of the same name already'
1118 raise error.Abort(_('a branch of the same name already'
1117 ' exists'),
1119 ' exists'),
1118 # i18n: "it" refers to an existing branch
1120 # i18n: "it" refers to an existing branch
1119 hint=_("use 'hg update' to switch to it"))
1121 hint=_("use 'hg update' to switch to it"))
1120 scmutil.checknewlabel(repo, label, 'branch')
1122 scmutil.checknewlabel(repo, label, 'branch')
1121 repo.dirstate.setbranch(label)
1123 repo.dirstate.setbranch(label)
1122 ui.status(_('marked working directory as branch %s\n') % label)
1124 ui.status(_('marked working directory as branch %s\n') % label)
1123
1125
1124 # find any open named branches aside from default
1126 # find any open named branches aside from default
1125 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1127 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1126 if n != "default" and not c]
1128 if n != "default" and not c]
1127 if not others:
1129 if not others:
1128 ui.status(_('(branches are permanent and global, '
1130 ui.status(_('(branches are permanent and global, '
1129 'did you want a bookmark?)\n'))
1131 'did you want a bookmark?)\n'))
1130
1132
1131 @command('branches',
1133 @command('branches',
1132 [('a', 'active', False,
1134 [('a', 'active', False,
1133 _('show only branches that have unmerged heads (DEPRECATED)')),
1135 _('show only branches that have unmerged heads (DEPRECATED)')),
1134 ('c', 'closed', False, _('show normal and closed branches')),
1136 ('c', 'closed', False, _('show normal and closed branches')),
1135 ] + formatteropts,
1137 ] + formatteropts,
1136 _('[-c]'))
1138 _('[-c]'))
1137 def branches(ui, repo, active=False, closed=False, **opts):
1139 def branches(ui, repo, active=False, closed=False, **opts):
1138 """list repository named branches
1140 """list repository named branches
1139
1141
1140 List the repository's named branches, indicating which ones are
1142 List the repository's named branches, indicating which ones are
1141 inactive. If -c/--closed is specified, also list branches which have
1143 inactive. If -c/--closed is specified, also list branches which have
1142 been marked closed (see :hg:`commit --close-branch`).
1144 been marked closed (see :hg:`commit --close-branch`).
1143
1145
1144 Use the command :hg:`update` to switch to an existing branch.
1146 Use the command :hg:`update` to switch to an existing branch.
1145
1147
1146 Returns 0.
1148 Returns 0.
1147 """
1149 """
1148
1150
1149 opts = pycompat.byteskwargs(opts)
1151 opts = pycompat.byteskwargs(opts)
1150 ui.pager('branches')
1152 ui.pager('branches')
1151 fm = ui.formatter('branches', opts)
1153 fm = ui.formatter('branches', opts)
1152 hexfunc = fm.hexfunc
1154 hexfunc = fm.hexfunc
1153
1155
1154 allheads = set(repo.heads())
1156 allheads = set(repo.heads())
1155 branches = []
1157 branches = []
1156 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1158 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1157 isactive = not isclosed and bool(set(heads) & allheads)
1159 isactive = not isclosed and bool(set(heads) & allheads)
1158 branches.append((tag, repo[tip], isactive, not isclosed))
1160 branches.append((tag, repo[tip], isactive, not isclosed))
1159 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1161 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1160 reverse=True)
1162 reverse=True)
1161
1163
1162 for tag, ctx, isactive, isopen in branches:
1164 for tag, ctx, isactive, isopen in branches:
1163 if active and not isactive:
1165 if active and not isactive:
1164 continue
1166 continue
1165 if isactive:
1167 if isactive:
1166 label = 'branches.active'
1168 label = 'branches.active'
1167 notice = ''
1169 notice = ''
1168 elif not isopen:
1170 elif not isopen:
1169 if not closed:
1171 if not closed:
1170 continue
1172 continue
1171 label = 'branches.closed'
1173 label = 'branches.closed'
1172 notice = _(' (closed)')
1174 notice = _(' (closed)')
1173 else:
1175 else:
1174 label = 'branches.inactive'
1176 label = 'branches.inactive'
1175 notice = _(' (inactive)')
1177 notice = _(' (inactive)')
1176 current = (tag == repo.dirstate.branch())
1178 current = (tag == repo.dirstate.branch())
1177 if current:
1179 if current:
1178 label = 'branches.current'
1180 label = 'branches.current'
1179
1181
1180 fm.startitem()
1182 fm.startitem()
1181 fm.write('branch', '%s', tag, label=label)
1183 fm.write('branch', '%s', tag, label=label)
1182 rev = ctx.rev()
1184 rev = ctx.rev()
1183 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1185 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1184 fmt = ' ' * padsize + ' %d:%s'
1186 fmt = ' ' * padsize + ' %d:%s'
1185 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1187 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1186 label='log.changeset changeset.%s' % ctx.phasestr())
1188 label='log.changeset changeset.%s' % ctx.phasestr())
1187 fm.context(ctx=ctx)
1189 fm.context(ctx=ctx)
1188 fm.data(active=isactive, closed=not isopen, current=current)
1190 fm.data(active=isactive, closed=not isopen, current=current)
1189 if not ui.quiet:
1191 if not ui.quiet:
1190 fm.plain(notice)
1192 fm.plain(notice)
1191 fm.plain('\n')
1193 fm.plain('\n')
1192 fm.end()
1194 fm.end()
1193
1195
1194 @command('bundle',
1196 @command('bundle',
1195 [('f', 'force', None, _('run even when the destination is unrelated')),
1197 [('f', 'force', None, _('run even when the destination is unrelated')),
1196 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1198 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1197 _('REV')),
1199 _('REV')),
1198 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1200 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1199 _('BRANCH')),
1201 _('BRANCH')),
1200 ('', 'base', [],
1202 ('', 'base', [],
1201 _('a base changeset assumed to be available at the destination'),
1203 _('a base changeset assumed to be available at the destination'),
1202 _('REV')),
1204 _('REV')),
1203 ('a', 'all', None, _('bundle all changesets in the repository')),
1205 ('a', 'all', None, _('bundle all changesets in the repository')),
1204 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1206 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1205 ] + remoteopts,
1207 ] + remoteopts,
1206 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1208 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1207 def bundle(ui, repo, fname, dest=None, **opts):
1209 def bundle(ui, repo, fname, dest=None, **opts):
1208 """create a bundle file
1210 """create a bundle file
1209
1211
1210 Generate a bundle file containing data to be added to a repository.
1212 Generate a bundle file containing data to be added to a repository.
1211
1213
1212 To create a bundle containing all changesets, use -a/--all
1214 To create a bundle containing all changesets, use -a/--all
1213 (or --base null). Otherwise, hg assumes the destination will have
1215 (or --base null). Otherwise, hg assumes the destination will have
1214 all the nodes you specify with --base parameters. Otherwise, hg
1216 all the nodes you specify with --base parameters. Otherwise, hg
1215 will assume the repository has all the nodes in destination, or
1217 will assume the repository has all the nodes in destination, or
1216 default-push/default if no destination is specified.
1218 default-push/default if no destination is specified.
1217
1219
1218 You can change bundle format with the -t/--type option. See
1220 You can change bundle format with the -t/--type option. See
1219 :hg:`help bundlespec` for documentation on this format. By default,
1221 :hg:`help bundlespec` for documentation on this format. By default,
1220 the most appropriate format is used and compression defaults to
1222 the most appropriate format is used and compression defaults to
1221 bzip2.
1223 bzip2.
1222
1224
1223 The bundle file can then be transferred using conventional means
1225 The bundle file can then be transferred using conventional means
1224 and applied to another repository with the unbundle or pull
1226 and applied to another repository with the unbundle or pull
1225 command. This is useful when direct push and pull are not
1227 command. This is useful when direct push and pull are not
1226 available or when exporting an entire repository is undesirable.
1228 available or when exporting an entire repository is undesirable.
1227
1229
1228 Applying bundles preserves all changeset contents including
1230 Applying bundles preserves all changeset contents including
1229 permissions, copy/rename information, and revision history.
1231 permissions, copy/rename information, and revision history.
1230
1232
1231 Returns 0 on success, 1 if no changes found.
1233 Returns 0 on success, 1 if no changes found.
1232 """
1234 """
1233 opts = pycompat.byteskwargs(opts)
1235 opts = pycompat.byteskwargs(opts)
1234 revs = None
1236 revs = None
1235 if 'rev' in opts:
1237 if 'rev' in opts:
1236 revstrings = opts['rev']
1238 revstrings = opts['rev']
1237 revs = scmutil.revrange(repo, revstrings)
1239 revs = scmutil.revrange(repo, revstrings)
1238 if revstrings and not revs:
1240 if revstrings and not revs:
1239 raise error.Abort(_('no commits to bundle'))
1241 raise error.Abort(_('no commits to bundle'))
1240
1242
1241 bundletype = opts.get('type', 'bzip2').lower()
1243 bundletype = opts.get('type', 'bzip2').lower()
1242 try:
1244 try:
1243 bcompression, cgversion, params = exchange.parsebundlespec(
1245 bcompression, cgversion, params = exchange.parsebundlespec(
1244 repo, bundletype, strict=False)
1246 repo, bundletype, strict=False)
1245 except error.UnsupportedBundleSpecification as e:
1247 except error.UnsupportedBundleSpecification as e:
1246 raise error.Abort(str(e),
1248 raise error.Abort(str(e),
1247 hint=_("see 'hg help bundlespec' for supported "
1249 hint=_("see 'hg help bundlespec' for supported "
1248 "values for --type"))
1250 "values for --type"))
1249
1251
1250 # Packed bundles are a pseudo bundle format for now.
1252 # Packed bundles are a pseudo bundle format for now.
1251 if cgversion == 's1':
1253 if cgversion == 's1':
1252 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1254 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1253 hint=_("use 'hg debugcreatestreamclonebundle'"))
1255 hint=_("use 'hg debugcreatestreamclonebundle'"))
1254
1256
1255 if opts.get('all'):
1257 if opts.get('all'):
1256 if dest:
1258 if dest:
1257 raise error.Abort(_("--all is incompatible with specifying "
1259 raise error.Abort(_("--all is incompatible with specifying "
1258 "a destination"))
1260 "a destination"))
1259 if opts.get('base'):
1261 if opts.get('base'):
1260 ui.warn(_("ignoring --base because --all was specified\n"))
1262 ui.warn(_("ignoring --base because --all was specified\n"))
1261 base = ['null']
1263 base = ['null']
1262 else:
1264 else:
1263 base = scmutil.revrange(repo, opts.get('base'))
1265 base = scmutil.revrange(repo, opts.get('base'))
1264 if cgversion not in changegroup.supportedoutgoingversions(repo):
1266 if cgversion not in changegroup.supportedoutgoingversions(repo):
1265 raise error.Abort(_("repository does not support bundle version %s") %
1267 raise error.Abort(_("repository does not support bundle version %s") %
1266 cgversion)
1268 cgversion)
1267
1269
1268 if base:
1270 if base:
1269 if dest:
1271 if dest:
1270 raise error.Abort(_("--base is incompatible with specifying "
1272 raise error.Abort(_("--base is incompatible with specifying "
1271 "a destination"))
1273 "a destination"))
1272 common = [repo.lookup(rev) for rev in base]
1274 common = [repo.lookup(rev) for rev in base]
1273 heads = revs and map(repo.lookup, revs) or None
1275 heads = revs and map(repo.lookup, revs) or None
1274 outgoing = discovery.outgoing(repo, common, heads)
1276 outgoing = discovery.outgoing(repo, common, heads)
1275 else:
1277 else:
1276 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1278 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1277 dest, branches = hg.parseurl(dest, opts.get('branch'))
1279 dest, branches = hg.parseurl(dest, opts.get('branch'))
1278 other = hg.peer(repo, opts, dest)
1280 other = hg.peer(repo, opts, dest)
1279 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1281 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1280 heads = revs and map(repo.lookup, revs) or revs
1282 heads = revs and map(repo.lookup, revs) or revs
1281 outgoing = discovery.findcommonoutgoing(repo, other,
1283 outgoing = discovery.findcommonoutgoing(repo, other,
1282 onlyheads=heads,
1284 onlyheads=heads,
1283 force=opts.get('force'),
1285 force=opts.get('force'),
1284 portable=True)
1286 portable=True)
1285
1287
1286 if not outgoing.missing:
1288 if not outgoing.missing:
1287 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1289 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1288 return 1
1290 return 1
1289
1291
1290 if cgversion == '01': #bundle1
1292 if cgversion == '01': #bundle1
1291 if bcompression is None:
1293 if bcompression is None:
1292 bcompression = 'UN'
1294 bcompression = 'UN'
1293 bversion = 'HG10' + bcompression
1295 bversion = 'HG10' + bcompression
1294 bcompression = None
1296 bcompression = None
1295 elif cgversion in ('02', '03'):
1297 elif cgversion in ('02', '03'):
1296 bversion = 'HG20'
1298 bversion = 'HG20'
1297 else:
1299 else:
1298 raise error.ProgrammingError(
1300 raise error.ProgrammingError(
1299 'bundle: unexpected changegroup version %s' % cgversion)
1301 'bundle: unexpected changegroup version %s' % cgversion)
1300
1302
1301 # TODO compression options should be derived from bundlespec parsing.
1303 # TODO compression options should be derived from bundlespec parsing.
1302 # This is a temporary hack to allow adjusting bundle compression
1304 # This is a temporary hack to allow adjusting bundle compression
1303 # level without a) formalizing the bundlespec changes to declare it
1305 # level without a) formalizing the bundlespec changes to declare it
1304 # b) introducing a command flag.
1306 # b) introducing a command flag.
1305 compopts = {}
1307 compopts = {}
1306 complevel = ui.configint('experimental', 'bundlecomplevel')
1308 complevel = ui.configint('experimental', 'bundlecomplevel')
1307 if complevel is not None:
1309 if complevel is not None:
1308 compopts['level'] = complevel
1310 compopts['level'] = complevel
1309
1311
1310
1312
1311 contentopts = {'cg.version': cgversion}
1313 contentopts = {'cg.version': cgversion}
1312 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1314 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1313 contentopts, compression=bcompression,
1315 contentopts, compression=bcompression,
1314 compopts=compopts)
1316 compopts=compopts)
1315
1317
1316 @command('cat',
1318 @command('cat',
1317 [('o', 'output', '',
1319 [('o', 'output', '',
1318 _('print output to file with formatted name'), _('FORMAT')),
1320 _('print output to file with formatted name'), _('FORMAT')),
1319 ('r', 'rev', '', _('print the given revision'), _('REV')),
1321 ('r', 'rev', '', _('print the given revision'), _('REV')),
1320 ('', 'decode', None, _('apply any matching decode filter')),
1322 ('', 'decode', None, _('apply any matching decode filter')),
1321 ] + walkopts,
1323 ] + walkopts,
1322 _('[OPTION]... FILE...'),
1324 _('[OPTION]... FILE...'),
1323 inferrepo=True)
1325 inferrepo=True)
1324 def cat(ui, repo, file1, *pats, **opts):
1326 def cat(ui, repo, file1, *pats, **opts):
1325 """output the current or given revision of files
1327 """output the current or given revision of files
1326
1328
1327 Print the specified files as they were at the given revision. If
1329 Print the specified files as they were at the given revision. If
1328 no revision is given, the parent of the working directory is used.
1330 no revision is given, the parent of the working directory is used.
1329
1331
1330 Output may be to a file, in which case the name of the file is
1332 Output may be to a file, in which case the name of the file is
1331 given using a format string. The formatting rules as follows:
1333 given using a format string. The formatting rules as follows:
1332
1334
1333 :``%%``: literal "%" character
1335 :``%%``: literal "%" character
1334 :``%s``: basename of file being printed
1336 :``%s``: basename of file being printed
1335 :``%d``: dirname of file being printed, or '.' if in repository root
1337 :``%d``: dirname of file being printed, or '.' if in repository root
1336 :``%p``: root-relative path name of file being printed
1338 :``%p``: root-relative path name of file being printed
1337 :``%H``: changeset hash (40 hexadecimal digits)
1339 :``%H``: changeset hash (40 hexadecimal digits)
1338 :``%R``: changeset revision number
1340 :``%R``: changeset revision number
1339 :``%h``: short-form changeset hash (12 hexadecimal digits)
1341 :``%h``: short-form changeset hash (12 hexadecimal digits)
1340 :``%r``: zero-padded changeset revision number
1342 :``%r``: zero-padded changeset revision number
1341 :``%b``: basename of the exporting repository
1343 :``%b``: basename of the exporting repository
1342
1344
1343 Returns 0 on success.
1345 Returns 0 on success.
1344 """
1346 """
1345 ctx = scmutil.revsingle(repo, opts.get('rev'))
1347 ctx = scmutil.revsingle(repo, opts.get('rev'))
1346 m = scmutil.match(ctx, (file1,) + pats, opts)
1348 m = scmutil.match(ctx, (file1,) + pats, opts)
1347
1349
1348 ui.pager('cat')
1350 ui.pager('cat')
1349 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1351 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1350
1352
1351 @command('^clone',
1353 @command('^clone',
1352 [('U', 'noupdate', None, _('the clone will include an empty working '
1354 [('U', 'noupdate', None, _('the clone will include an empty working '
1353 'directory (only a repository)')),
1355 'directory (only a repository)')),
1354 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1356 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1355 _('REV')),
1357 _('REV')),
1356 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1358 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1357 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1359 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1358 ('', 'pull', None, _('use pull protocol to copy metadata')),
1360 ('', 'pull', None, _('use pull protocol to copy metadata')),
1359 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1361 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1360 ] + remoteopts,
1362 ] + remoteopts,
1361 _('[OPTION]... SOURCE [DEST]'),
1363 _('[OPTION]... SOURCE [DEST]'),
1362 norepo=True)
1364 norepo=True)
1363 def clone(ui, source, dest=None, **opts):
1365 def clone(ui, source, dest=None, **opts):
1364 """make a copy of an existing repository
1366 """make a copy of an existing repository
1365
1367
1366 Create a copy of an existing repository in a new directory.
1368 Create a copy of an existing repository in a new directory.
1367
1369
1368 If no destination directory name is specified, it defaults to the
1370 If no destination directory name is specified, it defaults to the
1369 basename of the source.
1371 basename of the source.
1370
1372
1371 The location of the source is added to the new repository's
1373 The location of the source is added to the new repository's
1372 ``.hg/hgrc`` file, as the default to be used for future pulls.
1374 ``.hg/hgrc`` file, as the default to be used for future pulls.
1373
1375
1374 Only local paths and ``ssh://`` URLs are supported as
1376 Only local paths and ``ssh://`` URLs are supported as
1375 destinations. For ``ssh://`` destinations, no working directory or
1377 destinations. For ``ssh://`` destinations, no working directory or
1376 ``.hg/hgrc`` will be created on the remote side.
1378 ``.hg/hgrc`` will be created on the remote side.
1377
1379
1378 If the source repository has a bookmark called '@' set, that
1380 If the source repository has a bookmark called '@' set, that
1379 revision will be checked out in the new repository by default.
1381 revision will be checked out in the new repository by default.
1380
1382
1381 To check out a particular version, use -u/--update, or
1383 To check out a particular version, use -u/--update, or
1382 -U/--noupdate to create a clone with no working directory.
1384 -U/--noupdate to create a clone with no working directory.
1383
1385
1384 To pull only a subset of changesets, specify one or more revisions
1386 To pull only a subset of changesets, specify one or more revisions
1385 identifiers with -r/--rev or branches with -b/--branch. The
1387 identifiers with -r/--rev or branches with -b/--branch. The
1386 resulting clone will contain only the specified changesets and
1388 resulting clone will contain only the specified changesets and
1387 their ancestors. These options (or 'clone src#rev dest') imply
1389 their ancestors. These options (or 'clone src#rev dest') imply
1388 --pull, even for local source repositories.
1390 --pull, even for local source repositories.
1389
1391
1390 .. note::
1392 .. note::
1391
1393
1392 Specifying a tag will include the tagged changeset but not the
1394 Specifying a tag will include the tagged changeset but not the
1393 changeset containing the tag.
1395 changeset containing the tag.
1394
1396
1395 .. container:: verbose
1397 .. container:: verbose
1396
1398
1397 For efficiency, hardlinks are used for cloning whenever the
1399 For efficiency, hardlinks are used for cloning whenever the
1398 source and destination are on the same filesystem (note this
1400 source and destination are on the same filesystem (note this
1399 applies only to the repository data, not to the working
1401 applies only to the repository data, not to the working
1400 directory). Some filesystems, such as AFS, implement hardlinking
1402 directory). Some filesystems, such as AFS, implement hardlinking
1401 incorrectly, but do not report errors. In these cases, use the
1403 incorrectly, but do not report errors. In these cases, use the
1402 --pull option to avoid hardlinking.
1404 --pull option to avoid hardlinking.
1403
1405
1404 In some cases, you can clone repositories and the working
1406 In some cases, you can clone repositories and the working
1405 directory using full hardlinks with ::
1407 directory using full hardlinks with ::
1406
1408
1407 $ cp -al REPO REPOCLONE
1409 $ cp -al REPO REPOCLONE
1408
1410
1409 This is the fastest way to clone, but it is not always safe. The
1411 This is the fastest way to clone, but it is not always safe. The
1410 operation is not atomic (making sure REPO is not modified during
1412 operation is not atomic (making sure REPO is not modified during
1411 the operation is up to you) and you have to make sure your
1413 the operation is up to you) and you have to make sure your
1412 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1414 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1413 so). Also, this is not compatible with certain extensions that
1415 so). Also, this is not compatible with certain extensions that
1414 place their metadata under the .hg directory, such as mq.
1416 place their metadata under the .hg directory, such as mq.
1415
1417
1416 Mercurial will update the working directory to the first applicable
1418 Mercurial will update the working directory to the first applicable
1417 revision from this list:
1419 revision from this list:
1418
1420
1419 a) null if -U or the source repository has no changesets
1421 a) null if -U or the source repository has no changesets
1420 b) if -u . and the source repository is local, the first parent of
1422 b) if -u . and the source repository is local, the first parent of
1421 the source repository's working directory
1423 the source repository's working directory
1422 c) the changeset specified with -u (if a branch name, this means the
1424 c) the changeset specified with -u (if a branch name, this means the
1423 latest head of that branch)
1425 latest head of that branch)
1424 d) the changeset specified with -r
1426 d) the changeset specified with -r
1425 e) the tipmost head specified with -b
1427 e) the tipmost head specified with -b
1426 f) the tipmost head specified with the url#branch source syntax
1428 f) the tipmost head specified with the url#branch source syntax
1427 g) the revision marked with the '@' bookmark, if present
1429 g) the revision marked with the '@' bookmark, if present
1428 h) the tipmost head of the default branch
1430 h) the tipmost head of the default branch
1429 i) tip
1431 i) tip
1430
1432
1431 When cloning from servers that support it, Mercurial may fetch
1433 When cloning from servers that support it, Mercurial may fetch
1432 pre-generated data from a server-advertised URL. When this is done,
1434 pre-generated data from a server-advertised URL. When this is done,
1433 hooks operating on incoming changesets and changegroups may fire twice,
1435 hooks operating on incoming changesets and changegroups may fire twice,
1434 once for the bundle fetched from the URL and another for any additional
1436 once for the bundle fetched from the URL and another for any additional
1435 data not fetched from this URL. In addition, if an error occurs, the
1437 data not fetched from this URL. In addition, if an error occurs, the
1436 repository may be rolled back to a partial clone. This behavior may
1438 repository may be rolled back to a partial clone. This behavior may
1437 change in future releases. See :hg:`help -e clonebundles` for more.
1439 change in future releases. See :hg:`help -e clonebundles` for more.
1438
1440
1439 Examples:
1441 Examples:
1440
1442
1441 - clone a remote repository to a new directory named hg/::
1443 - clone a remote repository to a new directory named hg/::
1442
1444
1443 hg clone https://www.mercurial-scm.org/repo/hg/
1445 hg clone https://www.mercurial-scm.org/repo/hg/
1444
1446
1445 - create a lightweight local clone::
1447 - create a lightweight local clone::
1446
1448
1447 hg clone project/ project-feature/
1449 hg clone project/ project-feature/
1448
1450
1449 - clone from an absolute path on an ssh server (note double-slash)::
1451 - clone from an absolute path on an ssh server (note double-slash)::
1450
1452
1451 hg clone ssh://user@server//home/projects/alpha/
1453 hg clone ssh://user@server//home/projects/alpha/
1452
1454
1453 - do a high-speed clone over a LAN while checking out a
1455 - do a high-speed clone over a LAN while checking out a
1454 specified version::
1456 specified version::
1455
1457
1456 hg clone --uncompressed http://server/repo -u 1.5
1458 hg clone --uncompressed http://server/repo -u 1.5
1457
1459
1458 - create a repository without changesets after a particular revision::
1460 - create a repository without changesets after a particular revision::
1459
1461
1460 hg clone -r 04e544 experimental/ good/
1462 hg clone -r 04e544 experimental/ good/
1461
1463
1462 - clone (and track) a particular named branch::
1464 - clone (and track) a particular named branch::
1463
1465
1464 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1466 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1465
1467
1466 See :hg:`help urls` for details on specifying URLs.
1468 See :hg:`help urls` for details on specifying URLs.
1467
1469
1468 Returns 0 on success.
1470 Returns 0 on success.
1469 """
1471 """
1470 opts = pycompat.byteskwargs(opts)
1472 opts = pycompat.byteskwargs(opts)
1471 if opts.get('noupdate') and opts.get('updaterev'):
1473 if opts.get('noupdate') and opts.get('updaterev'):
1472 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1474 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1473
1475
1474 r = hg.clone(ui, opts, source, dest,
1476 r = hg.clone(ui, opts, source, dest,
1475 pull=opts.get('pull'),
1477 pull=opts.get('pull'),
1476 stream=opts.get('uncompressed'),
1478 stream=opts.get('uncompressed'),
1477 rev=opts.get('rev'),
1479 rev=opts.get('rev'),
1478 update=opts.get('updaterev') or not opts.get('noupdate'),
1480 update=opts.get('updaterev') or not opts.get('noupdate'),
1479 branch=opts.get('branch'),
1481 branch=opts.get('branch'),
1480 shareopts=opts.get('shareopts'))
1482 shareopts=opts.get('shareopts'))
1481
1483
1482 return r is None
1484 return r is None
1483
1485
1484 @command('^commit|ci',
1486 @command('^commit|ci',
1485 [('A', 'addremove', None,
1487 [('A', 'addremove', None,
1486 _('mark new/missing files as added/removed before committing')),
1488 _('mark new/missing files as added/removed before committing')),
1487 ('', 'close-branch', None,
1489 ('', 'close-branch', None,
1488 _('mark a branch head as closed')),
1490 _('mark a branch head as closed')),
1489 ('', 'amend', None, _('amend the parent of the working directory')),
1491 ('', 'amend', None, _('amend the parent of the working directory')),
1490 ('s', 'secret', None, _('use the secret phase for committing')),
1492 ('s', 'secret', None, _('use the secret phase for committing')),
1491 ('e', 'edit', None, _('invoke editor on commit messages')),
1493 ('e', 'edit', None, _('invoke editor on commit messages')),
1492 ('i', 'interactive', None, _('use interactive mode')),
1494 ('i', 'interactive', None, _('use interactive mode')),
1493 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1495 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1494 _('[OPTION]... [FILE]...'),
1496 _('[OPTION]... [FILE]...'),
1495 inferrepo=True)
1497 inferrepo=True)
1496 def commit(ui, repo, *pats, **opts):
1498 def commit(ui, repo, *pats, **opts):
1497 """commit the specified files or all outstanding changes
1499 """commit the specified files or all outstanding changes
1498
1500
1499 Commit changes to the given files into the repository. Unlike a
1501 Commit changes to the given files into the repository. Unlike a
1500 centralized SCM, this operation is a local operation. See
1502 centralized SCM, this operation is a local operation. See
1501 :hg:`push` for a way to actively distribute your changes.
1503 :hg:`push` for a way to actively distribute your changes.
1502
1504
1503 If a list of files is omitted, all changes reported by :hg:`status`
1505 If a list of files is omitted, all changes reported by :hg:`status`
1504 will be committed.
1506 will be committed.
1505
1507
1506 If you are committing the result of a merge, do not provide any
1508 If you are committing the result of a merge, do not provide any
1507 filenames or -I/-X filters.
1509 filenames or -I/-X filters.
1508
1510
1509 If no commit message is specified, Mercurial starts your
1511 If no commit message is specified, Mercurial starts your
1510 configured editor where you can enter a message. In case your
1512 configured editor where you can enter a message. In case your
1511 commit fails, you will find a backup of your message in
1513 commit fails, you will find a backup of your message in
1512 ``.hg/last-message.txt``.
1514 ``.hg/last-message.txt``.
1513
1515
1514 The --close-branch flag can be used to mark the current branch
1516 The --close-branch flag can be used to mark the current branch
1515 head closed. When all heads of a branch are closed, the branch
1517 head closed. When all heads of a branch are closed, the branch
1516 will be considered closed and no longer listed.
1518 will be considered closed and no longer listed.
1517
1519
1518 The --amend flag can be used to amend the parent of the
1520 The --amend flag can be used to amend the parent of the
1519 working directory with a new commit that contains the changes
1521 working directory with a new commit that contains the changes
1520 in the parent in addition to those currently reported by :hg:`status`,
1522 in the parent in addition to those currently reported by :hg:`status`,
1521 if there are any. The old commit is stored in a backup bundle in
1523 if there are any. The old commit is stored in a backup bundle in
1522 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1524 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1523 on how to restore it).
1525 on how to restore it).
1524
1526
1525 Message, user and date are taken from the amended commit unless
1527 Message, user and date are taken from the amended commit unless
1526 specified. When a message isn't specified on the command line,
1528 specified. When a message isn't specified on the command line,
1527 the editor will open with the message of the amended commit.
1529 the editor will open with the message of the amended commit.
1528
1530
1529 It is not possible to amend public changesets (see :hg:`help phases`)
1531 It is not possible to amend public changesets (see :hg:`help phases`)
1530 or changesets that have children.
1532 or changesets that have children.
1531
1533
1532 See :hg:`help dates` for a list of formats valid for -d/--date.
1534 See :hg:`help dates` for a list of formats valid for -d/--date.
1533
1535
1534 Returns 0 on success, 1 if nothing changed.
1536 Returns 0 on success, 1 if nothing changed.
1535
1537
1536 .. container:: verbose
1538 .. container:: verbose
1537
1539
1538 Examples:
1540 Examples:
1539
1541
1540 - commit all files ending in .py::
1542 - commit all files ending in .py::
1541
1543
1542 hg commit --include "set:**.py"
1544 hg commit --include "set:**.py"
1543
1545
1544 - commit all non-binary files::
1546 - commit all non-binary files::
1545
1547
1546 hg commit --exclude "set:binary()"
1548 hg commit --exclude "set:binary()"
1547
1549
1548 - amend the current commit and set the date to now::
1550 - amend the current commit and set the date to now::
1549
1551
1550 hg commit --amend --date now
1552 hg commit --amend --date now
1551 """
1553 """
1552 wlock = lock = None
1554 wlock = lock = None
1553 try:
1555 try:
1554 wlock = repo.wlock()
1556 wlock = repo.wlock()
1555 lock = repo.lock()
1557 lock = repo.lock()
1556 return _docommit(ui, repo, *pats, **opts)
1558 return _docommit(ui, repo, *pats, **opts)
1557 finally:
1559 finally:
1558 release(lock, wlock)
1560 release(lock, wlock)
1559
1561
1560 def _docommit(ui, repo, *pats, **opts):
1562 def _docommit(ui, repo, *pats, **opts):
1561 if opts.get(r'interactive'):
1563 if opts.get(r'interactive'):
1562 opts.pop(r'interactive')
1564 opts.pop(r'interactive')
1563 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1565 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1564 cmdutil.recordfilter, *pats,
1566 cmdutil.recordfilter, *pats,
1565 **opts)
1567 **opts)
1566 # ret can be 0 (no changes to record) or the value returned by
1568 # ret can be 0 (no changes to record) or the value returned by
1567 # commit(), 1 if nothing changed or None on success.
1569 # commit(), 1 if nothing changed or None on success.
1568 return 1 if ret == 0 else ret
1570 return 1 if ret == 0 else ret
1569
1571
1570 opts = pycompat.byteskwargs(opts)
1572 opts = pycompat.byteskwargs(opts)
1571 if opts.get('subrepos'):
1573 if opts.get('subrepos'):
1572 if opts.get('amend'):
1574 if opts.get('amend'):
1573 raise error.Abort(_('cannot amend with --subrepos'))
1575 raise error.Abort(_('cannot amend with --subrepos'))
1574 # Let --subrepos on the command line override config setting.
1576 # Let --subrepos on the command line override config setting.
1575 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1577 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1576
1578
1577 cmdutil.checkunfinished(repo, commit=True)
1579 cmdutil.checkunfinished(repo, commit=True)
1578
1580
1579 branch = repo[None].branch()
1581 branch = repo[None].branch()
1580 bheads = repo.branchheads(branch)
1582 bheads = repo.branchheads(branch)
1581
1583
1582 extra = {}
1584 extra = {}
1583 if opts.get('close_branch'):
1585 if opts.get('close_branch'):
1584 extra['close'] = 1
1586 extra['close'] = 1
1585
1587
1586 if not bheads:
1588 if not bheads:
1587 raise error.Abort(_('can only close branch heads'))
1589 raise error.Abort(_('can only close branch heads'))
1588 elif opts.get('amend'):
1590 elif opts.get('amend'):
1589 if repo[None].parents()[0].p1().branch() != branch and \
1591 if repo[None].parents()[0].p1().branch() != branch and \
1590 repo[None].parents()[0].p2().branch() != branch:
1592 repo[None].parents()[0].p2().branch() != branch:
1591 raise error.Abort(_('can only close branch heads'))
1593 raise error.Abort(_('can only close branch heads'))
1592
1594
1593 if opts.get('amend'):
1595 if opts.get('amend'):
1594 if ui.configbool('ui', 'commitsubrepos'):
1596 if ui.configbool('ui', 'commitsubrepos'):
1595 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1597 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1596
1598
1597 old = repo['.']
1599 old = repo['.']
1598 if not old.mutable():
1600 if not old.mutable():
1599 raise error.Abort(_('cannot amend public changesets'))
1601 raise error.Abort(_('cannot amend public changesets'))
1600 if len(repo[None].parents()) > 1:
1602 if len(repo[None].parents()) > 1:
1601 raise error.Abort(_('cannot amend while merging'))
1603 raise error.Abort(_('cannot amend while merging'))
1602 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1604 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1603 if not allowunstable and old.children():
1605 if not allowunstable and old.children():
1604 raise error.Abort(_('cannot amend changeset with children'))
1606 raise error.Abort(_('cannot amend changeset with children'))
1605
1607
1606 # Currently histedit gets confused if an amend happens while histedit
1608 # Currently histedit gets confused if an amend happens while histedit
1607 # is in progress. Since we have a checkunfinished command, we are
1609 # is in progress. Since we have a checkunfinished command, we are
1608 # temporarily honoring it.
1610 # temporarily honoring it.
1609 #
1611 #
1610 # Note: eventually this guard will be removed. Please do not expect
1612 # Note: eventually this guard will be removed. Please do not expect
1611 # this behavior to remain.
1613 # this behavior to remain.
1612 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1614 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1613 cmdutil.checkunfinished(repo)
1615 cmdutil.checkunfinished(repo)
1614
1616
1615 # commitfunc is used only for temporary amend commit by cmdutil.amend
1617 # commitfunc is used only for temporary amend commit by cmdutil.amend
1616 def commitfunc(ui, repo, message, match, opts):
1618 def commitfunc(ui, repo, message, match, opts):
1617 return repo.commit(message,
1619 return repo.commit(message,
1618 opts.get('user') or old.user(),
1620 opts.get('user') or old.user(),
1619 opts.get('date') or old.date(),
1621 opts.get('date') or old.date(),
1620 match,
1622 match,
1621 extra=extra)
1623 extra=extra)
1622
1624
1623 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1625 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1624 if node == old.node():
1626 if node == old.node():
1625 ui.status(_("nothing changed\n"))
1627 ui.status(_("nothing changed\n"))
1626 return 1
1628 return 1
1627 else:
1629 else:
1628 def commitfunc(ui, repo, message, match, opts):
1630 def commitfunc(ui, repo, message, match, opts):
1629 overrides = {}
1631 overrides = {}
1630 if opts.get('secret'):
1632 if opts.get('secret'):
1631 overrides[('phases', 'new-commit')] = 'secret'
1633 overrides[('phases', 'new-commit')] = 'secret'
1632
1634
1633 baseui = repo.baseui
1635 baseui = repo.baseui
1634 with baseui.configoverride(overrides, 'commit'):
1636 with baseui.configoverride(overrides, 'commit'):
1635 with ui.configoverride(overrides, 'commit'):
1637 with ui.configoverride(overrides, 'commit'):
1636 editform = cmdutil.mergeeditform(repo[None],
1638 editform = cmdutil.mergeeditform(repo[None],
1637 'commit.normal')
1639 'commit.normal')
1638 editor = cmdutil.getcommiteditor(
1640 editor = cmdutil.getcommiteditor(
1639 editform=editform, **pycompat.strkwargs(opts))
1641 editform=editform, **pycompat.strkwargs(opts))
1640 return repo.commit(message,
1642 return repo.commit(message,
1641 opts.get('user'),
1643 opts.get('user'),
1642 opts.get('date'),
1644 opts.get('date'),
1643 match,
1645 match,
1644 editor=editor,
1646 editor=editor,
1645 extra=extra)
1647 extra=extra)
1646
1648
1647 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1649 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1648
1650
1649 if not node:
1651 if not node:
1650 stat = cmdutil.postcommitstatus(repo, pats, opts)
1652 stat = cmdutil.postcommitstatus(repo, pats, opts)
1651 if stat[3]:
1653 if stat[3]:
1652 ui.status(_("nothing changed (%d missing files, see "
1654 ui.status(_("nothing changed (%d missing files, see "
1653 "'hg status')\n") % len(stat[3]))
1655 "'hg status')\n") % len(stat[3]))
1654 else:
1656 else:
1655 ui.status(_("nothing changed\n"))
1657 ui.status(_("nothing changed\n"))
1656 return 1
1658 return 1
1657
1659
1658 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1660 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1659
1661
1660 @command('config|showconfig|debugconfig',
1662 @command('config|showconfig|debugconfig',
1661 [('u', 'untrusted', None, _('show untrusted configuration options')),
1663 [('u', 'untrusted', None, _('show untrusted configuration options')),
1662 ('e', 'edit', None, _('edit user config')),
1664 ('e', 'edit', None, _('edit user config')),
1663 ('l', 'local', None, _('edit repository config')),
1665 ('l', 'local', None, _('edit repository config')),
1664 ('g', 'global', None, _('edit global config'))] + formatteropts,
1666 ('g', 'global', None, _('edit global config'))] + formatteropts,
1665 _('[-u] [NAME]...'),
1667 _('[-u] [NAME]...'),
1666 optionalrepo=True)
1668 optionalrepo=True)
1667 def config(ui, repo, *values, **opts):
1669 def config(ui, repo, *values, **opts):
1668 """show combined config settings from all hgrc files
1670 """show combined config settings from all hgrc files
1669
1671
1670 With no arguments, print names and values of all config items.
1672 With no arguments, print names and values of all config items.
1671
1673
1672 With one argument of the form section.name, print just the value
1674 With one argument of the form section.name, print just the value
1673 of that config item.
1675 of that config item.
1674
1676
1675 With multiple arguments, print names and values of all config
1677 With multiple arguments, print names and values of all config
1676 items with matching section names.
1678 items with matching section names.
1677
1679
1678 With --edit, start an editor on the user-level config file. With
1680 With --edit, start an editor on the user-level config file. With
1679 --global, edit the system-wide config file. With --local, edit the
1681 --global, edit the system-wide config file. With --local, edit the
1680 repository-level config file.
1682 repository-level config file.
1681
1683
1682 With --debug, the source (filename and line number) is printed
1684 With --debug, the source (filename and line number) is printed
1683 for each config item.
1685 for each config item.
1684
1686
1685 See :hg:`help config` for more information about config files.
1687 See :hg:`help config` for more information about config files.
1686
1688
1687 Returns 0 on success, 1 if NAME does not exist.
1689 Returns 0 on success, 1 if NAME does not exist.
1688
1690
1689 """
1691 """
1690
1692
1691 opts = pycompat.byteskwargs(opts)
1693 opts = pycompat.byteskwargs(opts)
1692 if opts.get('edit') or opts.get('local') or opts.get('global'):
1694 if opts.get('edit') or opts.get('local') or opts.get('global'):
1693 if opts.get('local') and opts.get('global'):
1695 if opts.get('local') and opts.get('global'):
1694 raise error.Abort(_("can't use --local and --global together"))
1696 raise error.Abort(_("can't use --local and --global together"))
1695
1697
1696 if opts.get('local'):
1698 if opts.get('local'):
1697 if not repo:
1699 if not repo:
1698 raise error.Abort(_("can't use --local outside a repository"))
1700 raise error.Abort(_("can't use --local outside a repository"))
1699 paths = [repo.vfs.join('hgrc')]
1701 paths = [repo.vfs.join('hgrc')]
1700 elif opts.get('global'):
1702 elif opts.get('global'):
1701 paths = rcutil.systemrcpath()
1703 paths = rcutil.systemrcpath()
1702 else:
1704 else:
1703 paths = rcutil.userrcpath()
1705 paths = rcutil.userrcpath()
1704
1706
1705 for f in paths:
1707 for f in paths:
1706 if os.path.exists(f):
1708 if os.path.exists(f):
1707 break
1709 break
1708 else:
1710 else:
1709 if opts.get('global'):
1711 if opts.get('global'):
1710 samplehgrc = uimod.samplehgrcs['global']
1712 samplehgrc = uimod.samplehgrcs['global']
1711 elif opts.get('local'):
1713 elif opts.get('local'):
1712 samplehgrc = uimod.samplehgrcs['local']
1714 samplehgrc = uimod.samplehgrcs['local']
1713 else:
1715 else:
1714 samplehgrc = uimod.samplehgrcs['user']
1716 samplehgrc = uimod.samplehgrcs['user']
1715
1717
1716 f = paths[0]
1718 f = paths[0]
1717 fp = open(f, "w")
1719 fp = open(f, "w")
1718 fp.write(samplehgrc)
1720 fp.write(samplehgrc)
1719 fp.close()
1721 fp.close()
1720
1722
1721 editor = ui.geteditor()
1723 editor = ui.geteditor()
1722 ui.system("%s \"%s\"" % (editor, f),
1724 ui.system("%s \"%s\"" % (editor, f),
1723 onerr=error.Abort, errprefix=_("edit failed"),
1725 onerr=error.Abort, errprefix=_("edit failed"),
1724 blockedtag='config_edit')
1726 blockedtag='config_edit')
1725 return
1727 return
1726 ui.pager('config')
1728 ui.pager('config')
1727 fm = ui.formatter('config', opts)
1729 fm = ui.formatter('config', opts)
1728 for t, f in rcutil.rccomponents():
1730 for t, f in rcutil.rccomponents():
1729 if t == 'path':
1731 if t == 'path':
1730 ui.debug('read config from: %s\n' % f)
1732 ui.debug('read config from: %s\n' % f)
1731 elif t == 'items':
1733 elif t == 'items':
1732 for section, name, value, source in f:
1734 for section, name, value, source in f:
1733 ui.debug('set config by: %s\n' % source)
1735 ui.debug('set config by: %s\n' % source)
1734 else:
1736 else:
1735 raise error.ProgrammingError('unknown rctype: %s' % t)
1737 raise error.ProgrammingError('unknown rctype: %s' % t)
1736 untrusted = bool(opts.get('untrusted'))
1738 untrusted = bool(opts.get('untrusted'))
1737 if values:
1739 if values:
1738 sections = [v for v in values if '.' not in v]
1740 sections = [v for v in values if '.' not in v]
1739 items = [v for v in values if '.' in v]
1741 items = [v for v in values if '.' in v]
1740 if len(items) > 1 or items and sections:
1742 if len(items) > 1 or items and sections:
1741 raise error.Abort(_('only one config item permitted'))
1743 raise error.Abort(_('only one config item permitted'))
1742 matched = False
1744 matched = False
1743 for section, name, value in ui.walkconfig(untrusted=untrusted):
1745 for section, name, value in ui.walkconfig(untrusted=untrusted):
1744 source = ui.configsource(section, name, untrusted)
1746 source = ui.configsource(section, name, untrusted)
1745 value = pycompat.bytestr(value)
1747 value = pycompat.bytestr(value)
1746 if fm.isplain():
1748 if fm.isplain():
1747 source = source or 'none'
1749 source = source or 'none'
1748 value = value.replace('\n', '\\n')
1750 value = value.replace('\n', '\\n')
1749 entryname = section + '.' + name
1751 entryname = section + '.' + name
1750 if values:
1752 if values:
1751 for v in values:
1753 for v in values:
1752 if v == section:
1754 if v == section:
1753 fm.startitem()
1755 fm.startitem()
1754 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1756 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1755 fm.write('name value', '%s=%s\n', entryname, value)
1757 fm.write('name value', '%s=%s\n', entryname, value)
1756 matched = True
1758 matched = True
1757 elif v == entryname:
1759 elif v == entryname:
1758 fm.startitem()
1760 fm.startitem()
1759 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1761 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1760 fm.write('value', '%s\n', value)
1762 fm.write('value', '%s\n', value)
1761 fm.data(name=entryname)
1763 fm.data(name=entryname)
1762 matched = True
1764 matched = True
1763 else:
1765 else:
1764 fm.startitem()
1766 fm.startitem()
1765 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1767 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1766 fm.write('name value', '%s=%s\n', entryname, value)
1768 fm.write('name value', '%s=%s\n', entryname, value)
1767 matched = True
1769 matched = True
1768 fm.end()
1770 fm.end()
1769 if matched:
1771 if matched:
1770 return 0
1772 return 0
1771 return 1
1773 return 1
1772
1774
1773 @command('copy|cp',
1775 @command('copy|cp',
1774 [('A', 'after', None, _('record a copy that has already occurred')),
1776 [('A', 'after', None, _('record a copy that has already occurred')),
1775 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1777 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1776 ] + walkopts + dryrunopts,
1778 ] + walkopts + dryrunopts,
1777 _('[OPTION]... [SOURCE]... DEST'))
1779 _('[OPTION]... [SOURCE]... DEST'))
1778 def copy(ui, repo, *pats, **opts):
1780 def copy(ui, repo, *pats, **opts):
1779 """mark files as copied for the next commit
1781 """mark files as copied for the next commit
1780
1782
1781 Mark dest as having copies of source files. If dest is a
1783 Mark dest as having copies of source files. If dest is a
1782 directory, copies are put in that directory. If dest is a file,
1784 directory, copies are put in that directory. If dest is a file,
1783 the source must be a single file.
1785 the source must be a single file.
1784
1786
1785 By default, this command copies the contents of files as they
1787 By default, this command copies the contents of files as they
1786 exist in the working directory. If invoked with -A/--after, the
1788 exist in the working directory. If invoked with -A/--after, the
1787 operation is recorded, but no copying is performed.
1789 operation is recorded, but no copying is performed.
1788
1790
1789 This command takes effect with the next commit. To undo a copy
1791 This command takes effect with the next commit. To undo a copy
1790 before that, see :hg:`revert`.
1792 before that, see :hg:`revert`.
1791
1793
1792 Returns 0 on success, 1 if errors are encountered.
1794 Returns 0 on success, 1 if errors are encountered.
1793 """
1795 """
1794 opts = pycompat.byteskwargs(opts)
1796 opts = pycompat.byteskwargs(opts)
1795 with repo.wlock(False):
1797 with repo.wlock(False):
1796 return cmdutil.copy(ui, repo, pats, opts)
1798 return cmdutil.copy(ui, repo, pats, opts)
1797
1799
1798 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1800 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1799 def debugcommands(ui, cmd='', *args):
1801 def debugcommands(ui, cmd='', *args):
1800 """list all available commands and options"""
1802 """list all available commands and options"""
1801 for cmd, vals in sorted(table.iteritems()):
1803 for cmd, vals in sorted(table.iteritems()):
1802 cmd = cmd.split('|')[0].strip('^')
1804 cmd = cmd.split('|')[0].strip('^')
1803 opts = ', '.join([i[1] for i in vals[1]])
1805 opts = ', '.join([i[1] for i in vals[1]])
1804 ui.write('%s: %s\n' % (cmd, opts))
1806 ui.write('%s: %s\n' % (cmd, opts))
1805
1807
1806 @command('debugcomplete',
1808 @command('debugcomplete',
1807 [('o', 'options', None, _('show the command options'))],
1809 [('o', 'options', None, _('show the command options'))],
1808 _('[-o] CMD'),
1810 _('[-o] CMD'),
1809 norepo=True)
1811 norepo=True)
1810 def debugcomplete(ui, cmd='', **opts):
1812 def debugcomplete(ui, cmd='', **opts):
1811 """returns the completion list associated with the given command"""
1813 """returns the completion list associated with the given command"""
1812
1814
1813 if opts.get('options'):
1815 if opts.get('options'):
1814 options = []
1816 options = []
1815 otables = [globalopts]
1817 otables = [globalopts]
1816 if cmd:
1818 if cmd:
1817 aliases, entry = cmdutil.findcmd(cmd, table, False)
1819 aliases, entry = cmdutil.findcmd(cmd, table, False)
1818 otables.append(entry[1])
1820 otables.append(entry[1])
1819 for t in otables:
1821 for t in otables:
1820 for o in t:
1822 for o in t:
1821 if "(DEPRECATED)" in o[3]:
1823 if "(DEPRECATED)" in o[3]:
1822 continue
1824 continue
1823 if o[0]:
1825 if o[0]:
1824 options.append('-%s' % o[0])
1826 options.append('-%s' % o[0])
1825 options.append('--%s' % o[1])
1827 options.append('--%s' % o[1])
1826 ui.write("%s\n" % "\n".join(options))
1828 ui.write("%s\n" % "\n".join(options))
1827 return
1829 return
1828
1830
1829 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1831 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1830 if ui.verbose:
1832 if ui.verbose:
1831 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1833 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1832 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1834 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1833
1835
1834 @command('^diff',
1836 @command('^diff',
1835 [('r', 'rev', [], _('revision'), _('REV')),
1837 [('r', 'rev', [], _('revision'), _('REV')),
1836 ('c', 'change', '', _('change made by revision'), _('REV'))
1838 ('c', 'change', '', _('change made by revision'), _('REV'))
1837 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1839 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1838 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1840 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1839 inferrepo=True)
1841 inferrepo=True)
1840 def diff(ui, repo, *pats, **opts):
1842 def diff(ui, repo, *pats, **opts):
1841 """diff repository (or selected files)
1843 """diff repository (or selected files)
1842
1844
1843 Show differences between revisions for the specified files.
1845 Show differences between revisions for the specified files.
1844
1846
1845 Differences between files are shown using the unified diff format.
1847 Differences between files are shown using the unified diff format.
1846
1848
1847 .. note::
1849 .. note::
1848
1850
1849 :hg:`diff` may generate unexpected results for merges, as it will
1851 :hg:`diff` may generate unexpected results for merges, as it will
1850 default to comparing against the working directory's first
1852 default to comparing against the working directory's first
1851 parent changeset if no revisions are specified.
1853 parent changeset if no revisions are specified.
1852
1854
1853 When two revision arguments are given, then changes are shown
1855 When two revision arguments are given, then changes are shown
1854 between those revisions. If only one revision is specified then
1856 between those revisions. If only one revision is specified then
1855 that revision is compared to the working directory, and, when no
1857 that revision is compared to the working directory, and, when no
1856 revisions are specified, the working directory files are compared
1858 revisions are specified, the working directory files are compared
1857 to its first parent.
1859 to its first parent.
1858
1860
1859 Alternatively you can specify -c/--change with a revision to see
1861 Alternatively you can specify -c/--change with a revision to see
1860 the changes in that changeset relative to its first parent.
1862 the changes in that changeset relative to its first parent.
1861
1863
1862 Without the -a/--text option, diff will avoid generating diffs of
1864 Without the -a/--text option, diff will avoid generating diffs of
1863 files it detects as binary. With -a, diff will generate a diff
1865 files it detects as binary. With -a, diff will generate a diff
1864 anyway, probably with undesirable results.
1866 anyway, probably with undesirable results.
1865
1867
1866 Use the -g/--git option to generate diffs in the git extended diff
1868 Use the -g/--git option to generate diffs in the git extended diff
1867 format. For more information, read :hg:`help diffs`.
1869 format. For more information, read :hg:`help diffs`.
1868
1870
1869 .. container:: verbose
1871 .. container:: verbose
1870
1872
1871 Examples:
1873 Examples:
1872
1874
1873 - compare a file in the current working directory to its parent::
1875 - compare a file in the current working directory to its parent::
1874
1876
1875 hg diff foo.c
1877 hg diff foo.c
1876
1878
1877 - compare two historical versions of a directory, with rename info::
1879 - compare two historical versions of a directory, with rename info::
1878
1880
1879 hg diff --git -r 1.0:1.2 lib/
1881 hg diff --git -r 1.0:1.2 lib/
1880
1882
1881 - get change stats relative to the last change on some date::
1883 - get change stats relative to the last change on some date::
1882
1884
1883 hg diff --stat -r "date('may 2')"
1885 hg diff --stat -r "date('may 2')"
1884
1886
1885 - diff all newly-added files that contain a keyword::
1887 - diff all newly-added files that contain a keyword::
1886
1888
1887 hg diff "set:added() and grep(GNU)"
1889 hg diff "set:added() and grep(GNU)"
1888
1890
1889 - compare a revision and its parents::
1891 - compare a revision and its parents::
1890
1892
1891 hg diff -c 9353 # compare against first parent
1893 hg diff -c 9353 # compare against first parent
1892 hg diff -r 9353^:9353 # same using revset syntax
1894 hg diff -r 9353^:9353 # same using revset syntax
1893 hg diff -r 9353^2:9353 # compare against the second parent
1895 hg diff -r 9353^2:9353 # compare against the second parent
1894
1896
1895 Returns 0 on success.
1897 Returns 0 on success.
1896 """
1898 """
1897
1899
1898 opts = pycompat.byteskwargs(opts)
1900 opts = pycompat.byteskwargs(opts)
1899 revs = opts.get('rev')
1901 revs = opts.get('rev')
1900 change = opts.get('change')
1902 change = opts.get('change')
1901 stat = opts.get('stat')
1903 stat = opts.get('stat')
1902 reverse = opts.get('reverse')
1904 reverse = opts.get('reverse')
1903
1905
1904 if revs and change:
1906 if revs and change:
1905 msg = _('cannot specify --rev and --change at the same time')
1907 msg = _('cannot specify --rev and --change at the same time')
1906 raise error.Abort(msg)
1908 raise error.Abort(msg)
1907 elif change:
1909 elif change:
1908 node2 = scmutil.revsingle(repo, change, None).node()
1910 node2 = scmutil.revsingle(repo, change, None).node()
1909 node1 = repo[node2].p1().node()
1911 node1 = repo[node2].p1().node()
1910 else:
1912 else:
1911 node1, node2 = scmutil.revpair(repo, revs)
1913 node1, node2 = scmutil.revpair(repo, revs)
1912
1914
1913 if reverse:
1915 if reverse:
1914 node1, node2 = node2, node1
1916 node1, node2 = node2, node1
1915
1917
1916 diffopts = patch.diffallopts(ui, opts)
1918 diffopts = patch.diffallopts(ui, opts)
1917 m = scmutil.match(repo[node2], pats, opts)
1919 m = scmutil.match(repo[node2], pats, opts)
1918 ui.pager('diff')
1920 ui.pager('diff')
1919 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1921 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1920 listsubrepos=opts.get('subrepos'),
1922 listsubrepos=opts.get('subrepos'),
1921 root=opts.get('root'))
1923 root=opts.get('root'))
1922
1924
1923 @command('^export',
1925 @command('^export',
1924 [('o', 'output', '',
1926 [('o', 'output', '',
1925 _('print output to file with formatted name'), _('FORMAT')),
1927 _('print output to file with formatted name'), _('FORMAT')),
1926 ('', 'switch-parent', None, _('diff against the second parent')),
1928 ('', 'switch-parent', None, _('diff against the second parent')),
1927 ('r', 'rev', [], _('revisions to export'), _('REV')),
1929 ('r', 'rev', [], _('revisions to export'), _('REV')),
1928 ] + diffopts,
1930 ] + diffopts,
1929 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
1931 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
1930 def export(ui, repo, *changesets, **opts):
1932 def export(ui, repo, *changesets, **opts):
1931 """dump the header and diffs for one or more changesets
1933 """dump the header and diffs for one or more changesets
1932
1934
1933 Print the changeset header and diffs for one or more revisions.
1935 Print the changeset header and diffs for one or more revisions.
1934 If no revision is given, the parent of the working directory is used.
1936 If no revision is given, the parent of the working directory is used.
1935
1937
1936 The information shown in the changeset header is: author, date,
1938 The information shown in the changeset header is: author, date,
1937 branch name (if non-default), changeset hash, parent(s) and commit
1939 branch name (if non-default), changeset hash, parent(s) and commit
1938 comment.
1940 comment.
1939
1941
1940 .. note::
1942 .. note::
1941
1943
1942 :hg:`export` may generate unexpected diff output for merge
1944 :hg:`export` may generate unexpected diff output for merge
1943 changesets, as it will compare the merge changeset against its
1945 changesets, as it will compare the merge changeset against its
1944 first parent only.
1946 first parent only.
1945
1947
1946 Output may be to a file, in which case the name of the file is
1948 Output may be to a file, in which case the name of the file is
1947 given using a format string. The formatting rules are as follows:
1949 given using a format string. The formatting rules are as follows:
1948
1950
1949 :``%%``: literal "%" character
1951 :``%%``: literal "%" character
1950 :``%H``: changeset hash (40 hexadecimal digits)
1952 :``%H``: changeset hash (40 hexadecimal digits)
1951 :``%N``: number of patches being generated
1953 :``%N``: number of patches being generated
1952 :``%R``: changeset revision number
1954 :``%R``: changeset revision number
1953 :``%b``: basename of the exporting repository
1955 :``%b``: basename of the exporting repository
1954 :``%h``: short-form changeset hash (12 hexadecimal digits)
1956 :``%h``: short-form changeset hash (12 hexadecimal digits)
1955 :``%m``: first line of the commit message (only alphanumeric characters)
1957 :``%m``: first line of the commit message (only alphanumeric characters)
1956 :``%n``: zero-padded sequence number, starting at 1
1958 :``%n``: zero-padded sequence number, starting at 1
1957 :``%r``: zero-padded changeset revision number
1959 :``%r``: zero-padded changeset revision number
1958
1960
1959 Without the -a/--text option, export will avoid generating diffs
1961 Without the -a/--text option, export will avoid generating diffs
1960 of files it detects as binary. With -a, export will generate a
1962 of files it detects as binary. With -a, export will generate a
1961 diff anyway, probably with undesirable results.
1963 diff anyway, probably with undesirable results.
1962
1964
1963 Use the -g/--git option to generate diffs in the git extended diff
1965 Use the -g/--git option to generate diffs in the git extended diff
1964 format. See :hg:`help diffs` for more information.
1966 format. See :hg:`help diffs` for more information.
1965
1967
1966 With the --switch-parent option, the diff will be against the
1968 With the --switch-parent option, the diff will be against the
1967 second parent. It can be useful to review a merge.
1969 second parent. It can be useful to review a merge.
1968
1970
1969 .. container:: verbose
1971 .. container:: verbose
1970
1972
1971 Examples:
1973 Examples:
1972
1974
1973 - use export and import to transplant a bugfix to the current
1975 - use export and import to transplant a bugfix to the current
1974 branch::
1976 branch::
1975
1977
1976 hg export -r 9353 | hg import -
1978 hg export -r 9353 | hg import -
1977
1979
1978 - export all the changesets between two revisions to a file with
1980 - export all the changesets between two revisions to a file with
1979 rename information::
1981 rename information::
1980
1982
1981 hg export --git -r 123:150 > changes.txt
1983 hg export --git -r 123:150 > changes.txt
1982
1984
1983 - split outgoing changes into a series of patches with
1985 - split outgoing changes into a series of patches with
1984 descriptive names::
1986 descriptive names::
1985
1987
1986 hg export -r "outgoing()" -o "%n-%m.patch"
1988 hg export -r "outgoing()" -o "%n-%m.patch"
1987
1989
1988 Returns 0 on success.
1990 Returns 0 on success.
1989 """
1991 """
1990 opts = pycompat.byteskwargs(opts)
1992 opts = pycompat.byteskwargs(opts)
1991 changesets += tuple(opts.get('rev', []))
1993 changesets += tuple(opts.get('rev', []))
1992 if not changesets:
1994 if not changesets:
1993 changesets = ['.']
1995 changesets = ['.']
1994 revs = scmutil.revrange(repo, changesets)
1996 revs = scmutil.revrange(repo, changesets)
1995 if not revs:
1997 if not revs:
1996 raise error.Abort(_("export requires at least one changeset"))
1998 raise error.Abort(_("export requires at least one changeset"))
1997 if len(revs) > 1:
1999 if len(revs) > 1:
1998 ui.note(_('exporting patches:\n'))
2000 ui.note(_('exporting patches:\n'))
1999 else:
2001 else:
2000 ui.note(_('exporting patch:\n'))
2002 ui.note(_('exporting patch:\n'))
2001 ui.pager('export')
2003 ui.pager('export')
2002 cmdutil.export(repo, revs, template=opts.get('output'),
2004 cmdutil.export(repo, revs, template=opts.get('output'),
2003 switch_parent=opts.get('switch_parent'),
2005 switch_parent=opts.get('switch_parent'),
2004 opts=patch.diffallopts(ui, opts))
2006 opts=patch.diffallopts(ui, opts))
2005
2007
2006 @command('files',
2008 @command('files',
2007 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
2009 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
2008 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2010 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2009 ] + walkopts + formatteropts + subrepoopts,
2011 ] + walkopts + formatteropts + subrepoopts,
2010 _('[OPTION]... [FILE]...'))
2012 _('[OPTION]... [FILE]...'))
2011 def files(ui, repo, *pats, **opts):
2013 def files(ui, repo, *pats, **opts):
2012 """list tracked files
2014 """list tracked files
2013
2015
2014 Print files under Mercurial control in the working directory or
2016 Print files under Mercurial control in the working directory or
2015 specified revision for given files (excluding removed files).
2017 specified revision for given files (excluding removed files).
2016 Files can be specified as filenames or filesets.
2018 Files can be specified as filenames or filesets.
2017
2019
2018 If no files are given to match, this command prints the names
2020 If no files are given to match, this command prints the names
2019 of all files under Mercurial control.
2021 of all files under Mercurial control.
2020
2022
2021 .. container:: verbose
2023 .. container:: verbose
2022
2024
2023 Examples:
2025 Examples:
2024
2026
2025 - list all files under the current directory::
2027 - list all files under the current directory::
2026
2028
2027 hg files .
2029 hg files .
2028
2030
2029 - shows sizes and flags for current revision::
2031 - shows sizes and flags for current revision::
2030
2032
2031 hg files -vr .
2033 hg files -vr .
2032
2034
2033 - list all files named README::
2035 - list all files named README::
2034
2036
2035 hg files -I "**/README"
2037 hg files -I "**/README"
2036
2038
2037 - list all binary files::
2039 - list all binary files::
2038
2040
2039 hg files "set:binary()"
2041 hg files "set:binary()"
2040
2042
2041 - find files containing a regular expression::
2043 - find files containing a regular expression::
2042
2044
2043 hg files "set:grep('bob')"
2045 hg files "set:grep('bob')"
2044
2046
2045 - search tracked file contents with xargs and grep::
2047 - search tracked file contents with xargs and grep::
2046
2048
2047 hg files -0 | xargs -0 grep foo
2049 hg files -0 | xargs -0 grep foo
2048
2050
2049 See :hg:`help patterns` and :hg:`help filesets` for more information
2051 See :hg:`help patterns` and :hg:`help filesets` for more information
2050 on specifying file patterns.
2052 on specifying file patterns.
2051
2053
2052 Returns 0 if a match is found, 1 otherwise.
2054 Returns 0 if a match is found, 1 otherwise.
2053
2055
2054 """
2056 """
2055
2057
2056 opts = pycompat.byteskwargs(opts)
2058 opts = pycompat.byteskwargs(opts)
2057 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2059 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2058
2060
2059 end = '\n'
2061 end = '\n'
2060 if opts.get('print0'):
2062 if opts.get('print0'):
2061 end = '\0'
2063 end = '\0'
2062 fmt = '%s' + end
2064 fmt = '%s' + end
2063
2065
2064 m = scmutil.match(ctx, pats, opts)
2066 m = scmutil.match(ctx, pats, opts)
2065 ui.pager('files')
2067 ui.pager('files')
2066 with ui.formatter('files', opts) as fm:
2068 with ui.formatter('files', opts) as fm:
2067 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2069 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2068
2070
2069 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
2071 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
2070 def forget(ui, repo, *pats, **opts):
2072 def forget(ui, repo, *pats, **opts):
2071 """forget the specified files on the next commit
2073 """forget the specified files on the next commit
2072
2074
2073 Mark the specified files so they will no longer be tracked
2075 Mark the specified files so they will no longer be tracked
2074 after the next commit.
2076 after the next commit.
2075
2077
2076 This only removes files from the current branch, not from the
2078 This only removes files from the current branch, not from the
2077 entire project history, and it does not delete them from the
2079 entire project history, and it does not delete them from the
2078 working directory.
2080 working directory.
2079
2081
2080 To delete the file from the working directory, see :hg:`remove`.
2082 To delete the file from the working directory, see :hg:`remove`.
2081
2083
2082 To undo a forget before the next commit, see :hg:`add`.
2084 To undo a forget before the next commit, see :hg:`add`.
2083
2085
2084 .. container:: verbose
2086 .. container:: verbose
2085
2087
2086 Examples:
2088 Examples:
2087
2089
2088 - forget newly-added binary files::
2090 - forget newly-added binary files::
2089
2091
2090 hg forget "set:added() and binary()"
2092 hg forget "set:added() and binary()"
2091
2093
2092 - forget files that would be excluded by .hgignore::
2094 - forget files that would be excluded by .hgignore::
2093
2095
2094 hg forget "set:hgignore()"
2096 hg forget "set:hgignore()"
2095
2097
2096 Returns 0 on success.
2098 Returns 0 on success.
2097 """
2099 """
2098
2100
2099 opts = pycompat.byteskwargs(opts)
2101 opts = pycompat.byteskwargs(opts)
2100 if not pats:
2102 if not pats:
2101 raise error.Abort(_('no files specified'))
2103 raise error.Abort(_('no files specified'))
2102
2104
2103 m = scmutil.match(repo[None], pats, opts)
2105 m = scmutil.match(repo[None], pats, opts)
2104 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2106 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2105 return rejected and 1 or 0
2107 return rejected and 1 or 0
2106
2108
2107 @command(
2109 @command(
2108 'graft',
2110 'graft',
2109 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2111 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2110 ('c', 'continue', False, _('resume interrupted graft')),
2112 ('c', 'continue', False, _('resume interrupted graft')),
2111 ('e', 'edit', False, _('invoke editor on commit messages')),
2113 ('e', 'edit', False, _('invoke editor on commit messages')),
2112 ('', 'log', None, _('append graft info to log message')),
2114 ('', 'log', None, _('append graft info to log message')),
2113 ('f', 'force', False, _('force graft')),
2115 ('f', 'force', False, _('force graft')),
2114 ('D', 'currentdate', False,
2116 ('D', 'currentdate', False,
2115 _('record the current date as commit date')),
2117 _('record the current date as commit date')),
2116 ('U', 'currentuser', False,
2118 ('U', 'currentuser', False,
2117 _('record the current user as committer'), _('DATE'))]
2119 _('record the current user as committer'), _('DATE'))]
2118 + commitopts2 + mergetoolopts + dryrunopts,
2120 + commitopts2 + mergetoolopts + dryrunopts,
2119 _('[OPTION]... [-r REV]... REV...'))
2121 _('[OPTION]... [-r REV]... REV...'))
2120 def graft(ui, repo, *revs, **opts):
2122 def graft(ui, repo, *revs, **opts):
2121 '''copy changes from other branches onto the current branch
2123 '''copy changes from other branches onto the current branch
2122
2124
2123 This command uses Mercurial's merge logic to copy individual
2125 This command uses Mercurial's merge logic to copy individual
2124 changes from other branches without merging branches in the
2126 changes from other branches without merging branches in the
2125 history graph. This is sometimes known as 'backporting' or
2127 history graph. This is sometimes known as 'backporting' or
2126 'cherry-picking'. By default, graft will copy user, date, and
2128 'cherry-picking'. By default, graft will copy user, date, and
2127 description from the source changesets.
2129 description from the source changesets.
2128
2130
2129 Changesets that are ancestors of the current revision, that have
2131 Changesets that are ancestors of the current revision, that have
2130 already been grafted, or that are merges will be skipped.
2132 already been grafted, or that are merges will be skipped.
2131
2133
2132 If --log is specified, log messages will have a comment appended
2134 If --log is specified, log messages will have a comment appended
2133 of the form::
2135 of the form::
2134
2136
2135 (grafted from CHANGESETHASH)
2137 (grafted from CHANGESETHASH)
2136
2138
2137 If --force is specified, revisions will be grafted even if they
2139 If --force is specified, revisions will be grafted even if they
2138 are already ancestors of or have been grafted to the destination.
2140 are already ancestors of or have been grafted to the destination.
2139 This is useful when the revisions have since been backed out.
2141 This is useful when the revisions have since been backed out.
2140
2142
2141 If a graft merge results in conflicts, the graft process is
2143 If a graft merge results in conflicts, the graft process is
2142 interrupted so that the current merge can be manually resolved.
2144 interrupted so that the current merge can be manually resolved.
2143 Once all conflicts are addressed, the graft process can be
2145 Once all conflicts are addressed, the graft process can be
2144 continued with the -c/--continue option.
2146 continued with the -c/--continue option.
2145
2147
2146 .. note::
2148 .. note::
2147
2149
2148 The -c/--continue option does not reapply earlier options, except
2150 The -c/--continue option does not reapply earlier options, except
2149 for --force.
2151 for --force.
2150
2152
2151 .. container:: verbose
2153 .. container:: verbose
2152
2154
2153 Examples:
2155 Examples:
2154
2156
2155 - copy a single change to the stable branch and edit its description::
2157 - copy a single change to the stable branch and edit its description::
2156
2158
2157 hg update stable
2159 hg update stable
2158 hg graft --edit 9393
2160 hg graft --edit 9393
2159
2161
2160 - graft a range of changesets with one exception, updating dates::
2162 - graft a range of changesets with one exception, updating dates::
2161
2163
2162 hg graft -D "2085::2093 and not 2091"
2164 hg graft -D "2085::2093 and not 2091"
2163
2165
2164 - continue a graft after resolving conflicts::
2166 - continue a graft after resolving conflicts::
2165
2167
2166 hg graft -c
2168 hg graft -c
2167
2169
2168 - show the source of a grafted changeset::
2170 - show the source of a grafted changeset::
2169
2171
2170 hg log --debug -r .
2172 hg log --debug -r .
2171
2173
2172 - show revisions sorted by date::
2174 - show revisions sorted by date::
2173
2175
2174 hg log -r "sort(all(), date)"
2176 hg log -r "sort(all(), date)"
2175
2177
2176 See :hg:`help revisions` for more about specifying revisions.
2178 See :hg:`help revisions` for more about specifying revisions.
2177
2179
2178 Returns 0 on successful completion.
2180 Returns 0 on successful completion.
2179 '''
2181 '''
2180 with repo.wlock():
2182 with repo.wlock():
2181 return _dograft(ui, repo, *revs, **opts)
2183 return _dograft(ui, repo, *revs, **opts)
2182
2184
2183 def _dograft(ui, repo, *revs, **opts):
2185 def _dograft(ui, repo, *revs, **opts):
2184 opts = pycompat.byteskwargs(opts)
2186 opts = pycompat.byteskwargs(opts)
2185 if revs and opts.get('rev'):
2187 if revs and opts.get('rev'):
2186 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2188 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2187 'revision ordering!\n'))
2189 'revision ordering!\n'))
2188
2190
2189 revs = list(revs)
2191 revs = list(revs)
2190 revs.extend(opts.get('rev'))
2192 revs.extend(opts.get('rev'))
2191
2193
2192 if not opts.get('user') and opts.get('currentuser'):
2194 if not opts.get('user') and opts.get('currentuser'):
2193 opts['user'] = ui.username()
2195 opts['user'] = ui.username()
2194 if not opts.get('date') and opts.get('currentdate'):
2196 if not opts.get('date') and opts.get('currentdate'):
2195 opts['date'] = "%d %d" % util.makedate()
2197 opts['date'] = "%d %d" % util.makedate()
2196
2198
2197 editor = cmdutil.getcommiteditor(editform='graft',
2199 editor = cmdutil.getcommiteditor(editform='graft',
2198 **pycompat.strkwargs(opts))
2200 **pycompat.strkwargs(opts))
2199
2201
2200 cont = False
2202 cont = False
2201 if opts.get('continue'):
2203 if opts.get('continue'):
2202 cont = True
2204 cont = True
2203 if revs:
2205 if revs:
2204 raise error.Abort(_("can't specify --continue and revisions"))
2206 raise error.Abort(_("can't specify --continue and revisions"))
2205 # read in unfinished revisions
2207 # read in unfinished revisions
2206 try:
2208 try:
2207 nodes = repo.vfs.read('graftstate').splitlines()
2209 nodes = repo.vfs.read('graftstate').splitlines()
2208 revs = [repo[node].rev() for node in nodes]
2210 revs = [repo[node].rev() for node in nodes]
2209 except IOError as inst:
2211 except IOError as inst:
2210 if inst.errno != errno.ENOENT:
2212 if inst.errno != errno.ENOENT:
2211 raise
2213 raise
2212 cmdutil.wrongtooltocontinue(repo, _('graft'))
2214 cmdutil.wrongtooltocontinue(repo, _('graft'))
2213 else:
2215 else:
2214 cmdutil.checkunfinished(repo)
2216 cmdutil.checkunfinished(repo)
2215 cmdutil.bailifchanged(repo)
2217 cmdutil.bailifchanged(repo)
2216 if not revs:
2218 if not revs:
2217 raise error.Abort(_('no revisions specified'))
2219 raise error.Abort(_('no revisions specified'))
2218 revs = scmutil.revrange(repo, revs)
2220 revs = scmutil.revrange(repo, revs)
2219
2221
2220 skipped = set()
2222 skipped = set()
2221 # check for merges
2223 # check for merges
2222 for rev in repo.revs('%ld and merge()', revs):
2224 for rev in repo.revs('%ld and merge()', revs):
2223 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2225 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2224 skipped.add(rev)
2226 skipped.add(rev)
2225 revs = [r for r in revs if r not in skipped]
2227 revs = [r for r in revs if r not in skipped]
2226 if not revs:
2228 if not revs:
2227 return -1
2229 return -1
2228
2230
2229 # Don't check in the --continue case, in effect retaining --force across
2231 # Don't check in the --continue case, in effect retaining --force across
2230 # --continues. That's because without --force, any revisions we decided to
2232 # --continues. That's because without --force, any revisions we decided to
2231 # skip would have been filtered out here, so they wouldn't have made their
2233 # skip would have been filtered out here, so they wouldn't have made their
2232 # way to the graftstate. With --force, any revisions we would have otherwise
2234 # way to the graftstate. With --force, any revisions we would have otherwise
2233 # skipped would not have been filtered out, and if they hadn't been applied
2235 # skipped would not have been filtered out, and if they hadn't been applied
2234 # already, they'd have been in the graftstate.
2236 # already, they'd have been in the graftstate.
2235 if not (cont or opts.get('force')):
2237 if not (cont or opts.get('force')):
2236 # check for ancestors of dest branch
2238 # check for ancestors of dest branch
2237 crev = repo['.'].rev()
2239 crev = repo['.'].rev()
2238 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2240 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2239 # XXX make this lazy in the future
2241 # XXX make this lazy in the future
2240 # don't mutate while iterating, create a copy
2242 # don't mutate while iterating, create a copy
2241 for rev in list(revs):
2243 for rev in list(revs):
2242 if rev in ancestors:
2244 if rev in ancestors:
2243 ui.warn(_('skipping ancestor revision %d:%s\n') %
2245 ui.warn(_('skipping ancestor revision %d:%s\n') %
2244 (rev, repo[rev]))
2246 (rev, repo[rev]))
2245 # XXX remove on list is slow
2247 # XXX remove on list is slow
2246 revs.remove(rev)
2248 revs.remove(rev)
2247 if not revs:
2249 if not revs:
2248 return -1
2250 return -1
2249
2251
2250 # analyze revs for earlier grafts
2252 # analyze revs for earlier grafts
2251 ids = {}
2253 ids = {}
2252 for ctx in repo.set("%ld", revs):
2254 for ctx in repo.set("%ld", revs):
2253 ids[ctx.hex()] = ctx.rev()
2255 ids[ctx.hex()] = ctx.rev()
2254 n = ctx.extra().get('source')
2256 n = ctx.extra().get('source')
2255 if n:
2257 if n:
2256 ids[n] = ctx.rev()
2258 ids[n] = ctx.rev()
2257
2259
2258 # check ancestors for earlier grafts
2260 # check ancestors for earlier grafts
2259 ui.debug('scanning for duplicate grafts\n')
2261 ui.debug('scanning for duplicate grafts\n')
2260
2262
2261 # The only changesets we can be sure doesn't contain grafts of any
2263 # The only changesets we can be sure doesn't contain grafts of any
2262 # revs, are the ones that are common ancestors of *all* revs:
2264 # revs, are the ones that are common ancestors of *all* revs:
2263 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2265 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2264 ctx = repo[rev]
2266 ctx = repo[rev]
2265 n = ctx.extra().get('source')
2267 n = ctx.extra().get('source')
2266 if n in ids:
2268 if n in ids:
2267 try:
2269 try:
2268 r = repo[n].rev()
2270 r = repo[n].rev()
2269 except error.RepoLookupError:
2271 except error.RepoLookupError:
2270 r = None
2272 r = None
2271 if r in revs:
2273 if r in revs:
2272 ui.warn(_('skipping revision %d:%s '
2274 ui.warn(_('skipping revision %d:%s '
2273 '(already grafted to %d:%s)\n')
2275 '(already grafted to %d:%s)\n')
2274 % (r, repo[r], rev, ctx))
2276 % (r, repo[r], rev, ctx))
2275 revs.remove(r)
2277 revs.remove(r)
2276 elif ids[n] in revs:
2278 elif ids[n] in revs:
2277 if r is None:
2279 if r is None:
2278 ui.warn(_('skipping already grafted revision %d:%s '
2280 ui.warn(_('skipping already grafted revision %d:%s '
2279 '(%d:%s also has unknown origin %s)\n')
2281 '(%d:%s also has unknown origin %s)\n')
2280 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2282 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2281 else:
2283 else:
2282 ui.warn(_('skipping already grafted revision %d:%s '
2284 ui.warn(_('skipping already grafted revision %d:%s '
2283 '(%d:%s also has origin %d:%s)\n')
2285 '(%d:%s also has origin %d:%s)\n')
2284 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2286 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2285 revs.remove(ids[n])
2287 revs.remove(ids[n])
2286 elif ctx.hex() in ids:
2288 elif ctx.hex() in ids:
2287 r = ids[ctx.hex()]
2289 r = ids[ctx.hex()]
2288 ui.warn(_('skipping already grafted revision %d:%s '
2290 ui.warn(_('skipping already grafted revision %d:%s '
2289 '(was grafted from %d:%s)\n') %
2291 '(was grafted from %d:%s)\n') %
2290 (r, repo[r], rev, ctx))
2292 (r, repo[r], rev, ctx))
2291 revs.remove(r)
2293 revs.remove(r)
2292 if not revs:
2294 if not revs:
2293 return -1
2295 return -1
2294
2296
2295 for pos, ctx in enumerate(repo.set("%ld", revs)):
2297 for pos, ctx in enumerate(repo.set("%ld", revs)):
2296 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2298 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2297 ctx.description().split('\n', 1)[0])
2299 ctx.description().split('\n', 1)[0])
2298 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2300 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2299 if names:
2301 if names:
2300 desc += ' (%s)' % ' '.join(names)
2302 desc += ' (%s)' % ' '.join(names)
2301 ui.status(_('grafting %s\n') % desc)
2303 ui.status(_('grafting %s\n') % desc)
2302 if opts.get('dry_run'):
2304 if opts.get('dry_run'):
2303 continue
2305 continue
2304
2306
2305 source = ctx.extra().get('source')
2307 source = ctx.extra().get('source')
2306 extra = {}
2308 extra = {}
2307 if source:
2309 if source:
2308 extra['source'] = source
2310 extra['source'] = source
2309 extra['intermediate-source'] = ctx.hex()
2311 extra['intermediate-source'] = ctx.hex()
2310 else:
2312 else:
2311 extra['source'] = ctx.hex()
2313 extra['source'] = ctx.hex()
2312 user = ctx.user()
2314 user = ctx.user()
2313 if opts.get('user'):
2315 if opts.get('user'):
2314 user = opts['user']
2316 user = opts['user']
2315 date = ctx.date()
2317 date = ctx.date()
2316 if opts.get('date'):
2318 if opts.get('date'):
2317 date = opts['date']
2319 date = opts['date']
2318 message = ctx.description()
2320 message = ctx.description()
2319 if opts.get('log'):
2321 if opts.get('log'):
2320 message += '\n(grafted from %s)' % ctx.hex()
2322 message += '\n(grafted from %s)' % ctx.hex()
2321
2323
2322 # we don't merge the first commit when continuing
2324 # we don't merge the first commit when continuing
2323 if not cont:
2325 if not cont:
2324 # perform the graft merge with p1(rev) as 'ancestor'
2326 # perform the graft merge with p1(rev) as 'ancestor'
2325 try:
2327 try:
2326 # ui.forcemerge is an internal variable, do not document
2328 # ui.forcemerge is an internal variable, do not document
2327 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
2329 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
2328 'graft')
2330 'graft')
2329 stats = mergemod.graft(repo, ctx, ctx.p1(),
2331 stats = mergemod.graft(repo, ctx, ctx.p1(),
2330 ['local', 'graft'])
2332 ['local', 'graft'])
2331 finally:
2333 finally:
2332 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
2334 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
2333 # report any conflicts
2335 # report any conflicts
2334 if stats and stats[3] > 0:
2336 if stats and stats[3] > 0:
2335 # write out state for --continue
2337 # write out state for --continue
2336 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2338 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2337 repo.vfs.write('graftstate', ''.join(nodelines))
2339 repo.vfs.write('graftstate', ''.join(nodelines))
2338 extra = ''
2340 extra = ''
2339 if opts.get('user'):
2341 if opts.get('user'):
2340 extra += ' --user %s' % util.shellquote(opts['user'])
2342 extra += ' --user %s' % util.shellquote(opts['user'])
2341 if opts.get('date'):
2343 if opts.get('date'):
2342 extra += ' --date %s' % util.shellquote(opts['date'])
2344 extra += ' --date %s' % util.shellquote(opts['date'])
2343 if opts.get('log'):
2345 if opts.get('log'):
2344 extra += ' --log'
2346 extra += ' --log'
2345 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
2347 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
2346 raise error.Abort(
2348 raise error.Abort(
2347 _("unresolved conflicts, can't continue"),
2349 _("unresolved conflicts, can't continue"),
2348 hint=hint)
2350 hint=hint)
2349 else:
2351 else:
2350 cont = False
2352 cont = False
2351
2353
2352 # commit
2354 # commit
2353 node = repo.commit(text=message, user=user,
2355 node = repo.commit(text=message, user=user,
2354 date=date, extra=extra, editor=editor)
2356 date=date, extra=extra, editor=editor)
2355 if node is None:
2357 if node is None:
2356 ui.warn(
2358 ui.warn(
2357 _('note: graft of %d:%s created no changes to commit\n') %
2359 _('note: graft of %d:%s created no changes to commit\n') %
2358 (ctx.rev(), ctx))
2360 (ctx.rev(), ctx))
2359
2361
2360 # remove state when we complete successfully
2362 # remove state when we complete successfully
2361 if not opts.get('dry_run'):
2363 if not opts.get('dry_run'):
2362 repo.vfs.unlinkpath('graftstate', ignoremissing=True)
2364 repo.vfs.unlinkpath('graftstate', ignoremissing=True)
2363
2365
2364 return 0
2366 return 0
2365
2367
2366 @command('grep',
2368 @command('grep',
2367 [('0', 'print0', None, _('end fields with NUL')),
2369 [('0', 'print0', None, _('end fields with NUL')),
2368 ('', 'all', None, _('print all revisions that match')),
2370 ('', 'all', None, _('print all revisions that match')),
2369 ('a', 'text', None, _('treat all files as text')),
2371 ('a', 'text', None, _('treat all files as text')),
2370 ('f', 'follow', None,
2372 ('f', 'follow', None,
2371 _('follow changeset history,'
2373 _('follow changeset history,'
2372 ' or file history across copies and renames')),
2374 ' or file history across copies and renames')),
2373 ('i', 'ignore-case', None, _('ignore case when matching')),
2375 ('i', 'ignore-case', None, _('ignore case when matching')),
2374 ('l', 'files-with-matches', None,
2376 ('l', 'files-with-matches', None,
2375 _('print only filenames and revisions that match')),
2377 _('print only filenames and revisions that match')),
2376 ('n', 'line-number', None, _('print matching line numbers')),
2378 ('n', 'line-number', None, _('print matching line numbers')),
2377 ('r', 'rev', [],
2379 ('r', 'rev', [],
2378 _('only search files changed within revision range'), _('REV')),
2380 _('only search files changed within revision range'), _('REV')),
2379 ('u', 'user', None, _('list the author (long with -v)')),
2381 ('u', 'user', None, _('list the author (long with -v)')),
2380 ('d', 'date', None, _('list the date (short with -q)')),
2382 ('d', 'date', None, _('list the date (short with -q)')),
2381 ] + formatteropts + walkopts,
2383 ] + formatteropts + walkopts,
2382 _('[OPTION]... PATTERN [FILE]...'),
2384 _('[OPTION]... PATTERN [FILE]...'),
2383 inferrepo=True)
2385 inferrepo=True)
2384 def grep(ui, repo, pattern, *pats, **opts):
2386 def grep(ui, repo, pattern, *pats, **opts):
2385 """search revision history for a pattern in specified files
2387 """search revision history for a pattern in specified files
2386
2388
2387 Search revision history for a regular expression in the specified
2389 Search revision history for a regular expression in the specified
2388 files or the entire project.
2390 files or the entire project.
2389
2391
2390 By default, grep prints the most recent revision number for each
2392 By default, grep prints the most recent revision number for each
2391 file in which it finds a match. To get it to print every revision
2393 file in which it finds a match. To get it to print every revision
2392 that contains a change in match status ("-" for a match that becomes
2394 that contains a change in match status ("-" for a match that becomes
2393 a non-match, or "+" for a non-match that becomes a match), use the
2395 a non-match, or "+" for a non-match that becomes a match), use the
2394 --all flag.
2396 --all flag.
2395
2397
2396 PATTERN can be any Python (roughly Perl-compatible) regular
2398 PATTERN can be any Python (roughly Perl-compatible) regular
2397 expression.
2399 expression.
2398
2400
2399 If no FILEs are specified (and -f/--follow isn't set), all files in
2401 If no FILEs are specified (and -f/--follow isn't set), all files in
2400 the repository are searched, including those that don't exist in the
2402 the repository are searched, including those that don't exist in the
2401 current branch or have been deleted in a prior changeset.
2403 current branch or have been deleted in a prior changeset.
2402
2404
2403 Returns 0 if a match is found, 1 otherwise.
2405 Returns 0 if a match is found, 1 otherwise.
2404 """
2406 """
2405 opts = pycompat.byteskwargs(opts)
2407 opts = pycompat.byteskwargs(opts)
2406 reflags = re.M
2408 reflags = re.M
2407 if opts.get('ignore_case'):
2409 if opts.get('ignore_case'):
2408 reflags |= re.I
2410 reflags |= re.I
2409 try:
2411 try:
2410 regexp = util.re.compile(pattern, reflags)
2412 regexp = util.re.compile(pattern, reflags)
2411 except re.error as inst:
2413 except re.error as inst:
2412 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2414 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2413 return 1
2415 return 1
2414 sep, eol = ':', '\n'
2416 sep, eol = ':', '\n'
2415 if opts.get('print0'):
2417 if opts.get('print0'):
2416 sep = eol = '\0'
2418 sep = eol = '\0'
2417
2419
2418 getfile = util.lrucachefunc(repo.file)
2420 getfile = util.lrucachefunc(repo.file)
2419
2421
2420 def matchlines(body):
2422 def matchlines(body):
2421 begin = 0
2423 begin = 0
2422 linenum = 0
2424 linenum = 0
2423 while begin < len(body):
2425 while begin < len(body):
2424 match = regexp.search(body, begin)
2426 match = regexp.search(body, begin)
2425 if not match:
2427 if not match:
2426 break
2428 break
2427 mstart, mend = match.span()
2429 mstart, mend = match.span()
2428 linenum += body.count('\n', begin, mstart) + 1
2430 linenum += body.count('\n', begin, mstart) + 1
2429 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2431 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2430 begin = body.find('\n', mend) + 1 or len(body) + 1
2432 begin = body.find('\n', mend) + 1 or len(body) + 1
2431 lend = begin - 1
2433 lend = begin - 1
2432 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2434 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2433
2435
2434 class linestate(object):
2436 class linestate(object):
2435 def __init__(self, line, linenum, colstart, colend):
2437 def __init__(self, line, linenum, colstart, colend):
2436 self.line = line
2438 self.line = line
2437 self.linenum = linenum
2439 self.linenum = linenum
2438 self.colstart = colstart
2440 self.colstart = colstart
2439 self.colend = colend
2441 self.colend = colend
2440
2442
2441 def __hash__(self):
2443 def __hash__(self):
2442 return hash((self.linenum, self.line))
2444 return hash((self.linenum, self.line))
2443
2445
2444 def __eq__(self, other):
2446 def __eq__(self, other):
2445 return self.line == other.line
2447 return self.line == other.line
2446
2448
2447 def findpos(self):
2449 def findpos(self):
2448 """Iterate all (start, end) indices of matches"""
2450 """Iterate all (start, end) indices of matches"""
2449 yield self.colstart, self.colend
2451 yield self.colstart, self.colend
2450 p = self.colend
2452 p = self.colend
2451 while p < len(self.line):
2453 while p < len(self.line):
2452 m = regexp.search(self.line, p)
2454 m = regexp.search(self.line, p)
2453 if not m:
2455 if not m:
2454 break
2456 break
2455 yield m.span()
2457 yield m.span()
2456 p = m.end()
2458 p = m.end()
2457
2459
2458 matches = {}
2460 matches = {}
2459 copies = {}
2461 copies = {}
2460 def grepbody(fn, rev, body):
2462 def grepbody(fn, rev, body):
2461 matches[rev].setdefault(fn, [])
2463 matches[rev].setdefault(fn, [])
2462 m = matches[rev][fn]
2464 m = matches[rev][fn]
2463 for lnum, cstart, cend, line in matchlines(body):
2465 for lnum, cstart, cend, line in matchlines(body):
2464 s = linestate(line, lnum, cstart, cend)
2466 s = linestate(line, lnum, cstart, cend)
2465 m.append(s)
2467 m.append(s)
2466
2468
2467 def difflinestates(a, b):
2469 def difflinestates(a, b):
2468 sm = difflib.SequenceMatcher(None, a, b)
2470 sm = difflib.SequenceMatcher(None, a, b)
2469 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2471 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2470 if tag == 'insert':
2472 if tag == 'insert':
2471 for i in xrange(blo, bhi):
2473 for i in xrange(blo, bhi):
2472 yield ('+', b[i])
2474 yield ('+', b[i])
2473 elif tag == 'delete':
2475 elif tag == 'delete':
2474 for i in xrange(alo, ahi):
2476 for i in xrange(alo, ahi):
2475 yield ('-', a[i])
2477 yield ('-', a[i])
2476 elif tag == 'replace':
2478 elif tag == 'replace':
2477 for i in xrange(alo, ahi):
2479 for i in xrange(alo, ahi):
2478 yield ('-', a[i])
2480 yield ('-', a[i])
2479 for i in xrange(blo, bhi):
2481 for i in xrange(blo, bhi):
2480 yield ('+', b[i])
2482 yield ('+', b[i])
2481
2483
2482 def display(fm, fn, ctx, pstates, states):
2484 def display(fm, fn, ctx, pstates, states):
2483 rev = ctx.rev()
2485 rev = ctx.rev()
2484 if fm.isplain():
2486 if fm.isplain():
2485 formatuser = ui.shortuser
2487 formatuser = ui.shortuser
2486 else:
2488 else:
2487 formatuser = str
2489 formatuser = str
2488 if ui.quiet:
2490 if ui.quiet:
2489 datefmt = '%Y-%m-%d'
2491 datefmt = '%Y-%m-%d'
2490 else:
2492 else:
2491 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2493 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2492 found = False
2494 found = False
2493 @util.cachefunc
2495 @util.cachefunc
2494 def binary():
2496 def binary():
2495 flog = getfile(fn)
2497 flog = getfile(fn)
2496 return util.binary(flog.read(ctx.filenode(fn)))
2498 return util.binary(flog.read(ctx.filenode(fn)))
2497
2499
2498 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
2500 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
2499 if opts.get('all'):
2501 if opts.get('all'):
2500 iter = difflinestates(pstates, states)
2502 iter = difflinestates(pstates, states)
2501 else:
2503 else:
2502 iter = [('', l) for l in states]
2504 iter = [('', l) for l in states]
2503 for change, l in iter:
2505 for change, l in iter:
2504 fm.startitem()
2506 fm.startitem()
2505 fm.data(node=fm.hexfunc(ctx.node()))
2507 fm.data(node=fm.hexfunc(ctx.node()))
2506 cols = [
2508 cols = [
2507 ('filename', fn, True),
2509 ('filename', fn, True),
2508 ('rev', rev, True),
2510 ('rev', rev, True),
2509 ('linenumber', l.linenum, opts.get('line_number')),
2511 ('linenumber', l.linenum, opts.get('line_number')),
2510 ]
2512 ]
2511 if opts.get('all'):
2513 if opts.get('all'):
2512 cols.append(('change', change, True))
2514 cols.append(('change', change, True))
2513 cols.extend([
2515 cols.extend([
2514 ('user', formatuser(ctx.user()), opts.get('user')),
2516 ('user', formatuser(ctx.user()), opts.get('user')),
2515 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
2517 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
2516 ])
2518 ])
2517 lastcol = next(name for name, data, cond in reversed(cols) if cond)
2519 lastcol = next(name for name, data, cond in reversed(cols) if cond)
2518 for name, data, cond in cols:
2520 for name, data, cond in cols:
2519 field = fieldnamemap.get(name, name)
2521 field = fieldnamemap.get(name, name)
2520 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
2522 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
2521 if cond and name != lastcol:
2523 if cond and name != lastcol:
2522 fm.plain(sep, label='grep.sep')
2524 fm.plain(sep, label='grep.sep')
2523 if not opts.get('files_with_matches'):
2525 if not opts.get('files_with_matches'):
2524 fm.plain(sep, label='grep.sep')
2526 fm.plain(sep, label='grep.sep')
2525 if not opts.get('text') and binary():
2527 if not opts.get('text') and binary():
2526 fm.plain(_(" Binary file matches"))
2528 fm.plain(_(" Binary file matches"))
2527 else:
2529 else:
2528 displaymatches(fm.nested('texts'), l)
2530 displaymatches(fm.nested('texts'), l)
2529 fm.plain(eol)
2531 fm.plain(eol)
2530 found = True
2532 found = True
2531 if opts.get('files_with_matches'):
2533 if opts.get('files_with_matches'):
2532 break
2534 break
2533 return found
2535 return found
2534
2536
2535 def displaymatches(fm, l):
2537 def displaymatches(fm, l):
2536 p = 0
2538 p = 0
2537 for s, e in l.findpos():
2539 for s, e in l.findpos():
2538 if p < s:
2540 if p < s:
2539 fm.startitem()
2541 fm.startitem()
2540 fm.write('text', '%s', l.line[p:s])
2542 fm.write('text', '%s', l.line[p:s])
2541 fm.data(matched=False)
2543 fm.data(matched=False)
2542 fm.startitem()
2544 fm.startitem()
2543 fm.write('text', '%s', l.line[s:e], label='grep.match')
2545 fm.write('text', '%s', l.line[s:e], label='grep.match')
2544 fm.data(matched=True)
2546 fm.data(matched=True)
2545 p = e
2547 p = e
2546 if p < len(l.line):
2548 if p < len(l.line):
2547 fm.startitem()
2549 fm.startitem()
2548 fm.write('text', '%s', l.line[p:])
2550 fm.write('text', '%s', l.line[p:])
2549 fm.data(matched=False)
2551 fm.data(matched=False)
2550 fm.end()
2552 fm.end()
2551
2553
2552 skip = {}
2554 skip = {}
2553 revfiles = {}
2555 revfiles = {}
2554 matchfn = scmutil.match(repo[None], pats, opts)
2556 matchfn = scmutil.match(repo[None], pats, opts)
2555 found = False
2557 found = False
2556 follow = opts.get('follow')
2558 follow = opts.get('follow')
2557
2559
2558 def prep(ctx, fns):
2560 def prep(ctx, fns):
2559 rev = ctx.rev()
2561 rev = ctx.rev()
2560 pctx = ctx.p1()
2562 pctx = ctx.p1()
2561 parent = pctx.rev()
2563 parent = pctx.rev()
2562 matches.setdefault(rev, {})
2564 matches.setdefault(rev, {})
2563 matches.setdefault(parent, {})
2565 matches.setdefault(parent, {})
2564 files = revfiles.setdefault(rev, [])
2566 files = revfiles.setdefault(rev, [])
2565 for fn in fns:
2567 for fn in fns:
2566 flog = getfile(fn)
2568 flog = getfile(fn)
2567 try:
2569 try:
2568 fnode = ctx.filenode(fn)
2570 fnode = ctx.filenode(fn)
2569 except error.LookupError:
2571 except error.LookupError:
2570 continue
2572 continue
2571
2573
2572 copied = flog.renamed(fnode)
2574 copied = flog.renamed(fnode)
2573 copy = follow and copied and copied[0]
2575 copy = follow and copied and copied[0]
2574 if copy:
2576 if copy:
2575 copies.setdefault(rev, {})[fn] = copy
2577 copies.setdefault(rev, {})[fn] = copy
2576 if fn in skip:
2578 if fn in skip:
2577 if copy:
2579 if copy:
2578 skip[copy] = True
2580 skip[copy] = True
2579 continue
2581 continue
2580 files.append(fn)
2582 files.append(fn)
2581
2583
2582 if fn not in matches[rev]:
2584 if fn not in matches[rev]:
2583 grepbody(fn, rev, flog.read(fnode))
2585 grepbody(fn, rev, flog.read(fnode))
2584
2586
2585 pfn = copy or fn
2587 pfn = copy or fn
2586 if pfn not in matches[parent]:
2588 if pfn not in matches[parent]:
2587 try:
2589 try:
2588 fnode = pctx.filenode(pfn)
2590 fnode = pctx.filenode(pfn)
2589 grepbody(pfn, parent, flog.read(fnode))
2591 grepbody(pfn, parent, flog.read(fnode))
2590 except error.LookupError:
2592 except error.LookupError:
2591 pass
2593 pass
2592
2594
2593 ui.pager('grep')
2595 ui.pager('grep')
2594 fm = ui.formatter('grep', opts)
2596 fm = ui.formatter('grep', opts)
2595 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2597 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2596 rev = ctx.rev()
2598 rev = ctx.rev()
2597 parent = ctx.p1().rev()
2599 parent = ctx.p1().rev()
2598 for fn in sorted(revfiles.get(rev, [])):
2600 for fn in sorted(revfiles.get(rev, [])):
2599 states = matches[rev][fn]
2601 states = matches[rev][fn]
2600 copy = copies.get(rev, {}).get(fn)
2602 copy = copies.get(rev, {}).get(fn)
2601 if fn in skip:
2603 if fn in skip:
2602 if copy:
2604 if copy:
2603 skip[copy] = True
2605 skip[copy] = True
2604 continue
2606 continue
2605 pstates = matches.get(parent, {}).get(copy or fn, [])
2607 pstates = matches.get(parent, {}).get(copy or fn, [])
2606 if pstates or states:
2608 if pstates or states:
2607 r = display(fm, fn, ctx, pstates, states)
2609 r = display(fm, fn, ctx, pstates, states)
2608 found = found or r
2610 found = found or r
2609 if r and not opts.get('all'):
2611 if r and not opts.get('all'):
2610 skip[fn] = True
2612 skip[fn] = True
2611 if copy:
2613 if copy:
2612 skip[copy] = True
2614 skip[copy] = True
2613 del matches[rev]
2615 del matches[rev]
2614 del revfiles[rev]
2616 del revfiles[rev]
2615 fm.end()
2617 fm.end()
2616
2618
2617 return not found
2619 return not found
2618
2620
2619 @command('heads',
2621 @command('heads',
2620 [('r', 'rev', '',
2622 [('r', 'rev', '',
2621 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2623 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2622 ('t', 'topo', False, _('show topological heads only')),
2624 ('t', 'topo', False, _('show topological heads only')),
2623 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2625 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2624 ('c', 'closed', False, _('show normal and closed branch heads')),
2626 ('c', 'closed', False, _('show normal and closed branch heads')),
2625 ] + templateopts,
2627 ] + templateopts,
2626 _('[-ct] [-r STARTREV] [REV]...'))
2628 _('[-ct] [-r STARTREV] [REV]...'))
2627 def heads(ui, repo, *branchrevs, **opts):
2629 def heads(ui, repo, *branchrevs, **opts):
2628 """show branch heads
2630 """show branch heads
2629
2631
2630 With no arguments, show all open branch heads in the repository.
2632 With no arguments, show all open branch heads in the repository.
2631 Branch heads are changesets that have no descendants on the
2633 Branch heads are changesets that have no descendants on the
2632 same branch. They are where development generally takes place and
2634 same branch. They are where development generally takes place and
2633 are the usual targets for update and merge operations.
2635 are the usual targets for update and merge operations.
2634
2636
2635 If one or more REVs are given, only open branch heads on the
2637 If one or more REVs are given, only open branch heads on the
2636 branches associated with the specified changesets are shown. This
2638 branches associated with the specified changesets are shown. This
2637 means that you can use :hg:`heads .` to see the heads on the
2639 means that you can use :hg:`heads .` to see the heads on the
2638 currently checked-out branch.
2640 currently checked-out branch.
2639
2641
2640 If -c/--closed is specified, also show branch heads marked closed
2642 If -c/--closed is specified, also show branch heads marked closed
2641 (see :hg:`commit --close-branch`).
2643 (see :hg:`commit --close-branch`).
2642
2644
2643 If STARTREV is specified, only those heads that are descendants of
2645 If STARTREV is specified, only those heads that are descendants of
2644 STARTREV will be displayed.
2646 STARTREV will be displayed.
2645
2647
2646 If -t/--topo is specified, named branch mechanics will be ignored and only
2648 If -t/--topo is specified, named branch mechanics will be ignored and only
2647 topological heads (changesets with no children) will be shown.
2649 topological heads (changesets with no children) will be shown.
2648
2650
2649 Returns 0 if matching heads are found, 1 if not.
2651 Returns 0 if matching heads are found, 1 if not.
2650 """
2652 """
2651
2653
2652 opts = pycompat.byteskwargs(opts)
2654 opts = pycompat.byteskwargs(opts)
2653 start = None
2655 start = None
2654 if 'rev' in opts:
2656 if 'rev' in opts:
2655 start = scmutil.revsingle(repo, opts['rev'], None).node()
2657 start = scmutil.revsingle(repo, opts['rev'], None).node()
2656
2658
2657 if opts.get('topo'):
2659 if opts.get('topo'):
2658 heads = [repo[h] for h in repo.heads(start)]
2660 heads = [repo[h] for h in repo.heads(start)]
2659 else:
2661 else:
2660 heads = []
2662 heads = []
2661 for branch in repo.branchmap():
2663 for branch in repo.branchmap():
2662 heads += repo.branchheads(branch, start, opts.get('closed'))
2664 heads += repo.branchheads(branch, start, opts.get('closed'))
2663 heads = [repo[h] for h in heads]
2665 heads = [repo[h] for h in heads]
2664
2666
2665 if branchrevs:
2667 if branchrevs:
2666 branches = set(repo[br].branch() for br in branchrevs)
2668 branches = set(repo[br].branch() for br in branchrevs)
2667 heads = [h for h in heads if h.branch() in branches]
2669 heads = [h for h in heads if h.branch() in branches]
2668
2670
2669 if opts.get('active') and branchrevs:
2671 if opts.get('active') and branchrevs:
2670 dagheads = repo.heads(start)
2672 dagheads = repo.heads(start)
2671 heads = [h for h in heads if h.node() in dagheads]
2673 heads = [h for h in heads if h.node() in dagheads]
2672
2674
2673 if branchrevs:
2675 if branchrevs:
2674 haveheads = set(h.branch() for h in heads)
2676 haveheads = set(h.branch() for h in heads)
2675 if branches - haveheads:
2677 if branches - haveheads:
2676 headless = ', '.join(b for b in branches - haveheads)
2678 headless = ', '.join(b for b in branches - haveheads)
2677 msg = _('no open branch heads found on branches %s')
2679 msg = _('no open branch heads found on branches %s')
2678 if opts.get('rev'):
2680 if opts.get('rev'):
2679 msg += _(' (started at %s)') % opts['rev']
2681 msg += _(' (started at %s)') % opts['rev']
2680 ui.warn((msg + '\n') % headless)
2682 ui.warn((msg + '\n') % headless)
2681
2683
2682 if not heads:
2684 if not heads:
2683 return 1
2685 return 1
2684
2686
2685 ui.pager('heads')
2687 ui.pager('heads')
2686 heads = sorted(heads, key=lambda x: -x.rev())
2688 heads = sorted(heads, key=lambda x: -x.rev())
2687 displayer = cmdutil.show_changeset(ui, repo, opts)
2689 displayer = cmdutil.show_changeset(ui, repo, opts)
2688 for ctx in heads:
2690 for ctx in heads:
2689 displayer.show(ctx)
2691 displayer.show(ctx)
2690 displayer.close()
2692 displayer.close()
2691
2693
2692 @command('help',
2694 @command('help',
2693 [('e', 'extension', None, _('show only help for extensions')),
2695 [('e', 'extension', None, _('show only help for extensions')),
2694 ('c', 'command', None, _('show only help for commands')),
2696 ('c', 'command', None, _('show only help for commands')),
2695 ('k', 'keyword', None, _('show topics matching keyword')),
2697 ('k', 'keyword', None, _('show topics matching keyword')),
2696 ('s', 'system', [], _('show help for specific platform(s)')),
2698 ('s', 'system', [], _('show help for specific platform(s)')),
2697 ],
2699 ],
2698 _('[-ecks] [TOPIC]'),
2700 _('[-ecks] [TOPIC]'),
2699 norepo=True)
2701 norepo=True)
2700 def help_(ui, name=None, **opts):
2702 def help_(ui, name=None, **opts):
2701 """show help for a given topic or a help overview
2703 """show help for a given topic or a help overview
2702
2704
2703 With no arguments, print a list of commands with short help messages.
2705 With no arguments, print a list of commands with short help messages.
2704
2706
2705 Given a topic, extension, or command name, print help for that
2707 Given a topic, extension, or command name, print help for that
2706 topic.
2708 topic.
2707
2709
2708 Returns 0 if successful.
2710 Returns 0 if successful.
2709 """
2711 """
2710
2712
2711 keep = opts.get(r'system') or []
2713 keep = opts.get(r'system') or []
2712 if len(keep) == 0:
2714 if len(keep) == 0:
2713 if pycompat.sysplatform.startswith('win'):
2715 if pycompat.sysplatform.startswith('win'):
2714 keep.append('windows')
2716 keep.append('windows')
2715 elif pycompat.sysplatform == 'OpenVMS':
2717 elif pycompat.sysplatform == 'OpenVMS':
2716 keep.append('vms')
2718 keep.append('vms')
2717 elif pycompat.sysplatform == 'plan9':
2719 elif pycompat.sysplatform == 'plan9':
2718 keep.append('plan9')
2720 keep.append('plan9')
2719 else:
2721 else:
2720 keep.append('unix')
2722 keep.append('unix')
2721 keep.append(pycompat.sysplatform.lower())
2723 keep.append(pycompat.sysplatform.lower())
2722 if ui.verbose:
2724 if ui.verbose:
2723 keep.append('verbose')
2725 keep.append('verbose')
2724
2726
2725 formatted = help.formattedhelp(ui, name, keep=keep, **opts)
2727 formatted = help.formattedhelp(ui, name, keep=keep, **opts)
2726 ui.pager('help')
2728 ui.pager('help')
2727 ui.write(formatted)
2729 ui.write(formatted)
2728
2730
2729
2731
2730 @command('identify|id',
2732 @command('identify|id',
2731 [('r', 'rev', '',
2733 [('r', 'rev', '',
2732 _('identify the specified revision'), _('REV')),
2734 _('identify the specified revision'), _('REV')),
2733 ('n', 'num', None, _('show local revision number')),
2735 ('n', 'num', None, _('show local revision number')),
2734 ('i', 'id', None, _('show global revision id')),
2736 ('i', 'id', None, _('show global revision id')),
2735 ('b', 'branch', None, _('show branch')),
2737 ('b', 'branch', None, _('show branch')),
2736 ('t', 'tags', None, _('show tags')),
2738 ('t', 'tags', None, _('show tags')),
2737 ('B', 'bookmarks', None, _('show bookmarks')),
2739 ('B', 'bookmarks', None, _('show bookmarks')),
2738 ] + remoteopts,
2740 ] + remoteopts,
2739 _('[-nibtB] [-r REV] [SOURCE]'),
2741 _('[-nibtB] [-r REV] [SOURCE]'),
2740 optionalrepo=True)
2742 optionalrepo=True)
2741 def identify(ui, repo, source=None, rev=None,
2743 def identify(ui, repo, source=None, rev=None,
2742 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
2744 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
2743 """identify the working directory or specified revision
2745 """identify the working directory or specified revision
2744
2746
2745 Print a summary identifying the repository state at REV using one or
2747 Print a summary identifying the repository state at REV using one or
2746 two parent hash identifiers, followed by a "+" if the working
2748 two parent hash identifiers, followed by a "+" if the working
2747 directory has uncommitted changes, the branch name (if not default),
2749 directory has uncommitted changes, the branch name (if not default),
2748 a list of tags, and a list of bookmarks.
2750 a list of tags, and a list of bookmarks.
2749
2751
2750 When REV is not given, print a summary of the current state of the
2752 When REV is not given, print a summary of the current state of the
2751 repository.
2753 repository.
2752
2754
2753 Specifying a path to a repository root or Mercurial bundle will
2755 Specifying a path to a repository root or Mercurial bundle will
2754 cause lookup to operate on that repository/bundle.
2756 cause lookup to operate on that repository/bundle.
2755
2757
2756 .. container:: verbose
2758 .. container:: verbose
2757
2759
2758 Examples:
2760 Examples:
2759
2761
2760 - generate a build identifier for the working directory::
2762 - generate a build identifier for the working directory::
2761
2763
2762 hg id --id > build-id.dat
2764 hg id --id > build-id.dat
2763
2765
2764 - find the revision corresponding to a tag::
2766 - find the revision corresponding to a tag::
2765
2767
2766 hg id -n -r 1.3
2768 hg id -n -r 1.3
2767
2769
2768 - check the most recent revision of a remote repository::
2770 - check the most recent revision of a remote repository::
2769
2771
2770 hg id -r tip https://www.mercurial-scm.org/repo/hg/
2772 hg id -r tip https://www.mercurial-scm.org/repo/hg/
2771
2773
2772 See :hg:`log` for generating more information about specific revisions,
2774 See :hg:`log` for generating more information about specific revisions,
2773 including full hash identifiers.
2775 including full hash identifiers.
2774
2776
2775 Returns 0 if successful.
2777 Returns 0 if successful.
2776 """
2778 """
2777
2779
2778 opts = pycompat.byteskwargs(opts)
2780 opts = pycompat.byteskwargs(opts)
2779 if not repo and not source:
2781 if not repo and not source:
2780 raise error.Abort(_("there is no Mercurial repository here "
2782 raise error.Abort(_("there is no Mercurial repository here "
2781 "(.hg not found)"))
2783 "(.hg not found)"))
2782
2784
2783 if ui.debugflag:
2785 if ui.debugflag:
2784 hexfunc = hex
2786 hexfunc = hex
2785 else:
2787 else:
2786 hexfunc = short
2788 hexfunc = short
2787 default = not (num or id or branch or tags or bookmarks)
2789 default = not (num or id or branch or tags or bookmarks)
2788 output = []
2790 output = []
2789 revs = []
2791 revs = []
2790
2792
2791 if source:
2793 if source:
2792 source, branches = hg.parseurl(ui.expandpath(source))
2794 source, branches = hg.parseurl(ui.expandpath(source))
2793 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
2795 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
2794 repo = peer.local()
2796 repo = peer.local()
2795 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
2797 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
2796
2798
2797 if not repo:
2799 if not repo:
2798 if num or branch or tags:
2800 if num or branch or tags:
2799 raise error.Abort(
2801 raise error.Abort(
2800 _("can't query remote revision number, branch, or tags"))
2802 _("can't query remote revision number, branch, or tags"))
2801 if not rev and revs:
2803 if not rev and revs:
2802 rev = revs[0]
2804 rev = revs[0]
2803 if not rev:
2805 if not rev:
2804 rev = "tip"
2806 rev = "tip"
2805
2807
2806 remoterev = peer.lookup(rev)
2808 remoterev = peer.lookup(rev)
2807 if default or id:
2809 if default or id:
2808 output = [hexfunc(remoterev)]
2810 output = [hexfunc(remoterev)]
2809
2811
2810 def getbms():
2812 def getbms():
2811 bms = []
2813 bms = []
2812
2814
2813 if 'bookmarks' in peer.listkeys('namespaces'):
2815 if 'bookmarks' in peer.listkeys('namespaces'):
2814 hexremoterev = hex(remoterev)
2816 hexremoterev = hex(remoterev)
2815 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
2817 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
2816 if bmr == hexremoterev]
2818 if bmr == hexremoterev]
2817
2819
2818 return sorted(bms)
2820 return sorted(bms)
2819
2821
2820 if bookmarks:
2822 if bookmarks:
2821 output.extend(getbms())
2823 output.extend(getbms())
2822 elif default and not ui.quiet:
2824 elif default and not ui.quiet:
2823 # multiple bookmarks for a single parent separated by '/'
2825 # multiple bookmarks for a single parent separated by '/'
2824 bm = '/'.join(getbms())
2826 bm = '/'.join(getbms())
2825 if bm:
2827 if bm:
2826 output.append(bm)
2828 output.append(bm)
2827 else:
2829 else:
2828 ctx = scmutil.revsingle(repo, rev, None)
2830 ctx = scmutil.revsingle(repo, rev, None)
2829
2831
2830 if ctx.rev() is None:
2832 if ctx.rev() is None:
2831 ctx = repo[None]
2833 ctx = repo[None]
2832 parents = ctx.parents()
2834 parents = ctx.parents()
2833 taglist = []
2835 taglist = []
2834 for p in parents:
2836 for p in parents:
2835 taglist.extend(p.tags())
2837 taglist.extend(p.tags())
2836
2838
2837 changed = ""
2839 changed = ""
2838 if default or id or num:
2840 if default or id or num:
2839 if (any(repo.status())
2841 if (any(repo.status())
2840 or any(ctx.sub(s).dirty() for s in ctx.substate)):
2842 or any(ctx.sub(s).dirty() for s in ctx.substate)):
2841 changed = '+'
2843 changed = '+'
2842 if default or id:
2844 if default or id:
2843 output = ["%s%s" %
2845 output = ["%s%s" %
2844 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
2846 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
2845 if num:
2847 if num:
2846 output.append("%s%s" %
2848 output.append("%s%s" %
2847 ('+'.join([str(p.rev()) for p in parents]), changed))
2849 ('+'.join([str(p.rev()) for p in parents]), changed))
2848 else:
2850 else:
2849 if default or id:
2851 if default or id:
2850 output = [hexfunc(ctx.node())]
2852 output = [hexfunc(ctx.node())]
2851 if num:
2853 if num:
2852 output.append(str(ctx.rev()))
2854 output.append(str(ctx.rev()))
2853 taglist = ctx.tags()
2855 taglist = ctx.tags()
2854
2856
2855 if default and not ui.quiet:
2857 if default and not ui.quiet:
2856 b = ctx.branch()
2858 b = ctx.branch()
2857 if b != 'default':
2859 if b != 'default':
2858 output.append("(%s)" % b)
2860 output.append("(%s)" % b)
2859
2861
2860 # multiple tags for a single parent separated by '/'
2862 # multiple tags for a single parent separated by '/'
2861 t = '/'.join(taglist)
2863 t = '/'.join(taglist)
2862 if t:
2864 if t:
2863 output.append(t)
2865 output.append(t)
2864
2866
2865 # multiple bookmarks for a single parent separated by '/'
2867 # multiple bookmarks for a single parent separated by '/'
2866 bm = '/'.join(ctx.bookmarks())
2868 bm = '/'.join(ctx.bookmarks())
2867 if bm:
2869 if bm:
2868 output.append(bm)
2870 output.append(bm)
2869 else:
2871 else:
2870 if branch:
2872 if branch:
2871 output.append(ctx.branch())
2873 output.append(ctx.branch())
2872
2874
2873 if tags:
2875 if tags:
2874 output.extend(taglist)
2876 output.extend(taglist)
2875
2877
2876 if bookmarks:
2878 if bookmarks:
2877 output.extend(ctx.bookmarks())
2879 output.extend(ctx.bookmarks())
2878
2880
2879 ui.write("%s\n" % ' '.join(output))
2881 ui.write("%s\n" % ' '.join(output))
2880
2882
2881 @command('import|patch',
2883 @command('import|patch',
2882 [('p', 'strip', 1,
2884 [('p', 'strip', 1,
2883 _('directory strip option for patch. This has the same '
2885 _('directory strip option for patch. This has the same '
2884 'meaning as the corresponding patch option'), _('NUM')),
2886 'meaning as the corresponding patch option'), _('NUM')),
2885 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
2887 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
2886 ('e', 'edit', False, _('invoke editor on commit messages')),
2888 ('e', 'edit', False, _('invoke editor on commit messages')),
2887 ('f', 'force', None,
2889 ('f', 'force', None,
2888 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
2890 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
2889 ('', 'no-commit', None,
2891 ('', 'no-commit', None,
2890 _("don't commit, just update the working directory")),
2892 _("don't commit, just update the working directory")),
2891 ('', 'bypass', None,
2893 ('', 'bypass', None,
2892 _("apply patch without touching the working directory")),
2894 _("apply patch without touching the working directory")),
2893 ('', 'partial', None,
2895 ('', 'partial', None,
2894 _('commit even if some hunks fail')),
2896 _('commit even if some hunks fail')),
2895 ('', 'exact', None,
2897 ('', 'exact', None,
2896 _('abort if patch would apply lossily')),
2898 _('abort if patch would apply lossily')),
2897 ('', 'prefix', '',
2899 ('', 'prefix', '',
2898 _('apply patch to subdirectory'), _('DIR')),
2900 _('apply patch to subdirectory'), _('DIR')),
2899 ('', 'import-branch', None,
2901 ('', 'import-branch', None,
2900 _('use any branch information in patch (implied by --exact)'))] +
2902 _('use any branch information in patch (implied by --exact)'))] +
2901 commitopts + commitopts2 + similarityopts,
2903 commitopts + commitopts2 + similarityopts,
2902 _('[OPTION]... PATCH...'))
2904 _('[OPTION]... PATCH...'))
2903 def import_(ui, repo, patch1=None, *patches, **opts):
2905 def import_(ui, repo, patch1=None, *patches, **opts):
2904 """import an ordered set of patches
2906 """import an ordered set of patches
2905
2907
2906 Import a list of patches and commit them individually (unless
2908 Import a list of patches and commit them individually (unless
2907 --no-commit is specified).
2909 --no-commit is specified).
2908
2910
2909 To read a patch from standard input (stdin), use "-" as the patch
2911 To read a patch from standard input (stdin), use "-" as the patch
2910 name. If a URL is specified, the patch will be downloaded from
2912 name. If a URL is specified, the patch will be downloaded from
2911 there.
2913 there.
2912
2914
2913 Import first applies changes to the working directory (unless
2915 Import first applies changes to the working directory (unless
2914 --bypass is specified), import will abort if there are outstanding
2916 --bypass is specified), import will abort if there are outstanding
2915 changes.
2917 changes.
2916
2918
2917 Use --bypass to apply and commit patches directly to the
2919 Use --bypass to apply and commit patches directly to the
2918 repository, without affecting the working directory. Without
2920 repository, without affecting the working directory. Without
2919 --exact, patches will be applied on top of the working directory
2921 --exact, patches will be applied on top of the working directory
2920 parent revision.
2922 parent revision.
2921
2923
2922 You can import a patch straight from a mail message. Even patches
2924 You can import a patch straight from a mail message. Even patches
2923 as attachments work (to use the body part, it must have type
2925 as attachments work (to use the body part, it must have type
2924 text/plain or text/x-patch). From and Subject headers of email
2926 text/plain or text/x-patch). From and Subject headers of email
2925 message are used as default committer and commit message. All
2927 message are used as default committer and commit message. All
2926 text/plain body parts before first diff are added to the commit
2928 text/plain body parts before first diff are added to the commit
2927 message.
2929 message.
2928
2930
2929 If the imported patch was generated by :hg:`export`, user and
2931 If the imported patch was generated by :hg:`export`, user and
2930 description from patch override values from message headers and
2932 description from patch override values from message headers and
2931 body. Values given on command line with -m/--message and -u/--user
2933 body. Values given on command line with -m/--message and -u/--user
2932 override these.
2934 override these.
2933
2935
2934 If --exact is specified, import will set the working directory to
2936 If --exact is specified, import will set the working directory to
2935 the parent of each patch before applying it, and will abort if the
2937 the parent of each patch before applying it, and will abort if the
2936 resulting changeset has a different ID than the one recorded in
2938 resulting changeset has a different ID than the one recorded in
2937 the patch. This will guard against various ways that portable
2939 the patch. This will guard against various ways that portable
2938 patch formats and mail systems might fail to transfer Mercurial
2940 patch formats and mail systems might fail to transfer Mercurial
2939 data or metadata. See :hg:`bundle` for lossless transmission.
2941 data or metadata. See :hg:`bundle` for lossless transmission.
2940
2942
2941 Use --partial to ensure a changeset will be created from the patch
2943 Use --partial to ensure a changeset will be created from the patch
2942 even if some hunks fail to apply. Hunks that fail to apply will be
2944 even if some hunks fail to apply. Hunks that fail to apply will be
2943 written to a <target-file>.rej file. Conflicts can then be resolved
2945 written to a <target-file>.rej file. Conflicts can then be resolved
2944 by hand before :hg:`commit --amend` is run to update the created
2946 by hand before :hg:`commit --amend` is run to update the created
2945 changeset. This flag exists to let people import patches that
2947 changeset. This flag exists to let people import patches that
2946 partially apply without losing the associated metadata (author,
2948 partially apply without losing the associated metadata (author,
2947 date, description, ...).
2949 date, description, ...).
2948
2950
2949 .. note::
2951 .. note::
2950
2952
2951 When no hunks apply cleanly, :hg:`import --partial` will create
2953 When no hunks apply cleanly, :hg:`import --partial` will create
2952 an empty changeset, importing only the patch metadata.
2954 an empty changeset, importing only the patch metadata.
2953
2955
2954 With -s/--similarity, hg will attempt to discover renames and
2956 With -s/--similarity, hg will attempt to discover renames and
2955 copies in the patch in the same way as :hg:`addremove`.
2957 copies in the patch in the same way as :hg:`addremove`.
2956
2958
2957 It is possible to use external patch programs to perform the patch
2959 It is possible to use external patch programs to perform the patch
2958 by setting the ``ui.patch`` configuration option. For the default
2960 by setting the ``ui.patch`` configuration option. For the default
2959 internal tool, the fuzz can also be configured via ``patch.fuzz``.
2961 internal tool, the fuzz can also be configured via ``patch.fuzz``.
2960 See :hg:`help config` for more information about configuration
2962 See :hg:`help config` for more information about configuration
2961 files and how to use these options.
2963 files and how to use these options.
2962
2964
2963 See :hg:`help dates` for a list of formats valid for -d/--date.
2965 See :hg:`help dates` for a list of formats valid for -d/--date.
2964
2966
2965 .. container:: verbose
2967 .. container:: verbose
2966
2968
2967 Examples:
2969 Examples:
2968
2970
2969 - import a traditional patch from a website and detect renames::
2971 - import a traditional patch from a website and detect renames::
2970
2972
2971 hg import -s 80 http://example.com/bugfix.patch
2973 hg import -s 80 http://example.com/bugfix.patch
2972
2974
2973 - import a changeset from an hgweb server::
2975 - import a changeset from an hgweb server::
2974
2976
2975 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
2977 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
2976
2978
2977 - import all the patches in an Unix-style mbox::
2979 - import all the patches in an Unix-style mbox::
2978
2980
2979 hg import incoming-patches.mbox
2981 hg import incoming-patches.mbox
2980
2982
2981 - import patches from stdin::
2983 - import patches from stdin::
2982
2984
2983 hg import -
2985 hg import -
2984
2986
2985 - attempt to exactly restore an exported changeset (not always
2987 - attempt to exactly restore an exported changeset (not always
2986 possible)::
2988 possible)::
2987
2989
2988 hg import --exact proposed-fix.patch
2990 hg import --exact proposed-fix.patch
2989
2991
2990 - use an external tool to apply a patch which is too fuzzy for
2992 - use an external tool to apply a patch which is too fuzzy for
2991 the default internal tool.
2993 the default internal tool.
2992
2994
2993 hg import --config ui.patch="patch --merge" fuzzy.patch
2995 hg import --config ui.patch="patch --merge" fuzzy.patch
2994
2996
2995 - change the default fuzzing from 2 to a less strict 7
2997 - change the default fuzzing from 2 to a less strict 7
2996
2998
2997 hg import --config ui.fuzz=7 fuzz.patch
2999 hg import --config ui.fuzz=7 fuzz.patch
2998
3000
2999 Returns 0 on success, 1 on partial success (see --partial).
3001 Returns 0 on success, 1 on partial success (see --partial).
3000 """
3002 """
3001
3003
3002 opts = pycompat.byteskwargs(opts)
3004 opts = pycompat.byteskwargs(opts)
3003 if not patch1:
3005 if not patch1:
3004 raise error.Abort(_('need at least one patch to import'))
3006 raise error.Abort(_('need at least one patch to import'))
3005
3007
3006 patches = (patch1,) + patches
3008 patches = (patch1,) + patches
3007
3009
3008 date = opts.get('date')
3010 date = opts.get('date')
3009 if date:
3011 if date:
3010 opts['date'] = util.parsedate(date)
3012 opts['date'] = util.parsedate(date)
3011
3013
3012 exact = opts.get('exact')
3014 exact = opts.get('exact')
3013 update = not opts.get('bypass')
3015 update = not opts.get('bypass')
3014 if not update and opts.get('no_commit'):
3016 if not update and opts.get('no_commit'):
3015 raise error.Abort(_('cannot use --no-commit with --bypass'))
3017 raise error.Abort(_('cannot use --no-commit with --bypass'))
3016 try:
3018 try:
3017 sim = float(opts.get('similarity') or 0)
3019 sim = float(opts.get('similarity') or 0)
3018 except ValueError:
3020 except ValueError:
3019 raise error.Abort(_('similarity must be a number'))
3021 raise error.Abort(_('similarity must be a number'))
3020 if sim < 0 or sim > 100:
3022 if sim < 0 or sim > 100:
3021 raise error.Abort(_('similarity must be between 0 and 100'))
3023 raise error.Abort(_('similarity must be between 0 and 100'))
3022 if sim and not update:
3024 if sim and not update:
3023 raise error.Abort(_('cannot use --similarity with --bypass'))
3025 raise error.Abort(_('cannot use --similarity with --bypass'))
3024 if exact:
3026 if exact:
3025 if opts.get('edit'):
3027 if opts.get('edit'):
3026 raise error.Abort(_('cannot use --exact with --edit'))
3028 raise error.Abort(_('cannot use --exact with --edit'))
3027 if opts.get('prefix'):
3029 if opts.get('prefix'):
3028 raise error.Abort(_('cannot use --exact with --prefix'))
3030 raise error.Abort(_('cannot use --exact with --prefix'))
3029
3031
3030 base = opts["base"]
3032 base = opts["base"]
3031 wlock = dsguard = lock = tr = None
3033 wlock = dsguard = lock = tr = None
3032 msgs = []
3034 msgs = []
3033 ret = 0
3035 ret = 0
3034
3036
3035
3037
3036 try:
3038 try:
3037 wlock = repo.wlock()
3039 wlock = repo.wlock()
3038
3040
3039 if update:
3041 if update:
3040 cmdutil.checkunfinished(repo)
3042 cmdutil.checkunfinished(repo)
3041 if (exact or not opts.get('force')):
3043 if (exact or not opts.get('force')):
3042 cmdutil.bailifchanged(repo)
3044 cmdutil.bailifchanged(repo)
3043
3045
3044 if not opts.get('no_commit'):
3046 if not opts.get('no_commit'):
3045 lock = repo.lock()
3047 lock = repo.lock()
3046 tr = repo.transaction('import')
3048 tr = repo.transaction('import')
3047 else:
3049 else:
3048 dsguard = dirstateguard.dirstateguard(repo, 'import')
3050 dsguard = dirstateguard.dirstateguard(repo, 'import')
3049 parents = repo[None].parents()
3051 parents = repo[None].parents()
3050 for patchurl in patches:
3052 for patchurl in patches:
3051 if patchurl == '-':
3053 if patchurl == '-':
3052 ui.status(_('applying patch from stdin\n'))
3054 ui.status(_('applying patch from stdin\n'))
3053 patchfile = ui.fin
3055 patchfile = ui.fin
3054 patchurl = 'stdin' # for error message
3056 patchurl = 'stdin' # for error message
3055 else:
3057 else:
3056 patchurl = os.path.join(base, patchurl)
3058 patchurl = os.path.join(base, patchurl)
3057 ui.status(_('applying %s\n') % patchurl)
3059 ui.status(_('applying %s\n') % patchurl)
3058 patchfile = hg.openpath(ui, patchurl)
3060 patchfile = hg.openpath(ui, patchurl)
3059
3061
3060 haspatch = False
3062 haspatch = False
3061 for hunk in patch.split(patchfile):
3063 for hunk in patch.split(patchfile):
3062 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
3064 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
3063 parents, opts,
3065 parents, opts,
3064 msgs, hg.clean)
3066 msgs, hg.clean)
3065 if msg:
3067 if msg:
3066 haspatch = True
3068 haspatch = True
3067 ui.note(msg + '\n')
3069 ui.note(msg + '\n')
3068 if update or exact:
3070 if update or exact:
3069 parents = repo[None].parents()
3071 parents = repo[None].parents()
3070 else:
3072 else:
3071 parents = [repo[node]]
3073 parents = [repo[node]]
3072 if rej:
3074 if rej:
3073 ui.write_err(_("patch applied partially\n"))
3075 ui.write_err(_("patch applied partially\n"))
3074 ui.write_err(_("(fix the .rej files and run "
3076 ui.write_err(_("(fix the .rej files and run "
3075 "`hg commit --amend`)\n"))
3077 "`hg commit --amend`)\n"))
3076 ret = 1
3078 ret = 1
3077 break
3079 break
3078
3080
3079 if not haspatch:
3081 if not haspatch:
3080 raise error.Abort(_('%s: no diffs found') % patchurl)
3082 raise error.Abort(_('%s: no diffs found') % patchurl)
3081
3083
3082 if tr:
3084 if tr:
3083 tr.close()
3085 tr.close()
3084 if msgs:
3086 if msgs:
3085 repo.savecommitmessage('\n* * *\n'.join(msgs))
3087 repo.savecommitmessage('\n* * *\n'.join(msgs))
3086 if dsguard:
3088 if dsguard:
3087 dsguard.close()
3089 dsguard.close()
3088 return ret
3090 return ret
3089 finally:
3091 finally:
3090 if tr:
3092 if tr:
3091 tr.release()
3093 tr.release()
3092 release(lock, dsguard, wlock)
3094 release(lock, dsguard, wlock)
3093
3095
3094 @command('incoming|in',
3096 @command('incoming|in',
3095 [('f', 'force', None,
3097 [('f', 'force', None,
3096 _('run even if remote repository is unrelated')),
3098 _('run even if remote repository is unrelated')),
3097 ('n', 'newest-first', None, _('show newest record first')),
3099 ('n', 'newest-first', None, _('show newest record first')),
3098 ('', 'bundle', '',
3100 ('', 'bundle', '',
3099 _('file to store the bundles into'), _('FILE')),
3101 _('file to store the bundles into'), _('FILE')),
3100 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3102 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3101 ('B', 'bookmarks', False, _("compare bookmarks")),
3103 ('B', 'bookmarks', False, _("compare bookmarks")),
3102 ('b', 'branch', [],
3104 ('b', 'branch', [],
3103 _('a specific branch you would like to pull'), _('BRANCH')),
3105 _('a specific branch you would like to pull'), _('BRANCH')),
3104 ] + logopts + remoteopts + subrepoopts,
3106 ] + logopts + remoteopts + subrepoopts,
3105 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3107 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3106 def incoming(ui, repo, source="default", **opts):
3108 def incoming(ui, repo, source="default", **opts):
3107 """show new changesets found in source
3109 """show new changesets found in source
3108
3110
3109 Show new changesets found in the specified path/URL or the default
3111 Show new changesets found in the specified path/URL or the default
3110 pull location. These are the changesets that would have been pulled
3112 pull location. These are the changesets that would have been pulled
3111 if a pull at the time you issued this command.
3113 if a pull at the time you issued this command.
3112
3114
3113 See pull for valid source format details.
3115 See pull for valid source format details.
3114
3116
3115 .. container:: verbose
3117 .. container:: verbose
3116
3118
3117 With -B/--bookmarks, the result of bookmark comparison between
3119 With -B/--bookmarks, the result of bookmark comparison between
3118 local and remote repositories is displayed. With -v/--verbose,
3120 local and remote repositories is displayed. With -v/--verbose,
3119 status is also displayed for each bookmark like below::
3121 status is also displayed for each bookmark like below::
3120
3122
3121 BM1 01234567890a added
3123 BM1 01234567890a added
3122 BM2 1234567890ab advanced
3124 BM2 1234567890ab advanced
3123 BM3 234567890abc diverged
3125 BM3 234567890abc diverged
3124 BM4 34567890abcd changed
3126 BM4 34567890abcd changed
3125
3127
3126 The action taken locally when pulling depends on the
3128 The action taken locally when pulling depends on the
3127 status of each bookmark:
3129 status of each bookmark:
3128
3130
3129 :``added``: pull will create it
3131 :``added``: pull will create it
3130 :``advanced``: pull will update it
3132 :``advanced``: pull will update it
3131 :``diverged``: pull will create a divergent bookmark
3133 :``diverged``: pull will create a divergent bookmark
3132 :``changed``: result depends on remote changesets
3134 :``changed``: result depends on remote changesets
3133
3135
3134 From the point of view of pulling behavior, bookmark
3136 From the point of view of pulling behavior, bookmark
3135 existing only in the remote repository are treated as ``added``,
3137 existing only in the remote repository are treated as ``added``,
3136 even if it is in fact locally deleted.
3138 even if it is in fact locally deleted.
3137
3139
3138 .. container:: verbose
3140 .. container:: verbose
3139
3141
3140 For remote repository, using --bundle avoids downloading the
3142 For remote repository, using --bundle avoids downloading the
3141 changesets twice if the incoming is followed by a pull.
3143 changesets twice if the incoming is followed by a pull.
3142
3144
3143 Examples:
3145 Examples:
3144
3146
3145 - show incoming changes with patches and full description::
3147 - show incoming changes with patches and full description::
3146
3148
3147 hg incoming -vp
3149 hg incoming -vp
3148
3150
3149 - show incoming changes excluding merges, store a bundle::
3151 - show incoming changes excluding merges, store a bundle::
3150
3152
3151 hg in -vpM --bundle incoming.hg
3153 hg in -vpM --bundle incoming.hg
3152 hg pull incoming.hg
3154 hg pull incoming.hg
3153
3155
3154 - briefly list changes inside a bundle::
3156 - briefly list changes inside a bundle::
3155
3157
3156 hg in changes.hg -T "{desc|firstline}\\n"
3158 hg in changes.hg -T "{desc|firstline}\\n"
3157
3159
3158 Returns 0 if there are incoming changes, 1 otherwise.
3160 Returns 0 if there are incoming changes, 1 otherwise.
3159 """
3161 """
3160 opts = pycompat.byteskwargs(opts)
3162 opts = pycompat.byteskwargs(opts)
3161 if opts.get('graph'):
3163 if opts.get('graph'):
3162 cmdutil.checkunsupportedgraphflags([], opts)
3164 cmdutil.checkunsupportedgraphflags([], opts)
3163 def display(other, chlist, displayer):
3165 def display(other, chlist, displayer):
3164 revdag = cmdutil.graphrevs(other, chlist, opts)
3166 revdag = cmdutil.graphrevs(other, chlist, opts)
3165 cmdutil.displaygraph(ui, repo, revdag, displayer,
3167 cmdutil.displaygraph(ui, repo, revdag, displayer,
3166 graphmod.asciiedges)
3168 graphmod.asciiedges)
3167
3169
3168 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3170 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3169 return 0
3171 return 0
3170
3172
3171 if opts.get('bundle') and opts.get('subrepos'):
3173 if opts.get('bundle') and opts.get('subrepos'):
3172 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3174 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3173
3175
3174 if opts.get('bookmarks'):
3176 if opts.get('bookmarks'):
3175 source, branches = hg.parseurl(ui.expandpath(source),
3177 source, branches = hg.parseurl(ui.expandpath(source),
3176 opts.get('branch'))
3178 opts.get('branch'))
3177 other = hg.peer(repo, opts, source)
3179 other = hg.peer(repo, opts, source)
3178 if 'bookmarks' not in other.listkeys('namespaces'):
3180 if 'bookmarks' not in other.listkeys('namespaces'):
3179 ui.warn(_("remote doesn't support bookmarks\n"))
3181 ui.warn(_("remote doesn't support bookmarks\n"))
3180 return 0
3182 return 0
3181 ui.pager('incoming')
3183 ui.pager('incoming')
3182 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3184 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3183 return bookmarks.incoming(ui, repo, other)
3185 return bookmarks.incoming(ui, repo, other)
3184
3186
3185 repo._subtoppath = ui.expandpath(source)
3187 repo._subtoppath = ui.expandpath(source)
3186 try:
3188 try:
3187 return hg.incoming(ui, repo, source, opts)
3189 return hg.incoming(ui, repo, source, opts)
3188 finally:
3190 finally:
3189 del repo._subtoppath
3191 del repo._subtoppath
3190
3192
3191
3193
3192 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3194 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3193 norepo=True)
3195 norepo=True)
3194 def init(ui, dest=".", **opts):
3196 def init(ui, dest=".", **opts):
3195 """create a new repository in the given directory
3197 """create a new repository in the given directory
3196
3198
3197 Initialize a new repository in the given directory. If the given
3199 Initialize a new repository in the given directory. If the given
3198 directory does not exist, it will be created.
3200 directory does not exist, it will be created.
3199
3201
3200 If no directory is given, the current directory is used.
3202 If no directory is given, the current directory is used.
3201
3203
3202 It is possible to specify an ``ssh://`` URL as the destination.
3204 It is possible to specify an ``ssh://`` URL as the destination.
3203 See :hg:`help urls` for more information.
3205 See :hg:`help urls` for more information.
3204
3206
3205 Returns 0 on success.
3207 Returns 0 on success.
3206 """
3208 """
3207 opts = pycompat.byteskwargs(opts)
3209 opts = pycompat.byteskwargs(opts)
3208 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3210 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3209
3211
3210 @command('locate',
3212 @command('locate',
3211 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3213 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3212 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3214 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3213 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3215 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3214 ] + walkopts,
3216 ] + walkopts,
3215 _('[OPTION]... [PATTERN]...'))
3217 _('[OPTION]... [PATTERN]...'))
3216 def locate(ui, repo, *pats, **opts):
3218 def locate(ui, repo, *pats, **opts):
3217 """locate files matching specific patterns (DEPRECATED)
3219 """locate files matching specific patterns (DEPRECATED)
3218
3220
3219 Print files under Mercurial control in the working directory whose
3221 Print files under Mercurial control in the working directory whose
3220 names match the given patterns.
3222 names match the given patterns.
3221
3223
3222 By default, this command searches all directories in the working
3224 By default, this command searches all directories in the working
3223 directory. To search just the current directory and its
3225 directory. To search just the current directory and its
3224 subdirectories, use "--include .".
3226 subdirectories, use "--include .".
3225
3227
3226 If no patterns are given to match, this command prints the names
3228 If no patterns are given to match, this command prints the names
3227 of all files under Mercurial control in the working directory.
3229 of all files under Mercurial control in the working directory.
3228
3230
3229 If you want to feed the output of this command into the "xargs"
3231 If you want to feed the output of this command into the "xargs"
3230 command, use the -0 option to both this command and "xargs". This
3232 command, use the -0 option to both this command and "xargs". This
3231 will avoid the problem of "xargs" treating single filenames that
3233 will avoid the problem of "xargs" treating single filenames that
3232 contain whitespace as multiple filenames.
3234 contain whitespace as multiple filenames.
3233
3235
3234 See :hg:`help files` for a more versatile command.
3236 See :hg:`help files` for a more versatile command.
3235
3237
3236 Returns 0 if a match is found, 1 otherwise.
3238 Returns 0 if a match is found, 1 otherwise.
3237 """
3239 """
3238 opts = pycompat.byteskwargs(opts)
3240 opts = pycompat.byteskwargs(opts)
3239 if opts.get('print0'):
3241 if opts.get('print0'):
3240 end = '\0'
3242 end = '\0'
3241 else:
3243 else:
3242 end = '\n'
3244 end = '\n'
3243 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3245 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3244
3246
3245 ret = 1
3247 ret = 1
3246 ctx = repo[rev]
3248 ctx = repo[rev]
3247 m = scmutil.match(ctx, pats, opts, default='relglob',
3249 m = scmutil.match(ctx, pats, opts, default='relglob',
3248 badfn=lambda x, y: False)
3250 badfn=lambda x, y: False)
3249
3251
3250 ui.pager('locate')
3252 ui.pager('locate')
3251 for abs in ctx.matches(m):
3253 for abs in ctx.matches(m):
3252 if opts.get('fullpath'):
3254 if opts.get('fullpath'):
3253 ui.write(repo.wjoin(abs), end)
3255 ui.write(repo.wjoin(abs), end)
3254 else:
3256 else:
3255 ui.write(((pats and m.rel(abs)) or abs), end)
3257 ui.write(((pats and m.rel(abs)) or abs), end)
3256 ret = 0
3258 ret = 0
3257
3259
3258 return ret
3260 return ret
3259
3261
3260 @command('^log|history',
3262 @command('^log|history',
3261 [('f', 'follow', None,
3263 [('f', 'follow', None,
3262 _('follow changeset history, or file history across copies and renames')),
3264 _('follow changeset history, or file history across copies and renames')),
3263 ('', 'follow-first', None,
3265 ('', 'follow-first', None,
3264 _('only follow the first parent of merge changesets (DEPRECATED)')),
3266 _('only follow the first parent of merge changesets (DEPRECATED)')),
3265 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3267 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3266 ('C', 'copies', None, _('show copied files')),
3268 ('C', 'copies', None, _('show copied files')),
3267 ('k', 'keyword', [],
3269 ('k', 'keyword', [],
3268 _('do case-insensitive search for a given text'), _('TEXT')),
3270 _('do case-insensitive search for a given text'), _('TEXT')),
3269 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3271 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3270 ('', 'removed', None, _('include revisions where files were removed')),
3272 ('', 'removed', None, _('include revisions where files were removed')),
3271 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3273 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3272 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3274 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3273 ('', 'only-branch', [],
3275 ('', 'only-branch', [],
3274 _('show only changesets within the given named branch (DEPRECATED)'),
3276 _('show only changesets within the given named branch (DEPRECATED)'),
3275 _('BRANCH')),
3277 _('BRANCH')),
3276 ('b', 'branch', [],
3278 ('b', 'branch', [],
3277 _('show changesets within the given named branch'), _('BRANCH')),
3279 _('show changesets within the given named branch'), _('BRANCH')),
3278 ('P', 'prune', [],
3280 ('P', 'prune', [],
3279 _('do not display revision or any of its ancestors'), _('REV')),
3281 _('do not display revision or any of its ancestors'), _('REV')),
3280 ] + logopts + walkopts,
3282 ] + logopts + walkopts,
3281 _('[OPTION]... [FILE]'),
3283 _('[OPTION]... [FILE]'),
3282 inferrepo=True)
3284 inferrepo=True)
3283 def log(ui, repo, *pats, **opts):
3285 def log(ui, repo, *pats, **opts):
3284 """show revision history of entire repository or files
3286 """show revision history of entire repository or files
3285
3287
3286 Print the revision history of the specified files or the entire
3288 Print the revision history of the specified files or the entire
3287 project.
3289 project.
3288
3290
3289 If no revision range is specified, the default is ``tip:0`` unless
3291 If no revision range is specified, the default is ``tip:0`` unless
3290 --follow is set, in which case the working directory parent is
3292 --follow is set, in which case the working directory parent is
3291 used as the starting revision.
3293 used as the starting revision.
3292
3294
3293 File history is shown without following rename or copy history of
3295 File history is shown without following rename or copy history of
3294 files. Use -f/--follow with a filename to follow history across
3296 files. Use -f/--follow with a filename to follow history across
3295 renames and copies. --follow without a filename will only show
3297 renames and copies. --follow without a filename will only show
3296 ancestors or descendants of the starting revision.
3298 ancestors or descendants of the starting revision.
3297
3299
3298 By default this command prints revision number and changeset id,
3300 By default this command prints revision number and changeset id,
3299 tags, non-trivial parents, user, date and time, and a summary for
3301 tags, non-trivial parents, user, date and time, and a summary for
3300 each commit. When the -v/--verbose switch is used, the list of
3302 each commit. When the -v/--verbose switch is used, the list of
3301 changed files and full commit message are shown.
3303 changed files and full commit message are shown.
3302
3304
3303 With --graph the revisions are shown as an ASCII art DAG with the most
3305 With --graph the revisions are shown as an ASCII art DAG with the most
3304 recent changeset at the top.
3306 recent changeset at the top.
3305 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
3307 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
3306 and '+' represents a fork where the changeset from the lines below is a
3308 and '+' represents a fork where the changeset from the lines below is a
3307 parent of the 'o' merge on the same line.
3309 parent of the 'o' merge on the same line.
3308 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3310 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3309 of a '|' indicates one or more revisions in a path are omitted.
3311 of a '|' indicates one or more revisions in a path are omitted.
3310
3312
3311 .. note::
3313 .. note::
3312
3314
3313 :hg:`log --patch` may generate unexpected diff output for merge
3315 :hg:`log --patch` may generate unexpected diff output for merge
3314 changesets, as it will only compare the merge changeset against
3316 changesets, as it will only compare the merge changeset against
3315 its first parent. Also, only files different from BOTH parents
3317 its first parent. Also, only files different from BOTH parents
3316 will appear in files:.
3318 will appear in files:.
3317
3319
3318 .. note::
3320 .. note::
3319
3321
3320 For performance reasons, :hg:`log FILE` may omit duplicate changes
3322 For performance reasons, :hg:`log FILE` may omit duplicate changes
3321 made on branches and will not show removals or mode changes. To
3323 made on branches and will not show removals or mode changes. To
3322 see all such changes, use the --removed switch.
3324 see all such changes, use the --removed switch.
3323
3325
3324 .. container:: verbose
3326 .. container:: verbose
3325
3327
3326 Some examples:
3328 Some examples:
3327
3329
3328 - changesets with full descriptions and file lists::
3330 - changesets with full descriptions and file lists::
3329
3331
3330 hg log -v
3332 hg log -v
3331
3333
3332 - changesets ancestral to the working directory::
3334 - changesets ancestral to the working directory::
3333
3335
3334 hg log -f
3336 hg log -f
3335
3337
3336 - last 10 commits on the current branch::
3338 - last 10 commits on the current branch::
3337
3339
3338 hg log -l 10 -b .
3340 hg log -l 10 -b .
3339
3341
3340 - changesets showing all modifications of a file, including removals::
3342 - changesets showing all modifications of a file, including removals::
3341
3343
3342 hg log --removed file.c
3344 hg log --removed file.c
3343
3345
3344 - all changesets that touch a directory, with diffs, excluding merges::
3346 - all changesets that touch a directory, with diffs, excluding merges::
3345
3347
3346 hg log -Mp lib/
3348 hg log -Mp lib/
3347
3349
3348 - all revision numbers that match a keyword::
3350 - all revision numbers that match a keyword::
3349
3351
3350 hg log -k bug --template "{rev}\\n"
3352 hg log -k bug --template "{rev}\\n"
3351
3353
3352 - the full hash identifier of the working directory parent::
3354 - the full hash identifier of the working directory parent::
3353
3355
3354 hg log -r . --template "{node}\\n"
3356 hg log -r . --template "{node}\\n"
3355
3357
3356 - list available log templates::
3358 - list available log templates::
3357
3359
3358 hg log -T list
3360 hg log -T list
3359
3361
3360 - check if a given changeset is included in a tagged release::
3362 - check if a given changeset is included in a tagged release::
3361
3363
3362 hg log -r "a21ccf and ancestor(1.9)"
3364 hg log -r "a21ccf and ancestor(1.9)"
3363
3365
3364 - find all changesets by some user in a date range::
3366 - find all changesets by some user in a date range::
3365
3367
3366 hg log -k alice -d "may 2008 to jul 2008"
3368 hg log -k alice -d "may 2008 to jul 2008"
3367
3369
3368 - summary of all changesets after the last tag::
3370 - summary of all changesets after the last tag::
3369
3371
3370 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3372 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3371
3373
3372 See :hg:`help dates` for a list of formats valid for -d/--date.
3374 See :hg:`help dates` for a list of formats valid for -d/--date.
3373
3375
3374 See :hg:`help revisions` for more about specifying and ordering
3376 See :hg:`help revisions` for more about specifying and ordering
3375 revisions.
3377 revisions.
3376
3378
3377 See :hg:`help templates` for more about pre-packaged styles and
3379 See :hg:`help templates` for more about pre-packaged styles and
3378 specifying custom templates.
3380 specifying custom templates.
3379
3381
3380 Returns 0 on success.
3382 Returns 0 on success.
3381
3383
3382 """
3384 """
3383 opts = pycompat.byteskwargs(opts)
3385 opts = pycompat.byteskwargs(opts)
3384 if opts.get('follow') and opts.get('rev'):
3386 if opts.get('follow') and opts.get('rev'):
3385 opts['rev'] = [revsetlang.formatspec('reverse(::%lr)', opts.get('rev'))]
3387 opts['rev'] = [revsetlang.formatspec('reverse(::%lr)', opts.get('rev'))]
3386 del opts['follow']
3388 del opts['follow']
3387
3389
3388 if opts.get('graph'):
3390 if opts.get('graph'):
3389 return cmdutil.graphlog(ui, repo, pats, opts)
3391 return cmdutil.graphlog(ui, repo, pats, opts)
3390
3392
3391 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
3393 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
3392 limit = cmdutil.loglimit(opts)
3394 limit = cmdutil.loglimit(opts)
3393 count = 0
3395 count = 0
3394
3396
3395 getrenamed = None
3397 getrenamed = None
3396 if opts.get('copies'):
3398 if opts.get('copies'):
3397 endrev = None
3399 endrev = None
3398 if opts.get('rev'):
3400 if opts.get('rev'):
3399 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
3401 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
3400 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3402 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3401
3403
3402 ui.pager('log')
3404 ui.pager('log')
3403 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3405 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3404 for rev in revs:
3406 for rev in revs:
3405 if count == limit:
3407 if count == limit:
3406 break
3408 break
3407 ctx = repo[rev]
3409 ctx = repo[rev]
3408 copies = None
3410 copies = None
3409 if getrenamed is not None and rev:
3411 if getrenamed is not None and rev:
3410 copies = []
3412 copies = []
3411 for fn in ctx.files():
3413 for fn in ctx.files():
3412 rename = getrenamed(fn, rev)
3414 rename = getrenamed(fn, rev)
3413 if rename:
3415 if rename:
3414 copies.append((fn, rename[0]))
3416 copies.append((fn, rename[0]))
3415 if filematcher:
3417 if filematcher:
3416 revmatchfn = filematcher(ctx.rev())
3418 revmatchfn = filematcher(ctx.rev())
3417 else:
3419 else:
3418 revmatchfn = None
3420 revmatchfn = None
3419 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3421 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3420 if displayer.flush(ctx):
3422 if displayer.flush(ctx):
3421 count += 1
3423 count += 1
3422
3424
3423 displayer.close()
3425 displayer.close()
3424
3426
3425 @command('manifest',
3427 @command('manifest',
3426 [('r', 'rev', '', _('revision to display'), _('REV')),
3428 [('r', 'rev', '', _('revision to display'), _('REV')),
3427 ('', 'all', False, _("list files from all revisions"))]
3429 ('', 'all', False, _("list files from all revisions"))]
3428 + formatteropts,
3430 + formatteropts,
3429 _('[-r REV]'))
3431 _('[-r REV]'))
3430 def manifest(ui, repo, node=None, rev=None, **opts):
3432 def manifest(ui, repo, node=None, rev=None, **opts):
3431 """output the current or given revision of the project manifest
3433 """output the current or given revision of the project manifest
3432
3434
3433 Print a list of version controlled files for the given revision.
3435 Print a list of version controlled files for the given revision.
3434 If no revision is given, the first parent of the working directory
3436 If no revision is given, the first parent of the working directory
3435 is used, or the null revision if no revision is checked out.
3437 is used, or the null revision if no revision is checked out.
3436
3438
3437 With -v, print file permissions, symlink and executable bits.
3439 With -v, print file permissions, symlink and executable bits.
3438 With --debug, print file revision hashes.
3440 With --debug, print file revision hashes.
3439
3441
3440 If option --all is specified, the list of all files from all revisions
3442 If option --all is specified, the list of all files from all revisions
3441 is printed. This includes deleted and renamed files.
3443 is printed. This includes deleted and renamed files.
3442
3444
3443 Returns 0 on success.
3445 Returns 0 on success.
3444 """
3446 """
3445 opts = pycompat.byteskwargs(opts)
3447 opts = pycompat.byteskwargs(opts)
3446 fm = ui.formatter('manifest', opts)
3448 fm = ui.formatter('manifest', opts)
3447
3449
3448 if opts.get('all'):
3450 if opts.get('all'):
3449 if rev or node:
3451 if rev or node:
3450 raise error.Abort(_("can't specify a revision with --all"))
3452 raise error.Abort(_("can't specify a revision with --all"))
3451
3453
3452 res = []
3454 res = []
3453 prefix = "data/"
3455 prefix = "data/"
3454 suffix = ".i"
3456 suffix = ".i"
3455 plen = len(prefix)
3457 plen = len(prefix)
3456 slen = len(suffix)
3458 slen = len(suffix)
3457 with repo.lock():
3459 with repo.lock():
3458 for fn, b, size in repo.store.datafiles():
3460 for fn, b, size in repo.store.datafiles():
3459 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3461 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3460 res.append(fn[plen:-slen])
3462 res.append(fn[plen:-slen])
3461 ui.pager('manifest')
3463 ui.pager('manifest')
3462 for f in res:
3464 for f in res:
3463 fm.startitem()
3465 fm.startitem()
3464 fm.write("path", '%s\n', f)
3466 fm.write("path", '%s\n', f)
3465 fm.end()
3467 fm.end()
3466 return
3468 return
3467
3469
3468 if rev and node:
3470 if rev and node:
3469 raise error.Abort(_("please specify just one revision"))
3471 raise error.Abort(_("please specify just one revision"))
3470
3472
3471 if not node:
3473 if not node:
3472 node = rev
3474 node = rev
3473
3475
3474 char = {'l': '@', 'x': '*', '': ''}
3476 char = {'l': '@', 'x': '*', '': ''}
3475 mode = {'l': '644', 'x': '755', '': '644'}
3477 mode = {'l': '644', 'x': '755', '': '644'}
3476 ctx = scmutil.revsingle(repo, node)
3478 ctx = scmutil.revsingle(repo, node)
3477 mf = ctx.manifest()
3479 mf = ctx.manifest()
3478 ui.pager('manifest')
3480 ui.pager('manifest')
3479 for f in ctx:
3481 for f in ctx:
3480 fm.startitem()
3482 fm.startitem()
3481 fl = ctx[f].flags()
3483 fl = ctx[f].flags()
3482 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3484 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3483 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3485 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3484 fm.write('path', '%s\n', f)
3486 fm.write('path', '%s\n', f)
3485 fm.end()
3487 fm.end()
3486
3488
3487 @command('^merge',
3489 @command('^merge',
3488 [('f', 'force', None,
3490 [('f', 'force', None,
3489 _('force a merge including outstanding changes (DEPRECATED)')),
3491 _('force a merge including outstanding changes (DEPRECATED)')),
3490 ('r', 'rev', '', _('revision to merge'), _('REV')),
3492 ('r', 'rev', '', _('revision to merge'), _('REV')),
3491 ('P', 'preview', None,
3493 ('P', 'preview', None,
3492 _('review revisions to merge (no merge is performed)'))
3494 _('review revisions to merge (no merge is performed)'))
3493 ] + mergetoolopts,
3495 ] + mergetoolopts,
3494 _('[-P] [[-r] REV]'))
3496 _('[-P] [[-r] REV]'))
3495 def merge(ui, repo, node=None, **opts):
3497 def merge(ui, repo, node=None, **opts):
3496 """merge another revision into working directory
3498 """merge another revision into working directory
3497
3499
3498 The current working directory is updated with all changes made in
3500 The current working directory is updated with all changes made in
3499 the requested revision since the last common predecessor revision.
3501 the requested revision since the last common predecessor revision.
3500
3502
3501 Files that changed between either parent are marked as changed for
3503 Files that changed between either parent are marked as changed for
3502 the next commit and a commit must be performed before any further
3504 the next commit and a commit must be performed before any further
3503 updates to the repository are allowed. The next commit will have
3505 updates to the repository are allowed. The next commit will have
3504 two parents.
3506 two parents.
3505
3507
3506 ``--tool`` can be used to specify the merge tool used for file
3508 ``--tool`` can be used to specify the merge tool used for file
3507 merges. It overrides the HGMERGE environment variable and your
3509 merges. It overrides the HGMERGE environment variable and your
3508 configuration files. See :hg:`help merge-tools` for options.
3510 configuration files. See :hg:`help merge-tools` for options.
3509
3511
3510 If no revision is specified, the working directory's parent is a
3512 If no revision is specified, the working directory's parent is a
3511 head revision, and the current branch contains exactly one other
3513 head revision, and the current branch contains exactly one other
3512 head, the other head is merged with by default. Otherwise, an
3514 head, the other head is merged with by default. Otherwise, an
3513 explicit revision with which to merge with must be provided.
3515 explicit revision with which to merge with must be provided.
3514
3516
3515 See :hg:`help resolve` for information on handling file conflicts.
3517 See :hg:`help resolve` for information on handling file conflicts.
3516
3518
3517 To undo an uncommitted merge, use :hg:`update --clean .` which
3519 To undo an uncommitted merge, use :hg:`update --clean .` which
3518 will check out a clean copy of the original merge parent, losing
3520 will check out a clean copy of the original merge parent, losing
3519 all changes.
3521 all changes.
3520
3522
3521 Returns 0 on success, 1 if there are unresolved files.
3523 Returns 0 on success, 1 if there are unresolved files.
3522 """
3524 """
3523
3525
3524 opts = pycompat.byteskwargs(opts)
3526 opts = pycompat.byteskwargs(opts)
3525 if opts.get('rev') and node:
3527 if opts.get('rev') and node:
3526 raise error.Abort(_("please specify just one revision"))
3528 raise error.Abort(_("please specify just one revision"))
3527 if not node:
3529 if not node:
3528 node = opts.get('rev')
3530 node = opts.get('rev')
3529
3531
3530 if node:
3532 if node:
3531 node = scmutil.revsingle(repo, node).node()
3533 node = scmutil.revsingle(repo, node).node()
3532
3534
3533 if not node:
3535 if not node:
3534 node = repo[destutil.destmerge(repo)].node()
3536 node = repo[destutil.destmerge(repo)].node()
3535
3537
3536 if opts.get('preview'):
3538 if opts.get('preview'):
3537 # find nodes that are ancestors of p2 but not of p1
3539 # find nodes that are ancestors of p2 but not of p1
3538 p1 = repo.lookup('.')
3540 p1 = repo.lookup('.')
3539 p2 = repo.lookup(node)
3541 p2 = repo.lookup(node)
3540 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3542 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3541
3543
3542 displayer = cmdutil.show_changeset(ui, repo, opts)
3544 displayer = cmdutil.show_changeset(ui, repo, opts)
3543 for node in nodes:
3545 for node in nodes:
3544 displayer.show(repo[node])
3546 displayer.show(repo[node])
3545 displayer.close()
3547 displayer.close()
3546 return 0
3548 return 0
3547
3549
3548 try:
3550 try:
3549 # ui.forcemerge is an internal variable, do not document
3551 # ui.forcemerge is an internal variable, do not document
3550 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
3552 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
3551 force = opts.get('force')
3553 force = opts.get('force')
3552 labels = ['working copy', 'merge rev']
3554 labels = ['working copy', 'merge rev']
3553 return hg.merge(repo, node, force=force, mergeforce=force,
3555 return hg.merge(repo, node, force=force, mergeforce=force,
3554 labels=labels)
3556 labels=labels)
3555 finally:
3557 finally:
3556 ui.setconfig('ui', 'forcemerge', '', 'merge')
3558 ui.setconfig('ui', 'forcemerge', '', 'merge')
3557
3559
3558 @command('outgoing|out',
3560 @command('outgoing|out',
3559 [('f', 'force', None, _('run even when the destination is unrelated')),
3561 [('f', 'force', None, _('run even when the destination is unrelated')),
3560 ('r', 'rev', [],
3562 ('r', 'rev', [],
3561 _('a changeset intended to be included in the destination'), _('REV')),
3563 _('a changeset intended to be included in the destination'), _('REV')),
3562 ('n', 'newest-first', None, _('show newest record first')),
3564 ('n', 'newest-first', None, _('show newest record first')),
3563 ('B', 'bookmarks', False, _('compare bookmarks')),
3565 ('B', 'bookmarks', False, _('compare bookmarks')),
3564 ('b', 'branch', [], _('a specific branch you would like to push'),
3566 ('b', 'branch', [], _('a specific branch you would like to push'),
3565 _('BRANCH')),
3567 _('BRANCH')),
3566 ] + logopts + remoteopts + subrepoopts,
3568 ] + logopts + remoteopts + subrepoopts,
3567 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3569 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3568 def outgoing(ui, repo, dest=None, **opts):
3570 def outgoing(ui, repo, dest=None, **opts):
3569 """show changesets not found in the destination
3571 """show changesets not found in the destination
3570
3572
3571 Show changesets not found in the specified destination repository
3573 Show changesets not found in the specified destination repository
3572 or the default push location. These are the changesets that would
3574 or the default push location. These are the changesets that would
3573 be pushed if a push was requested.
3575 be pushed if a push was requested.
3574
3576
3575 See pull for details of valid destination formats.
3577 See pull for details of valid destination formats.
3576
3578
3577 .. container:: verbose
3579 .. container:: verbose
3578
3580
3579 With -B/--bookmarks, the result of bookmark comparison between
3581 With -B/--bookmarks, the result of bookmark comparison between
3580 local and remote repositories is displayed. With -v/--verbose,
3582 local and remote repositories is displayed. With -v/--verbose,
3581 status is also displayed for each bookmark like below::
3583 status is also displayed for each bookmark like below::
3582
3584
3583 BM1 01234567890a added
3585 BM1 01234567890a added
3584 BM2 deleted
3586 BM2 deleted
3585 BM3 234567890abc advanced
3587 BM3 234567890abc advanced
3586 BM4 34567890abcd diverged
3588 BM4 34567890abcd diverged
3587 BM5 4567890abcde changed
3589 BM5 4567890abcde changed
3588
3590
3589 The action taken when pushing depends on the
3591 The action taken when pushing depends on the
3590 status of each bookmark:
3592 status of each bookmark:
3591
3593
3592 :``added``: push with ``-B`` will create it
3594 :``added``: push with ``-B`` will create it
3593 :``deleted``: push with ``-B`` will delete it
3595 :``deleted``: push with ``-B`` will delete it
3594 :``advanced``: push will update it
3596 :``advanced``: push will update it
3595 :``diverged``: push with ``-B`` will update it
3597 :``diverged``: push with ``-B`` will update it
3596 :``changed``: push with ``-B`` will update it
3598 :``changed``: push with ``-B`` will update it
3597
3599
3598 From the point of view of pushing behavior, bookmarks
3600 From the point of view of pushing behavior, bookmarks
3599 existing only in the remote repository are treated as
3601 existing only in the remote repository are treated as
3600 ``deleted``, even if it is in fact added remotely.
3602 ``deleted``, even if it is in fact added remotely.
3601
3603
3602 Returns 0 if there are outgoing changes, 1 otherwise.
3604 Returns 0 if there are outgoing changes, 1 otherwise.
3603 """
3605 """
3604 opts = pycompat.byteskwargs(opts)
3606 opts = pycompat.byteskwargs(opts)
3605 if opts.get('graph'):
3607 if opts.get('graph'):
3606 cmdutil.checkunsupportedgraphflags([], opts)
3608 cmdutil.checkunsupportedgraphflags([], opts)
3607 o, other = hg._outgoing(ui, repo, dest, opts)
3609 o, other = hg._outgoing(ui, repo, dest, opts)
3608 if not o:
3610 if not o:
3609 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3611 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3610 return
3612 return
3611
3613
3612 revdag = cmdutil.graphrevs(repo, o, opts)
3614 revdag = cmdutil.graphrevs(repo, o, opts)
3613 ui.pager('outgoing')
3615 ui.pager('outgoing')
3614 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3616 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3615 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
3617 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
3616 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3618 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3617 return 0
3619 return 0
3618
3620
3619 if opts.get('bookmarks'):
3621 if opts.get('bookmarks'):
3620 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3622 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3621 dest, branches = hg.parseurl(dest, opts.get('branch'))
3623 dest, branches = hg.parseurl(dest, opts.get('branch'))
3622 other = hg.peer(repo, opts, dest)
3624 other = hg.peer(repo, opts, dest)
3623 if 'bookmarks' not in other.listkeys('namespaces'):
3625 if 'bookmarks' not in other.listkeys('namespaces'):
3624 ui.warn(_("remote doesn't support bookmarks\n"))
3626 ui.warn(_("remote doesn't support bookmarks\n"))
3625 return 0
3627 return 0
3626 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3628 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3627 ui.pager('outgoing')
3629 ui.pager('outgoing')
3628 return bookmarks.outgoing(ui, repo, other)
3630 return bookmarks.outgoing(ui, repo, other)
3629
3631
3630 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3632 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3631 try:
3633 try:
3632 return hg.outgoing(ui, repo, dest, opts)
3634 return hg.outgoing(ui, repo, dest, opts)
3633 finally:
3635 finally:
3634 del repo._subtoppath
3636 del repo._subtoppath
3635
3637
3636 @command('parents',
3638 @command('parents',
3637 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3639 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3638 ] + templateopts,
3640 ] + templateopts,
3639 _('[-r REV] [FILE]'),
3641 _('[-r REV] [FILE]'),
3640 inferrepo=True)
3642 inferrepo=True)
3641 def parents(ui, repo, file_=None, **opts):
3643 def parents(ui, repo, file_=None, **opts):
3642 """show the parents of the working directory or revision (DEPRECATED)
3644 """show the parents of the working directory or revision (DEPRECATED)
3643
3645
3644 Print the working directory's parent revisions. If a revision is
3646 Print the working directory's parent revisions. If a revision is
3645 given via -r/--rev, the parent of that revision will be printed.
3647 given via -r/--rev, the parent of that revision will be printed.
3646 If a file argument is given, the revision in which the file was
3648 If a file argument is given, the revision in which the file was
3647 last changed (before the working directory revision or the
3649 last changed (before the working directory revision or the
3648 argument to --rev if given) is printed.
3650 argument to --rev if given) is printed.
3649
3651
3650 This command is equivalent to::
3652 This command is equivalent to::
3651
3653
3652 hg log -r "p1()+p2()" or
3654 hg log -r "p1()+p2()" or
3653 hg log -r "p1(REV)+p2(REV)" or
3655 hg log -r "p1(REV)+p2(REV)" or
3654 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
3656 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
3655 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
3657 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
3656
3658
3657 See :hg:`summary` and :hg:`help revsets` for related information.
3659 See :hg:`summary` and :hg:`help revsets` for related information.
3658
3660
3659 Returns 0 on success.
3661 Returns 0 on success.
3660 """
3662 """
3661
3663
3662 opts = pycompat.byteskwargs(opts)
3664 opts = pycompat.byteskwargs(opts)
3663 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3665 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3664
3666
3665 if file_:
3667 if file_:
3666 m = scmutil.match(ctx, (file_,), opts)
3668 m = scmutil.match(ctx, (file_,), opts)
3667 if m.anypats() or len(m.files()) != 1:
3669 if m.anypats() or len(m.files()) != 1:
3668 raise error.Abort(_('can only specify an explicit filename'))
3670 raise error.Abort(_('can only specify an explicit filename'))
3669 file_ = m.files()[0]
3671 file_ = m.files()[0]
3670 filenodes = []
3672 filenodes = []
3671 for cp in ctx.parents():
3673 for cp in ctx.parents():
3672 if not cp:
3674 if not cp:
3673 continue
3675 continue
3674 try:
3676 try:
3675 filenodes.append(cp.filenode(file_))
3677 filenodes.append(cp.filenode(file_))
3676 except error.LookupError:
3678 except error.LookupError:
3677 pass
3679 pass
3678 if not filenodes:
3680 if not filenodes:
3679 raise error.Abort(_("'%s' not found in manifest!") % file_)
3681 raise error.Abort(_("'%s' not found in manifest!") % file_)
3680 p = []
3682 p = []
3681 for fn in filenodes:
3683 for fn in filenodes:
3682 fctx = repo.filectx(file_, fileid=fn)
3684 fctx = repo.filectx(file_, fileid=fn)
3683 p.append(fctx.node())
3685 p.append(fctx.node())
3684 else:
3686 else:
3685 p = [cp.node() for cp in ctx.parents()]
3687 p = [cp.node() for cp in ctx.parents()]
3686
3688
3687 displayer = cmdutil.show_changeset(ui, repo, opts)
3689 displayer = cmdutil.show_changeset(ui, repo, opts)
3688 for n in p:
3690 for n in p:
3689 if n != nullid:
3691 if n != nullid:
3690 displayer.show(repo[n])
3692 displayer.show(repo[n])
3691 displayer.close()
3693 displayer.close()
3692
3694
3693 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True)
3695 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True)
3694 def paths(ui, repo, search=None, **opts):
3696 def paths(ui, repo, search=None, **opts):
3695 """show aliases for remote repositories
3697 """show aliases for remote repositories
3696
3698
3697 Show definition of symbolic path name NAME. If no name is given,
3699 Show definition of symbolic path name NAME. If no name is given,
3698 show definition of all available names.
3700 show definition of all available names.
3699
3701
3700 Option -q/--quiet suppresses all output when searching for NAME
3702 Option -q/--quiet suppresses all output when searching for NAME
3701 and shows only the path names when listing all definitions.
3703 and shows only the path names when listing all definitions.
3702
3704
3703 Path names are defined in the [paths] section of your
3705 Path names are defined in the [paths] section of your
3704 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3706 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3705 repository, ``.hg/hgrc`` is used, too.
3707 repository, ``.hg/hgrc`` is used, too.
3706
3708
3707 The path names ``default`` and ``default-push`` have a special
3709 The path names ``default`` and ``default-push`` have a special
3708 meaning. When performing a push or pull operation, they are used
3710 meaning. When performing a push or pull operation, they are used
3709 as fallbacks if no location is specified on the command-line.
3711 as fallbacks if no location is specified on the command-line.
3710 When ``default-push`` is set, it will be used for push and
3712 When ``default-push`` is set, it will be used for push and
3711 ``default`` will be used for pull; otherwise ``default`` is used
3713 ``default`` will be used for pull; otherwise ``default`` is used
3712 as the fallback for both. When cloning a repository, the clone
3714 as the fallback for both. When cloning a repository, the clone
3713 source is written as ``default`` in ``.hg/hgrc``.
3715 source is written as ``default`` in ``.hg/hgrc``.
3714
3716
3715 .. note::
3717 .. note::
3716
3718
3717 ``default`` and ``default-push`` apply to all inbound (e.g.
3719 ``default`` and ``default-push`` apply to all inbound (e.g.
3718 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
3720 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
3719 and :hg:`bundle`) operations.
3721 and :hg:`bundle`) operations.
3720
3722
3721 See :hg:`help urls` for more information.
3723 See :hg:`help urls` for more information.
3722
3724
3723 Returns 0 on success.
3725 Returns 0 on success.
3724 """
3726 """
3725
3727
3726 opts = pycompat.byteskwargs(opts)
3728 opts = pycompat.byteskwargs(opts)
3727 ui.pager('paths')
3729 ui.pager('paths')
3728 if search:
3730 if search:
3729 pathitems = [(name, path) for name, path in ui.paths.iteritems()
3731 pathitems = [(name, path) for name, path in ui.paths.iteritems()
3730 if name == search]
3732 if name == search]
3731 else:
3733 else:
3732 pathitems = sorted(ui.paths.iteritems())
3734 pathitems = sorted(ui.paths.iteritems())
3733
3735
3734 fm = ui.formatter('paths', opts)
3736 fm = ui.formatter('paths', opts)
3735 if fm.isplain():
3737 if fm.isplain():
3736 hidepassword = util.hidepassword
3738 hidepassword = util.hidepassword
3737 else:
3739 else:
3738 hidepassword = str
3740 hidepassword = str
3739 if ui.quiet:
3741 if ui.quiet:
3740 namefmt = '%s\n'
3742 namefmt = '%s\n'
3741 else:
3743 else:
3742 namefmt = '%s = '
3744 namefmt = '%s = '
3743 showsubopts = not search and not ui.quiet
3745 showsubopts = not search and not ui.quiet
3744
3746
3745 for name, path in pathitems:
3747 for name, path in pathitems:
3746 fm.startitem()
3748 fm.startitem()
3747 fm.condwrite(not search, 'name', namefmt, name)
3749 fm.condwrite(not search, 'name', namefmt, name)
3748 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
3750 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
3749 for subopt, value in sorted(path.suboptions.items()):
3751 for subopt, value in sorted(path.suboptions.items()):
3750 assert subopt not in ('name', 'url')
3752 assert subopt not in ('name', 'url')
3751 if showsubopts:
3753 if showsubopts:
3752 fm.plain('%s:%s = ' % (name, subopt))
3754 fm.plain('%s:%s = ' % (name, subopt))
3753 fm.condwrite(showsubopts, subopt, '%s\n', value)
3755 fm.condwrite(showsubopts, subopt, '%s\n', value)
3754
3756
3755 fm.end()
3757 fm.end()
3756
3758
3757 if search and not pathitems:
3759 if search and not pathitems:
3758 if not ui.quiet:
3760 if not ui.quiet:
3759 ui.warn(_("not found!\n"))
3761 ui.warn(_("not found!\n"))
3760 return 1
3762 return 1
3761 else:
3763 else:
3762 return 0
3764 return 0
3763
3765
3764 @command('phase',
3766 @command('phase',
3765 [('p', 'public', False, _('set changeset phase to public')),
3767 [('p', 'public', False, _('set changeset phase to public')),
3766 ('d', 'draft', False, _('set changeset phase to draft')),
3768 ('d', 'draft', False, _('set changeset phase to draft')),
3767 ('s', 'secret', False, _('set changeset phase to secret')),
3769 ('s', 'secret', False, _('set changeset phase to secret')),
3768 ('f', 'force', False, _('allow to move boundary backward')),
3770 ('f', 'force', False, _('allow to move boundary backward')),
3769 ('r', 'rev', [], _('target revision'), _('REV')),
3771 ('r', 'rev', [], _('target revision'), _('REV')),
3770 ],
3772 ],
3771 _('[-p|-d|-s] [-f] [-r] [REV...]'))
3773 _('[-p|-d|-s] [-f] [-r] [REV...]'))
3772 def phase(ui, repo, *revs, **opts):
3774 def phase(ui, repo, *revs, **opts):
3773 """set or show the current phase name
3775 """set or show the current phase name
3774
3776
3775 With no argument, show the phase name of the current revision(s).
3777 With no argument, show the phase name of the current revision(s).
3776
3778
3777 With one of -p/--public, -d/--draft or -s/--secret, change the
3779 With one of -p/--public, -d/--draft or -s/--secret, change the
3778 phase value of the specified revisions.
3780 phase value of the specified revisions.
3779
3781
3780 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
3782 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
3781 lower phase to an higher phase. Phases are ordered as follows::
3783 lower phase to an higher phase. Phases are ordered as follows::
3782
3784
3783 public < draft < secret
3785 public < draft < secret
3784
3786
3785 Returns 0 on success, 1 if some phases could not be changed.
3787 Returns 0 on success, 1 if some phases could not be changed.
3786
3788
3787 (For more information about the phases concept, see :hg:`help phases`.)
3789 (For more information about the phases concept, see :hg:`help phases`.)
3788 """
3790 """
3789 opts = pycompat.byteskwargs(opts)
3791 opts = pycompat.byteskwargs(opts)
3790 # search for a unique phase argument
3792 # search for a unique phase argument
3791 targetphase = None
3793 targetphase = None
3792 for idx, name in enumerate(phases.phasenames):
3794 for idx, name in enumerate(phases.phasenames):
3793 if opts[name]:
3795 if opts[name]:
3794 if targetphase is not None:
3796 if targetphase is not None:
3795 raise error.Abort(_('only one phase can be specified'))
3797 raise error.Abort(_('only one phase can be specified'))
3796 targetphase = idx
3798 targetphase = idx
3797
3799
3798 # look for specified revision
3800 # look for specified revision
3799 revs = list(revs)
3801 revs = list(revs)
3800 revs.extend(opts['rev'])
3802 revs.extend(opts['rev'])
3801 if not revs:
3803 if not revs:
3802 # display both parents as the second parent phase can influence
3804 # display both parents as the second parent phase can influence
3803 # the phase of a merge commit
3805 # the phase of a merge commit
3804 revs = [c.rev() for c in repo[None].parents()]
3806 revs = [c.rev() for c in repo[None].parents()]
3805
3807
3806 revs = scmutil.revrange(repo, revs)
3808 revs = scmutil.revrange(repo, revs)
3807
3809
3808 lock = None
3810 lock = None
3809 ret = 0
3811 ret = 0
3810 if targetphase is None:
3812 if targetphase is None:
3811 # display
3813 # display
3812 for r in revs:
3814 for r in revs:
3813 ctx = repo[r]
3815 ctx = repo[r]
3814 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
3816 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
3815 else:
3817 else:
3816 tr = None
3818 tr = None
3817 lock = repo.lock()
3819 lock = repo.lock()
3818 try:
3820 try:
3819 tr = repo.transaction("phase")
3821 tr = repo.transaction("phase")
3820 # set phase
3822 # set phase
3821 if not revs:
3823 if not revs:
3822 raise error.Abort(_('empty revision set'))
3824 raise error.Abort(_('empty revision set'))
3823 nodes = [repo[r].node() for r in revs]
3825 nodes = [repo[r].node() for r in revs]
3824 # moving revision from public to draft may hide them
3826 # moving revision from public to draft may hide them
3825 # We have to check result on an unfiltered repository
3827 # We have to check result on an unfiltered repository
3826 unfi = repo.unfiltered()
3828 unfi = repo.unfiltered()
3827 getphase = unfi._phasecache.phase
3829 getphase = unfi._phasecache.phase
3828 olddata = [getphase(unfi, r) for r in unfi]
3830 olddata = [getphase(unfi, r) for r in unfi]
3829 phases.advanceboundary(repo, tr, targetphase, nodes)
3831 phases.advanceboundary(repo, tr, targetphase, nodes)
3830 if opts['force']:
3832 if opts['force']:
3831 phases.retractboundary(repo, tr, targetphase, nodes)
3833 phases.retractboundary(repo, tr, targetphase, nodes)
3832 tr.close()
3834 tr.close()
3833 finally:
3835 finally:
3834 if tr is not None:
3836 if tr is not None:
3835 tr.release()
3837 tr.release()
3836 lock.release()
3838 lock.release()
3837 getphase = unfi._phasecache.phase
3839 getphase = unfi._phasecache.phase
3838 newdata = [getphase(unfi, r) for r in unfi]
3840 newdata = [getphase(unfi, r) for r in unfi]
3839 changes = sum(newdata[r] != olddata[r] for r in unfi)
3841 changes = sum(newdata[r] != olddata[r] for r in unfi)
3840 cl = unfi.changelog
3842 cl = unfi.changelog
3841 rejected = [n for n in nodes
3843 rejected = [n for n in nodes
3842 if newdata[cl.rev(n)] < targetphase]
3844 if newdata[cl.rev(n)] < targetphase]
3843 if rejected:
3845 if rejected:
3844 ui.warn(_('cannot move %i changesets to a higher '
3846 ui.warn(_('cannot move %i changesets to a higher '
3845 'phase, use --force\n') % len(rejected))
3847 'phase, use --force\n') % len(rejected))
3846 ret = 1
3848 ret = 1
3847 if changes:
3849 if changes:
3848 msg = _('phase changed for %i changesets\n') % changes
3850 msg = _('phase changed for %i changesets\n') % changes
3849 if ret:
3851 if ret:
3850 ui.status(msg)
3852 ui.status(msg)
3851 else:
3853 else:
3852 ui.note(msg)
3854 ui.note(msg)
3853 else:
3855 else:
3854 ui.warn(_('no phases changed\n'))
3856 ui.warn(_('no phases changed\n'))
3855 return ret
3857 return ret
3856
3858
3857 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
3859 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
3858 """Run after a changegroup has been added via pull/unbundle
3860 """Run after a changegroup has been added via pull/unbundle
3859
3861
3860 This takes arguments below:
3862 This takes arguments below:
3861
3863
3862 :modheads: change of heads by pull/unbundle
3864 :modheads: change of heads by pull/unbundle
3863 :optupdate: updating working directory is needed or not
3865 :optupdate: updating working directory is needed or not
3864 :checkout: update destination revision (or None to default destination)
3866 :checkout: update destination revision (or None to default destination)
3865 :brev: a name, which might be a bookmark to be activated after updating
3867 :brev: a name, which might be a bookmark to be activated after updating
3866 """
3868 """
3867 if modheads == 0:
3869 if modheads == 0:
3868 return
3870 return
3869 if optupdate:
3871 if optupdate:
3870 try:
3872 try:
3871 return hg.updatetotally(ui, repo, checkout, brev)
3873 return hg.updatetotally(ui, repo, checkout, brev)
3872 except error.UpdateAbort as inst:
3874 except error.UpdateAbort as inst:
3873 msg = _("not updating: %s") % str(inst)
3875 msg = _("not updating: %s") % str(inst)
3874 hint = inst.hint
3876 hint = inst.hint
3875 raise error.UpdateAbort(msg, hint=hint)
3877 raise error.UpdateAbort(msg, hint=hint)
3876 if modheads > 1:
3878 if modheads > 1:
3877 currentbranchheads = len(repo.branchheads())
3879 currentbranchheads = len(repo.branchheads())
3878 if currentbranchheads == modheads:
3880 if currentbranchheads == modheads:
3879 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3881 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3880 elif currentbranchheads > 1:
3882 elif currentbranchheads > 1:
3881 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
3883 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
3882 "merge)\n"))
3884 "merge)\n"))
3883 else:
3885 else:
3884 ui.status(_("(run 'hg heads' to see heads)\n"))
3886 ui.status(_("(run 'hg heads' to see heads)\n"))
3885 else:
3887 else:
3886 ui.status(_("(run 'hg update' to get a working copy)\n"))
3888 ui.status(_("(run 'hg update' to get a working copy)\n"))
3887
3889
3888 @command('^pull',
3890 @command('^pull',
3889 [('u', 'update', None,
3891 [('u', 'update', None,
3890 _('update to new branch head if changesets were pulled')),
3892 _('update to new branch head if changesets were pulled')),
3891 ('f', 'force', None, _('run even when remote repository is unrelated')),
3893 ('f', 'force', None, _('run even when remote repository is unrelated')),
3892 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3894 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3893 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3895 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3894 ('b', 'branch', [], _('a specific branch you would like to pull'),
3896 ('b', 'branch', [], _('a specific branch you would like to pull'),
3895 _('BRANCH')),
3897 _('BRANCH')),
3896 ] + remoteopts,
3898 ] + remoteopts,
3897 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3899 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3898 def pull(ui, repo, source="default", **opts):
3900 def pull(ui, repo, source="default", **opts):
3899 """pull changes from the specified source
3901 """pull changes from the specified source
3900
3902
3901 Pull changes from a remote repository to a local one.
3903 Pull changes from a remote repository to a local one.
3902
3904
3903 This finds all changes from the repository at the specified path
3905 This finds all changes from the repository at the specified path
3904 or URL and adds them to a local repository (the current one unless
3906 or URL and adds them to a local repository (the current one unless
3905 -R is specified). By default, this does not update the copy of the
3907 -R is specified). By default, this does not update the copy of the
3906 project in the working directory.
3908 project in the working directory.
3907
3909
3908 Use :hg:`incoming` if you want to see what would have been added
3910 Use :hg:`incoming` if you want to see what would have been added
3909 by a pull at the time you issued this command. If you then decide
3911 by a pull at the time you issued this command. If you then decide
3910 to add those changes to the repository, you should use :hg:`pull
3912 to add those changes to the repository, you should use :hg:`pull
3911 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3913 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3912
3914
3913 If SOURCE is omitted, the 'default' path will be used.
3915 If SOURCE is omitted, the 'default' path will be used.
3914 See :hg:`help urls` for more information.
3916 See :hg:`help urls` for more information.
3915
3917
3916 Specifying bookmark as ``.`` is equivalent to specifying the active
3918 Specifying bookmark as ``.`` is equivalent to specifying the active
3917 bookmark's name.
3919 bookmark's name.
3918
3920
3919 Returns 0 on success, 1 if an update had unresolved files.
3921 Returns 0 on success, 1 if an update had unresolved files.
3920 """
3922 """
3921
3923
3922 opts = pycompat.byteskwargs(opts)
3924 opts = pycompat.byteskwargs(opts)
3923 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
3925 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
3924 msg = _('update destination required by configuration')
3926 msg = _('update destination required by configuration')
3925 hint = _('use hg pull followed by hg update DEST')
3927 hint = _('use hg pull followed by hg update DEST')
3926 raise error.Abort(msg, hint=hint)
3928 raise error.Abort(msg, hint=hint)
3927
3929
3928 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3930 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3929 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3931 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3930 other = hg.peer(repo, opts, source)
3932 other = hg.peer(repo, opts, source)
3931 try:
3933 try:
3932 revs, checkout = hg.addbranchrevs(repo, other, branches,
3934 revs, checkout = hg.addbranchrevs(repo, other, branches,
3933 opts.get('rev'))
3935 opts.get('rev'))
3934
3936
3935
3937
3936 pullopargs = {}
3938 pullopargs = {}
3937 if opts.get('bookmark'):
3939 if opts.get('bookmark'):
3938 if not revs:
3940 if not revs:
3939 revs = []
3941 revs = []
3940 # The list of bookmark used here is not the one used to actually
3942 # The list of bookmark used here is not the one used to actually
3941 # update the bookmark name. This can result in the revision pulled
3943 # update the bookmark name. This can result in the revision pulled
3942 # not ending up with the name of the bookmark because of a race
3944 # not ending up with the name of the bookmark because of a race
3943 # condition on the server. (See issue 4689 for details)
3945 # condition on the server. (See issue 4689 for details)
3944 remotebookmarks = other.listkeys('bookmarks')
3946 remotebookmarks = other.listkeys('bookmarks')
3945 pullopargs['remotebookmarks'] = remotebookmarks
3947 pullopargs['remotebookmarks'] = remotebookmarks
3946 for b in opts['bookmark']:
3948 for b in opts['bookmark']:
3947 b = repo._bookmarks.expandname(b)
3949 b = repo._bookmarks.expandname(b)
3948 if b not in remotebookmarks:
3950 if b not in remotebookmarks:
3949 raise error.Abort(_('remote bookmark %s not found!') % b)
3951 raise error.Abort(_('remote bookmark %s not found!') % b)
3950 revs.append(remotebookmarks[b])
3952 revs.append(remotebookmarks[b])
3951
3953
3952 if revs:
3954 if revs:
3953 try:
3955 try:
3954 # When 'rev' is a bookmark name, we cannot guarantee that it
3956 # When 'rev' is a bookmark name, we cannot guarantee that it
3955 # will be updated with that name because of a race condition
3957 # will be updated with that name because of a race condition
3956 # server side. (See issue 4689 for details)
3958 # server side. (See issue 4689 for details)
3957 oldrevs = revs
3959 oldrevs = revs
3958 revs = [] # actually, nodes
3960 revs = [] # actually, nodes
3959 for r in oldrevs:
3961 for r in oldrevs:
3960 node = other.lookup(r)
3962 node = other.lookup(r)
3961 revs.append(node)
3963 revs.append(node)
3962 if r == checkout:
3964 if r == checkout:
3963 checkout = node
3965 checkout = node
3964 except error.CapabilityError:
3966 except error.CapabilityError:
3965 err = _("other repository doesn't support revision lookup, "
3967 err = _("other repository doesn't support revision lookup, "
3966 "so a rev cannot be specified.")
3968 "so a rev cannot be specified.")
3967 raise error.Abort(err)
3969 raise error.Abort(err)
3968
3970
3969 pullopargs.update(opts.get('opargs', {}))
3971 pullopargs.update(opts.get('opargs', {}))
3970 modheads = exchange.pull(repo, other, heads=revs,
3972 modheads = exchange.pull(repo, other, heads=revs,
3971 force=opts.get('force'),
3973 force=opts.get('force'),
3972 bookmarks=opts.get('bookmark', ()),
3974 bookmarks=opts.get('bookmark', ()),
3973 opargs=pullopargs).cgresult
3975 opargs=pullopargs).cgresult
3974
3976
3975 # brev is a name, which might be a bookmark to be activated at
3977 # brev is a name, which might be a bookmark to be activated at
3976 # the end of the update. In other words, it is an explicit
3978 # the end of the update. In other words, it is an explicit
3977 # destination of the update
3979 # destination of the update
3978 brev = None
3980 brev = None
3979
3981
3980 if checkout:
3982 if checkout:
3981 checkout = str(repo.changelog.rev(checkout))
3983 checkout = str(repo.changelog.rev(checkout))
3982
3984
3983 # order below depends on implementation of
3985 # order below depends on implementation of
3984 # hg.addbranchrevs(). opts['bookmark'] is ignored,
3986 # hg.addbranchrevs(). opts['bookmark'] is ignored,
3985 # because 'checkout' is determined without it.
3987 # because 'checkout' is determined without it.
3986 if opts.get('rev'):
3988 if opts.get('rev'):
3987 brev = opts['rev'][0]
3989 brev = opts['rev'][0]
3988 elif opts.get('branch'):
3990 elif opts.get('branch'):
3989 brev = opts['branch'][0]
3991 brev = opts['branch'][0]
3990 else:
3992 else:
3991 brev = branches[0]
3993 brev = branches[0]
3992 repo._subtoppath = source
3994 repo._subtoppath = source
3993 try:
3995 try:
3994 ret = postincoming(ui, repo, modheads, opts.get('update'),
3996 ret = postincoming(ui, repo, modheads, opts.get('update'),
3995 checkout, brev)
3997 checkout, brev)
3996
3998
3997 finally:
3999 finally:
3998 del repo._subtoppath
4000 del repo._subtoppath
3999
4001
4000 finally:
4002 finally:
4001 other.close()
4003 other.close()
4002 return ret
4004 return ret
4003
4005
4004 @command('^push',
4006 @command('^push',
4005 [('f', 'force', None, _('force push')),
4007 [('f', 'force', None, _('force push')),
4006 ('r', 'rev', [],
4008 ('r', 'rev', [],
4007 _('a changeset intended to be included in the destination'),
4009 _('a changeset intended to be included in the destination'),
4008 _('REV')),
4010 _('REV')),
4009 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4011 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4010 ('b', 'branch', [],
4012 ('b', 'branch', [],
4011 _('a specific branch you would like to push'), _('BRANCH')),
4013 _('a specific branch you would like to push'), _('BRANCH')),
4012 ('', 'new-branch', False, _('allow pushing a new branch')),
4014 ('', 'new-branch', False, _('allow pushing a new branch')),
4013 ] + remoteopts,
4015 ] + remoteopts,
4014 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4016 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4015 def push(ui, repo, dest=None, **opts):
4017 def push(ui, repo, dest=None, **opts):
4016 """push changes to the specified destination
4018 """push changes to the specified destination
4017
4019
4018 Push changesets from the local repository to the specified
4020 Push changesets from the local repository to the specified
4019 destination.
4021 destination.
4020
4022
4021 This operation is symmetrical to pull: it is identical to a pull
4023 This operation is symmetrical to pull: it is identical to a pull
4022 in the destination repository from the current one.
4024 in the destination repository from the current one.
4023
4025
4024 By default, push will not allow creation of new heads at the
4026 By default, push will not allow creation of new heads at the
4025 destination, since multiple heads would make it unclear which head
4027 destination, since multiple heads would make it unclear which head
4026 to use. In this situation, it is recommended to pull and merge
4028 to use. In this situation, it is recommended to pull and merge
4027 before pushing.
4029 before pushing.
4028
4030
4029 Use --new-branch if you want to allow push to create a new named
4031 Use --new-branch if you want to allow push to create a new named
4030 branch that is not present at the destination. This allows you to
4032 branch that is not present at the destination. This allows you to
4031 only create a new branch without forcing other changes.
4033 only create a new branch without forcing other changes.
4032
4034
4033 .. note::
4035 .. note::
4034
4036
4035 Extra care should be taken with the -f/--force option,
4037 Extra care should be taken with the -f/--force option,
4036 which will push all new heads on all branches, an action which will
4038 which will push all new heads on all branches, an action which will
4037 almost always cause confusion for collaborators.
4039 almost always cause confusion for collaborators.
4038
4040
4039 If -r/--rev is used, the specified revision and all its ancestors
4041 If -r/--rev is used, the specified revision and all its ancestors
4040 will be pushed to the remote repository.
4042 will be pushed to the remote repository.
4041
4043
4042 If -B/--bookmark is used, the specified bookmarked revision, its
4044 If -B/--bookmark is used, the specified bookmarked revision, its
4043 ancestors, and the bookmark will be pushed to the remote
4045 ancestors, and the bookmark will be pushed to the remote
4044 repository. Specifying ``.`` is equivalent to specifying the active
4046 repository. Specifying ``.`` is equivalent to specifying the active
4045 bookmark's name.
4047 bookmark's name.
4046
4048
4047 Please see :hg:`help urls` for important details about ``ssh://``
4049 Please see :hg:`help urls` for important details about ``ssh://``
4048 URLs. If DESTINATION is omitted, a default path will be used.
4050 URLs. If DESTINATION is omitted, a default path will be used.
4049
4051
4050 Returns 0 if push was successful, 1 if nothing to push.
4052 Returns 0 if push was successful, 1 if nothing to push.
4051 """
4053 """
4052
4054
4053 opts = pycompat.byteskwargs(opts)
4055 opts = pycompat.byteskwargs(opts)
4054 if opts.get('bookmark'):
4056 if opts.get('bookmark'):
4055 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4057 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4056 for b in opts['bookmark']:
4058 for b in opts['bookmark']:
4057 # translate -B options to -r so changesets get pushed
4059 # translate -B options to -r so changesets get pushed
4058 b = repo._bookmarks.expandname(b)
4060 b = repo._bookmarks.expandname(b)
4059 if b in repo._bookmarks:
4061 if b in repo._bookmarks:
4060 opts.setdefault('rev', []).append(b)
4062 opts.setdefault('rev', []).append(b)
4061 else:
4063 else:
4062 # if we try to push a deleted bookmark, translate it to null
4064 # if we try to push a deleted bookmark, translate it to null
4063 # this lets simultaneous -r, -b options continue working
4065 # this lets simultaneous -r, -b options continue working
4064 opts.setdefault('rev', []).append("null")
4066 opts.setdefault('rev', []).append("null")
4065
4067
4066 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4068 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4067 if not path:
4069 if not path:
4068 raise error.Abort(_('default repository not configured!'),
4070 raise error.Abort(_('default repository not configured!'),
4069 hint=_("see 'hg help config.paths'"))
4071 hint=_("see 'hg help config.paths'"))
4070 dest = path.pushloc or path.loc
4072 dest = path.pushloc or path.loc
4071 branches = (path.branch, opts.get('branch') or [])
4073 branches = (path.branch, opts.get('branch') or [])
4072 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4074 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4073 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4075 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4074 other = hg.peer(repo, opts, dest)
4076 other = hg.peer(repo, opts, dest)
4075
4077
4076 if revs:
4078 if revs:
4077 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4079 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4078 if not revs:
4080 if not revs:
4079 raise error.Abort(_("specified revisions evaluate to an empty set"),
4081 raise error.Abort(_("specified revisions evaluate to an empty set"),
4080 hint=_("use different revision arguments"))
4082 hint=_("use different revision arguments"))
4081 elif path.pushrev:
4083 elif path.pushrev:
4082 # It doesn't make any sense to specify ancestor revisions. So limit
4084 # It doesn't make any sense to specify ancestor revisions. So limit
4083 # to DAG heads to make discovery simpler.
4085 # to DAG heads to make discovery simpler.
4084 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4086 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4085 revs = scmutil.revrange(repo, [expr])
4087 revs = scmutil.revrange(repo, [expr])
4086 revs = [repo[rev].node() for rev in revs]
4088 revs = [repo[rev].node() for rev in revs]
4087 if not revs:
4089 if not revs:
4088 raise error.Abort(_('default push revset for path evaluates to an '
4090 raise error.Abort(_('default push revset for path evaluates to an '
4089 'empty set'))
4091 'empty set'))
4090
4092
4091 repo._subtoppath = dest
4093 repo._subtoppath = dest
4092 try:
4094 try:
4093 # push subrepos depth-first for coherent ordering
4095 # push subrepos depth-first for coherent ordering
4094 c = repo['']
4096 c = repo['']
4095 subs = c.substate # only repos that are committed
4097 subs = c.substate # only repos that are committed
4096 for s in sorted(subs):
4098 for s in sorted(subs):
4097 result = c.sub(s).push(opts)
4099 result = c.sub(s).push(opts)
4098 if result == 0:
4100 if result == 0:
4099 return not result
4101 return not result
4100 finally:
4102 finally:
4101 del repo._subtoppath
4103 del repo._subtoppath
4102 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4104 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4103 newbranch=opts.get('new_branch'),
4105 newbranch=opts.get('new_branch'),
4104 bookmarks=opts.get('bookmark', ()),
4106 bookmarks=opts.get('bookmark', ()),
4105 opargs=opts.get('opargs'))
4107 opargs=opts.get('opargs'))
4106
4108
4107 result = not pushop.cgresult
4109 result = not pushop.cgresult
4108
4110
4109 if pushop.bkresult is not None:
4111 if pushop.bkresult is not None:
4110 if pushop.bkresult == 2:
4112 if pushop.bkresult == 2:
4111 result = 2
4113 result = 2
4112 elif not result and pushop.bkresult:
4114 elif not result and pushop.bkresult:
4113 result = 2
4115 result = 2
4114
4116
4115 return result
4117 return result
4116
4118
4117 @command('recover', [])
4119 @command('recover', [])
4118 def recover(ui, repo):
4120 def recover(ui, repo):
4119 """roll back an interrupted transaction
4121 """roll back an interrupted transaction
4120
4122
4121 Recover from an interrupted commit or pull.
4123 Recover from an interrupted commit or pull.
4122
4124
4123 This command tries to fix the repository status after an
4125 This command tries to fix the repository status after an
4124 interrupted operation. It should only be necessary when Mercurial
4126 interrupted operation. It should only be necessary when Mercurial
4125 suggests it.
4127 suggests it.
4126
4128
4127 Returns 0 if successful, 1 if nothing to recover or verify fails.
4129 Returns 0 if successful, 1 if nothing to recover or verify fails.
4128 """
4130 """
4129 if repo.recover():
4131 if repo.recover():
4130 return hg.verify(repo)
4132 return hg.verify(repo)
4131 return 1
4133 return 1
4132
4134
4133 @command('^remove|rm',
4135 @command('^remove|rm',
4134 [('A', 'after', None, _('record delete for missing files')),
4136 [('A', 'after', None, _('record delete for missing files')),
4135 ('f', 'force', None,
4137 ('f', 'force', None,
4136 _('forget added files, delete modified files')),
4138 _('forget added files, delete modified files')),
4137 ] + subrepoopts + walkopts,
4139 ] + subrepoopts + walkopts,
4138 _('[OPTION]... FILE...'),
4140 _('[OPTION]... FILE...'),
4139 inferrepo=True)
4141 inferrepo=True)
4140 def remove(ui, repo, *pats, **opts):
4142 def remove(ui, repo, *pats, **opts):
4141 """remove the specified files on the next commit
4143 """remove the specified files on the next commit
4142
4144
4143 Schedule the indicated files for removal from the current branch.
4145 Schedule the indicated files for removal from the current branch.
4144
4146
4145 This command schedules the files to be removed at the next commit.
4147 This command schedules the files to be removed at the next commit.
4146 To undo a remove before that, see :hg:`revert`. To undo added
4148 To undo a remove before that, see :hg:`revert`. To undo added
4147 files, see :hg:`forget`.
4149 files, see :hg:`forget`.
4148
4150
4149 .. container:: verbose
4151 .. container:: verbose
4150
4152
4151 -A/--after can be used to remove only files that have already
4153 -A/--after can be used to remove only files that have already
4152 been deleted, -f/--force can be used to force deletion, and -Af
4154 been deleted, -f/--force can be used to force deletion, and -Af
4153 can be used to remove files from the next revision without
4155 can be used to remove files from the next revision without
4154 deleting them from the working directory.
4156 deleting them from the working directory.
4155
4157
4156 The following table details the behavior of remove for different
4158 The following table details the behavior of remove for different
4157 file states (columns) and option combinations (rows). The file
4159 file states (columns) and option combinations (rows). The file
4158 states are Added [A], Clean [C], Modified [M] and Missing [!]
4160 states are Added [A], Clean [C], Modified [M] and Missing [!]
4159 (as reported by :hg:`status`). The actions are Warn, Remove
4161 (as reported by :hg:`status`). The actions are Warn, Remove
4160 (from branch) and Delete (from disk):
4162 (from branch) and Delete (from disk):
4161
4163
4162 ========= == == == ==
4164 ========= == == == ==
4163 opt/state A C M !
4165 opt/state A C M !
4164 ========= == == == ==
4166 ========= == == == ==
4165 none W RD W R
4167 none W RD W R
4166 -f R RD RD R
4168 -f R RD RD R
4167 -A W W W R
4169 -A W W W R
4168 -Af R R R R
4170 -Af R R R R
4169 ========= == == == ==
4171 ========= == == == ==
4170
4172
4171 .. note::
4173 .. note::
4172
4174
4173 :hg:`remove` never deletes files in Added [A] state from the
4175 :hg:`remove` never deletes files in Added [A] state from the
4174 working directory, not even if ``--force`` is specified.
4176 working directory, not even if ``--force`` is specified.
4175
4177
4176 Returns 0 on success, 1 if any warnings encountered.
4178 Returns 0 on success, 1 if any warnings encountered.
4177 """
4179 """
4178
4180
4179 opts = pycompat.byteskwargs(opts)
4181 opts = pycompat.byteskwargs(opts)
4180 after, force = opts.get('after'), opts.get('force')
4182 after, force = opts.get('after'), opts.get('force')
4181 if not pats and not after:
4183 if not pats and not after:
4182 raise error.Abort(_('no files specified'))
4184 raise error.Abort(_('no files specified'))
4183
4185
4184 m = scmutil.match(repo[None], pats, opts)
4186 m = scmutil.match(repo[None], pats, opts)
4185 subrepos = opts.get('subrepos')
4187 subrepos = opts.get('subrepos')
4186 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
4188 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
4187
4189
4188 @command('rename|move|mv',
4190 @command('rename|move|mv',
4189 [('A', 'after', None, _('record a rename that has already occurred')),
4191 [('A', 'after', None, _('record a rename that has already occurred')),
4190 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4192 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4191 ] + walkopts + dryrunopts,
4193 ] + walkopts + dryrunopts,
4192 _('[OPTION]... SOURCE... DEST'))
4194 _('[OPTION]... SOURCE... DEST'))
4193 def rename(ui, repo, *pats, **opts):
4195 def rename(ui, repo, *pats, **opts):
4194 """rename files; equivalent of copy + remove
4196 """rename files; equivalent of copy + remove
4195
4197
4196 Mark dest as copies of sources; mark sources for deletion. If dest
4198 Mark dest as copies of sources; mark sources for deletion. If dest
4197 is a directory, copies are put in that directory. If dest is a
4199 is a directory, copies are put in that directory. If dest is a
4198 file, there can only be one source.
4200 file, there can only be one source.
4199
4201
4200 By default, this command copies the contents of files as they
4202 By default, this command copies the contents of files as they
4201 exist in the working directory. If invoked with -A/--after, the
4203 exist in the working directory. If invoked with -A/--after, the
4202 operation is recorded, but no copying is performed.
4204 operation is recorded, but no copying is performed.
4203
4205
4204 This command takes effect at the next commit. To undo a rename
4206 This command takes effect at the next commit. To undo a rename
4205 before that, see :hg:`revert`.
4207 before that, see :hg:`revert`.
4206
4208
4207 Returns 0 on success, 1 if errors are encountered.
4209 Returns 0 on success, 1 if errors are encountered.
4208 """
4210 """
4209 opts = pycompat.byteskwargs(opts)
4211 opts = pycompat.byteskwargs(opts)
4210 with repo.wlock(False):
4212 with repo.wlock(False):
4211 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4213 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4212
4214
4213 @command('resolve',
4215 @command('resolve',
4214 [('a', 'all', None, _('select all unresolved files')),
4216 [('a', 'all', None, _('select all unresolved files')),
4215 ('l', 'list', None, _('list state of files needing merge')),
4217 ('l', 'list', None, _('list state of files needing merge')),
4216 ('m', 'mark', None, _('mark files as resolved')),
4218 ('m', 'mark', None, _('mark files as resolved')),
4217 ('u', 'unmark', None, _('mark files as unresolved')),
4219 ('u', 'unmark', None, _('mark files as unresolved')),
4218 ('n', 'no-status', None, _('hide status prefix'))]
4220 ('n', 'no-status', None, _('hide status prefix'))]
4219 + mergetoolopts + walkopts + formatteropts,
4221 + mergetoolopts + walkopts + formatteropts,
4220 _('[OPTION]... [FILE]...'),
4222 _('[OPTION]... [FILE]...'),
4221 inferrepo=True)
4223 inferrepo=True)
4222 def resolve(ui, repo, *pats, **opts):
4224 def resolve(ui, repo, *pats, **opts):
4223 """redo merges or set/view the merge status of files
4225 """redo merges or set/view the merge status of files
4224
4226
4225 Merges with unresolved conflicts are often the result of
4227 Merges with unresolved conflicts are often the result of
4226 non-interactive merging using the ``internal:merge`` configuration
4228 non-interactive merging using the ``internal:merge`` configuration
4227 setting, or a command-line merge tool like ``diff3``. The resolve
4229 setting, or a command-line merge tool like ``diff3``. The resolve
4228 command is used to manage the files involved in a merge, after
4230 command is used to manage the files involved in a merge, after
4229 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4231 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4230 working directory must have two parents). See :hg:`help
4232 working directory must have two parents). See :hg:`help
4231 merge-tools` for information on configuring merge tools.
4233 merge-tools` for information on configuring merge tools.
4232
4234
4233 The resolve command can be used in the following ways:
4235 The resolve command can be used in the following ways:
4234
4236
4235 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4237 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4236 files, discarding any previous merge attempts. Re-merging is not
4238 files, discarding any previous merge attempts. Re-merging is not
4237 performed for files already marked as resolved. Use ``--all/-a``
4239 performed for files already marked as resolved. Use ``--all/-a``
4238 to select all unresolved files. ``--tool`` can be used to specify
4240 to select all unresolved files. ``--tool`` can be used to specify
4239 the merge tool used for the given files. It overrides the HGMERGE
4241 the merge tool used for the given files. It overrides the HGMERGE
4240 environment variable and your configuration files. Previous file
4242 environment variable and your configuration files. Previous file
4241 contents are saved with a ``.orig`` suffix.
4243 contents are saved with a ``.orig`` suffix.
4242
4244
4243 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4245 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4244 (e.g. after having manually fixed-up the files). The default is
4246 (e.g. after having manually fixed-up the files). The default is
4245 to mark all unresolved files.
4247 to mark all unresolved files.
4246
4248
4247 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4249 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4248 default is to mark all resolved files.
4250 default is to mark all resolved files.
4249
4251
4250 - :hg:`resolve -l`: list files which had or still have conflicts.
4252 - :hg:`resolve -l`: list files which had or still have conflicts.
4251 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4253 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4252 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4254 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4253 the list. See :hg:`help filesets` for details.
4255 the list. See :hg:`help filesets` for details.
4254
4256
4255 .. note::
4257 .. note::
4256
4258
4257 Mercurial will not let you commit files with unresolved merge
4259 Mercurial will not let you commit files with unresolved merge
4258 conflicts. You must use :hg:`resolve -m ...` before you can
4260 conflicts. You must use :hg:`resolve -m ...` before you can
4259 commit after a conflicting merge.
4261 commit after a conflicting merge.
4260
4262
4261 Returns 0 on success, 1 if any files fail a resolve attempt.
4263 Returns 0 on success, 1 if any files fail a resolve attempt.
4262 """
4264 """
4263
4265
4264 opts = pycompat.byteskwargs(opts)
4266 opts = pycompat.byteskwargs(opts)
4265 flaglist = 'all mark unmark list no_status'.split()
4267 flaglist = 'all mark unmark list no_status'.split()
4266 all, mark, unmark, show, nostatus = \
4268 all, mark, unmark, show, nostatus = \
4267 [opts.get(o) for o in flaglist]
4269 [opts.get(o) for o in flaglist]
4268
4270
4269 if (show and (mark or unmark)) or (mark and unmark):
4271 if (show and (mark or unmark)) or (mark and unmark):
4270 raise error.Abort(_("too many options specified"))
4272 raise error.Abort(_("too many options specified"))
4271 if pats and all:
4273 if pats and all:
4272 raise error.Abort(_("can't specify --all and patterns"))
4274 raise error.Abort(_("can't specify --all and patterns"))
4273 if not (all or pats or show or mark or unmark):
4275 if not (all or pats or show or mark or unmark):
4274 raise error.Abort(_('no files or directories specified'),
4276 raise error.Abort(_('no files or directories specified'),
4275 hint=('use --all to re-merge all unresolved files'))
4277 hint=('use --all to re-merge all unresolved files'))
4276
4278
4277 if show:
4279 if show:
4278 ui.pager('resolve')
4280 ui.pager('resolve')
4279 fm = ui.formatter('resolve', opts)
4281 fm = ui.formatter('resolve', opts)
4280 ms = mergemod.mergestate.read(repo)
4282 ms = mergemod.mergestate.read(repo)
4281 m = scmutil.match(repo[None], pats, opts)
4283 m = scmutil.match(repo[None], pats, opts)
4282 for f in ms:
4284 for f in ms:
4283 if not m(f):
4285 if not m(f):
4284 continue
4286 continue
4285 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved',
4287 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved',
4286 'd': 'driverresolved'}[ms[f]]
4288 'd': 'driverresolved'}[ms[f]]
4287 fm.startitem()
4289 fm.startitem()
4288 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
4290 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
4289 fm.write('path', '%s\n', f, label=l)
4291 fm.write('path', '%s\n', f, label=l)
4290 fm.end()
4292 fm.end()
4291 return 0
4293 return 0
4292
4294
4293 with repo.wlock():
4295 with repo.wlock():
4294 ms = mergemod.mergestate.read(repo)
4296 ms = mergemod.mergestate.read(repo)
4295
4297
4296 if not (ms.active() or repo.dirstate.p2() != nullid):
4298 if not (ms.active() or repo.dirstate.p2() != nullid):
4297 raise error.Abort(
4299 raise error.Abort(
4298 _('resolve command not applicable when not merging'))
4300 _('resolve command not applicable when not merging'))
4299
4301
4300 wctx = repo[None]
4302 wctx = repo[None]
4301
4303
4302 if ms.mergedriver and ms.mdstate() == 'u':
4304 if ms.mergedriver and ms.mdstate() == 'u':
4303 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4305 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4304 ms.commit()
4306 ms.commit()
4305 # allow mark and unmark to go through
4307 # allow mark and unmark to go through
4306 if not mark and not unmark and not proceed:
4308 if not mark and not unmark and not proceed:
4307 return 1
4309 return 1
4308
4310
4309 m = scmutil.match(wctx, pats, opts)
4311 m = scmutil.match(wctx, pats, opts)
4310 ret = 0
4312 ret = 0
4311 didwork = False
4313 didwork = False
4312 runconclude = False
4314 runconclude = False
4313
4315
4314 tocomplete = []
4316 tocomplete = []
4315 for f in ms:
4317 for f in ms:
4316 if not m(f):
4318 if not m(f):
4317 continue
4319 continue
4318
4320
4319 didwork = True
4321 didwork = True
4320
4322
4321 # don't let driver-resolved files be marked, and run the conclude
4323 # don't let driver-resolved files be marked, and run the conclude
4322 # step if asked to resolve
4324 # step if asked to resolve
4323 if ms[f] == "d":
4325 if ms[f] == "d":
4324 exact = m.exact(f)
4326 exact = m.exact(f)
4325 if mark:
4327 if mark:
4326 if exact:
4328 if exact:
4327 ui.warn(_('not marking %s as it is driver-resolved\n')
4329 ui.warn(_('not marking %s as it is driver-resolved\n')
4328 % f)
4330 % f)
4329 elif unmark:
4331 elif unmark:
4330 if exact:
4332 if exact:
4331 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4333 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4332 % f)
4334 % f)
4333 else:
4335 else:
4334 runconclude = True
4336 runconclude = True
4335 continue
4337 continue
4336
4338
4337 if mark:
4339 if mark:
4338 ms.mark(f, "r")
4340 ms.mark(f, "r")
4339 elif unmark:
4341 elif unmark:
4340 ms.mark(f, "u")
4342 ms.mark(f, "u")
4341 else:
4343 else:
4342 # backup pre-resolve (merge uses .orig for its own purposes)
4344 # backup pre-resolve (merge uses .orig for its own purposes)
4343 a = repo.wjoin(f)
4345 a = repo.wjoin(f)
4344 try:
4346 try:
4345 util.copyfile(a, a + ".resolve")
4347 util.copyfile(a, a + ".resolve")
4346 except (IOError, OSError) as inst:
4348 except (IOError, OSError) as inst:
4347 if inst.errno != errno.ENOENT:
4349 if inst.errno != errno.ENOENT:
4348 raise
4350 raise
4349
4351
4350 try:
4352 try:
4351 # preresolve file
4353 # preresolve file
4352 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4354 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4353 'resolve')
4355 'resolve')
4354 complete, r = ms.preresolve(f, wctx)
4356 complete, r = ms.preresolve(f, wctx)
4355 if not complete:
4357 if not complete:
4356 tocomplete.append(f)
4358 tocomplete.append(f)
4357 elif r:
4359 elif r:
4358 ret = 1
4360 ret = 1
4359 finally:
4361 finally:
4360 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4362 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4361 ms.commit()
4363 ms.commit()
4362
4364
4363 # replace filemerge's .orig file with our resolve file, but only
4365 # replace filemerge's .orig file with our resolve file, but only
4364 # for merges that are complete
4366 # for merges that are complete
4365 if complete:
4367 if complete:
4366 try:
4368 try:
4367 util.rename(a + ".resolve",
4369 util.rename(a + ".resolve",
4368 scmutil.origpath(ui, repo, a))
4370 scmutil.origpath(ui, repo, a))
4369 except OSError as inst:
4371 except OSError as inst:
4370 if inst.errno != errno.ENOENT:
4372 if inst.errno != errno.ENOENT:
4371 raise
4373 raise
4372
4374
4373 for f in tocomplete:
4375 for f in tocomplete:
4374 try:
4376 try:
4375 # resolve file
4377 # resolve file
4376 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4378 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4377 'resolve')
4379 'resolve')
4378 r = ms.resolve(f, wctx)
4380 r = ms.resolve(f, wctx)
4379 if r:
4381 if r:
4380 ret = 1
4382 ret = 1
4381 finally:
4383 finally:
4382 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4384 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4383 ms.commit()
4385 ms.commit()
4384
4386
4385 # replace filemerge's .orig file with our resolve file
4387 # replace filemerge's .orig file with our resolve file
4386 a = repo.wjoin(f)
4388 a = repo.wjoin(f)
4387 try:
4389 try:
4388 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4390 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4389 except OSError as inst:
4391 except OSError as inst:
4390 if inst.errno != errno.ENOENT:
4392 if inst.errno != errno.ENOENT:
4391 raise
4393 raise
4392
4394
4393 ms.commit()
4395 ms.commit()
4394 ms.recordactions()
4396 ms.recordactions()
4395
4397
4396 if not didwork and pats:
4398 if not didwork and pats:
4397 hint = None
4399 hint = None
4398 if not any([p for p in pats if p.find(':') >= 0]):
4400 if not any([p for p in pats if p.find(':') >= 0]):
4399 pats = ['path:%s' % p for p in pats]
4401 pats = ['path:%s' % p for p in pats]
4400 m = scmutil.match(wctx, pats, opts)
4402 m = scmutil.match(wctx, pats, opts)
4401 for f in ms:
4403 for f in ms:
4402 if not m(f):
4404 if not m(f):
4403 continue
4405 continue
4404 flags = ''.join(['-%s ' % o[0] for o in flaglist
4406 flags = ''.join(['-%s ' % o[0] for o in flaglist
4405 if opts.get(o)])
4407 if opts.get(o)])
4406 hint = _("(try: hg resolve %s%s)\n") % (
4408 hint = _("(try: hg resolve %s%s)\n") % (
4407 flags,
4409 flags,
4408 ' '.join(pats))
4410 ' '.join(pats))
4409 break
4411 break
4410 ui.warn(_("arguments do not match paths that need resolving\n"))
4412 ui.warn(_("arguments do not match paths that need resolving\n"))
4411 if hint:
4413 if hint:
4412 ui.warn(hint)
4414 ui.warn(hint)
4413 elif ms.mergedriver and ms.mdstate() != 's':
4415 elif ms.mergedriver and ms.mdstate() != 's':
4414 # run conclude step when either a driver-resolved file is requested
4416 # run conclude step when either a driver-resolved file is requested
4415 # or there are no driver-resolved files
4417 # or there are no driver-resolved files
4416 # we can't use 'ret' to determine whether any files are unresolved
4418 # we can't use 'ret' to determine whether any files are unresolved
4417 # because we might not have tried to resolve some
4419 # because we might not have tried to resolve some
4418 if ((runconclude or not list(ms.driverresolved()))
4420 if ((runconclude or not list(ms.driverresolved()))
4419 and not list(ms.unresolved())):
4421 and not list(ms.unresolved())):
4420 proceed = mergemod.driverconclude(repo, ms, wctx)
4422 proceed = mergemod.driverconclude(repo, ms, wctx)
4421 ms.commit()
4423 ms.commit()
4422 if not proceed:
4424 if not proceed:
4423 return 1
4425 return 1
4424
4426
4425 # Nudge users into finishing an unfinished operation
4427 # Nudge users into finishing an unfinished operation
4426 unresolvedf = list(ms.unresolved())
4428 unresolvedf = list(ms.unresolved())
4427 driverresolvedf = list(ms.driverresolved())
4429 driverresolvedf = list(ms.driverresolved())
4428 if not unresolvedf and not driverresolvedf:
4430 if not unresolvedf and not driverresolvedf:
4429 ui.status(_('(no more unresolved files)\n'))
4431 ui.status(_('(no more unresolved files)\n'))
4430 cmdutil.checkafterresolved(repo)
4432 cmdutil.checkafterresolved(repo)
4431 elif not unresolvedf:
4433 elif not unresolvedf:
4432 ui.status(_('(no more unresolved files -- '
4434 ui.status(_('(no more unresolved files -- '
4433 'run "hg resolve --all" to conclude)\n'))
4435 'run "hg resolve --all" to conclude)\n'))
4434
4436
4435 return ret
4437 return ret
4436
4438
4437 @command('revert',
4439 @command('revert',
4438 [('a', 'all', None, _('revert all changes when no arguments given')),
4440 [('a', 'all', None, _('revert all changes when no arguments given')),
4439 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4441 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4440 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4442 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4441 ('C', 'no-backup', None, _('do not save backup copies of files')),
4443 ('C', 'no-backup', None, _('do not save backup copies of files')),
4442 ('i', 'interactive', None,
4444 ('i', 'interactive', None,
4443 _('interactively select the changes (EXPERIMENTAL)')),
4445 _('interactively select the changes (EXPERIMENTAL)')),
4444 ] + walkopts + dryrunopts,
4446 ] + walkopts + dryrunopts,
4445 _('[OPTION]... [-r REV] [NAME]...'))
4447 _('[OPTION]... [-r REV] [NAME]...'))
4446 def revert(ui, repo, *pats, **opts):
4448 def revert(ui, repo, *pats, **opts):
4447 """restore files to their checkout state
4449 """restore files to their checkout state
4448
4450
4449 .. note::
4451 .. note::
4450
4452
4451 To check out earlier revisions, you should use :hg:`update REV`.
4453 To check out earlier revisions, you should use :hg:`update REV`.
4452 To cancel an uncommitted merge (and lose your changes),
4454 To cancel an uncommitted merge (and lose your changes),
4453 use :hg:`update --clean .`.
4455 use :hg:`update --clean .`.
4454
4456
4455 With no revision specified, revert the specified files or directories
4457 With no revision specified, revert the specified files or directories
4456 to the contents they had in the parent of the working directory.
4458 to the contents they had in the parent of the working directory.
4457 This restores the contents of files to an unmodified
4459 This restores the contents of files to an unmodified
4458 state and unschedules adds, removes, copies, and renames. If the
4460 state and unschedules adds, removes, copies, and renames. If the
4459 working directory has two parents, you must explicitly specify a
4461 working directory has two parents, you must explicitly specify a
4460 revision.
4462 revision.
4461
4463
4462 Using the -r/--rev or -d/--date options, revert the given files or
4464 Using the -r/--rev or -d/--date options, revert the given files or
4463 directories to their states as of a specific revision. Because
4465 directories to their states as of a specific revision. Because
4464 revert does not change the working directory parents, this will
4466 revert does not change the working directory parents, this will
4465 cause these files to appear modified. This can be helpful to "back
4467 cause these files to appear modified. This can be helpful to "back
4466 out" some or all of an earlier change. See :hg:`backout` for a
4468 out" some or all of an earlier change. See :hg:`backout` for a
4467 related method.
4469 related method.
4468
4470
4469 Modified files are saved with a .orig suffix before reverting.
4471 Modified files are saved with a .orig suffix before reverting.
4470 To disable these backups, use --no-backup. It is possible to store
4472 To disable these backups, use --no-backup. It is possible to store
4471 the backup files in a custom directory relative to the root of the
4473 the backup files in a custom directory relative to the root of the
4472 repository by setting the ``ui.origbackuppath`` configuration
4474 repository by setting the ``ui.origbackuppath`` configuration
4473 option.
4475 option.
4474
4476
4475 See :hg:`help dates` for a list of formats valid for -d/--date.
4477 See :hg:`help dates` for a list of formats valid for -d/--date.
4476
4478
4477 See :hg:`help backout` for a way to reverse the effect of an
4479 See :hg:`help backout` for a way to reverse the effect of an
4478 earlier changeset.
4480 earlier changeset.
4479
4481
4480 Returns 0 on success.
4482 Returns 0 on success.
4481 """
4483 """
4482
4484
4483 if opts.get("date"):
4485 if opts.get("date"):
4484 if opts.get("rev"):
4486 if opts.get("rev"):
4485 raise error.Abort(_("you can't specify a revision and a date"))
4487 raise error.Abort(_("you can't specify a revision and a date"))
4486 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4488 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4487
4489
4488 parent, p2 = repo.dirstate.parents()
4490 parent, p2 = repo.dirstate.parents()
4489 if not opts.get('rev') and p2 != nullid:
4491 if not opts.get('rev') and p2 != nullid:
4490 # revert after merge is a trap for new users (issue2915)
4492 # revert after merge is a trap for new users (issue2915)
4491 raise error.Abort(_('uncommitted merge with no revision specified'),
4493 raise error.Abort(_('uncommitted merge with no revision specified'),
4492 hint=_("use 'hg update' or see 'hg help revert'"))
4494 hint=_("use 'hg update' or see 'hg help revert'"))
4493
4495
4494 ctx = scmutil.revsingle(repo, opts.get('rev'))
4496 ctx = scmutil.revsingle(repo, opts.get('rev'))
4495
4497
4496 if (not (pats or opts.get('include') or opts.get('exclude') or
4498 if (not (pats or opts.get('include') or opts.get('exclude') or
4497 opts.get('all') or opts.get('interactive'))):
4499 opts.get('all') or opts.get('interactive'))):
4498 msg = _("no files or directories specified")
4500 msg = _("no files or directories specified")
4499 if p2 != nullid:
4501 if p2 != nullid:
4500 hint = _("uncommitted merge, use --all to discard all changes,"
4502 hint = _("uncommitted merge, use --all to discard all changes,"
4501 " or 'hg update -C .' to abort the merge")
4503 " or 'hg update -C .' to abort the merge")
4502 raise error.Abort(msg, hint=hint)
4504 raise error.Abort(msg, hint=hint)
4503 dirty = any(repo.status())
4505 dirty = any(repo.status())
4504 node = ctx.node()
4506 node = ctx.node()
4505 if node != parent:
4507 if node != parent:
4506 if dirty:
4508 if dirty:
4507 hint = _("uncommitted changes, use --all to discard all"
4509 hint = _("uncommitted changes, use --all to discard all"
4508 " changes, or 'hg update %s' to update") % ctx.rev()
4510 " changes, or 'hg update %s' to update") % ctx.rev()
4509 else:
4511 else:
4510 hint = _("use --all to revert all files,"
4512 hint = _("use --all to revert all files,"
4511 " or 'hg update %s' to update") % ctx.rev()
4513 " or 'hg update %s' to update") % ctx.rev()
4512 elif dirty:
4514 elif dirty:
4513 hint = _("uncommitted changes, use --all to discard all changes")
4515 hint = _("uncommitted changes, use --all to discard all changes")
4514 else:
4516 else:
4515 hint = _("use --all to revert all files")
4517 hint = _("use --all to revert all files")
4516 raise error.Abort(msg, hint=hint)
4518 raise error.Abort(msg, hint=hint)
4517
4519
4518 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
4520 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
4519
4521
4520 @command('rollback', dryrunopts +
4522 @command('rollback', dryrunopts +
4521 [('f', 'force', False, _('ignore safety measures'))])
4523 [('f', 'force', False, _('ignore safety measures'))])
4522 def rollback(ui, repo, **opts):
4524 def rollback(ui, repo, **opts):
4523 """roll back the last transaction (DANGEROUS) (DEPRECATED)
4525 """roll back the last transaction (DANGEROUS) (DEPRECATED)
4524
4526
4525 Please use :hg:`commit --amend` instead of rollback to correct
4527 Please use :hg:`commit --amend` instead of rollback to correct
4526 mistakes in the last commit.
4528 mistakes in the last commit.
4527
4529
4528 This command should be used with care. There is only one level of
4530 This command should be used with care. There is only one level of
4529 rollback, and there is no way to undo a rollback. It will also
4531 rollback, and there is no way to undo a rollback. It will also
4530 restore the dirstate at the time of the last transaction, losing
4532 restore the dirstate at the time of the last transaction, losing
4531 any dirstate changes since that time. This command does not alter
4533 any dirstate changes since that time. This command does not alter
4532 the working directory.
4534 the working directory.
4533
4535
4534 Transactions are used to encapsulate the effects of all commands
4536 Transactions are used to encapsulate the effects of all commands
4535 that create new changesets or propagate existing changesets into a
4537 that create new changesets or propagate existing changesets into a
4536 repository.
4538 repository.
4537
4539
4538 .. container:: verbose
4540 .. container:: verbose
4539
4541
4540 For example, the following commands are transactional, and their
4542 For example, the following commands are transactional, and their
4541 effects can be rolled back:
4543 effects can be rolled back:
4542
4544
4543 - commit
4545 - commit
4544 - import
4546 - import
4545 - pull
4547 - pull
4546 - push (with this repository as the destination)
4548 - push (with this repository as the destination)
4547 - unbundle
4549 - unbundle
4548
4550
4549 To avoid permanent data loss, rollback will refuse to rollback a
4551 To avoid permanent data loss, rollback will refuse to rollback a
4550 commit transaction if it isn't checked out. Use --force to
4552 commit transaction if it isn't checked out. Use --force to
4551 override this protection.
4553 override this protection.
4552
4554
4553 The rollback command can be entirely disabled by setting the
4555 The rollback command can be entirely disabled by setting the
4554 ``ui.rollback`` configuration setting to false. If you're here
4556 ``ui.rollback`` configuration setting to false. If you're here
4555 because you want to use rollback and it's disabled, you can
4557 because you want to use rollback and it's disabled, you can
4556 re-enable the command by setting ``ui.rollback`` to true.
4558 re-enable the command by setting ``ui.rollback`` to true.
4557
4559
4558 This command is not intended for use on public repositories. Once
4560 This command is not intended for use on public repositories. Once
4559 changes are visible for pull by other users, rolling a transaction
4561 changes are visible for pull by other users, rolling a transaction
4560 back locally is ineffective (someone else may already have pulled
4562 back locally is ineffective (someone else may already have pulled
4561 the changes). Furthermore, a race is possible with readers of the
4563 the changes). Furthermore, a race is possible with readers of the
4562 repository; for example an in-progress pull from the repository
4564 repository; for example an in-progress pull from the repository
4563 may fail if a rollback is performed.
4565 may fail if a rollback is performed.
4564
4566
4565 Returns 0 on success, 1 if no rollback data is available.
4567 Returns 0 on success, 1 if no rollback data is available.
4566 """
4568 """
4567 if not ui.configbool('ui', 'rollback', True):
4569 if not ui.configbool('ui', 'rollback', True):
4568 raise error.Abort(_('rollback is disabled because it is unsafe'),
4570 raise error.Abort(_('rollback is disabled because it is unsafe'),
4569 hint=('see `hg help -v rollback` for information'))
4571 hint=('see `hg help -v rollback` for information'))
4570 return repo.rollback(dryrun=opts.get(r'dry_run'),
4572 return repo.rollback(dryrun=opts.get(r'dry_run'),
4571 force=opts.get(r'force'))
4573 force=opts.get(r'force'))
4572
4574
4573 @command('root', [])
4575 @command('root', [])
4574 def root(ui, repo):
4576 def root(ui, repo):
4575 """print the root (top) of the current working directory
4577 """print the root (top) of the current working directory
4576
4578
4577 Print the root directory of the current repository.
4579 Print the root directory of the current repository.
4578
4580
4579 Returns 0 on success.
4581 Returns 0 on success.
4580 """
4582 """
4581 ui.write(repo.root + "\n")
4583 ui.write(repo.root + "\n")
4582
4584
4583 @command('^serve',
4585 @command('^serve',
4584 [('A', 'accesslog', '', _('name of access log file to write to'),
4586 [('A', 'accesslog', '', _('name of access log file to write to'),
4585 _('FILE')),
4587 _('FILE')),
4586 ('d', 'daemon', None, _('run server in background')),
4588 ('d', 'daemon', None, _('run server in background')),
4587 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
4589 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
4588 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4590 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4589 # use string type, then we can check if something was passed
4591 # use string type, then we can check if something was passed
4590 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4592 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4591 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4593 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4592 _('ADDR')),
4594 _('ADDR')),
4593 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4595 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4594 _('PREFIX')),
4596 _('PREFIX')),
4595 ('n', 'name', '',
4597 ('n', 'name', '',
4596 _('name to show in web pages (default: working directory)'), _('NAME')),
4598 _('name to show in web pages (default: working directory)'), _('NAME')),
4597 ('', 'web-conf', '',
4599 ('', 'web-conf', '',
4598 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
4600 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
4599 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4601 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4600 _('FILE')),
4602 _('FILE')),
4601 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4603 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4602 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
4604 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
4603 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
4605 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
4604 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4606 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4605 ('', 'style', '', _('template style to use'), _('STYLE')),
4607 ('', 'style', '', _('template style to use'), _('STYLE')),
4606 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4608 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4607 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))]
4609 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))]
4608 + subrepoopts,
4610 + subrepoopts,
4609 _('[OPTION]...'),
4611 _('[OPTION]...'),
4610 optionalrepo=True)
4612 optionalrepo=True)
4611 def serve(ui, repo, **opts):
4613 def serve(ui, repo, **opts):
4612 """start stand-alone webserver
4614 """start stand-alone webserver
4613
4615
4614 Start a local HTTP repository browser and pull server. You can use
4616 Start a local HTTP repository browser and pull server. You can use
4615 this for ad-hoc sharing and browsing of repositories. It is
4617 this for ad-hoc sharing and browsing of repositories. It is
4616 recommended to use a real web server to serve a repository for
4618 recommended to use a real web server to serve a repository for
4617 longer periods of time.
4619 longer periods of time.
4618
4620
4619 Please note that the server does not implement access control.
4621 Please note that the server does not implement access control.
4620 This means that, by default, anybody can read from the server and
4622 This means that, by default, anybody can read from the server and
4621 nobody can write to it by default. Set the ``web.allow_push``
4623 nobody can write to it by default. Set the ``web.allow_push``
4622 option to ``*`` to allow everybody to push to the server. You
4624 option to ``*`` to allow everybody to push to the server. You
4623 should use a real web server if you need to authenticate users.
4625 should use a real web server if you need to authenticate users.
4624
4626
4625 By default, the server logs accesses to stdout and errors to
4627 By default, the server logs accesses to stdout and errors to
4626 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4628 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4627 files.
4629 files.
4628
4630
4629 To have the server choose a free port number to listen on, specify
4631 To have the server choose a free port number to listen on, specify
4630 a port number of 0; in this case, the server will print the port
4632 a port number of 0; in this case, the server will print the port
4631 number it uses.
4633 number it uses.
4632
4634
4633 Returns 0 on success.
4635 Returns 0 on success.
4634 """
4636 """
4635
4637
4636 opts = pycompat.byteskwargs(opts)
4638 opts = pycompat.byteskwargs(opts)
4637 if opts["stdio"] and opts["cmdserver"]:
4639 if opts["stdio"] and opts["cmdserver"]:
4638 raise error.Abort(_("cannot use --stdio with --cmdserver"))
4640 raise error.Abort(_("cannot use --stdio with --cmdserver"))
4639
4641
4640 if opts["stdio"]:
4642 if opts["stdio"]:
4641 if repo is None:
4643 if repo is None:
4642 raise error.RepoError(_("there is no Mercurial repository here"
4644 raise error.RepoError(_("there is no Mercurial repository here"
4643 " (.hg not found)"))
4645 " (.hg not found)"))
4644 s = sshserver.sshserver(ui, repo)
4646 s = sshserver.sshserver(ui, repo)
4645 s.serve_forever()
4647 s.serve_forever()
4646
4648
4647 service = server.createservice(ui, repo, opts)
4649 service = server.createservice(ui, repo, opts)
4648 return server.runservice(opts, initfn=service.init, runfn=service.run)
4650 return server.runservice(opts, initfn=service.init, runfn=service.run)
4649
4651
4650 @command('^status|st',
4652 @command('^status|st',
4651 [('A', 'all', None, _('show status of all files')),
4653 [('A', 'all', None, _('show status of all files')),
4652 ('m', 'modified', None, _('show only modified files')),
4654 ('m', 'modified', None, _('show only modified files')),
4653 ('a', 'added', None, _('show only added files')),
4655 ('a', 'added', None, _('show only added files')),
4654 ('r', 'removed', None, _('show only removed files')),
4656 ('r', 'removed', None, _('show only removed files')),
4655 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4657 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4656 ('c', 'clean', None, _('show only files without changes')),
4658 ('c', 'clean', None, _('show only files without changes')),
4657 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4659 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4658 ('i', 'ignored', None, _('show only ignored files')),
4660 ('i', 'ignored', None, _('show only ignored files')),
4659 ('n', 'no-status', None, _('hide status prefix')),
4661 ('n', 'no-status', None, _('hide status prefix')),
4660 ('C', 'copies', None, _('show source of copied files')),
4662 ('C', 'copies', None, _('show source of copied files')),
4661 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4663 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4662 ('', 'rev', [], _('show difference from revision'), _('REV')),
4664 ('', 'rev', [], _('show difference from revision'), _('REV')),
4663 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4665 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4664 ] + walkopts + subrepoopts + formatteropts,
4666 ] + walkopts + subrepoopts + formatteropts,
4665 _('[OPTION]... [FILE]...'),
4667 _('[OPTION]... [FILE]...'),
4666 inferrepo=True)
4668 inferrepo=True)
4667 def status(ui, repo, *pats, **opts):
4669 def status(ui, repo, *pats, **opts):
4668 """show changed files in the working directory
4670 """show changed files in the working directory
4669
4671
4670 Show status of files in the repository. If names are given, only
4672 Show status of files in the repository. If names are given, only
4671 files that match are shown. Files that are clean or ignored or
4673 files that match are shown. Files that are clean or ignored or
4672 the source of a copy/move operation, are not listed unless
4674 the source of a copy/move operation, are not listed unless
4673 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4675 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4674 Unless options described with "show only ..." are given, the
4676 Unless options described with "show only ..." are given, the
4675 options -mardu are used.
4677 options -mardu are used.
4676
4678
4677 Option -q/--quiet hides untracked (unknown and ignored) files
4679 Option -q/--quiet hides untracked (unknown and ignored) files
4678 unless explicitly requested with -u/--unknown or -i/--ignored.
4680 unless explicitly requested with -u/--unknown or -i/--ignored.
4679
4681
4680 .. note::
4682 .. note::
4681
4683
4682 :hg:`status` may appear to disagree with diff if permissions have
4684 :hg:`status` may appear to disagree with diff if permissions have
4683 changed or a merge has occurred. The standard diff format does
4685 changed or a merge has occurred. The standard diff format does
4684 not report permission changes and diff only reports changes
4686 not report permission changes and diff only reports changes
4685 relative to one merge parent.
4687 relative to one merge parent.
4686
4688
4687 If one revision is given, it is used as the base revision.
4689 If one revision is given, it is used as the base revision.
4688 If two revisions are given, the differences between them are
4690 If two revisions are given, the differences between them are
4689 shown. The --change option can also be used as a shortcut to list
4691 shown. The --change option can also be used as a shortcut to list
4690 the changed files of a revision from its first parent.
4692 the changed files of a revision from its first parent.
4691
4693
4692 The codes used to show the status of files are::
4694 The codes used to show the status of files are::
4693
4695
4694 M = modified
4696 M = modified
4695 A = added
4697 A = added
4696 R = removed
4698 R = removed
4697 C = clean
4699 C = clean
4698 ! = missing (deleted by non-hg command, but still tracked)
4700 ! = missing (deleted by non-hg command, but still tracked)
4699 ? = not tracked
4701 ? = not tracked
4700 I = ignored
4702 I = ignored
4701 = origin of the previous file (with --copies)
4703 = origin of the previous file (with --copies)
4702
4704
4703 .. container:: verbose
4705 .. container:: verbose
4704
4706
4705 Examples:
4707 Examples:
4706
4708
4707 - show changes in the working directory relative to a
4709 - show changes in the working directory relative to a
4708 changeset::
4710 changeset::
4709
4711
4710 hg status --rev 9353
4712 hg status --rev 9353
4711
4713
4712 - show changes in the working directory relative to the
4714 - show changes in the working directory relative to the
4713 current directory (see :hg:`help patterns` for more information)::
4715 current directory (see :hg:`help patterns` for more information)::
4714
4716
4715 hg status re:
4717 hg status re:
4716
4718
4717 - show all changes including copies in an existing changeset::
4719 - show all changes including copies in an existing changeset::
4718
4720
4719 hg status --copies --change 9353
4721 hg status --copies --change 9353
4720
4722
4721 - get a NUL separated list of added files, suitable for xargs::
4723 - get a NUL separated list of added files, suitable for xargs::
4722
4724
4723 hg status -an0
4725 hg status -an0
4724
4726
4725 Returns 0 on success.
4727 Returns 0 on success.
4726 """
4728 """
4727
4729
4728 opts = pycompat.byteskwargs(opts)
4730 opts = pycompat.byteskwargs(opts)
4729 revs = opts.get('rev')
4731 revs = opts.get('rev')
4730 change = opts.get('change')
4732 change = opts.get('change')
4731
4733
4732 if revs and change:
4734 if revs and change:
4733 msg = _('cannot specify --rev and --change at the same time')
4735 msg = _('cannot specify --rev and --change at the same time')
4734 raise error.Abort(msg)
4736 raise error.Abort(msg)
4735 elif change:
4737 elif change:
4736 node2 = scmutil.revsingle(repo, change, None).node()
4738 node2 = scmutil.revsingle(repo, change, None).node()
4737 node1 = repo[node2].p1().node()
4739 node1 = repo[node2].p1().node()
4738 else:
4740 else:
4739 node1, node2 = scmutil.revpair(repo, revs)
4741 node1, node2 = scmutil.revpair(repo, revs)
4740
4742
4741 if pats or ui.configbool('commands', 'status.relative'):
4743 if pats or ui.configbool('commands', 'status.relative'):
4742 cwd = repo.getcwd()
4744 cwd = repo.getcwd()
4743 else:
4745 else:
4744 cwd = ''
4746 cwd = ''
4745
4747
4746 if opts.get('print0'):
4748 if opts.get('print0'):
4747 end = '\0'
4749 end = '\0'
4748 else:
4750 else:
4749 end = '\n'
4751 end = '\n'
4750 copy = {}
4752 copy = {}
4751 states = 'modified added removed deleted unknown ignored clean'.split()
4753 states = 'modified added removed deleted unknown ignored clean'.split()
4752 show = [k for k in states if opts.get(k)]
4754 show = [k for k in states if opts.get(k)]
4753 if opts.get('all'):
4755 if opts.get('all'):
4754 show += ui.quiet and (states[:4] + ['clean']) or states
4756 show += ui.quiet and (states[:4] + ['clean']) or states
4755 if not show:
4757 if not show:
4756 if ui.quiet:
4758 if ui.quiet:
4757 show = states[:4]
4759 show = states[:4]
4758 else:
4760 else:
4759 show = states[:5]
4761 show = states[:5]
4760
4762
4761 m = scmutil.match(repo[node2], pats, opts)
4763 m = scmutil.match(repo[node2], pats, opts)
4762 stat = repo.status(node1, node2, m,
4764 stat = repo.status(node1, node2, m,
4763 'ignored' in show, 'clean' in show, 'unknown' in show,
4765 'ignored' in show, 'clean' in show, 'unknown' in show,
4764 opts.get('subrepos'))
4766 opts.get('subrepos'))
4765 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
4767 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
4766
4768
4767 if (opts.get('all') or opts.get('copies')
4769 if (opts.get('all') or opts.get('copies')
4768 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
4770 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
4769 copy = copies.pathcopies(repo[node1], repo[node2], m)
4771 copy = copies.pathcopies(repo[node1], repo[node2], m)
4770
4772
4771 ui.pager('status')
4773 ui.pager('status')
4772 fm = ui.formatter('status', opts)
4774 fm = ui.formatter('status', opts)
4773 fmt = '%s' + end
4775 fmt = '%s' + end
4774 showchar = not opts.get('no_status')
4776 showchar = not opts.get('no_status')
4775
4777
4776 for state, char, files in changestates:
4778 for state, char, files in changestates:
4777 if state in show:
4779 if state in show:
4778 label = 'status.' + state
4780 label = 'status.' + state
4779 for f in files:
4781 for f in files:
4780 fm.startitem()
4782 fm.startitem()
4781 fm.condwrite(showchar, 'status', '%s ', char, label=label)
4783 fm.condwrite(showchar, 'status', '%s ', char, label=label)
4782 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
4784 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
4783 if f in copy:
4785 if f in copy:
4784 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
4786 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
4785 label='status.copied')
4787 label='status.copied')
4786 fm.end()
4788 fm.end()
4787
4789
4788 @command('^summary|sum',
4790 @command('^summary|sum',
4789 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
4791 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
4790 def summary(ui, repo, **opts):
4792 def summary(ui, repo, **opts):
4791 """summarize working directory state
4793 """summarize working directory state
4792
4794
4793 This generates a brief summary of the working directory state,
4795 This generates a brief summary of the working directory state,
4794 including parents, branch, commit status, phase and available updates.
4796 including parents, branch, commit status, phase and available updates.
4795
4797
4796 With the --remote option, this will check the default paths for
4798 With the --remote option, this will check the default paths for
4797 incoming and outgoing changes. This can be time-consuming.
4799 incoming and outgoing changes. This can be time-consuming.
4798
4800
4799 Returns 0 on success.
4801 Returns 0 on success.
4800 """
4802 """
4801
4803
4802 opts = pycompat.byteskwargs(opts)
4804 opts = pycompat.byteskwargs(opts)
4803 ui.pager('summary')
4805 ui.pager('summary')
4804 ctx = repo[None]
4806 ctx = repo[None]
4805 parents = ctx.parents()
4807 parents = ctx.parents()
4806 pnode = parents[0].node()
4808 pnode = parents[0].node()
4807 marks = []
4809 marks = []
4808
4810
4809 ms = None
4811 ms = None
4810 try:
4812 try:
4811 ms = mergemod.mergestate.read(repo)
4813 ms = mergemod.mergestate.read(repo)
4812 except error.UnsupportedMergeRecords as e:
4814 except error.UnsupportedMergeRecords as e:
4813 s = ' '.join(e.recordtypes)
4815 s = ' '.join(e.recordtypes)
4814 ui.warn(
4816 ui.warn(
4815 _('warning: merge state has unsupported record types: %s\n') % s)
4817 _('warning: merge state has unsupported record types: %s\n') % s)
4816 unresolved = 0
4818 unresolved = 0
4817 else:
4819 else:
4818 unresolved = [f for f in ms if ms[f] == 'u']
4820 unresolved = [f for f in ms if ms[f] == 'u']
4819
4821
4820 for p in parents:
4822 for p in parents:
4821 # label with log.changeset (instead of log.parent) since this
4823 # label with log.changeset (instead of log.parent) since this
4822 # shows a working directory parent *changeset*:
4824 # shows a working directory parent *changeset*:
4823 # i18n: column positioning for "hg summary"
4825 # i18n: column positioning for "hg summary"
4824 ui.write(_('parent: %d:%s ') % (p.rev(), p),
4826 ui.write(_('parent: %d:%s ') % (p.rev(), p),
4825 label=cmdutil._changesetlabels(p))
4827 label=cmdutil._changesetlabels(p))
4826 ui.write(' '.join(p.tags()), label='log.tag')
4828 ui.write(' '.join(p.tags()), label='log.tag')
4827 if p.bookmarks():
4829 if p.bookmarks():
4828 marks.extend(p.bookmarks())
4830 marks.extend(p.bookmarks())
4829 if p.rev() == -1:
4831 if p.rev() == -1:
4830 if not len(repo):
4832 if not len(repo):
4831 ui.write(_(' (empty repository)'))
4833 ui.write(_(' (empty repository)'))
4832 else:
4834 else:
4833 ui.write(_(' (no revision checked out)'))
4835 ui.write(_(' (no revision checked out)'))
4834 if p.obsolete():
4836 if p.obsolete():
4835 ui.write(_(' (obsolete)'))
4837 ui.write(_(' (obsolete)'))
4836 if p.troubled():
4838 if p.troubled():
4837 ui.write(' ('
4839 ui.write(' ('
4838 + ', '.join(ui.label(trouble, 'trouble.%s' % trouble)
4840 + ', '.join(ui.label(trouble, 'trouble.%s' % trouble)
4839 for trouble in p.troubles())
4841 for trouble in p.troubles())
4840 + ')')
4842 + ')')
4841 ui.write('\n')
4843 ui.write('\n')
4842 if p.description():
4844 if p.description():
4843 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
4845 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
4844 label='log.summary')
4846 label='log.summary')
4845
4847
4846 branch = ctx.branch()
4848 branch = ctx.branch()
4847 bheads = repo.branchheads(branch)
4849 bheads = repo.branchheads(branch)
4848 # i18n: column positioning for "hg summary"
4850 # i18n: column positioning for "hg summary"
4849 m = _('branch: %s\n') % branch
4851 m = _('branch: %s\n') % branch
4850 if branch != 'default':
4852 if branch != 'default':
4851 ui.write(m, label='log.branch')
4853 ui.write(m, label='log.branch')
4852 else:
4854 else:
4853 ui.status(m, label='log.branch')
4855 ui.status(m, label='log.branch')
4854
4856
4855 if marks:
4857 if marks:
4856 active = repo._activebookmark
4858 active = repo._activebookmark
4857 # i18n: column positioning for "hg summary"
4859 # i18n: column positioning for "hg summary"
4858 ui.write(_('bookmarks:'), label='log.bookmark')
4860 ui.write(_('bookmarks:'), label='log.bookmark')
4859 if active is not None:
4861 if active is not None:
4860 if active in marks:
4862 if active in marks:
4861 ui.write(' *' + active, label=activebookmarklabel)
4863 ui.write(' *' + active, label=activebookmarklabel)
4862 marks.remove(active)
4864 marks.remove(active)
4863 else:
4865 else:
4864 ui.write(' [%s]' % active, label=activebookmarklabel)
4866 ui.write(' [%s]' % active, label=activebookmarklabel)
4865 for m in marks:
4867 for m in marks:
4866 ui.write(' ' + m, label='log.bookmark')
4868 ui.write(' ' + m, label='log.bookmark')
4867 ui.write('\n', label='log.bookmark')
4869 ui.write('\n', label='log.bookmark')
4868
4870
4869 status = repo.status(unknown=True)
4871 status = repo.status(unknown=True)
4870
4872
4871 c = repo.dirstate.copies()
4873 c = repo.dirstate.copies()
4872 copied, renamed = [], []
4874 copied, renamed = [], []
4873 for d, s in c.iteritems():
4875 for d, s in c.iteritems():
4874 if s in status.removed:
4876 if s in status.removed:
4875 status.removed.remove(s)
4877 status.removed.remove(s)
4876 renamed.append(d)
4878 renamed.append(d)
4877 else:
4879 else:
4878 copied.append(d)
4880 copied.append(d)
4879 if d in status.added:
4881 if d in status.added:
4880 status.added.remove(d)
4882 status.added.remove(d)
4881
4883
4882 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
4884 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
4883
4885
4884 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
4886 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
4885 (ui.label(_('%d added'), 'status.added'), status.added),
4887 (ui.label(_('%d added'), 'status.added'), status.added),
4886 (ui.label(_('%d removed'), 'status.removed'), status.removed),
4888 (ui.label(_('%d removed'), 'status.removed'), status.removed),
4887 (ui.label(_('%d renamed'), 'status.copied'), renamed),
4889 (ui.label(_('%d renamed'), 'status.copied'), renamed),
4888 (ui.label(_('%d copied'), 'status.copied'), copied),
4890 (ui.label(_('%d copied'), 'status.copied'), copied),
4889 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
4891 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
4890 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
4892 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
4891 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
4893 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
4892 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
4894 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
4893 t = []
4895 t = []
4894 for l, s in labels:
4896 for l, s in labels:
4895 if s:
4897 if s:
4896 t.append(l % len(s))
4898 t.append(l % len(s))
4897
4899
4898 t = ', '.join(t)
4900 t = ', '.join(t)
4899 cleanworkdir = False
4901 cleanworkdir = False
4900
4902
4901 if repo.vfs.exists('graftstate'):
4903 if repo.vfs.exists('graftstate'):
4902 t += _(' (graft in progress)')
4904 t += _(' (graft in progress)')
4903 if repo.vfs.exists('updatestate'):
4905 if repo.vfs.exists('updatestate'):
4904 t += _(' (interrupted update)')
4906 t += _(' (interrupted update)')
4905 elif len(parents) > 1:
4907 elif len(parents) > 1:
4906 t += _(' (merge)')
4908 t += _(' (merge)')
4907 elif branch != parents[0].branch():
4909 elif branch != parents[0].branch():
4908 t += _(' (new branch)')
4910 t += _(' (new branch)')
4909 elif (parents[0].closesbranch() and
4911 elif (parents[0].closesbranch() and
4910 pnode in repo.branchheads(branch, closed=True)):
4912 pnode in repo.branchheads(branch, closed=True)):
4911 t += _(' (head closed)')
4913 t += _(' (head closed)')
4912 elif not (status.modified or status.added or status.removed or renamed or
4914 elif not (status.modified or status.added or status.removed or renamed or
4913 copied or subs):
4915 copied or subs):
4914 t += _(' (clean)')
4916 t += _(' (clean)')
4915 cleanworkdir = True
4917 cleanworkdir = True
4916 elif pnode not in bheads:
4918 elif pnode not in bheads:
4917 t += _(' (new branch head)')
4919 t += _(' (new branch head)')
4918
4920
4919 if parents:
4921 if parents:
4920 pendingphase = max(p.phase() for p in parents)
4922 pendingphase = max(p.phase() for p in parents)
4921 else:
4923 else:
4922 pendingphase = phases.public
4924 pendingphase = phases.public
4923
4925
4924 if pendingphase > phases.newcommitphase(ui):
4926 if pendingphase > phases.newcommitphase(ui):
4925 t += ' (%s)' % phases.phasenames[pendingphase]
4927 t += ' (%s)' % phases.phasenames[pendingphase]
4926
4928
4927 if cleanworkdir:
4929 if cleanworkdir:
4928 # i18n: column positioning for "hg summary"
4930 # i18n: column positioning for "hg summary"
4929 ui.status(_('commit: %s\n') % t.strip())
4931 ui.status(_('commit: %s\n') % t.strip())
4930 else:
4932 else:
4931 # i18n: column positioning for "hg summary"
4933 # i18n: column positioning for "hg summary"
4932 ui.write(_('commit: %s\n') % t.strip())
4934 ui.write(_('commit: %s\n') % t.strip())
4933
4935
4934 # all ancestors of branch heads - all ancestors of parent = new csets
4936 # all ancestors of branch heads - all ancestors of parent = new csets
4935 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
4937 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
4936 bheads))
4938 bheads))
4937
4939
4938 if new == 0:
4940 if new == 0:
4939 # i18n: column positioning for "hg summary"
4941 # i18n: column positioning for "hg summary"
4940 ui.status(_('update: (current)\n'))
4942 ui.status(_('update: (current)\n'))
4941 elif pnode not in bheads:
4943 elif pnode not in bheads:
4942 # i18n: column positioning for "hg summary"
4944 # i18n: column positioning for "hg summary"
4943 ui.write(_('update: %d new changesets (update)\n') % new)
4945 ui.write(_('update: %d new changesets (update)\n') % new)
4944 else:
4946 else:
4945 # i18n: column positioning for "hg summary"
4947 # i18n: column positioning for "hg summary"
4946 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
4948 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
4947 (new, len(bheads)))
4949 (new, len(bheads)))
4948
4950
4949 t = []
4951 t = []
4950 draft = len(repo.revs('draft()'))
4952 draft = len(repo.revs('draft()'))
4951 if draft:
4953 if draft:
4952 t.append(_('%d draft') % draft)
4954 t.append(_('%d draft') % draft)
4953 secret = len(repo.revs('secret()'))
4955 secret = len(repo.revs('secret()'))
4954 if secret:
4956 if secret:
4955 t.append(_('%d secret') % secret)
4957 t.append(_('%d secret') % secret)
4956
4958
4957 if draft or secret:
4959 if draft or secret:
4958 ui.status(_('phases: %s\n') % ', '.join(t))
4960 ui.status(_('phases: %s\n') % ', '.join(t))
4959
4961
4960 if obsolete.isenabled(repo, obsolete.createmarkersopt):
4962 if obsolete.isenabled(repo, obsolete.createmarkersopt):
4961 for trouble in ("unstable", "divergent", "bumped"):
4963 for trouble in ("unstable", "divergent", "bumped"):
4962 numtrouble = len(repo.revs(trouble + "()"))
4964 numtrouble = len(repo.revs(trouble + "()"))
4963 # We write all the possibilities to ease translation
4965 # We write all the possibilities to ease translation
4964 troublemsg = {
4966 troublemsg = {
4965 "unstable": _("unstable: %d changesets"),
4967 "unstable": _("unstable: %d changesets"),
4966 "divergent": _("divergent: %d changesets"),
4968 "divergent": _("divergent: %d changesets"),
4967 "bumped": _("bumped: %d changesets"),
4969 "bumped": _("bumped: %d changesets"),
4968 }
4970 }
4969 if numtrouble > 0:
4971 if numtrouble > 0:
4970 ui.status(troublemsg[trouble] % numtrouble + "\n")
4972 ui.status(troublemsg[trouble] % numtrouble + "\n")
4971
4973
4972 cmdutil.summaryhooks(ui, repo)
4974 cmdutil.summaryhooks(ui, repo)
4973
4975
4974 if opts.get('remote'):
4976 if opts.get('remote'):
4975 needsincoming, needsoutgoing = True, True
4977 needsincoming, needsoutgoing = True, True
4976 else:
4978 else:
4977 needsincoming, needsoutgoing = False, False
4979 needsincoming, needsoutgoing = False, False
4978 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
4980 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
4979 if i:
4981 if i:
4980 needsincoming = True
4982 needsincoming = True
4981 if o:
4983 if o:
4982 needsoutgoing = True
4984 needsoutgoing = True
4983 if not needsincoming and not needsoutgoing:
4985 if not needsincoming and not needsoutgoing:
4984 return
4986 return
4985
4987
4986 def getincoming():
4988 def getincoming():
4987 source, branches = hg.parseurl(ui.expandpath('default'))
4989 source, branches = hg.parseurl(ui.expandpath('default'))
4988 sbranch = branches[0]
4990 sbranch = branches[0]
4989 try:
4991 try:
4990 other = hg.peer(repo, {}, source)
4992 other = hg.peer(repo, {}, source)
4991 except error.RepoError:
4993 except error.RepoError:
4992 if opts.get('remote'):
4994 if opts.get('remote'):
4993 raise
4995 raise
4994 return source, sbranch, None, None, None
4996 return source, sbranch, None, None, None
4995 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
4997 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
4996 if revs:
4998 if revs:
4997 revs = [other.lookup(rev) for rev in revs]
4999 revs = [other.lookup(rev) for rev in revs]
4998 ui.debug('comparing with %s\n' % util.hidepassword(source))
5000 ui.debug('comparing with %s\n' % util.hidepassword(source))
4999 repo.ui.pushbuffer()
5001 repo.ui.pushbuffer()
5000 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5002 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5001 repo.ui.popbuffer()
5003 repo.ui.popbuffer()
5002 return source, sbranch, other, commoninc, commoninc[1]
5004 return source, sbranch, other, commoninc, commoninc[1]
5003
5005
5004 if needsincoming:
5006 if needsincoming:
5005 source, sbranch, sother, commoninc, incoming = getincoming()
5007 source, sbranch, sother, commoninc, incoming = getincoming()
5006 else:
5008 else:
5007 source = sbranch = sother = commoninc = incoming = None
5009 source = sbranch = sother = commoninc = incoming = None
5008
5010
5009 def getoutgoing():
5011 def getoutgoing():
5010 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5012 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5011 dbranch = branches[0]
5013 dbranch = branches[0]
5012 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5014 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5013 if source != dest:
5015 if source != dest:
5014 try:
5016 try:
5015 dother = hg.peer(repo, {}, dest)
5017 dother = hg.peer(repo, {}, dest)
5016 except error.RepoError:
5018 except error.RepoError:
5017 if opts.get('remote'):
5019 if opts.get('remote'):
5018 raise
5020 raise
5019 return dest, dbranch, None, None
5021 return dest, dbranch, None, None
5020 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5022 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5021 elif sother is None:
5023 elif sother is None:
5022 # there is no explicit destination peer, but source one is invalid
5024 # there is no explicit destination peer, but source one is invalid
5023 return dest, dbranch, None, None
5025 return dest, dbranch, None, None
5024 else:
5026 else:
5025 dother = sother
5027 dother = sother
5026 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5028 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5027 common = None
5029 common = None
5028 else:
5030 else:
5029 common = commoninc
5031 common = commoninc
5030 if revs:
5032 if revs:
5031 revs = [repo.lookup(rev) for rev in revs]
5033 revs = [repo.lookup(rev) for rev in revs]
5032 repo.ui.pushbuffer()
5034 repo.ui.pushbuffer()
5033 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5035 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5034 commoninc=common)
5036 commoninc=common)
5035 repo.ui.popbuffer()
5037 repo.ui.popbuffer()
5036 return dest, dbranch, dother, outgoing
5038 return dest, dbranch, dother, outgoing
5037
5039
5038 if needsoutgoing:
5040 if needsoutgoing:
5039 dest, dbranch, dother, outgoing = getoutgoing()
5041 dest, dbranch, dother, outgoing = getoutgoing()
5040 else:
5042 else:
5041 dest = dbranch = dother = outgoing = None
5043 dest = dbranch = dother = outgoing = None
5042
5044
5043 if opts.get('remote'):
5045 if opts.get('remote'):
5044 t = []
5046 t = []
5045 if incoming:
5047 if incoming:
5046 t.append(_('1 or more incoming'))
5048 t.append(_('1 or more incoming'))
5047 o = outgoing.missing
5049 o = outgoing.missing
5048 if o:
5050 if o:
5049 t.append(_('%d outgoing') % len(o))
5051 t.append(_('%d outgoing') % len(o))
5050 other = dother or sother
5052 other = dother or sother
5051 if 'bookmarks' in other.listkeys('namespaces'):
5053 if 'bookmarks' in other.listkeys('namespaces'):
5052 counts = bookmarks.summary(repo, other)
5054 counts = bookmarks.summary(repo, other)
5053 if counts[0] > 0:
5055 if counts[0] > 0:
5054 t.append(_('%d incoming bookmarks') % counts[0])
5056 t.append(_('%d incoming bookmarks') % counts[0])
5055 if counts[1] > 0:
5057 if counts[1] > 0:
5056 t.append(_('%d outgoing bookmarks') % counts[1])
5058 t.append(_('%d outgoing bookmarks') % counts[1])
5057
5059
5058 if t:
5060 if t:
5059 # i18n: column positioning for "hg summary"
5061 # i18n: column positioning for "hg summary"
5060 ui.write(_('remote: %s\n') % (', '.join(t)))
5062 ui.write(_('remote: %s\n') % (', '.join(t)))
5061 else:
5063 else:
5062 # i18n: column positioning for "hg summary"
5064 # i18n: column positioning for "hg summary"
5063 ui.status(_('remote: (synced)\n'))
5065 ui.status(_('remote: (synced)\n'))
5064
5066
5065 cmdutil.summaryremotehooks(ui, repo, opts,
5067 cmdutil.summaryremotehooks(ui, repo, opts,
5066 ((source, sbranch, sother, commoninc),
5068 ((source, sbranch, sother, commoninc),
5067 (dest, dbranch, dother, outgoing)))
5069 (dest, dbranch, dother, outgoing)))
5068
5070
5069 @command('tag',
5071 @command('tag',
5070 [('f', 'force', None, _('force tag')),
5072 [('f', 'force', None, _('force tag')),
5071 ('l', 'local', None, _('make the tag local')),
5073 ('l', 'local', None, _('make the tag local')),
5072 ('r', 'rev', '', _('revision to tag'), _('REV')),
5074 ('r', 'rev', '', _('revision to tag'), _('REV')),
5073 ('', 'remove', None, _('remove a tag')),
5075 ('', 'remove', None, _('remove a tag')),
5074 # -l/--local is already there, commitopts cannot be used
5076 # -l/--local is already there, commitopts cannot be used
5075 ('e', 'edit', None, _('invoke editor on commit messages')),
5077 ('e', 'edit', None, _('invoke editor on commit messages')),
5076 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5078 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5077 ] + commitopts2,
5079 ] + commitopts2,
5078 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5080 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5079 def tag(ui, repo, name1, *names, **opts):
5081 def tag(ui, repo, name1, *names, **opts):
5080 """add one or more tags for the current or given revision
5082 """add one or more tags for the current or given revision
5081
5083
5082 Name a particular revision using <name>.
5084 Name a particular revision using <name>.
5083
5085
5084 Tags are used to name particular revisions of the repository and are
5086 Tags are used to name particular revisions of the repository and are
5085 very useful to compare different revisions, to go back to significant
5087 very useful to compare different revisions, to go back to significant
5086 earlier versions or to mark branch points as releases, etc. Changing
5088 earlier versions or to mark branch points as releases, etc. Changing
5087 an existing tag is normally disallowed; use -f/--force to override.
5089 an existing tag is normally disallowed; use -f/--force to override.
5088
5090
5089 If no revision is given, the parent of the working directory is
5091 If no revision is given, the parent of the working directory is
5090 used.
5092 used.
5091
5093
5092 To facilitate version control, distribution, and merging of tags,
5094 To facilitate version control, distribution, and merging of tags,
5093 they are stored as a file named ".hgtags" which is managed similarly
5095 they are stored as a file named ".hgtags" which is managed similarly
5094 to other project files and can be hand-edited if necessary. This
5096 to other project files and can be hand-edited if necessary. This
5095 also means that tagging creates a new commit. The file
5097 also means that tagging creates a new commit. The file
5096 ".hg/localtags" is used for local tags (not shared among
5098 ".hg/localtags" is used for local tags (not shared among
5097 repositories).
5099 repositories).
5098
5100
5099 Tag commits are usually made at the head of a branch. If the parent
5101 Tag commits are usually made at the head of a branch. If the parent
5100 of the working directory is not a branch head, :hg:`tag` aborts; use
5102 of the working directory is not a branch head, :hg:`tag` aborts; use
5101 -f/--force to force the tag commit to be based on a non-head
5103 -f/--force to force the tag commit to be based on a non-head
5102 changeset.
5104 changeset.
5103
5105
5104 See :hg:`help dates` for a list of formats valid for -d/--date.
5106 See :hg:`help dates` for a list of formats valid for -d/--date.
5105
5107
5106 Since tag names have priority over branch names during revision
5108 Since tag names have priority over branch names during revision
5107 lookup, using an existing branch name as a tag name is discouraged.
5109 lookup, using an existing branch name as a tag name is discouraged.
5108
5110
5109 Returns 0 on success.
5111 Returns 0 on success.
5110 """
5112 """
5111 opts = pycompat.byteskwargs(opts)
5113 opts = pycompat.byteskwargs(opts)
5112 wlock = lock = None
5114 wlock = lock = None
5113 try:
5115 try:
5114 wlock = repo.wlock()
5116 wlock = repo.wlock()
5115 lock = repo.lock()
5117 lock = repo.lock()
5116 rev_ = "."
5118 rev_ = "."
5117 names = [t.strip() for t in (name1,) + names]
5119 names = [t.strip() for t in (name1,) + names]
5118 if len(names) != len(set(names)):
5120 if len(names) != len(set(names)):
5119 raise error.Abort(_('tag names must be unique'))
5121 raise error.Abort(_('tag names must be unique'))
5120 for n in names:
5122 for n in names:
5121 scmutil.checknewlabel(repo, n, 'tag')
5123 scmutil.checknewlabel(repo, n, 'tag')
5122 if not n:
5124 if not n:
5123 raise error.Abort(_('tag names cannot consist entirely of '
5125 raise error.Abort(_('tag names cannot consist entirely of '
5124 'whitespace'))
5126 'whitespace'))
5125 if opts.get('rev') and opts.get('remove'):
5127 if opts.get('rev') and opts.get('remove'):
5126 raise error.Abort(_("--rev and --remove are incompatible"))
5128 raise error.Abort(_("--rev and --remove are incompatible"))
5127 if opts.get('rev'):
5129 if opts.get('rev'):
5128 rev_ = opts['rev']
5130 rev_ = opts['rev']
5129 message = opts.get('message')
5131 message = opts.get('message')
5130 if opts.get('remove'):
5132 if opts.get('remove'):
5131 if opts.get('local'):
5133 if opts.get('local'):
5132 expectedtype = 'local'
5134 expectedtype = 'local'
5133 else:
5135 else:
5134 expectedtype = 'global'
5136 expectedtype = 'global'
5135
5137
5136 for n in names:
5138 for n in names:
5137 if not repo.tagtype(n):
5139 if not repo.tagtype(n):
5138 raise error.Abort(_("tag '%s' does not exist") % n)
5140 raise error.Abort(_("tag '%s' does not exist") % n)
5139 if repo.tagtype(n) != expectedtype:
5141 if repo.tagtype(n) != expectedtype:
5140 if expectedtype == 'global':
5142 if expectedtype == 'global':
5141 raise error.Abort(_("tag '%s' is not a global tag") % n)
5143 raise error.Abort(_("tag '%s' is not a global tag") % n)
5142 else:
5144 else:
5143 raise error.Abort(_("tag '%s' is not a local tag") % n)
5145 raise error.Abort(_("tag '%s' is not a local tag") % n)
5144 rev_ = 'null'
5146 rev_ = 'null'
5145 if not message:
5147 if not message:
5146 # we don't translate commit messages
5148 # we don't translate commit messages
5147 message = 'Removed tag %s' % ', '.join(names)
5149 message = 'Removed tag %s' % ', '.join(names)
5148 elif not opts.get('force'):
5150 elif not opts.get('force'):
5149 for n in names:
5151 for n in names:
5150 if n in repo.tags():
5152 if n in repo.tags():
5151 raise error.Abort(_("tag '%s' already exists "
5153 raise error.Abort(_("tag '%s' already exists "
5152 "(use -f to force)") % n)
5154 "(use -f to force)") % n)
5153 if not opts.get('local'):
5155 if not opts.get('local'):
5154 p1, p2 = repo.dirstate.parents()
5156 p1, p2 = repo.dirstate.parents()
5155 if p2 != nullid:
5157 if p2 != nullid:
5156 raise error.Abort(_('uncommitted merge'))
5158 raise error.Abort(_('uncommitted merge'))
5157 bheads = repo.branchheads()
5159 bheads = repo.branchheads()
5158 if not opts.get('force') and bheads and p1 not in bheads:
5160 if not opts.get('force') and bheads and p1 not in bheads:
5159 raise error.Abort(_('working directory is not at a branch head '
5161 raise error.Abort(_('working directory is not at a branch head '
5160 '(use -f to force)'))
5162 '(use -f to force)'))
5161 r = scmutil.revsingle(repo, rev_).node()
5163 r = scmutil.revsingle(repo, rev_).node()
5162
5164
5163 if not message:
5165 if not message:
5164 # we don't translate commit messages
5166 # we don't translate commit messages
5165 message = ('Added tag %s for changeset %s' %
5167 message = ('Added tag %s for changeset %s' %
5166 (', '.join(names), short(r)))
5168 (', '.join(names), short(r)))
5167
5169
5168 date = opts.get('date')
5170 date = opts.get('date')
5169 if date:
5171 if date:
5170 date = util.parsedate(date)
5172 date = util.parsedate(date)
5171
5173
5172 if opts.get('remove'):
5174 if opts.get('remove'):
5173 editform = 'tag.remove'
5175 editform = 'tag.remove'
5174 else:
5176 else:
5175 editform = 'tag.add'
5177 editform = 'tag.add'
5176 editor = cmdutil.getcommiteditor(editform=editform,
5178 editor = cmdutil.getcommiteditor(editform=editform,
5177 **pycompat.strkwargs(opts))
5179 **pycompat.strkwargs(opts))
5178
5180
5179 # don't allow tagging the null rev
5181 # don't allow tagging the null rev
5180 if (not opts.get('remove') and
5182 if (not opts.get('remove') and
5181 scmutil.revsingle(repo, rev_).rev() == nullrev):
5183 scmutil.revsingle(repo, rev_).rev() == nullrev):
5182 raise error.Abort(_("cannot tag null revision"))
5184 raise error.Abort(_("cannot tag null revision"))
5183
5185
5184 tagsmod.tag(repo, names, r, message, opts.get('local'),
5186 tagsmod.tag(repo, names, r, message, opts.get('local'),
5185 opts.get('user'), date, editor=editor)
5187 opts.get('user'), date, editor=editor)
5186 finally:
5188 finally:
5187 release(lock, wlock)
5189 release(lock, wlock)
5188
5190
5189 @command('tags', formatteropts, '')
5191 @command('tags', formatteropts, '')
5190 def tags(ui, repo, **opts):
5192 def tags(ui, repo, **opts):
5191 """list repository tags
5193 """list repository tags
5192
5194
5193 This lists both regular and local tags. When the -v/--verbose
5195 This lists both regular and local tags. When the -v/--verbose
5194 switch is used, a third column "local" is printed for local tags.
5196 switch is used, a third column "local" is printed for local tags.
5195 When the -q/--quiet switch is used, only the tag name is printed.
5197 When the -q/--quiet switch is used, only the tag name is printed.
5196
5198
5197 Returns 0 on success.
5199 Returns 0 on success.
5198 """
5200 """
5199
5201
5200 opts = pycompat.byteskwargs(opts)
5202 opts = pycompat.byteskwargs(opts)
5201 ui.pager('tags')
5203 ui.pager('tags')
5202 fm = ui.formatter('tags', opts)
5204 fm = ui.formatter('tags', opts)
5203 hexfunc = fm.hexfunc
5205 hexfunc = fm.hexfunc
5204 tagtype = ""
5206 tagtype = ""
5205
5207
5206 for t, n in reversed(repo.tagslist()):
5208 for t, n in reversed(repo.tagslist()):
5207 hn = hexfunc(n)
5209 hn = hexfunc(n)
5208 label = 'tags.normal'
5210 label = 'tags.normal'
5209 tagtype = ''
5211 tagtype = ''
5210 if repo.tagtype(t) == 'local':
5212 if repo.tagtype(t) == 'local':
5211 label = 'tags.local'
5213 label = 'tags.local'
5212 tagtype = 'local'
5214 tagtype = 'local'
5213
5215
5214 fm.startitem()
5216 fm.startitem()
5215 fm.write('tag', '%s', t, label=label)
5217 fm.write('tag', '%s', t, label=label)
5216 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5218 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5217 fm.condwrite(not ui.quiet, 'rev node', fmt,
5219 fm.condwrite(not ui.quiet, 'rev node', fmt,
5218 repo.changelog.rev(n), hn, label=label)
5220 repo.changelog.rev(n), hn, label=label)
5219 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5221 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5220 tagtype, label=label)
5222 tagtype, label=label)
5221 fm.plain('\n')
5223 fm.plain('\n')
5222 fm.end()
5224 fm.end()
5223
5225
5224 @command('tip',
5226 @command('tip',
5225 [('p', 'patch', None, _('show patch')),
5227 [('p', 'patch', None, _('show patch')),
5226 ('g', 'git', None, _('use git extended diff format')),
5228 ('g', 'git', None, _('use git extended diff format')),
5227 ] + templateopts,
5229 ] + templateopts,
5228 _('[-p] [-g]'))
5230 _('[-p] [-g]'))
5229 def tip(ui, repo, **opts):
5231 def tip(ui, repo, **opts):
5230 """show the tip revision (DEPRECATED)
5232 """show the tip revision (DEPRECATED)
5231
5233
5232 The tip revision (usually just called the tip) is the changeset
5234 The tip revision (usually just called the tip) is the changeset
5233 most recently added to the repository (and therefore the most
5235 most recently added to the repository (and therefore the most
5234 recently changed head).
5236 recently changed head).
5235
5237
5236 If you have just made a commit, that commit will be the tip. If
5238 If you have just made a commit, that commit will be the tip. If
5237 you have just pulled changes from another repository, the tip of
5239 you have just pulled changes from another repository, the tip of
5238 that repository becomes the current tip. The "tip" tag is special
5240 that repository becomes the current tip. The "tip" tag is special
5239 and cannot be renamed or assigned to a different changeset.
5241 and cannot be renamed or assigned to a different changeset.
5240
5242
5241 This command is deprecated, please use :hg:`heads` instead.
5243 This command is deprecated, please use :hg:`heads` instead.
5242
5244
5243 Returns 0 on success.
5245 Returns 0 on success.
5244 """
5246 """
5245 opts = pycompat.byteskwargs(opts)
5247 opts = pycompat.byteskwargs(opts)
5246 displayer = cmdutil.show_changeset(ui, repo, opts)
5248 displayer = cmdutil.show_changeset(ui, repo, opts)
5247 displayer.show(repo['tip'])
5249 displayer.show(repo['tip'])
5248 displayer.close()
5250 displayer.close()
5249
5251
5250 @command('unbundle',
5252 @command('unbundle',
5251 [('u', 'update', None,
5253 [('u', 'update', None,
5252 _('update to new branch head if changesets were unbundled'))],
5254 _('update to new branch head if changesets were unbundled'))],
5253 _('[-u] FILE...'))
5255 _('[-u] FILE...'))
5254 def unbundle(ui, repo, fname1, *fnames, **opts):
5256 def unbundle(ui, repo, fname1, *fnames, **opts):
5255 """apply one or more bundle files
5257 """apply one or more bundle files
5256
5258
5257 Apply one or more bundle files generated by :hg:`bundle`.
5259 Apply one or more bundle files generated by :hg:`bundle`.
5258
5260
5259 Returns 0 on success, 1 if an update has unresolved files.
5261 Returns 0 on success, 1 if an update has unresolved files.
5260 """
5262 """
5261 fnames = (fname1,) + fnames
5263 fnames = (fname1,) + fnames
5262
5264
5263 with repo.lock():
5265 with repo.lock():
5264 for fname in fnames:
5266 for fname in fnames:
5265 f = hg.openpath(ui, fname)
5267 f = hg.openpath(ui, fname)
5266 gen = exchange.readbundle(ui, f, fname)
5268 gen = exchange.readbundle(ui, f, fname)
5267 if isinstance(gen, bundle2.unbundle20):
5269 if isinstance(gen, bundle2.unbundle20):
5268 tr = repo.transaction('unbundle')
5270 tr = repo.transaction('unbundle')
5269 try:
5271 try:
5270 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5272 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5271 url='bundle:' + fname)
5273 url='bundle:' + fname)
5272 tr.close()
5274 tr.close()
5273 except error.BundleUnknownFeatureError as exc:
5275 except error.BundleUnknownFeatureError as exc:
5274 raise error.Abort(_('%s: unknown bundle feature, %s')
5276 raise error.Abort(_('%s: unknown bundle feature, %s')
5275 % (fname, exc),
5277 % (fname, exc),
5276 hint=_("see https://mercurial-scm.org/"
5278 hint=_("see https://mercurial-scm.org/"
5277 "wiki/BundleFeature for more "
5279 "wiki/BundleFeature for more "
5278 "information"))
5280 "information"))
5279 finally:
5281 finally:
5280 if tr:
5282 if tr:
5281 tr.release()
5283 tr.release()
5282 changes = [r.get('return', 0)
5284 changes = [r.get('return', 0)
5283 for r in op.records['changegroup']]
5285 for r in op.records['changegroup']]
5284 modheads = changegroup.combineresults(changes)
5286 modheads = changegroup.combineresults(changes)
5285 elif isinstance(gen, streamclone.streamcloneapplier):
5287 elif isinstance(gen, streamclone.streamcloneapplier):
5286 raise error.Abort(
5288 raise error.Abort(
5287 _('packed bundles cannot be applied with '
5289 _('packed bundles cannot be applied with '
5288 '"hg unbundle"'),
5290 '"hg unbundle"'),
5289 hint=_('use "hg debugapplystreamclonebundle"'))
5291 hint=_('use "hg debugapplystreamclonebundle"'))
5290 else:
5292 else:
5291 modheads = gen.apply(repo, 'unbundle', 'bundle:' + fname)
5293 modheads = gen.apply(repo, 'unbundle', 'bundle:' + fname)
5292
5294
5293 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5295 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5294
5296
5295 @command('^update|up|checkout|co',
5297 @command('^update|up|checkout|co',
5296 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5298 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5297 ('c', 'check', None, _('require clean working directory')),
5299 ('c', 'check', None, _('require clean working directory')),
5298 ('m', 'merge', None, _('merge uncommitted changes')),
5300 ('m', 'merge', None, _('merge uncommitted changes')),
5299 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5301 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5300 ('r', 'rev', '', _('revision'), _('REV'))
5302 ('r', 'rev', '', _('revision'), _('REV'))
5301 ] + mergetoolopts,
5303 ] + mergetoolopts,
5302 _('[-C|-c|-m] [-d DATE] [[-r] REV]'))
5304 _('[-C|-c|-m] [-d DATE] [[-r] REV]'))
5303 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
5305 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
5304 merge=None, tool=None):
5306 merge=None, tool=None):
5305 """update working directory (or switch revisions)
5307 """update working directory (or switch revisions)
5306
5308
5307 Update the repository's working directory to the specified
5309 Update the repository's working directory to the specified
5308 changeset. If no changeset is specified, update to the tip of the
5310 changeset. If no changeset is specified, update to the tip of the
5309 current named branch and move the active bookmark (see :hg:`help
5311 current named branch and move the active bookmark (see :hg:`help
5310 bookmarks`).
5312 bookmarks`).
5311
5313
5312 Update sets the working directory's parent revision to the specified
5314 Update sets the working directory's parent revision to the specified
5313 changeset (see :hg:`help parents`).
5315 changeset (see :hg:`help parents`).
5314
5316
5315 If the changeset is not a descendant or ancestor of the working
5317 If the changeset is not a descendant or ancestor of the working
5316 directory's parent and there are uncommitted changes, the update is
5318 directory's parent and there are uncommitted changes, the update is
5317 aborted. With the -c/--check option, the working directory is checked
5319 aborted. With the -c/--check option, the working directory is checked
5318 for uncommitted changes; if none are found, the working directory is
5320 for uncommitted changes; if none are found, the working directory is
5319 updated to the specified changeset.
5321 updated to the specified changeset.
5320
5322
5321 .. container:: verbose
5323 .. container:: verbose
5322
5324
5323 The -C/--clean, -c/--check, and -m/--merge options control what
5325 The -C/--clean, -c/--check, and -m/--merge options control what
5324 happens if the working directory contains uncommitted changes.
5326 happens if the working directory contains uncommitted changes.
5325 At most of one of them can be specified.
5327 At most of one of them can be specified.
5326
5328
5327 1. If no option is specified, and if
5329 1. If no option is specified, and if
5328 the requested changeset is an ancestor or descendant of
5330 the requested changeset is an ancestor or descendant of
5329 the working directory's parent, the uncommitted changes
5331 the working directory's parent, the uncommitted changes
5330 are merged into the requested changeset and the merged
5332 are merged into the requested changeset and the merged
5331 result is left uncommitted. If the requested changeset is
5333 result is left uncommitted. If the requested changeset is
5332 not an ancestor or descendant (that is, it is on another
5334 not an ancestor or descendant (that is, it is on another
5333 branch), the update is aborted and the uncommitted changes
5335 branch), the update is aborted and the uncommitted changes
5334 are preserved.
5336 are preserved.
5335
5337
5336 2. With the -m/--merge option, the update is allowed even if the
5338 2. With the -m/--merge option, the update is allowed even if the
5337 requested changeset is not an ancestor or descendant of
5339 requested changeset is not an ancestor or descendant of
5338 the working directory's parent.
5340 the working directory's parent.
5339
5341
5340 3. With the -c/--check option, the update is aborted and the
5342 3. With the -c/--check option, the update is aborted and the
5341 uncommitted changes are preserved.
5343 uncommitted changes are preserved.
5342
5344
5343 4. With the -C/--clean option, uncommitted changes are discarded and
5345 4. With the -C/--clean option, uncommitted changes are discarded and
5344 the working directory is updated to the requested changeset.
5346 the working directory is updated to the requested changeset.
5345
5347
5346 To cancel an uncommitted merge (and lose your changes), use
5348 To cancel an uncommitted merge (and lose your changes), use
5347 :hg:`update --clean .`.
5349 :hg:`update --clean .`.
5348
5350
5349 Use null as the changeset to remove the working directory (like
5351 Use null as the changeset to remove the working directory (like
5350 :hg:`clone -U`).
5352 :hg:`clone -U`).
5351
5353
5352 If you want to revert just one file to an older revision, use
5354 If you want to revert just one file to an older revision, use
5353 :hg:`revert [-r REV] NAME`.
5355 :hg:`revert [-r REV] NAME`.
5354
5356
5355 See :hg:`help dates` for a list of formats valid for -d/--date.
5357 See :hg:`help dates` for a list of formats valid for -d/--date.
5356
5358
5357 Returns 0 on success, 1 if there are unresolved files.
5359 Returns 0 on success, 1 if there are unresolved files.
5358 """
5360 """
5359 if rev and node:
5361 if rev and node:
5360 raise error.Abort(_("please specify just one revision"))
5362 raise error.Abort(_("please specify just one revision"))
5361
5363
5362 if ui.configbool('commands', 'update.requiredest'):
5364 if ui.configbool('commands', 'update.requiredest'):
5363 if not node and not rev and not date:
5365 if not node and not rev and not date:
5364 raise error.Abort(_('you must specify a destination'),
5366 raise error.Abort(_('you must specify a destination'),
5365 hint=_('for example: hg update ".::"'))
5367 hint=_('for example: hg update ".::"'))
5366
5368
5367 if rev is None or rev == '':
5369 if rev is None or rev == '':
5368 rev = node
5370 rev = node
5369
5371
5370 if date and rev is not None:
5372 if date and rev is not None:
5371 raise error.Abort(_("you can't specify a revision and a date"))
5373 raise error.Abort(_("you can't specify a revision and a date"))
5372
5374
5373 if len([x for x in (clean, check, merge) if x]) > 1:
5375 if len([x for x in (clean, check, merge) if x]) > 1:
5374 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
5376 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
5375 "or -m/merge"))
5377 "or -m/merge"))
5376
5378
5377 updatecheck = None
5379 updatecheck = None
5378 if check:
5380 if check:
5379 updatecheck = 'abort'
5381 updatecheck = 'abort'
5380 elif merge:
5382 elif merge:
5381 updatecheck = 'none'
5383 updatecheck = 'none'
5382
5384
5383 with repo.wlock():
5385 with repo.wlock():
5384 cmdutil.clearunfinished(repo)
5386 cmdutil.clearunfinished(repo)
5385
5387
5386 if date:
5388 if date:
5387 rev = cmdutil.finddate(ui, repo, date)
5389 rev = cmdutil.finddate(ui, repo, date)
5388
5390
5389 # if we defined a bookmark, we have to remember the original name
5391 # if we defined a bookmark, we have to remember the original name
5390 brev = rev
5392 brev = rev
5391 rev = scmutil.revsingle(repo, rev, rev).rev()
5393 rev = scmutil.revsingle(repo, rev, rev).rev()
5392
5394
5393 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
5395 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
5394
5396
5395 return hg.updatetotally(ui, repo, rev, brev, clean=clean,
5397 return hg.updatetotally(ui, repo, rev, brev, clean=clean,
5396 updatecheck=updatecheck)
5398 updatecheck=updatecheck)
5397
5399
5398 @command('verify', [])
5400 @command('verify', [])
5399 def verify(ui, repo):
5401 def verify(ui, repo):
5400 """verify the integrity of the repository
5402 """verify the integrity of the repository
5401
5403
5402 Verify the integrity of the current repository.
5404 Verify the integrity of the current repository.
5403
5405
5404 This will perform an extensive check of the repository's
5406 This will perform an extensive check of the repository's
5405 integrity, validating the hashes and checksums of each entry in
5407 integrity, validating the hashes and checksums of each entry in
5406 the changelog, manifest, and tracked files, as well as the
5408 the changelog, manifest, and tracked files, as well as the
5407 integrity of their crosslinks and indices.
5409 integrity of their crosslinks and indices.
5408
5410
5409 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
5411 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
5410 for more information about recovery from corruption of the
5412 for more information about recovery from corruption of the
5411 repository.
5413 repository.
5412
5414
5413 Returns 0 on success, 1 if errors are encountered.
5415 Returns 0 on success, 1 if errors are encountered.
5414 """
5416 """
5415 return hg.verify(repo)
5417 return hg.verify(repo)
5416
5418
5417 @command('version', [] + formatteropts, norepo=True)
5419 @command('version', [] + formatteropts, norepo=True)
5418 def version_(ui, **opts):
5420 def version_(ui, **opts):
5419 """output version and copyright information"""
5421 """output version and copyright information"""
5420 opts = pycompat.byteskwargs(opts)
5422 opts = pycompat.byteskwargs(opts)
5421 if ui.verbose:
5423 if ui.verbose:
5422 ui.pager('version')
5424 ui.pager('version')
5423 fm = ui.formatter("version", opts)
5425 fm = ui.formatter("version", opts)
5424 fm.startitem()
5426 fm.startitem()
5425 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
5427 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
5426 util.version())
5428 util.version())
5427 license = _(
5429 license = _(
5428 "(see https://mercurial-scm.org for more information)\n"
5430 "(see https://mercurial-scm.org for more information)\n"
5429 "\nCopyright (C) 2005-2017 Matt Mackall and others\n"
5431 "\nCopyright (C) 2005-2017 Matt Mackall and others\n"
5430 "This is free software; see the source for copying conditions. "
5432 "This is free software; see the source for copying conditions. "
5431 "There is NO\nwarranty; "
5433 "There is NO\nwarranty; "
5432 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5434 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5433 )
5435 )
5434 if not ui.quiet:
5436 if not ui.quiet:
5435 fm.plain(license)
5437 fm.plain(license)
5436
5438
5437 if ui.verbose:
5439 if ui.verbose:
5438 fm.plain(_("\nEnabled extensions:\n\n"))
5440 fm.plain(_("\nEnabled extensions:\n\n"))
5439 # format names and versions into columns
5441 # format names and versions into columns
5440 names = []
5442 names = []
5441 vers = []
5443 vers = []
5442 isinternals = []
5444 isinternals = []
5443 for name, module in extensions.extensions():
5445 for name, module in extensions.extensions():
5444 names.append(name)
5446 names.append(name)
5445 vers.append(extensions.moduleversion(module) or None)
5447 vers.append(extensions.moduleversion(module) or None)
5446 isinternals.append(extensions.ismoduleinternal(module))
5448 isinternals.append(extensions.ismoduleinternal(module))
5447 fn = fm.nested("extensions")
5449 fn = fm.nested("extensions")
5448 if names:
5450 if names:
5449 namefmt = " %%-%ds " % max(len(n) for n in names)
5451 namefmt = " %%-%ds " % max(len(n) for n in names)
5450 places = [_("external"), _("internal")]
5452 places = [_("external"), _("internal")]
5451 for n, v, p in zip(names, vers, isinternals):
5453 for n, v, p in zip(names, vers, isinternals):
5452 fn.startitem()
5454 fn.startitem()
5453 fn.condwrite(ui.verbose, "name", namefmt, n)
5455 fn.condwrite(ui.verbose, "name", namefmt, n)
5454 if ui.verbose:
5456 if ui.verbose:
5455 fn.plain("%s " % places[p])
5457 fn.plain("%s " % places[p])
5456 fn.data(bundled=p)
5458 fn.data(bundled=p)
5457 fn.condwrite(ui.verbose and v, "ver", "%s", v)
5459 fn.condwrite(ui.verbose and v, "ver", "%s", v)
5458 if ui.verbose:
5460 if ui.verbose:
5459 fn.plain("\n")
5461 fn.plain("\n")
5460 fn.end()
5462 fn.end()
5461 fm.end()
5463 fm.end()
5462
5464
5463 def loadcmdtable(ui, name, cmdtable):
5465 def loadcmdtable(ui, name, cmdtable):
5464 """Load command functions from specified cmdtable
5466 """Load command functions from specified cmdtable
5465 """
5467 """
5466 overrides = [cmd for cmd in cmdtable if cmd in table]
5468 overrides = [cmd for cmd in cmdtable if cmd in table]
5467 if overrides:
5469 if overrides:
5468 ui.warn(_("extension '%s' overrides commands: %s\n")
5470 ui.warn(_("extension '%s' overrides commands: %s\n")
5469 % (name, " ".join(overrides)))
5471 % (name, " ".join(overrides)))
5470 table.update(cmdtable)
5472 table.update(cmdtable)
@@ -1,2168 +1,2165 b''
1 # debugcommands.py - command processing for debug* commands
1 # debugcommands.py - command processing for debug* commands
2 #
2 #
3 # Copyright 2005-2016 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2016 Matt Mackall <mpm@selenic.com>
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 difflib
10 import difflib
11 import errno
11 import errno
12 import operator
12 import operator
13 import os
13 import os
14 import random
14 import random
15 import socket
15 import socket
16 import string
16 import string
17 import sys
17 import sys
18 import tempfile
18 import tempfile
19 import time
19 import time
20
20
21 from .i18n import _
21 from .i18n import _
22 from .node import (
22 from .node import (
23 bin,
23 bin,
24 hex,
24 hex,
25 nullhex,
25 nullhex,
26 nullid,
26 nullid,
27 nullrev,
27 nullrev,
28 short,
28 short,
29 )
29 )
30 from . import (
30 from . import (
31 bundle2,
31 bundle2,
32 changegroup,
32 changegroup,
33 cmdutil,
33 cmdutil,
34 color,
34 color,
35 commands,
36 context,
35 context,
37 dagparser,
36 dagparser,
38 dagutil,
37 dagutil,
39 encoding,
38 encoding,
40 error,
39 error,
41 exchange,
40 exchange,
42 extensions,
41 extensions,
43 filemerge,
42 filemerge,
44 fileset,
43 fileset,
45 formatter,
44 formatter,
46 hg,
45 hg,
47 localrepo,
46 localrepo,
48 lock as lockmod,
47 lock as lockmod,
49 merge as mergemod,
48 merge as mergemod,
50 obsolete,
49 obsolete,
51 policy,
50 policy,
52 pvec,
51 pvec,
53 pycompat,
52 pycompat,
54 registrar,
53 registrar,
55 repair,
54 repair,
56 revlog,
55 revlog,
57 revset,
56 revset,
58 revsetlang,
57 revsetlang,
59 scmutil,
58 scmutil,
60 setdiscovery,
59 setdiscovery,
61 simplemerge,
60 simplemerge,
62 smartset,
61 smartset,
63 sslutil,
62 sslutil,
64 streamclone,
63 streamclone,
65 templater,
64 templater,
66 treediscovery,
65 treediscovery,
67 upgrade,
66 upgrade,
68 util,
67 util,
69 vfs as vfsmod,
68 vfs as vfsmod,
70 )
69 )
71
70
72 release = lockmod.release
71 release = lockmod.release
73
72
74 # We reuse the command table from commands because it is easier than
73 command = registrar.command()
75 # teaching dispatch about multiple tables.
76 command = registrar.command(commands.table)
77
74
78 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
75 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
79 def debugancestor(ui, repo, *args):
76 def debugancestor(ui, repo, *args):
80 """find the ancestor revision of two revisions in a given index"""
77 """find the ancestor revision of two revisions in a given index"""
81 if len(args) == 3:
78 if len(args) == 3:
82 index, rev1, rev2 = args
79 index, rev1, rev2 = args
83 r = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False), index)
80 r = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False), index)
84 lookup = r.lookup
81 lookup = r.lookup
85 elif len(args) == 2:
82 elif len(args) == 2:
86 if not repo:
83 if not repo:
87 raise error.Abort(_('there is no Mercurial repository here '
84 raise error.Abort(_('there is no Mercurial repository here '
88 '(.hg not found)'))
85 '(.hg not found)'))
89 rev1, rev2 = args
86 rev1, rev2 = args
90 r = repo.changelog
87 r = repo.changelog
91 lookup = repo.lookup
88 lookup = repo.lookup
92 else:
89 else:
93 raise error.Abort(_('either two or three arguments required'))
90 raise error.Abort(_('either two or three arguments required'))
94 a = r.ancestor(lookup(rev1), lookup(rev2))
91 a = r.ancestor(lookup(rev1), lookup(rev2))
95 ui.write('%d:%s\n' % (r.rev(a), hex(a)))
92 ui.write('%d:%s\n' % (r.rev(a), hex(a)))
96
93
97 @command('debugapplystreamclonebundle', [], 'FILE')
94 @command('debugapplystreamclonebundle', [], 'FILE')
98 def debugapplystreamclonebundle(ui, repo, fname):
95 def debugapplystreamclonebundle(ui, repo, fname):
99 """apply a stream clone bundle file"""
96 """apply a stream clone bundle file"""
100 f = hg.openpath(ui, fname)
97 f = hg.openpath(ui, fname)
101 gen = exchange.readbundle(ui, f, fname)
98 gen = exchange.readbundle(ui, f, fname)
102 gen.apply(repo)
99 gen.apply(repo)
103
100
104 @command('debugbuilddag',
101 @command('debugbuilddag',
105 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
102 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
106 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
103 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
107 ('n', 'new-file', None, _('add new file at each rev'))],
104 ('n', 'new-file', None, _('add new file at each rev'))],
108 _('[OPTION]... [TEXT]'))
105 _('[OPTION]... [TEXT]'))
109 def debugbuilddag(ui, repo, text=None,
106 def debugbuilddag(ui, repo, text=None,
110 mergeable_file=False,
107 mergeable_file=False,
111 overwritten_file=False,
108 overwritten_file=False,
112 new_file=False):
109 new_file=False):
113 """builds a repo with a given DAG from scratch in the current empty repo
110 """builds a repo with a given DAG from scratch in the current empty repo
114
111
115 The description of the DAG is read from stdin if not given on the
112 The description of the DAG is read from stdin if not given on the
116 command line.
113 command line.
117
114
118 Elements:
115 Elements:
119
116
120 - "+n" is a linear run of n nodes based on the current default parent
117 - "+n" is a linear run of n nodes based on the current default parent
121 - "." is a single node based on the current default parent
118 - "." is a single node based on the current default parent
122 - "$" resets the default parent to null (implied at the start);
119 - "$" resets the default parent to null (implied at the start);
123 otherwise the default parent is always the last node created
120 otherwise the default parent is always the last node created
124 - "<p" sets the default parent to the backref p
121 - "<p" sets the default parent to the backref p
125 - "*p" is a fork at parent p, which is a backref
122 - "*p" is a fork at parent p, which is a backref
126 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
123 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
127 - "/p2" is a merge of the preceding node and p2
124 - "/p2" is a merge of the preceding node and p2
128 - ":tag" defines a local tag for the preceding node
125 - ":tag" defines a local tag for the preceding node
129 - "@branch" sets the named branch for subsequent nodes
126 - "@branch" sets the named branch for subsequent nodes
130 - "#...\\n" is a comment up to the end of the line
127 - "#...\\n" is a comment up to the end of the line
131
128
132 Whitespace between the above elements is ignored.
129 Whitespace between the above elements is ignored.
133
130
134 A backref is either
131 A backref is either
135
132
136 - a number n, which references the node curr-n, where curr is the current
133 - a number n, which references the node curr-n, where curr is the current
137 node, or
134 node, or
138 - the name of a local tag you placed earlier using ":tag", or
135 - the name of a local tag you placed earlier using ":tag", or
139 - empty to denote the default parent.
136 - empty to denote the default parent.
140
137
141 All string valued-elements are either strictly alphanumeric, or must
138 All string valued-elements are either strictly alphanumeric, or must
142 be enclosed in double quotes ("..."), with "\\" as escape character.
139 be enclosed in double quotes ("..."), with "\\" as escape character.
143 """
140 """
144
141
145 if text is None:
142 if text is None:
146 ui.status(_("reading DAG from stdin\n"))
143 ui.status(_("reading DAG from stdin\n"))
147 text = ui.fin.read()
144 text = ui.fin.read()
148
145
149 cl = repo.changelog
146 cl = repo.changelog
150 if len(cl) > 0:
147 if len(cl) > 0:
151 raise error.Abort(_('repository is not empty'))
148 raise error.Abort(_('repository is not empty'))
152
149
153 # determine number of revs in DAG
150 # determine number of revs in DAG
154 total = 0
151 total = 0
155 for type, data in dagparser.parsedag(text):
152 for type, data in dagparser.parsedag(text):
156 if type == 'n':
153 if type == 'n':
157 total += 1
154 total += 1
158
155
159 if mergeable_file:
156 if mergeable_file:
160 linesperrev = 2
157 linesperrev = 2
161 # make a file with k lines per rev
158 # make a file with k lines per rev
162 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
159 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
163 initialmergedlines.append("")
160 initialmergedlines.append("")
164
161
165 tags = []
162 tags = []
166
163
167 wlock = lock = tr = None
164 wlock = lock = tr = None
168 try:
165 try:
169 wlock = repo.wlock()
166 wlock = repo.wlock()
170 lock = repo.lock()
167 lock = repo.lock()
171 tr = repo.transaction("builddag")
168 tr = repo.transaction("builddag")
172
169
173 at = -1
170 at = -1
174 atbranch = 'default'
171 atbranch = 'default'
175 nodeids = []
172 nodeids = []
176 id = 0
173 id = 0
177 ui.progress(_('building'), id, unit=_('revisions'), total=total)
174 ui.progress(_('building'), id, unit=_('revisions'), total=total)
178 for type, data in dagparser.parsedag(text):
175 for type, data in dagparser.parsedag(text):
179 if type == 'n':
176 if type == 'n':
180 ui.note(('node %s\n' % str(data)))
177 ui.note(('node %s\n' % str(data)))
181 id, ps = data
178 id, ps = data
182
179
183 files = []
180 files = []
184 fctxs = {}
181 fctxs = {}
185
182
186 p2 = None
183 p2 = None
187 if mergeable_file:
184 if mergeable_file:
188 fn = "mf"
185 fn = "mf"
189 p1 = repo[ps[0]]
186 p1 = repo[ps[0]]
190 if len(ps) > 1:
187 if len(ps) > 1:
191 p2 = repo[ps[1]]
188 p2 = repo[ps[1]]
192 pa = p1.ancestor(p2)
189 pa = p1.ancestor(p2)
193 base, local, other = [x[fn].data() for x in (pa, p1,
190 base, local, other = [x[fn].data() for x in (pa, p1,
194 p2)]
191 p2)]
195 m3 = simplemerge.Merge3Text(base, local, other)
192 m3 = simplemerge.Merge3Text(base, local, other)
196 ml = [l.strip() for l in m3.merge_lines()]
193 ml = [l.strip() for l in m3.merge_lines()]
197 ml.append("")
194 ml.append("")
198 elif at > 0:
195 elif at > 0:
199 ml = p1[fn].data().split("\n")
196 ml = p1[fn].data().split("\n")
200 else:
197 else:
201 ml = initialmergedlines
198 ml = initialmergedlines
202 ml[id * linesperrev] += " r%i" % id
199 ml[id * linesperrev] += " r%i" % id
203 mergedtext = "\n".join(ml)
200 mergedtext = "\n".join(ml)
204 files.append(fn)
201 files.append(fn)
205 fctxs[fn] = context.memfilectx(repo, fn, mergedtext)
202 fctxs[fn] = context.memfilectx(repo, fn, mergedtext)
206
203
207 if overwritten_file:
204 if overwritten_file:
208 fn = "of"
205 fn = "of"
209 files.append(fn)
206 files.append(fn)
210 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
207 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
211
208
212 if new_file:
209 if new_file:
213 fn = "nf%i" % id
210 fn = "nf%i" % id
214 files.append(fn)
211 files.append(fn)
215 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
212 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
216 if len(ps) > 1:
213 if len(ps) > 1:
217 if not p2:
214 if not p2:
218 p2 = repo[ps[1]]
215 p2 = repo[ps[1]]
219 for fn in p2:
216 for fn in p2:
220 if fn.startswith("nf"):
217 if fn.startswith("nf"):
221 files.append(fn)
218 files.append(fn)
222 fctxs[fn] = p2[fn]
219 fctxs[fn] = p2[fn]
223
220
224 def fctxfn(repo, cx, path):
221 def fctxfn(repo, cx, path):
225 return fctxs.get(path)
222 return fctxs.get(path)
226
223
227 if len(ps) == 0 or ps[0] < 0:
224 if len(ps) == 0 or ps[0] < 0:
228 pars = [None, None]
225 pars = [None, None]
229 elif len(ps) == 1:
226 elif len(ps) == 1:
230 pars = [nodeids[ps[0]], None]
227 pars = [nodeids[ps[0]], None]
231 else:
228 else:
232 pars = [nodeids[p] for p in ps]
229 pars = [nodeids[p] for p in ps]
233 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
230 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
234 date=(id, 0),
231 date=(id, 0),
235 user="debugbuilddag",
232 user="debugbuilddag",
236 extra={'branch': atbranch})
233 extra={'branch': atbranch})
237 nodeid = repo.commitctx(cx)
234 nodeid = repo.commitctx(cx)
238 nodeids.append(nodeid)
235 nodeids.append(nodeid)
239 at = id
236 at = id
240 elif type == 'l':
237 elif type == 'l':
241 id, name = data
238 id, name = data
242 ui.note(('tag %s\n' % name))
239 ui.note(('tag %s\n' % name))
243 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
240 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
244 elif type == 'a':
241 elif type == 'a':
245 ui.note(('branch %s\n' % data))
242 ui.note(('branch %s\n' % data))
246 atbranch = data
243 atbranch = data
247 ui.progress(_('building'), id, unit=_('revisions'), total=total)
244 ui.progress(_('building'), id, unit=_('revisions'), total=total)
248 tr.close()
245 tr.close()
249
246
250 if tags:
247 if tags:
251 repo.vfs.write("localtags", "".join(tags))
248 repo.vfs.write("localtags", "".join(tags))
252 finally:
249 finally:
253 ui.progress(_('building'), None)
250 ui.progress(_('building'), None)
254 release(tr, lock, wlock)
251 release(tr, lock, wlock)
255
252
256 def _debugchangegroup(ui, gen, all=None, indent=0, **opts):
253 def _debugchangegroup(ui, gen, all=None, indent=0, **opts):
257 indent_string = ' ' * indent
254 indent_string = ' ' * indent
258 if all:
255 if all:
259 ui.write(("%sformat: id, p1, p2, cset, delta base, len(delta)\n")
256 ui.write(("%sformat: id, p1, p2, cset, delta base, len(delta)\n")
260 % indent_string)
257 % indent_string)
261
258
262 def showchunks(named):
259 def showchunks(named):
263 ui.write("\n%s%s\n" % (indent_string, named))
260 ui.write("\n%s%s\n" % (indent_string, named))
264 chain = None
261 chain = None
265 for chunkdata in iter(lambda: gen.deltachunk(chain), {}):
262 for chunkdata in iter(lambda: gen.deltachunk(chain), {}):
266 node = chunkdata['node']
263 node = chunkdata['node']
267 p1 = chunkdata['p1']
264 p1 = chunkdata['p1']
268 p2 = chunkdata['p2']
265 p2 = chunkdata['p2']
269 cs = chunkdata['cs']
266 cs = chunkdata['cs']
270 deltabase = chunkdata['deltabase']
267 deltabase = chunkdata['deltabase']
271 delta = chunkdata['delta']
268 delta = chunkdata['delta']
272 ui.write("%s%s %s %s %s %s %s\n" %
269 ui.write("%s%s %s %s %s %s %s\n" %
273 (indent_string, hex(node), hex(p1), hex(p2),
270 (indent_string, hex(node), hex(p1), hex(p2),
274 hex(cs), hex(deltabase), len(delta)))
271 hex(cs), hex(deltabase), len(delta)))
275 chain = node
272 chain = node
276
273
277 chunkdata = gen.changelogheader()
274 chunkdata = gen.changelogheader()
278 showchunks("changelog")
275 showchunks("changelog")
279 chunkdata = gen.manifestheader()
276 chunkdata = gen.manifestheader()
280 showchunks("manifest")
277 showchunks("manifest")
281 for chunkdata in iter(gen.filelogheader, {}):
278 for chunkdata in iter(gen.filelogheader, {}):
282 fname = chunkdata['filename']
279 fname = chunkdata['filename']
283 showchunks(fname)
280 showchunks(fname)
284 else:
281 else:
285 if isinstance(gen, bundle2.unbundle20):
282 if isinstance(gen, bundle2.unbundle20):
286 raise error.Abort(_('use debugbundle2 for this file'))
283 raise error.Abort(_('use debugbundle2 for this file'))
287 chunkdata = gen.changelogheader()
284 chunkdata = gen.changelogheader()
288 chain = None
285 chain = None
289 for chunkdata in iter(lambda: gen.deltachunk(chain), {}):
286 for chunkdata in iter(lambda: gen.deltachunk(chain), {}):
290 node = chunkdata['node']
287 node = chunkdata['node']
291 ui.write("%s%s\n" % (indent_string, hex(node)))
288 ui.write("%s%s\n" % (indent_string, hex(node)))
292 chain = node
289 chain = node
293
290
294 def _debugbundle2(ui, gen, all=None, **opts):
291 def _debugbundle2(ui, gen, all=None, **opts):
295 """lists the contents of a bundle2"""
292 """lists the contents of a bundle2"""
296 if not isinstance(gen, bundle2.unbundle20):
293 if not isinstance(gen, bundle2.unbundle20):
297 raise error.Abort(_('not a bundle2 file'))
294 raise error.Abort(_('not a bundle2 file'))
298 ui.write(('Stream params: %s\n' % repr(gen.params)))
295 ui.write(('Stream params: %s\n' % repr(gen.params)))
299 for part in gen.iterparts():
296 for part in gen.iterparts():
300 ui.write('%s -- %r\n' % (part.type, repr(part.params)))
297 ui.write('%s -- %r\n' % (part.type, repr(part.params)))
301 if part.type == 'changegroup':
298 if part.type == 'changegroup':
302 version = part.params.get('version', '01')
299 version = part.params.get('version', '01')
303 cg = changegroup.getunbundler(version, part, 'UN')
300 cg = changegroup.getunbundler(version, part, 'UN')
304 _debugchangegroup(ui, cg, all=all, indent=4, **opts)
301 _debugchangegroup(ui, cg, all=all, indent=4, **opts)
305
302
306 @command('debugbundle',
303 @command('debugbundle',
307 [('a', 'all', None, _('show all details')),
304 [('a', 'all', None, _('show all details')),
308 ('', 'spec', None, _('print the bundlespec of the bundle'))],
305 ('', 'spec', None, _('print the bundlespec of the bundle'))],
309 _('FILE'),
306 _('FILE'),
310 norepo=True)
307 norepo=True)
311 def debugbundle(ui, bundlepath, all=None, spec=None, **opts):
308 def debugbundle(ui, bundlepath, all=None, spec=None, **opts):
312 """lists the contents of a bundle"""
309 """lists the contents of a bundle"""
313 with hg.openpath(ui, bundlepath) as f:
310 with hg.openpath(ui, bundlepath) as f:
314 if spec:
311 if spec:
315 spec = exchange.getbundlespec(ui, f)
312 spec = exchange.getbundlespec(ui, f)
316 ui.write('%s\n' % spec)
313 ui.write('%s\n' % spec)
317 return
314 return
318
315
319 gen = exchange.readbundle(ui, f, bundlepath)
316 gen = exchange.readbundle(ui, f, bundlepath)
320 if isinstance(gen, bundle2.unbundle20):
317 if isinstance(gen, bundle2.unbundle20):
321 return _debugbundle2(ui, gen, all=all, **opts)
318 return _debugbundle2(ui, gen, all=all, **opts)
322 _debugchangegroup(ui, gen, all=all, **opts)
319 _debugchangegroup(ui, gen, all=all, **opts)
323
320
324 @command('debugcheckstate', [], '')
321 @command('debugcheckstate', [], '')
325 def debugcheckstate(ui, repo):
322 def debugcheckstate(ui, repo):
326 """validate the correctness of the current dirstate"""
323 """validate the correctness of the current dirstate"""
327 parent1, parent2 = repo.dirstate.parents()
324 parent1, parent2 = repo.dirstate.parents()
328 m1 = repo[parent1].manifest()
325 m1 = repo[parent1].manifest()
329 m2 = repo[parent2].manifest()
326 m2 = repo[parent2].manifest()
330 errors = 0
327 errors = 0
331 for f in repo.dirstate:
328 for f in repo.dirstate:
332 state = repo.dirstate[f]
329 state = repo.dirstate[f]
333 if state in "nr" and f not in m1:
330 if state in "nr" and f not in m1:
334 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
331 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
335 errors += 1
332 errors += 1
336 if state in "a" and f in m1:
333 if state in "a" and f in m1:
337 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
334 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
338 errors += 1
335 errors += 1
339 if state in "m" and f not in m1 and f not in m2:
336 if state in "m" and f not in m1 and f not in m2:
340 ui.warn(_("%s in state %s, but not in either manifest\n") %
337 ui.warn(_("%s in state %s, but not in either manifest\n") %
341 (f, state))
338 (f, state))
342 errors += 1
339 errors += 1
343 for f in m1:
340 for f in m1:
344 state = repo.dirstate[f]
341 state = repo.dirstate[f]
345 if state not in "nrm":
342 if state not in "nrm":
346 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
343 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
347 errors += 1
344 errors += 1
348 if errors:
345 if errors:
349 error = _(".hg/dirstate inconsistent with current parent's manifest")
346 error = _(".hg/dirstate inconsistent with current parent's manifest")
350 raise error.Abort(error)
347 raise error.Abort(error)
351
348
352 @command('debugcolor',
349 @command('debugcolor',
353 [('', 'style', None, _('show all configured styles'))],
350 [('', 'style', None, _('show all configured styles'))],
354 'hg debugcolor')
351 'hg debugcolor')
355 def debugcolor(ui, repo, **opts):
352 def debugcolor(ui, repo, **opts):
356 """show available color, effects or style"""
353 """show available color, effects or style"""
357 ui.write(('color mode: %s\n') % ui._colormode)
354 ui.write(('color mode: %s\n') % ui._colormode)
358 if opts.get('style'):
355 if opts.get('style'):
359 return _debugdisplaystyle(ui)
356 return _debugdisplaystyle(ui)
360 else:
357 else:
361 return _debugdisplaycolor(ui)
358 return _debugdisplaycolor(ui)
362
359
363 def _debugdisplaycolor(ui):
360 def _debugdisplaycolor(ui):
364 ui = ui.copy()
361 ui = ui.copy()
365 ui._styles.clear()
362 ui._styles.clear()
366 for effect in color._activeeffects(ui).keys():
363 for effect in color._activeeffects(ui).keys():
367 ui._styles[effect] = effect
364 ui._styles[effect] = effect
368 if ui._terminfoparams:
365 if ui._terminfoparams:
369 for k, v in ui.configitems('color'):
366 for k, v in ui.configitems('color'):
370 if k.startswith('color.'):
367 if k.startswith('color.'):
371 ui._styles[k] = k[6:]
368 ui._styles[k] = k[6:]
372 elif k.startswith('terminfo.'):
369 elif k.startswith('terminfo.'):
373 ui._styles[k] = k[9:]
370 ui._styles[k] = k[9:]
374 ui.write(_('available colors:\n'))
371 ui.write(_('available colors:\n'))
375 # sort label with a '_' after the other to group '_background' entry.
372 # sort label with a '_' after the other to group '_background' entry.
376 items = sorted(ui._styles.items(),
373 items = sorted(ui._styles.items(),
377 key=lambda i: ('_' in i[0], i[0], i[1]))
374 key=lambda i: ('_' in i[0], i[0], i[1]))
378 for colorname, label in items:
375 for colorname, label in items:
379 ui.write(('%s\n') % colorname, label=label)
376 ui.write(('%s\n') % colorname, label=label)
380
377
381 def _debugdisplaystyle(ui):
378 def _debugdisplaystyle(ui):
382 ui.write(_('available style:\n'))
379 ui.write(_('available style:\n'))
383 width = max(len(s) for s in ui._styles)
380 width = max(len(s) for s in ui._styles)
384 for label, effects in sorted(ui._styles.items()):
381 for label, effects in sorted(ui._styles.items()):
385 ui.write('%s' % label, label=label)
382 ui.write('%s' % label, label=label)
386 if effects:
383 if effects:
387 # 50
384 # 50
388 ui.write(': ')
385 ui.write(': ')
389 ui.write(' ' * (max(0, width - len(label))))
386 ui.write(' ' * (max(0, width - len(label))))
390 ui.write(', '.join(ui.label(e, e) for e in effects.split()))
387 ui.write(', '.join(ui.label(e, e) for e in effects.split()))
391 ui.write('\n')
388 ui.write('\n')
392
389
393 @command('debugcreatestreamclonebundle', [], 'FILE')
390 @command('debugcreatestreamclonebundle', [], 'FILE')
394 def debugcreatestreamclonebundle(ui, repo, fname):
391 def debugcreatestreamclonebundle(ui, repo, fname):
395 """create a stream clone bundle file
392 """create a stream clone bundle file
396
393
397 Stream bundles are special bundles that are essentially archives of
394 Stream bundles are special bundles that are essentially archives of
398 revlog files. They are commonly used for cloning very quickly.
395 revlog files. They are commonly used for cloning very quickly.
399 """
396 """
400 requirements, gen = streamclone.generatebundlev1(repo)
397 requirements, gen = streamclone.generatebundlev1(repo)
401 changegroup.writechunks(ui, gen, fname)
398 changegroup.writechunks(ui, gen, fname)
402
399
403 ui.write(_('bundle requirements: %s\n') % ', '.join(sorted(requirements)))
400 ui.write(_('bundle requirements: %s\n') % ', '.join(sorted(requirements)))
404
401
405 @command('debugdag',
402 @command('debugdag',
406 [('t', 'tags', None, _('use tags as labels')),
403 [('t', 'tags', None, _('use tags as labels')),
407 ('b', 'branches', None, _('annotate with branch names')),
404 ('b', 'branches', None, _('annotate with branch names')),
408 ('', 'dots', None, _('use dots for runs')),
405 ('', 'dots', None, _('use dots for runs')),
409 ('s', 'spaces', None, _('separate elements by spaces'))],
406 ('s', 'spaces', None, _('separate elements by spaces'))],
410 _('[OPTION]... [FILE [REV]...]'),
407 _('[OPTION]... [FILE [REV]...]'),
411 optionalrepo=True)
408 optionalrepo=True)
412 def debugdag(ui, repo, file_=None, *revs, **opts):
409 def debugdag(ui, repo, file_=None, *revs, **opts):
413 """format the changelog or an index DAG as a concise textual description
410 """format the changelog or an index DAG as a concise textual description
414
411
415 If you pass a revlog index, the revlog's DAG is emitted. If you list
412 If you pass a revlog index, the revlog's DAG is emitted. If you list
416 revision numbers, they get labeled in the output as rN.
413 revision numbers, they get labeled in the output as rN.
417
414
418 Otherwise, the changelog DAG of the current repo is emitted.
415 Otherwise, the changelog DAG of the current repo is emitted.
419 """
416 """
420 spaces = opts.get('spaces')
417 spaces = opts.get('spaces')
421 dots = opts.get('dots')
418 dots = opts.get('dots')
422 if file_:
419 if file_:
423 rlog = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False),
420 rlog = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False),
424 file_)
421 file_)
425 revs = set((int(r) for r in revs))
422 revs = set((int(r) for r in revs))
426 def events():
423 def events():
427 for r in rlog:
424 for r in rlog:
428 yield 'n', (r, list(p for p in rlog.parentrevs(r)
425 yield 'n', (r, list(p for p in rlog.parentrevs(r)
429 if p != -1))
426 if p != -1))
430 if r in revs:
427 if r in revs:
431 yield 'l', (r, "r%i" % r)
428 yield 'l', (r, "r%i" % r)
432 elif repo:
429 elif repo:
433 cl = repo.changelog
430 cl = repo.changelog
434 tags = opts.get('tags')
431 tags = opts.get('tags')
435 branches = opts.get('branches')
432 branches = opts.get('branches')
436 if tags:
433 if tags:
437 labels = {}
434 labels = {}
438 for l, n in repo.tags().items():
435 for l, n in repo.tags().items():
439 labels.setdefault(cl.rev(n), []).append(l)
436 labels.setdefault(cl.rev(n), []).append(l)
440 def events():
437 def events():
441 b = "default"
438 b = "default"
442 for r in cl:
439 for r in cl:
443 if branches:
440 if branches:
444 newb = cl.read(cl.node(r))[5]['branch']
441 newb = cl.read(cl.node(r))[5]['branch']
445 if newb != b:
442 if newb != b:
446 yield 'a', newb
443 yield 'a', newb
447 b = newb
444 b = newb
448 yield 'n', (r, list(p for p in cl.parentrevs(r)
445 yield 'n', (r, list(p for p in cl.parentrevs(r)
449 if p != -1))
446 if p != -1))
450 if tags:
447 if tags:
451 ls = labels.get(r)
448 ls = labels.get(r)
452 if ls:
449 if ls:
453 for l in ls:
450 for l in ls:
454 yield 'l', (r, l)
451 yield 'l', (r, l)
455 else:
452 else:
456 raise error.Abort(_('need repo for changelog dag'))
453 raise error.Abort(_('need repo for changelog dag'))
457
454
458 for line in dagparser.dagtextlines(events(),
455 for line in dagparser.dagtextlines(events(),
459 addspaces=spaces,
456 addspaces=spaces,
460 wraplabels=True,
457 wraplabels=True,
461 wrapannotations=True,
458 wrapannotations=True,
462 wrapnonlinear=dots,
459 wrapnonlinear=dots,
463 usedots=dots,
460 usedots=dots,
464 maxlinewidth=70):
461 maxlinewidth=70):
465 ui.write(line)
462 ui.write(line)
466 ui.write("\n")
463 ui.write("\n")
467
464
468 @command('debugdata', cmdutil.debugrevlogopts, _('-c|-m|FILE REV'))
465 @command('debugdata', cmdutil.debugrevlogopts, _('-c|-m|FILE REV'))
469 def debugdata(ui, repo, file_, rev=None, **opts):
466 def debugdata(ui, repo, file_, rev=None, **opts):
470 """dump the contents of a data file revision"""
467 """dump the contents of a data file revision"""
471 if opts.get('changelog') or opts.get('manifest') or opts.get('dir'):
468 if opts.get('changelog') or opts.get('manifest') or opts.get('dir'):
472 if rev is not None:
469 if rev is not None:
473 raise error.CommandError('debugdata', _('invalid arguments'))
470 raise error.CommandError('debugdata', _('invalid arguments'))
474 file_, rev = None, file_
471 file_, rev = None, file_
475 elif rev is None:
472 elif rev is None:
476 raise error.CommandError('debugdata', _('invalid arguments'))
473 raise error.CommandError('debugdata', _('invalid arguments'))
477 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
474 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
478 try:
475 try:
479 ui.write(r.revision(r.lookup(rev), raw=True))
476 ui.write(r.revision(r.lookup(rev), raw=True))
480 except KeyError:
477 except KeyError:
481 raise error.Abort(_('invalid revision identifier %s') % rev)
478 raise error.Abort(_('invalid revision identifier %s') % rev)
482
479
483 @command('debugdate',
480 @command('debugdate',
484 [('e', 'extended', None, _('try extended date formats'))],
481 [('e', 'extended', None, _('try extended date formats'))],
485 _('[-e] DATE [RANGE]'),
482 _('[-e] DATE [RANGE]'),
486 norepo=True, optionalrepo=True)
483 norepo=True, optionalrepo=True)
487 def debugdate(ui, date, range=None, **opts):
484 def debugdate(ui, date, range=None, **opts):
488 """parse and display a date"""
485 """parse and display a date"""
489 if opts["extended"]:
486 if opts["extended"]:
490 d = util.parsedate(date, util.extendeddateformats)
487 d = util.parsedate(date, util.extendeddateformats)
491 else:
488 else:
492 d = util.parsedate(date)
489 d = util.parsedate(date)
493 ui.write(("internal: %s %s\n") % d)
490 ui.write(("internal: %s %s\n") % d)
494 ui.write(("standard: %s\n") % util.datestr(d))
491 ui.write(("standard: %s\n") % util.datestr(d))
495 if range:
492 if range:
496 m = util.matchdate(range)
493 m = util.matchdate(range)
497 ui.write(("match: %s\n") % m(d[0]))
494 ui.write(("match: %s\n") % m(d[0]))
498
495
499 @command('debugdeltachain',
496 @command('debugdeltachain',
500 cmdutil.debugrevlogopts + cmdutil.formatteropts,
497 cmdutil.debugrevlogopts + cmdutil.formatteropts,
501 _('-c|-m|FILE'),
498 _('-c|-m|FILE'),
502 optionalrepo=True)
499 optionalrepo=True)
503 def debugdeltachain(ui, repo, file_=None, **opts):
500 def debugdeltachain(ui, repo, file_=None, **opts):
504 """dump information about delta chains in a revlog
501 """dump information about delta chains in a revlog
505
502
506 Output can be templatized. Available template keywords are:
503 Output can be templatized. Available template keywords are:
507
504
508 :``rev``: revision number
505 :``rev``: revision number
509 :``chainid``: delta chain identifier (numbered by unique base)
506 :``chainid``: delta chain identifier (numbered by unique base)
510 :``chainlen``: delta chain length to this revision
507 :``chainlen``: delta chain length to this revision
511 :``prevrev``: previous revision in delta chain
508 :``prevrev``: previous revision in delta chain
512 :``deltatype``: role of delta / how it was computed
509 :``deltatype``: role of delta / how it was computed
513 :``compsize``: compressed size of revision
510 :``compsize``: compressed size of revision
514 :``uncompsize``: uncompressed size of revision
511 :``uncompsize``: uncompressed size of revision
515 :``chainsize``: total size of compressed revisions in chain
512 :``chainsize``: total size of compressed revisions in chain
516 :``chainratio``: total chain size divided by uncompressed revision size
513 :``chainratio``: total chain size divided by uncompressed revision size
517 (new delta chains typically start at ratio 2.00)
514 (new delta chains typically start at ratio 2.00)
518 :``lindist``: linear distance from base revision in delta chain to end
515 :``lindist``: linear distance from base revision in delta chain to end
519 of this revision
516 of this revision
520 :``extradist``: total size of revisions not part of this delta chain from
517 :``extradist``: total size of revisions not part of this delta chain from
521 base of delta chain to end of this revision; a measurement
518 base of delta chain to end of this revision; a measurement
522 of how much extra data we need to read/seek across to read
519 of how much extra data we need to read/seek across to read
523 the delta chain for this revision
520 the delta chain for this revision
524 :``extraratio``: extradist divided by chainsize; another representation of
521 :``extraratio``: extradist divided by chainsize; another representation of
525 how much unrelated data is needed to load this delta chain
522 how much unrelated data is needed to load this delta chain
526 """
523 """
527 r = cmdutil.openrevlog(repo, 'debugdeltachain', file_, opts)
524 r = cmdutil.openrevlog(repo, 'debugdeltachain', file_, opts)
528 index = r.index
525 index = r.index
529 generaldelta = r.version & revlog.FLAG_GENERALDELTA
526 generaldelta = r.version & revlog.FLAG_GENERALDELTA
530
527
531 def revinfo(rev):
528 def revinfo(rev):
532 e = index[rev]
529 e = index[rev]
533 compsize = e[1]
530 compsize = e[1]
534 uncompsize = e[2]
531 uncompsize = e[2]
535 chainsize = 0
532 chainsize = 0
536
533
537 if generaldelta:
534 if generaldelta:
538 if e[3] == e[5]:
535 if e[3] == e[5]:
539 deltatype = 'p1'
536 deltatype = 'p1'
540 elif e[3] == e[6]:
537 elif e[3] == e[6]:
541 deltatype = 'p2'
538 deltatype = 'p2'
542 elif e[3] == rev - 1:
539 elif e[3] == rev - 1:
543 deltatype = 'prev'
540 deltatype = 'prev'
544 elif e[3] == rev:
541 elif e[3] == rev:
545 deltatype = 'base'
542 deltatype = 'base'
546 else:
543 else:
547 deltatype = 'other'
544 deltatype = 'other'
548 else:
545 else:
549 if e[3] == rev:
546 if e[3] == rev:
550 deltatype = 'base'
547 deltatype = 'base'
551 else:
548 else:
552 deltatype = 'prev'
549 deltatype = 'prev'
553
550
554 chain = r._deltachain(rev)[0]
551 chain = r._deltachain(rev)[0]
555 for iterrev in chain:
552 for iterrev in chain:
556 e = index[iterrev]
553 e = index[iterrev]
557 chainsize += e[1]
554 chainsize += e[1]
558
555
559 return compsize, uncompsize, deltatype, chain, chainsize
556 return compsize, uncompsize, deltatype, chain, chainsize
560
557
561 fm = ui.formatter('debugdeltachain', opts)
558 fm = ui.formatter('debugdeltachain', opts)
562
559
563 fm.plain(' rev chain# chainlen prev delta '
560 fm.plain(' rev chain# chainlen prev delta '
564 'size rawsize chainsize ratio lindist extradist '
561 'size rawsize chainsize ratio lindist extradist '
565 'extraratio\n')
562 'extraratio\n')
566
563
567 chainbases = {}
564 chainbases = {}
568 for rev in r:
565 for rev in r:
569 comp, uncomp, deltatype, chain, chainsize = revinfo(rev)
566 comp, uncomp, deltatype, chain, chainsize = revinfo(rev)
570 chainbase = chain[0]
567 chainbase = chain[0]
571 chainid = chainbases.setdefault(chainbase, len(chainbases) + 1)
568 chainid = chainbases.setdefault(chainbase, len(chainbases) + 1)
572 basestart = r.start(chainbase)
569 basestart = r.start(chainbase)
573 revstart = r.start(rev)
570 revstart = r.start(rev)
574 lineardist = revstart + comp - basestart
571 lineardist = revstart + comp - basestart
575 extradist = lineardist - chainsize
572 extradist = lineardist - chainsize
576 try:
573 try:
577 prevrev = chain[-2]
574 prevrev = chain[-2]
578 except IndexError:
575 except IndexError:
579 prevrev = -1
576 prevrev = -1
580
577
581 chainratio = float(chainsize) / float(uncomp)
578 chainratio = float(chainsize) / float(uncomp)
582 extraratio = float(extradist) / float(chainsize)
579 extraratio = float(extradist) / float(chainsize)
583
580
584 fm.startitem()
581 fm.startitem()
585 fm.write('rev chainid chainlen prevrev deltatype compsize '
582 fm.write('rev chainid chainlen prevrev deltatype compsize '
586 'uncompsize chainsize chainratio lindist extradist '
583 'uncompsize chainsize chainratio lindist extradist '
587 'extraratio',
584 'extraratio',
588 '%7d %7d %8d %8d %7s %10d %10d %10d %9.5f %9d %9d %10.5f\n',
585 '%7d %7d %8d %8d %7s %10d %10d %10d %9.5f %9d %9d %10.5f\n',
589 rev, chainid, len(chain), prevrev, deltatype, comp,
586 rev, chainid, len(chain), prevrev, deltatype, comp,
590 uncomp, chainsize, chainratio, lineardist, extradist,
587 uncomp, chainsize, chainratio, lineardist, extradist,
591 extraratio,
588 extraratio,
592 rev=rev, chainid=chainid, chainlen=len(chain),
589 rev=rev, chainid=chainid, chainlen=len(chain),
593 prevrev=prevrev, deltatype=deltatype, compsize=comp,
590 prevrev=prevrev, deltatype=deltatype, compsize=comp,
594 uncompsize=uncomp, chainsize=chainsize,
591 uncompsize=uncomp, chainsize=chainsize,
595 chainratio=chainratio, lindist=lineardist,
592 chainratio=chainratio, lindist=lineardist,
596 extradist=extradist, extraratio=extraratio)
593 extradist=extradist, extraratio=extraratio)
597
594
598 fm.end()
595 fm.end()
599
596
600 @command('debugdirstate|debugstate',
597 @command('debugdirstate|debugstate',
601 [('', 'nodates', None, _('do not display the saved mtime')),
598 [('', 'nodates', None, _('do not display the saved mtime')),
602 ('', 'datesort', None, _('sort by saved mtime'))],
599 ('', 'datesort', None, _('sort by saved mtime'))],
603 _('[OPTION]...'))
600 _('[OPTION]...'))
604 def debugstate(ui, repo, **opts):
601 def debugstate(ui, repo, **opts):
605 """show the contents of the current dirstate"""
602 """show the contents of the current dirstate"""
606
603
607 nodates = opts.get('nodates')
604 nodates = opts.get('nodates')
608 datesort = opts.get('datesort')
605 datesort = opts.get('datesort')
609
606
610 timestr = ""
607 timestr = ""
611 if datesort:
608 if datesort:
612 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
609 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
613 else:
610 else:
614 keyfunc = None # sort by filename
611 keyfunc = None # sort by filename
615 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
612 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
616 if ent[3] == -1:
613 if ent[3] == -1:
617 timestr = 'unset '
614 timestr = 'unset '
618 elif nodates:
615 elif nodates:
619 timestr = 'set '
616 timestr = 'set '
620 else:
617 else:
621 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
618 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
622 time.localtime(ent[3]))
619 time.localtime(ent[3]))
623 if ent[1] & 0o20000:
620 if ent[1] & 0o20000:
624 mode = 'lnk'
621 mode = 'lnk'
625 else:
622 else:
626 mode = '%3o' % (ent[1] & 0o777 & ~util.umask)
623 mode = '%3o' % (ent[1] & 0o777 & ~util.umask)
627 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
624 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
628 for f in repo.dirstate.copies():
625 for f in repo.dirstate.copies():
629 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
626 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
630
627
631 @command('debugdiscovery',
628 @command('debugdiscovery',
632 [('', 'old', None, _('use old-style discovery')),
629 [('', 'old', None, _('use old-style discovery')),
633 ('', 'nonheads', None,
630 ('', 'nonheads', None,
634 _('use old-style discovery with non-heads included')),
631 _('use old-style discovery with non-heads included')),
635 ] + cmdutil.remoteopts,
632 ] + cmdutil.remoteopts,
636 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
633 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
637 def debugdiscovery(ui, repo, remoteurl="default", **opts):
634 def debugdiscovery(ui, repo, remoteurl="default", **opts):
638 """runs the changeset discovery protocol in isolation"""
635 """runs the changeset discovery protocol in isolation"""
639 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
636 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
640 opts.get('branch'))
637 opts.get('branch'))
641 remote = hg.peer(repo, opts, remoteurl)
638 remote = hg.peer(repo, opts, remoteurl)
642 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
639 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
643
640
644 # make sure tests are repeatable
641 # make sure tests are repeatable
645 random.seed(12323)
642 random.seed(12323)
646
643
647 def doit(localheads, remoteheads, remote=remote):
644 def doit(localheads, remoteheads, remote=remote):
648 if opts.get('old'):
645 if opts.get('old'):
649 if localheads:
646 if localheads:
650 raise error.Abort('cannot use localheads with old style '
647 raise error.Abort('cannot use localheads with old style '
651 'discovery')
648 'discovery')
652 if not util.safehasattr(remote, 'branches'):
649 if not util.safehasattr(remote, 'branches'):
653 # enable in-client legacy support
650 # enable in-client legacy support
654 remote = localrepo.locallegacypeer(remote.local())
651 remote = localrepo.locallegacypeer(remote.local())
655 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
652 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
656 force=True)
653 force=True)
657 common = set(common)
654 common = set(common)
658 if not opts.get('nonheads'):
655 if not opts.get('nonheads'):
659 ui.write(("unpruned common: %s\n") %
656 ui.write(("unpruned common: %s\n") %
660 " ".join(sorted(short(n) for n in common)))
657 " ".join(sorted(short(n) for n in common)))
661 dag = dagutil.revlogdag(repo.changelog)
658 dag = dagutil.revlogdag(repo.changelog)
662 all = dag.ancestorset(dag.internalizeall(common))
659 all = dag.ancestorset(dag.internalizeall(common))
663 common = dag.externalizeall(dag.headsetofconnecteds(all))
660 common = dag.externalizeall(dag.headsetofconnecteds(all))
664 else:
661 else:
665 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
662 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
666 common = set(common)
663 common = set(common)
667 rheads = set(hds)
664 rheads = set(hds)
668 lheads = set(repo.heads())
665 lheads = set(repo.heads())
669 ui.write(("common heads: %s\n") %
666 ui.write(("common heads: %s\n") %
670 " ".join(sorted(short(n) for n in common)))
667 " ".join(sorted(short(n) for n in common)))
671 if lheads <= common:
668 if lheads <= common:
672 ui.write(("local is subset\n"))
669 ui.write(("local is subset\n"))
673 elif rheads <= common:
670 elif rheads <= common:
674 ui.write(("remote is subset\n"))
671 ui.write(("remote is subset\n"))
675
672
676 serverlogs = opts.get('serverlog')
673 serverlogs = opts.get('serverlog')
677 if serverlogs:
674 if serverlogs:
678 for filename in serverlogs:
675 for filename in serverlogs:
679 with open(filename, 'r') as logfile:
676 with open(filename, 'r') as logfile:
680 line = logfile.readline()
677 line = logfile.readline()
681 while line:
678 while line:
682 parts = line.strip().split(';')
679 parts = line.strip().split(';')
683 op = parts[1]
680 op = parts[1]
684 if op == 'cg':
681 if op == 'cg':
685 pass
682 pass
686 elif op == 'cgss':
683 elif op == 'cgss':
687 doit(parts[2].split(' '), parts[3].split(' '))
684 doit(parts[2].split(' '), parts[3].split(' '))
688 elif op == 'unb':
685 elif op == 'unb':
689 doit(parts[3].split(' '), parts[2].split(' '))
686 doit(parts[3].split(' '), parts[2].split(' '))
690 line = logfile.readline()
687 line = logfile.readline()
691 else:
688 else:
692 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
689 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
693 opts.get('remote_head'))
690 opts.get('remote_head'))
694 localrevs = opts.get('local_head')
691 localrevs = opts.get('local_head')
695 doit(localrevs, remoterevs)
692 doit(localrevs, remoterevs)
696
693
697 @command('debugextensions', cmdutil.formatteropts, [], norepo=True)
694 @command('debugextensions', cmdutil.formatteropts, [], norepo=True)
698 def debugextensions(ui, **opts):
695 def debugextensions(ui, **opts):
699 '''show information about active extensions'''
696 '''show information about active extensions'''
700 exts = extensions.extensions(ui)
697 exts = extensions.extensions(ui)
701 hgver = util.version()
698 hgver = util.version()
702 fm = ui.formatter('debugextensions', opts)
699 fm = ui.formatter('debugextensions', opts)
703 for extname, extmod in sorted(exts, key=operator.itemgetter(0)):
700 for extname, extmod in sorted(exts, key=operator.itemgetter(0)):
704 isinternal = extensions.ismoduleinternal(extmod)
701 isinternal = extensions.ismoduleinternal(extmod)
705 extsource = pycompat.fsencode(extmod.__file__)
702 extsource = pycompat.fsencode(extmod.__file__)
706 if isinternal:
703 if isinternal:
707 exttestedwith = [] # never expose magic string to users
704 exttestedwith = [] # never expose magic string to users
708 else:
705 else:
709 exttestedwith = getattr(extmod, 'testedwith', '').split()
706 exttestedwith = getattr(extmod, 'testedwith', '').split()
710 extbuglink = getattr(extmod, 'buglink', None)
707 extbuglink = getattr(extmod, 'buglink', None)
711
708
712 fm.startitem()
709 fm.startitem()
713
710
714 if ui.quiet or ui.verbose:
711 if ui.quiet or ui.verbose:
715 fm.write('name', '%s\n', extname)
712 fm.write('name', '%s\n', extname)
716 else:
713 else:
717 fm.write('name', '%s', extname)
714 fm.write('name', '%s', extname)
718 if isinternal or hgver in exttestedwith:
715 if isinternal or hgver in exttestedwith:
719 fm.plain('\n')
716 fm.plain('\n')
720 elif not exttestedwith:
717 elif not exttestedwith:
721 fm.plain(_(' (untested!)\n'))
718 fm.plain(_(' (untested!)\n'))
722 else:
719 else:
723 lasttestedversion = exttestedwith[-1]
720 lasttestedversion = exttestedwith[-1]
724 fm.plain(' (%s!)\n' % lasttestedversion)
721 fm.plain(' (%s!)\n' % lasttestedversion)
725
722
726 fm.condwrite(ui.verbose and extsource, 'source',
723 fm.condwrite(ui.verbose and extsource, 'source',
727 _(' location: %s\n'), extsource or "")
724 _(' location: %s\n'), extsource or "")
728
725
729 if ui.verbose:
726 if ui.verbose:
730 fm.plain(_(' bundled: %s\n') % ['no', 'yes'][isinternal])
727 fm.plain(_(' bundled: %s\n') % ['no', 'yes'][isinternal])
731 fm.data(bundled=isinternal)
728 fm.data(bundled=isinternal)
732
729
733 fm.condwrite(ui.verbose and exttestedwith, 'testedwith',
730 fm.condwrite(ui.verbose and exttestedwith, 'testedwith',
734 _(' tested with: %s\n'),
731 _(' tested with: %s\n'),
735 fm.formatlist(exttestedwith, name='ver'))
732 fm.formatlist(exttestedwith, name='ver'))
736
733
737 fm.condwrite(ui.verbose and extbuglink, 'buglink',
734 fm.condwrite(ui.verbose and extbuglink, 'buglink',
738 _(' bug reporting: %s\n'), extbuglink or "")
735 _(' bug reporting: %s\n'), extbuglink or "")
739
736
740 fm.end()
737 fm.end()
741
738
742 @command('debugfileset',
739 @command('debugfileset',
743 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
740 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
744 _('[-r REV] FILESPEC'))
741 _('[-r REV] FILESPEC'))
745 def debugfileset(ui, repo, expr, **opts):
742 def debugfileset(ui, repo, expr, **opts):
746 '''parse and apply a fileset specification'''
743 '''parse and apply a fileset specification'''
747 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
744 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
748 if ui.verbose:
745 if ui.verbose:
749 tree = fileset.parse(expr)
746 tree = fileset.parse(expr)
750 ui.note(fileset.prettyformat(tree), "\n")
747 ui.note(fileset.prettyformat(tree), "\n")
751
748
752 for f in ctx.getfileset(expr):
749 for f in ctx.getfileset(expr):
753 ui.write("%s\n" % f)
750 ui.write("%s\n" % f)
754
751
755 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
752 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
756 def debugfsinfo(ui, path="."):
753 def debugfsinfo(ui, path="."):
757 """show information detected about current filesystem"""
754 """show information detected about current filesystem"""
758 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
755 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
759 ui.write(('fstype: %s\n') % (util.getfstype(path) or '(unknown)'))
756 ui.write(('fstype: %s\n') % (util.getfstype(path) or '(unknown)'))
760 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
757 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
761 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
758 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
762 casesensitive = '(unknown)'
759 casesensitive = '(unknown)'
763 try:
760 try:
764 with tempfile.NamedTemporaryFile(prefix='.debugfsinfo', dir=path) as f:
761 with tempfile.NamedTemporaryFile(prefix='.debugfsinfo', dir=path) as f:
765 casesensitive = util.fscasesensitive(f.name) and 'yes' or 'no'
762 casesensitive = util.fscasesensitive(f.name) and 'yes' or 'no'
766 except OSError:
763 except OSError:
767 pass
764 pass
768 ui.write(('case-sensitive: %s\n') % casesensitive)
765 ui.write(('case-sensitive: %s\n') % casesensitive)
769
766
770 @command('debuggetbundle',
767 @command('debuggetbundle',
771 [('H', 'head', [], _('id of head node'), _('ID')),
768 [('H', 'head', [], _('id of head node'), _('ID')),
772 ('C', 'common', [], _('id of common node'), _('ID')),
769 ('C', 'common', [], _('id of common node'), _('ID')),
773 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
770 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
774 _('REPO FILE [-H|-C ID]...'),
771 _('REPO FILE [-H|-C ID]...'),
775 norepo=True)
772 norepo=True)
776 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
773 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
777 """retrieves a bundle from a repo
774 """retrieves a bundle from a repo
778
775
779 Every ID must be a full-length hex node id string. Saves the bundle to the
776 Every ID must be a full-length hex node id string. Saves the bundle to the
780 given file.
777 given file.
781 """
778 """
782 repo = hg.peer(ui, opts, repopath)
779 repo = hg.peer(ui, opts, repopath)
783 if not repo.capable('getbundle'):
780 if not repo.capable('getbundle'):
784 raise error.Abort("getbundle() not supported by target repository")
781 raise error.Abort("getbundle() not supported by target repository")
785 args = {}
782 args = {}
786 if common:
783 if common:
787 args['common'] = [bin(s) for s in common]
784 args['common'] = [bin(s) for s in common]
788 if head:
785 if head:
789 args['heads'] = [bin(s) for s in head]
786 args['heads'] = [bin(s) for s in head]
790 # TODO: get desired bundlecaps from command line.
787 # TODO: get desired bundlecaps from command line.
791 args['bundlecaps'] = None
788 args['bundlecaps'] = None
792 bundle = repo.getbundle('debug', **args)
789 bundle = repo.getbundle('debug', **args)
793
790
794 bundletype = opts.get('type', 'bzip2').lower()
791 bundletype = opts.get('type', 'bzip2').lower()
795 btypes = {'none': 'HG10UN',
792 btypes = {'none': 'HG10UN',
796 'bzip2': 'HG10BZ',
793 'bzip2': 'HG10BZ',
797 'gzip': 'HG10GZ',
794 'gzip': 'HG10GZ',
798 'bundle2': 'HG20'}
795 'bundle2': 'HG20'}
799 bundletype = btypes.get(bundletype)
796 bundletype = btypes.get(bundletype)
800 if bundletype not in bundle2.bundletypes:
797 if bundletype not in bundle2.bundletypes:
801 raise error.Abort(_('unknown bundle type specified with --type'))
798 raise error.Abort(_('unknown bundle type specified with --type'))
802 bundle2.writebundle(ui, bundle, bundlepath, bundletype)
799 bundle2.writebundle(ui, bundle, bundlepath, bundletype)
803
800
804 @command('debugignore', [], '[FILE]')
801 @command('debugignore', [], '[FILE]')
805 def debugignore(ui, repo, *files, **opts):
802 def debugignore(ui, repo, *files, **opts):
806 """display the combined ignore pattern and information about ignored files
803 """display the combined ignore pattern and information about ignored files
807
804
808 With no argument display the combined ignore pattern.
805 With no argument display the combined ignore pattern.
809
806
810 Given space separated file names, shows if the given file is ignored and
807 Given space separated file names, shows if the given file is ignored and
811 if so, show the ignore rule (file and line number) that matched it.
808 if so, show the ignore rule (file and line number) that matched it.
812 """
809 """
813 ignore = repo.dirstate._ignore
810 ignore = repo.dirstate._ignore
814 if not files:
811 if not files:
815 # Show all the patterns
812 # Show all the patterns
816 includepat = getattr(ignore, 'includepat', None)
813 includepat = getattr(ignore, 'includepat', None)
817 if includepat is not None:
814 if includepat is not None:
818 ui.write("%s\n" % includepat)
815 ui.write("%s\n" % includepat)
819 else:
816 else:
820 raise error.Abort(_("no ignore patterns found"))
817 raise error.Abort(_("no ignore patterns found"))
821 else:
818 else:
822 for f in files:
819 for f in files:
823 nf = util.normpath(f)
820 nf = util.normpath(f)
824 ignored = None
821 ignored = None
825 ignoredata = None
822 ignoredata = None
826 if nf != '.':
823 if nf != '.':
827 if ignore(nf):
824 if ignore(nf):
828 ignored = nf
825 ignored = nf
829 ignoredata = repo.dirstate._ignorefileandline(nf)
826 ignoredata = repo.dirstate._ignorefileandline(nf)
830 else:
827 else:
831 for p in util.finddirs(nf):
828 for p in util.finddirs(nf):
832 if ignore(p):
829 if ignore(p):
833 ignored = p
830 ignored = p
834 ignoredata = repo.dirstate._ignorefileandline(p)
831 ignoredata = repo.dirstate._ignorefileandline(p)
835 break
832 break
836 if ignored:
833 if ignored:
837 if ignored == nf:
834 if ignored == nf:
838 ui.write(_("%s is ignored\n") % f)
835 ui.write(_("%s is ignored\n") % f)
839 else:
836 else:
840 ui.write(_("%s is ignored because of "
837 ui.write(_("%s is ignored because of "
841 "containing folder %s\n")
838 "containing folder %s\n")
842 % (f, ignored))
839 % (f, ignored))
843 ignorefile, lineno, line = ignoredata
840 ignorefile, lineno, line = ignoredata
844 ui.write(_("(ignore rule in %s, line %d: '%s')\n")
841 ui.write(_("(ignore rule in %s, line %d: '%s')\n")
845 % (ignorefile, lineno, line))
842 % (ignorefile, lineno, line))
846 else:
843 else:
847 ui.write(_("%s is not ignored\n") % f)
844 ui.write(_("%s is not ignored\n") % f)
848
845
849 @command('debugindex', cmdutil.debugrevlogopts +
846 @command('debugindex', cmdutil.debugrevlogopts +
850 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
847 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
851 _('[-f FORMAT] -c|-m|FILE'),
848 _('[-f FORMAT] -c|-m|FILE'),
852 optionalrepo=True)
849 optionalrepo=True)
853 def debugindex(ui, repo, file_=None, **opts):
850 def debugindex(ui, repo, file_=None, **opts):
854 """dump the contents of an index file"""
851 """dump the contents of an index file"""
855 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
852 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
856 format = opts.get('format', 0)
853 format = opts.get('format', 0)
857 if format not in (0, 1):
854 if format not in (0, 1):
858 raise error.Abort(_("unknown format %d") % format)
855 raise error.Abort(_("unknown format %d") % format)
859
856
860 generaldelta = r.version & revlog.FLAG_GENERALDELTA
857 generaldelta = r.version & revlog.FLAG_GENERALDELTA
861 if generaldelta:
858 if generaldelta:
862 basehdr = ' delta'
859 basehdr = ' delta'
863 else:
860 else:
864 basehdr = ' base'
861 basehdr = ' base'
865
862
866 if ui.debugflag:
863 if ui.debugflag:
867 shortfn = hex
864 shortfn = hex
868 else:
865 else:
869 shortfn = short
866 shortfn = short
870
867
871 # There might not be anything in r, so have a sane default
868 # There might not be anything in r, so have a sane default
872 idlen = 12
869 idlen = 12
873 for i in r:
870 for i in r:
874 idlen = len(shortfn(r.node(i)))
871 idlen = len(shortfn(r.node(i)))
875 break
872 break
876
873
877 if format == 0:
874 if format == 0:
878 ui.write((" rev offset length " + basehdr + " linkrev"
875 ui.write((" rev offset length " + basehdr + " linkrev"
879 " %s %s p2\n") % ("nodeid".ljust(idlen), "p1".ljust(idlen)))
876 " %s %s p2\n") % ("nodeid".ljust(idlen), "p1".ljust(idlen)))
880 elif format == 1:
877 elif format == 1:
881 ui.write((" rev flag offset length"
878 ui.write((" rev flag offset length"
882 " size " + basehdr + " link p1 p2"
879 " size " + basehdr + " link p1 p2"
883 " %s\n") % "nodeid".rjust(idlen))
880 " %s\n") % "nodeid".rjust(idlen))
884
881
885 for i in r:
882 for i in r:
886 node = r.node(i)
883 node = r.node(i)
887 if generaldelta:
884 if generaldelta:
888 base = r.deltaparent(i)
885 base = r.deltaparent(i)
889 else:
886 else:
890 base = r.chainbase(i)
887 base = r.chainbase(i)
891 if format == 0:
888 if format == 0:
892 try:
889 try:
893 pp = r.parents(node)
890 pp = r.parents(node)
894 except Exception:
891 except Exception:
895 pp = [nullid, nullid]
892 pp = [nullid, nullid]
896 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
893 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
897 i, r.start(i), r.length(i), base, r.linkrev(i),
894 i, r.start(i), r.length(i), base, r.linkrev(i),
898 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
895 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
899 elif format == 1:
896 elif format == 1:
900 pr = r.parentrevs(i)
897 pr = r.parentrevs(i)
901 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
898 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
902 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
899 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
903 base, r.linkrev(i), pr[0], pr[1], shortfn(node)))
900 base, r.linkrev(i), pr[0], pr[1], shortfn(node)))
904
901
905 @command('debugindexdot', cmdutil.debugrevlogopts,
902 @command('debugindexdot', cmdutil.debugrevlogopts,
906 _('-c|-m|FILE'), optionalrepo=True)
903 _('-c|-m|FILE'), optionalrepo=True)
907 def debugindexdot(ui, repo, file_=None, **opts):
904 def debugindexdot(ui, repo, file_=None, **opts):
908 """dump an index DAG as a graphviz dot file"""
905 """dump an index DAG as a graphviz dot file"""
909 r = cmdutil.openrevlog(repo, 'debugindexdot', file_, opts)
906 r = cmdutil.openrevlog(repo, 'debugindexdot', file_, opts)
910 ui.write(("digraph G {\n"))
907 ui.write(("digraph G {\n"))
911 for i in r:
908 for i in r:
912 node = r.node(i)
909 node = r.node(i)
913 pp = r.parents(node)
910 pp = r.parents(node)
914 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
911 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
915 if pp[1] != nullid:
912 if pp[1] != nullid:
916 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
913 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
917 ui.write("}\n")
914 ui.write("}\n")
918
915
919 @command('debuginstall', [] + cmdutil.formatteropts, '', norepo=True)
916 @command('debuginstall', [] + cmdutil.formatteropts, '', norepo=True)
920 def debuginstall(ui, **opts):
917 def debuginstall(ui, **opts):
921 '''test Mercurial installation
918 '''test Mercurial installation
922
919
923 Returns 0 on success.
920 Returns 0 on success.
924 '''
921 '''
925
922
926 def writetemp(contents):
923 def writetemp(contents):
927 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
924 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
928 f = os.fdopen(fd, pycompat.sysstr("wb"))
925 f = os.fdopen(fd, pycompat.sysstr("wb"))
929 f.write(contents)
926 f.write(contents)
930 f.close()
927 f.close()
931 return name
928 return name
932
929
933 problems = 0
930 problems = 0
934
931
935 fm = ui.formatter('debuginstall', opts)
932 fm = ui.formatter('debuginstall', opts)
936 fm.startitem()
933 fm.startitem()
937
934
938 # encoding
935 # encoding
939 fm.write('encoding', _("checking encoding (%s)...\n"), encoding.encoding)
936 fm.write('encoding', _("checking encoding (%s)...\n"), encoding.encoding)
940 err = None
937 err = None
941 try:
938 try:
942 encoding.fromlocal("test")
939 encoding.fromlocal("test")
943 except error.Abort as inst:
940 except error.Abort as inst:
944 err = inst
941 err = inst
945 problems += 1
942 problems += 1
946 fm.condwrite(err, 'encodingerror', _(" %s\n"
943 fm.condwrite(err, 'encodingerror', _(" %s\n"
947 " (check that your locale is properly set)\n"), err)
944 " (check that your locale is properly set)\n"), err)
948
945
949 # Python
946 # Python
950 fm.write('pythonexe', _("checking Python executable (%s)\n"),
947 fm.write('pythonexe', _("checking Python executable (%s)\n"),
951 pycompat.sysexecutable)
948 pycompat.sysexecutable)
952 fm.write('pythonver', _("checking Python version (%s)\n"),
949 fm.write('pythonver', _("checking Python version (%s)\n"),
953 ("%d.%d.%d" % sys.version_info[:3]))
950 ("%d.%d.%d" % sys.version_info[:3]))
954 fm.write('pythonlib', _("checking Python lib (%s)...\n"),
951 fm.write('pythonlib', _("checking Python lib (%s)...\n"),
955 os.path.dirname(pycompat.fsencode(os.__file__)))
952 os.path.dirname(pycompat.fsencode(os.__file__)))
956
953
957 security = set(sslutil.supportedprotocols)
954 security = set(sslutil.supportedprotocols)
958 if sslutil.hassni:
955 if sslutil.hassni:
959 security.add('sni')
956 security.add('sni')
960
957
961 fm.write('pythonsecurity', _("checking Python security support (%s)\n"),
958 fm.write('pythonsecurity', _("checking Python security support (%s)\n"),
962 fm.formatlist(sorted(security), name='protocol',
959 fm.formatlist(sorted(security), name='protocol',
963 fmt='%s', sep=','))
960 fmt='%s', sep=','))
964
961
965 # These are warnings, not errors. So don't increment problem count. This
962 # These are warnings, not errors. So don't increment problem count. This
966 # may change in the future.
963 # may change in the future.
967 if 'tls1.2' not in security:
964 if 'tls1.2' not in security:
968 fm.plain(_(' TLS 1.2 not supported by Python install; '
965 fm.plain(_(' TLS 1.2 not supported by Python install; '
969 'network connections lack modern security\n'))
966 'network connections lack modern security\n'))
970 if 'sni' not in security:
967 if 'sni' not in security:
971 fm.plain(_(' SNI not supported by Python install; may have '
968 fm.plain(_(' SNI not supported by Python install; may have '
972 'connectivity issues with some servers\n'))
969 'connectivity issues with some servers\n'))
973
970
974 # TODO print CA cert info
971 # TODO print CA cert info
975
972
976 # hg version
973 # hg version
977 hgver = util.version()
974 hgver = util.version()
978 fm.write('hgver', _("checking Mercurial version (%s)\n"),
975 fm.write('hgver', _("checking Mercurial version (%s)\n"),
979 hgver.split('+')[0])
976 hgver.split('+')[0])
980 fm.write('hgverextra', _("checking Mercurial custom build (%s)\n"),
977 fm.write('hgverextra', _("checking Mercurial custom build (%s)\n"),
981 '+'.join(hgver.split('+')[1:]))
978 '+'.join(hgver.split('+')[1:]))
982
979
983 # compiled modules
980 # compiled modules
984 fm.write('hgmodulepolicy', _("checking module policy (%s)\n"),
981 fm.write('hgmodulepolicy', _("checking module policy (%s)\n"),
985 policy.policy)
982 policy.policy)
986 fm.write('hgmodules', _("checking installed modules (%s)...\n"),
983 fm.write('hgmodules', _("checking installed modules (%s)...\n"),
987 os.path.dirname(pycompat.fsencode(__file__)))
984 os.path.dirname(pycompat.fsencode(__file__)))
988
985
989 if policy.policy in ('c', 'allow'):
986 if policy.policy in ('c', 'allow'):
990 err = None
987 err = None
991 try:
988 try:
992 from .cext import (
989 from .cext import (
993 base85,
990 base85,
994 bdiff,
991 bdiff,
995 mpatch,
992 mpatch,
996 osutil,
993 osutil,
997 )
994 )
998 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
995 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
999 except Exception as inst:
996 except Exception as inst:
1000 err = inst
997 err = inst
1001 problems += 1
998 problems += 1
1002 fm.condwrite(err, 'extensionserror', " %s\n", err)
999 fm.condwrite(err, 'extensionserror', " %s\n", err)
1003
1000
1004 compengines = util.compengines._engines.values()
1001 compengines = util.compengines._engines.values()
1005 fm.write('compengines', _('checking registered compression engines (%s)\n'),
1002 fm.write('compengines', _('checking registered compression engines (%s)\n'),
1006 fm.formatlist(sorted(e.name() for e in compengines),
1003 fm.formatlist(sorted(e.name() for e in compengines),
1007 name='compengine', fmt='%s', sep=', '))
1004 name='compengine', fmt='%s', sep=', '))
1008 fm.write('compenginesavail', _('checking available compression engines '
1005 fm.write('compenginesavail', _('checking available compression engines '
1009 '(%s)\n'),
1006 '(%s)\n'),
1010 fm.formatlist(sorted(e.name() for e in compengines
1007 fm.formatlist(sorted(e.name() for e in compengines
1011 if e.available()),
1008 if e.available()),
1012 name='compengine', fmt='%s', sep=', '))
1009 name='compengine', fmt='%s', sep=', '))
1013 wirecompengines = util.compengines.supportedwireengines(util.SERVERROLE)
1010 wirecompengines = util.compengines.supportedwireengines(util.SERVERROLE)
1014 fm.write('compenginesserver', _('checking available compression engines '
1011 fm.write('compenginesserver', _('checking available compression engines '
1015 'for wire protocol (%s)\n'),
1012 'for wire protocol (%s)\n'),
1016 fm.formatlist([e.name() for e in wirecompengines
1013 fm.formatlist([e.name() for e in wirecompengines
1017 if e.wireprotosupport()],
1014 if e.wireprotosupport()],
1018 name='compengine', fmt='%s', sep=', '))
1015 name='compengine', fmt='%s', sep=', '))
1019
1016
1020 # templates
1017 # templates
1021 p = templater.templatepaths()
1018 p = templater.templatepaths()
1022 fm.write('templatedirs', 'checking templates (%s)...\n', ' '.join(p))
1019 fm.write('templatedirs', 'checking templates (%s)...\n', ' '.join(p))
1023 fm.condwrite(not p, '', _(" no template directories found\n"))
1020 fm.condwrite(not p, '', _(" no template directories found\n"))
1024 if p:
1021 if p:
1025 m = templater.templatepath("map-cmdline.default")
1022 m = templater.templatepath("map-cmdline.default")
1026 if m:
1023 if m:
1027 # template found, check if it is working
1024 # template found, check if it is working
1028 err = None
1025 err = None
1029 try:
1026 try:
1030 templater.templater.frommapfile(m)
1027 templater.templater.frommapfile(m)
1031 except Exception as inst:
1028 except Exception as inst:
1032 err = inst
1029 err = inst
1033 p = None
1030 p = None
1034 fm.condwrite(err, 'defaulttemplateerror', " %s\n", err)
1031 fm.condwrite(err, 'defaulttemplateerror', " %s\n", err)
1035 else:
1032 else:
1036 p = None
1033 p = None
1037 fm.condwrite(p, 'defaulttemplate',
1034 fm.condwrite(p, 'defaulttemplate',
1038 _("checking default template (%s)\n"), m)
1035 _("checking default template (%s)\n"), m)
1039 fm.condwrite(not m, 'defaulttemplatenotfound',
1036 fm.condwrite(not m, 'defaulttemplatenotfound',
1040 _(" template '%s' not found\n"), "default")
1037 _(" template '%s' not found\n"), "default")
1041 if not p:
1038 if not p:
1042 problems += 1
1039 problems += 1
1043 fm.condwrite(not p, '',
1040 fm.condwrite(not p, '',
1044 _(" (templates seem to have been installed incorrectly)\n"))
1041 _(" (templates seem to have been installed incorrectly)\n"))
1045
1042
1046 # editor
1043 # editor
1047 editor = ui.geteditor()
1044 editor = ui.geteditor()
1048 editor = util.expandpath(editor)
1045 editor = util.expandpath(editor)
1049 fm.write('editor', _("checking commit editor... (%s)\n"), editor)
1046 fm.write('editor', _("checking commit editor... (%s)\n"), editor)
1050 cmdpath = util.findexe(pycompat.shlexsplit(editor)[0])
1047 cmdpath = util.findexe(pycompat.shlexsplit(editor)[0])
1051 fm.condwrite(not cmdpath and editor == 'vi', 'vinotfound',
1048 fm.condwrite(not cmdpath and editor == 'vi', 'vinotfound',
1052 _(" No commit editor set and can't find %s in PATH\n"
1049 _(" No commit editor set and can't find %s in PATH\n"
1053 " (specify a commit editor in your configuration"
1050 " (specify a commit editor in your configuration"
1054 " file)\n"), not cmdpath and editor == 'vi' and editor)
1051 " file)\n"), not cmdpath and editor == 'vi' and editor)
1055 fm.condwrite(not cmdpath and editor != 'vi', 'editornotfound',
1052 fm.condwrite(not cmdpath and editor != 'vi', 'editornotfound',
1056 _(" Can't find editor '%s' in PATH\n"
1053 _(" Can't find editor '%s' in PATH\n"
1057 " (specify a commit editor in your configuration"
1054 " (specify a commit editor in your configuration"
1058 " file)\n"), not cmdpath and editor)
1055 " file)\n"), not cmdpath and editor)
1059 if not cmdpath and editor != 'vi':
1056 if not cmdpath and editor != 'vi':
1060 problems += 1
1057 problems += 1
1061
1058
1062 # check username
1059 # check username
1063 username = None
1060 username = None
1064 err = None
1061 err = None
1065 try:
1062 try:
1066 username = ui.username()
1063 username = ui.username()
1067 except error.Abort as e:
1064 except error.Abort as e:
1068 err = e
1065 err = e
1069 problems += 1
1066 problems += 1
1070
1067
1071 fm.condwrite(username, 'username', _("checking username (%s)\n"), username)
1068 fm.condwrite(username, 'username', _("checking username (%s)\n"), username)
1072 fm.condwrite(err, 'usernameerror', _("checking username...\n %s\n"
1069 fm.condwrite(err, 'usernameerror', _("checking username...\n %s\n"
1073 " (specify a username in your configuration file)\n"), err)
1070 " (specify a username in your configuration file)\n"), err)
1074
1071
1075 fm.condwrite(not problems, '',
1072 fm.condwrite(not problems, '',
1076 _("no problems detected\n"))
1073 _("no problems detected\n"))
1077 if not problems:
1074 if not problems:
1078 fm.data(problems=problems)
1075 fm.data(problems=problems)
1079 fm.condwrite(problems, 'problems',
1076 fm.condwrite(problems, 'problems',
1080 _("%d problems detected,"
1077 _("%d problems detected,"
1081 " please check your install!\n"), problems)
1078 " please check your install!\n"), problems)
1082 fm.end()
1079 fm.end()
1083
1080
1084 return problems
1081 return problems
1085
1082
1086 @command('debugknown', [], _('REPO ID...'), norepo=True)
1083 @command('debugknown', [], _('REPO ID...'), norepo=True)
1087 def debugknown(ui, repopath, *ids, **opts):
1084 def debugknown(ui, repopath, *ids, **opts):
1088 """test whether node ids are known to a repo
1085 """test whether node ids are known to a repo
1089
1086
1090 Every ID must be a full-length hex node id string. Returns a list of 0s
1087 Every ID must be a full-length hex node id string. Returns a list of 0s
1091 and 1s indicating unknown/known.
1088 and 1s indicating unknown/known.
1092 """
1089 """
1093 repo = hg.peer(ui, opts, repopath)
1090 repo = hg.peer(ui, opts, repopath)
1094 if not repo.capable('known'):
1091 if not repo.capable('known'):
1095 raise error.Abort("known() not supported by target repository")
1092 raise error.Abort("known() not supported by target repository")
1096 flags = repo.known([bin(s) for s in ids])
1093 flags = repo.known([bin(s) for s in ids])
1097 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1094 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1098
1095
1099 @command('debuglabelcomplete', [], _('LABEL...'))
1096 @command('debuglabelcomplete', [], _('LABEL...'))
1100 def debuglabelcomplete(ui, repo, *args):
1097 def debuglabelcomplete(ui, repo, *args):
1101 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
1098 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
1102 debugnamecomplete(ui, repo, *args)
1099 debugnamecomplete(ui, repo, *args)
1103
1100
1104 @command('debuglocks',
1101 @command('debuglocks',
1105 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
1102 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
1106 ('W', 'force-wlock', None,
1103 ('W', 'force-wlock', None,
1107 _('free the working state lock (DANGEROUS)'))],
1104 _('free the working state lock (DANGEROUS)'))],
1108 _('[OPTION]...'))
1105 _('[OPTION]...'))
1109 def debuglocks(ui, repo, **opts):
1106 def debuglocks(ui, repo, **opts):
1110 """show or modify state of locks
1107 """show or modify state of locks
1111
1108
1112 By default, this command will show which locks are held. This
1109 By default, this command will show which locks are held. This
1113 includes the user and process holding the lock, the amount of time
1110 includes the user and process holding the lock, the amount of time
1114 the lock has been held, and the machine name where the process is
1111 the lock has been held, and the machine name where the process is
1115 running if it's not local.
1112 running if it's not local.
1116
1113
1117 Locks protect the integrity of Mercurial's data, so should be
1114 Locks protect the integrity of Mercurial's data, so should be
1118 treated with care. System crashes or other interruptions may cause
1115 treated with care. System crashes or other interruptions may cause
1119 locks to not be properly released, though Mercurial will usually
1116 locks to not be properly released, though Mercurial will usually
1120 detect and remove such stale locks automatically.
1117 detect and remove such stale locks automatically.
1121
1118
1122 However, detecting stale locks may not always be possible (for
1119 However, detecting stale locks may not always be possible (for
1123 instance, on a shared filesystem). Removing locks may also be
1120 instance, on a shared filesystem). Removing locks may also be
1124 blocked by filesystem permissions.
1121 blocked by filesystem permissions.
1125
1122
1126 Returns 0 if no locks are held.
1123 Returns 0 if no locks are held.
1127
1124
1128 """
1125 """
1129
1126
1130 if opts.get('force_lock'):
1127 if opts.get('force_lock'):
1131 repo.svfs.unlink('lock')
1128 repo.svfs.unlink('lock')
1132 if opts.get('force_wlock'):
1129 if opts.get('force_wlock'):
1133 repo.vfs.unlink('wlock')
1130 repo.vfs.unlink('wlock')
1134 if opts.get('force_lock') or opts.get('force_lock'):
1131 if opts.get('force_lock') or opts.get('force_lock'):
1135 return 0
1132 return 0
1136
1133
1137 now = time.time()
1134 now = time.time()
1138 held = 0
1135 held = 0
1139
1136
1140 def report(vfs, name, method):
1137 def report(vfs, name, method):
1141 # this causes stale locks to get reaped for more accurate reporting
1138 # this causes stale locks to get reaped for more accurate reporting
1142 try:
1139 try:
1143 l = method(False)
1140 l = method(False)
1144 except error.LockHeld:
1141 except error.LockHeld:
1145 l = None
1142 l = None
1146
1143
1147 if l:
1144 if l:
1148 l.release()
1145 l.release()
1149 else:
1146 else:
1150 try:
1147 try:
1151 stat = vfs.lstat(name)
1148 stat = vfs.lstat(name)
1152 age = now - stat.st_mtime
1149 age = now - stat.st_mtime
1153 user = util.username(stat.st_uid)
1150 user = util.username(stat.st_uid)
1154 locker = vfs.readlock(name)
1151 locker = vfs.readlock(name)
1155 if ":" in locker:
1152 if ":" in locker:
1156 host, pid = locker.split(':')
1153 host, pid = locker.split(':')
1157 if host == socket.gethostname():
1154 if host == socket.gethostname():
1158 locker = 'user %s, process %s' % (user, pid)
1155 locker = 'user %s, process %s' % (user, pid)
1159 else:
1156 else:
1160 locker = 'user %s, process %s, host %s' \
1157 locker = 'user %s, process %s, host %s' \
1161 % (user, pid, host)
1158 % (user, pid, host)
1162 ui.write(("%-6s %s (%ds)\n") % (name + ":", locker, age))
1159 ui.write(("%-6s %s (%ds)\n") % (name + ":", locker, age))
1163 return 1
1160 return 1
1164 except OSError as e:
1161 except OSError as e:
1165 if e.errno != errno.ENOENT:
1162 if e.errno != errno.ENOENT:
1166 raise
1163 raise
1167
1164
1168 ui.write(("%-6s free\n") % (name + ":"))
1165 ui.write(("%-6s free\n") % (name + ":"))
1169 return 0
1166 return 0
1170
1167
1171 held += report(repo.svfs, "lock", repo.lock)
1168 held += report(repo.svfs, "lock", repo.lock)
1172 held += report(repo.vfs, "wlock", repo.wlock)
1169 held += report(repo.vfs, "wlock", repo.wlock)
1173
1170
1174 return held
1171 return held
1175
1172
1176 @command('debugmergestate', [], '')
1173 @command('debugmergestate', [], '')
1177 def debugmergestate(ui, repo, *args):
1174 def debugmergestate(ui, repo, *args):
1178 """print merge state
1175 """print merge state
1179
1176
1180 Use --verbose to print out information about whether v1 or v2 merge state
1177 Use --verbose to print out information about whether v1 or v2 merge state
1181 was chosen."""
1178 was chosen."""
1182 def _hashornull(h):
1179 def _hashornull(h):
1183 if h == nullhex:
1180 if h == nullhex:
1184 return 'null'
1181 return 'null'
1185 else:
1182 else:
1186 return h
1183 return h
1187
1184
1188 def printrecords(version):
1185 def printrecords(version):
1189 ui.write(('* version %s records\n') % version)
1186 ui.write(('* version %s records\n') % version)
1190 if version == 1:
1187 if version == 1:
1191 records = v1records
1188 records = v1records
1192 else:
1189 else:
1193 records = v2records
1190 records = v2records
1194
1191
1195 for rtype, record in records:
1192 for rtype, record in records:
1196 # pretty print some record types
1193 # pretty print some record types
1197 if rtype == 'L':
1194 if rtype == 'L':
1198 ui.write(('local: %s\n') % record)
1195 ui.write(('local: %s\n') % record)
1199 elif rtype == 'O':
1196 elif rtype == 'O':
1200 ui.write(('other: %s\n') % record)
1197 ui.write(('other: %s\n') % record)
1201 elif rtype == 'm':
1198 elif rtype == 'm':
1202 driver, mdstate = record.split('\0', 1)
1199 driver, mdstate = record.split('\0', 1)
1203 ui.write(('merge driver: %s (state "%s")\n')
1200 ui.write(('merge driver: %s (state "%s")\n')
1204 % (driver, mdstate))
1201 % (driver, mdstate))
1205 elif rtype in 'FDC':
1202 elif rtype in 'FDC':
1206 r = record.split('\0')
1203 r = record.split('\0')
1207 f, state, hash, lfile, afile, anode, ofile = r[0:7]
1204 f, state, hash, lfile, afile, anode, ofile = r[0:7]
1208 if version == 1:
1205 if version == 1:
1209 onode = 'not stored in v1 format'
1206 onode = 'not stored in v1 format'
1210 flags = r[7]
1207 flags = r[7]
1211 else:
1208 else:
1212 onode, flags = r[7:9]
1209 onode, flags = r[7:9]
1213 ui.write(('file: %s (record type "%s", state "%s", hash %s)\n')
1210 ui.write(('file: %s (record type "%s", state "%s", hash %s)\n')
1214 % (f, rtype, state, _hashornull(hash)))
1211 % (f, rtype, state, _hashornull(hash)))
1215 ui.write((' local path: %s (flags "%s")\n') % (lfile, flags))
1212 ui.write((' local path: %s (flags "%s")\n') % (lfile, flags))
1216 ui.write((' ancestor path: %s (node %s)\n')
1213 ui.write((' ancestor path: %s (node %s)\n')
1217 % (afile, _hashornull(anode)))
1214 % (afile, _hashornull(anode)))
1218 ui.write((' other path: %s (node %s)\n')
1215 ui.write((' other path: %s (node %s)\n')
1219 % (ofile, _hashornull(onode)))
1216 % (ofile, _hashornull(onode)))
1220 elif rtype == 'f':
1217 elif rtype == 'f':
1221 filename, rawextras = record.split('\0', 1)
1218 filename, rawextras = record.split('\0', 1)
1222 extras = rawextras.split('\0')
1219 extras = rawextras.split('\0')
1223 i = 0
1220 i = 0
1224 extrastrings = []
1221 extrastrings = []
1225 while i < len(extras):
1222 while i < len(extras):
1226 extrastrings.append('%s = %s' % (extras[i], extras[i + 1]))
1223 extrastrings.append('%s = %s' % (extras[i], extras[i + 1]))
1227 i += 2
1224 i += 2
1228
1225
1229 ui.write(('file extras: %s (%s)\n')
1226 ui.write(('file extras: %s (%s)\n')
1230 % (filename, ', '.join(extrastrings)))
1227 % (filename, ', '.join(extrastrings)))
1231 elif rtype == 'l':
1228 elif rtype == 'l':
1232 labels = record.split('\0', 2)
1229 labels = record.split('\0', 2)
1233 labels = [l for l in labels if len(l) > 0]
1230 labels = [l for l in labels if len(l) > 0]
1234 ui.write(('labels:\n'))
1231 ui.write(('labels:\n'))
1235 ui.write((' local: %s\n' % labels[0]))
1232 ui.write((' local: %s\n' % labels[0]))
1236 ui.write((' other: %s\n' % labels[1]))
1233 ui.write((' other: %s\n' % labels[1]))
1237 if len(labels) > 2:
1234 if len(labels) > 2:
1238 ui.write((' base: %s\n' % labels[2]))
1235 ui.write((' base: %s\n' % labels[2]))
1239 else:
1236 else:
1240 ui.write(('unrecognized entry: %s\t%s\n')
1237 ui.write(('unrecognized entry: %s\t%s\n')
1241 % (rtype, record.replace('\0', '\t')))
1238 % (rtype, record.replace('\0', '\t')))
1242
1239
1243 # Avoid mergestate.read() since it may raise an exception for unsupported
1240 # Avoid mergestate.read() since it may raise an exception for unsupported
1244 # merge state records. We shouldn't be doing this, but this is OK since this
1241 # merge state records. We shouldn't be doing this, but this is OK since this
1245 # command is pretty low-level.
1242 # command is pretty low-level.
1246 ms = mergemod.mergestate(repo)
1243 ms = mergemod.mergestate(repo)
1247
1244
1248 # sort so that reasonable information is on top
1245 # sort so that reasonable information is on top
1249 v1records = ms._readrecordsv1()
1246 v1records = ms._readrecordsv1()
1250 v2records = ms._readrecordsv2()
1247 v2records = ms._readrecordsv2()
1251 order = 'LOml'
1248 order = 'LOml'
1252 def key(r):
1249 def key(r):
1253 idx = order.find(r[0])
1250 idx = order.find(r[0])
1254 if idx == -1:
1251 if idx == -1:
1255 return (1, r[1])
1252 return (1, r[1])
1256 else:
1253 else:
1257 return (0, idx)
1254 return (0, idx)
1258 v1records.sort(key=key)
1255 v1records.sort(key=key)
1259 v2records.sort(key=key)
1256 v2records.sort(key=key)
1260
1257
1261 if not v1records and not v2records:
1258 if not v1records and not v2records:
1262 ui.write(('no merge state found\n'))
1259 ui.write(('no merge state found\n'))
1263 elif not v2records:
1260 elif not v2records:
1264 ui.note(('no version 2 merge state\n'))
1261 ui.note(('no version 2 merge state\n'))
1265 printrecords(1)
1262 printrecords(1)
1266 elif ms._v1v2match(v1records, v2records):
1263 elif ms._v1v2match(v1records, v2records):
1267 ui.note(('v1 and v2 states match: using v2\n'))
1264 ui.note(('v1 and v2 states match: using v2\n'))
1268 printrecords(2)
1265 printrecords(2)
1269 else:
1266 else:
1270 ui.note(('v1 and v2 states mismatch: using v1\n'))
1267 ui.note(('v1 and v2 states mismatch: using v1\n'))
1271 printrecords(1)
1268 printrecords(1)
1272 if ui.verbose:
1269 if ui.verbose:
1273 printrecords(2)
1270 printrecords(2)
1274
1271
1275 @command('debugnamecomplete', [], _('NAME...'))
1272 @command('debugnamecomplete', [], _('NAME...'))
1276 def debugnamecomplete(ui, repo, *args):
1273 def debugnamecomplete(ui, repo, *args):
1277 '''complete "names" - tags, open branch names, bookmark names'''
1274 '''complete "names" - tags, open branch names, bookmark names'''
1278
1275
1279 names = set()
1276 names = set()
1280 # since we previously only listed open branches, we will handle that
1277 # since we previously only listed open branches, we will handle that
1281 # specially (after this for loop)
1278 # specially (after this for loop)
1282 for name, ns in repo.names.iteritems():
1279 for name, ns in repo.names.iteritems():
1283 if name != 'branches':
1280 if name != 'branches':
1284 names.update(ns.listnames(repo))
1281 names.update(ns.listnames(repo))
1285 names.update(tag for (tag, heads, tip, closed)
1282 names.update(tag for (tag, heads, tip, closed)
1286 in repo.branchmap().iterbranches() if not closed)
1283 in repo.branchmap().iterbranches() if not closed)
1287 completions = set()
1284 completions = set()
1288 if not args:
1285 if not args:
1289 args = ['']
1286 args = ['']
1290 for a in args:
1287 for a in args:
1291 completions.update(n for n in names if n.startswith(a))
1288 completions.update(n for n in names if n.startswith(a))
1292 ui.write('\n'.join(sorted(completions)))
1289 ui.write('\n'.join(sorted(completions)))
1293 ui.write('\n')
1290 ui.write('\n')
1294
1291
1295 @command('debugobsolete',
1292 @command('debugobsolete',
1296 [('', 'flags', 0, _('markers flag')),
1293 [('', 'flags', 0, _('markers flag')),
1297 ('', 'record-parents', False,
1294 ('', 'record-parents', False,
1298 _('record parent information for the precursor')),
1295 _('record parent information for the precursor')),
1299 ('r', 'rev', [], _('display markers relevant to REV')),
1296 ('r', 'rev', [], _('display markers relevant to REV')),
1300 ('', 'index', False, _('display index of the marker')),
1297 ('', 'index', False, _('display index of the marker')),
1301 ('', 'delete', [], _('delete markers specified by indices')),
1298 ('', 'delete', [], _('delete markers specified by indices')),
1302 ] + cmdutil.commitopts2 + cmdutil.formatteropts,
1299 ] + cmdutil.commitopts2 + cmdutil.formatteropts,
1303 _('[OBSOLETED [REPLACEMENT ...]]'))
1300 _('[OBSOLETED [REPLACEMENT ...]]'))
1304 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
1301 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
1305 """create arbitrary obsolete marker
1302 """create arbitrary obsolete marker
1306
1303
1307 With no arguments, displays the list of obsolescence markers."""
1304 With no arguments, displays the list of obsolescence markers."""
1308
1305
1309 def parsenodeid(s):
1306 def parsenodeid(s):
1310 try:
1307 try:
1311 # We do not use revsingle/revrange functions here to accept
1308 # We do not use revsingle/revrange functions here to accept
1312 # arbitrary node identifiers, possibly not present in the
1309 # arbitrary node identifiers, possibly not present in the
1313 # local repository.
1310 # local repository.
1314 n = bin(s)
1311 n = bin(s)
1315 if len(n) != len(nullid):
1312 if len(n) != len(nullid):
1316 raise TypeError()
1313 raise TypeError()
1317 return n
1314 return n
1318 except TypeError:
1315 except TypeError:
1319 raise error.Abort('changeset references must be full hexadecimal '
1316 raise error.Abort('changeset references must be full hexadecimal '
1320 'node identifiers')
1317 'node identifiers')
1321
1318
1322 if opts.get('delete'):
1319 if opts.get('delete'):
1323 indices = []
1320 indices = []
1324 for v in opts.get('delete'):
1321 for v in opts.get('delete'):
1325 try:
1322 try:
1326 indices.append(int(v))
1323 indices.append(int(v))
1327 except ValueError:
1324 except ValueError:
1328 raise error.Abort(_('invalid index value: %r') % v,
1325 raise error.Abort(_('invalid index value: %r') % v,
1329 hint=_('use integers for indices'))
1326 hint=_('use integers for indices'))
1330
1327
1331 if repo.currenttransaction():
1328 if repo.currenttransaction():
1332 raise error.Abort(_('cannot delete obsmarkers in the middle '
1329 raise error.Abort(_('cannot delete obsmarkers in the middle '
1333 'of transaction.'))
1330 'of transaction.'))
1334
1331
1335 with repo.lock():
1332 with repo.lock():
1336 n = repair.deleteobsmarkers(repo.obsstore, indices)
1333 n = repair.deleteobsmarkers(repo.obsstore, indices)
1337 ui.write(_('deleted %i obsolescence markers\n') % n)
1334 ui.write(_('deleted %i obsolescence markers\n') % n)
1338
1335
1339 return
1336 return
1340
1337
1341 if precursor is not None:
1338 if precursor is not None:
1342 if opts['rev']:
1339 if opts['rev']:
1343 raise error.Abort('cannot select revision when creating marker')
1340 raise error.Abort('cannot select revision when creating marker')
1344 metadata = {}
1341 metadata = {}
1345 metadata['user'] = opts['user'] or ui.username()
1342 metadata['user'] = opts['user'] or ui.username()
1346 succs = tuple(parsenodeid(succ) for succ in successors)
1343 succs = tuple(parsenodeid(succ) for succ in successors)
1347 l = repo.lock()
1344 l = repo.lock()
1348 try:
1345 try:
1349 tr = repo.transaction('debugobsolete')
1346 tr = repo.transaction('debugobsolete')
1350 try:
1347 try:
1351 date = opts.get('date')
1348 date = opts.get('date')
1352 if date:
1349 if date:
1353 date = util.parsedate(date)
1350 date = util.parsedate(date)
1354 else:
1351 else:
1355 date = None
1352 date = None
1356 prec = parsenodeid(precursor)
1353 prec = parsenodeid(precursor)
1357 parents = None
1354 parents = None
1358 if opts['record_parents']:
1355 if opts['record_parents']:
1359 if prec not in repo.unfiltered():
1356 if prec not in repo.unfiltered():
1360 raise error.Abort('cannot used --record-parents on '
1357 raise error.Abort('cannot used --record-parents on '
1361 'unknown changesets')
1358 'unknown changesets')
1362 parents = repo.unfiltered()[prec].parents()
1359 parents = repo.unfiltered()[prec].parents()
1363 parents = tuple(p.node() for p in parents)
1360 parents = tuple(p.node() for p in parents)
1364 repo.obsstore.create(tr, prec, succs, opts['flags'],
1361 repo.obsstore.create(tr, prec, succs, opts['flags'],
1365 parents=parents, date=date,
1362 parents=parents, date=date,
1366 metadata=metadata)
1363 metadata=metadata)
1367 tr.close()
1364 tr.close()
1368 except ValueError as exc:
1365 except ValueError as exc:
1369 raise error.Abort(_('bad obsmarker input: %s') % exc)
1366 raise error.Abort(_('bad obsmarker input: %s') % exc)
1370 finally:
1367 finally:
1371 tr.release()
1368 tr.release()
1372 finally:
1369 finally:
1373 l.release()
1370 l.release()
1374 else:
1371 else:
1375 if opts['rev']:
1372 if opts['rev']:
1376 revs = scmutil.revrange(repo, opts['rev'])
1373 revs = scmutil.revrange(repo, opts['rev'])
1377 nodes = [repo[r].node() for r in revs]
1374 nodes = [repo[r].node() for r in revs]
1378 markers = list(obsolete.getmarkers(repo, nodes=nodes))
1375 markers = list(obsolete.getmarkers(repo, nodes=nodes))
1379 markers.sort(key=lambda x: x._data)
1376 markers.sort(key=lambda x: x._data)
1380 else:
1377 else:
1381 markers = obsolete.getmarkers(repo)
1378 markers = obsolete.getmarkers(repo)
1382
1379
1383 markerstoiter = markers
1380 markerstoiter = markers
1384 isrelevant = lambda m: True
1381 isrelevant = lambda m: True
1385 if opts.get('rev') and opts.get('index'):
1382 if opts.get('rev') and opts.get('index'):
1386 markerstoiter = obsolete.getmarkers(repo)
1383 markerstoiter = obsolete.getmarkers(repo)
1387 markerset = set(markers)
1384 markerset = set(markers)
1388 isrelevant = lambda m: m in markerset
1385 isrelevant = lambda m: m in markerset
1389
1386
1390 fm = ui.formatter('debugobsolete', opts)
1387 fm = ui.formatter('debugobsolete', opts)
1391 for i, m in enumerate(markerstoiter):
1388 for i, m in enumerate(markerstoiter):
1392 if not isrelevant(m):
1389 if not isrelevant(m):
1393 # marker can be irrelevant when we're iterating over a set
1390 # marker can be irrelevant when we're iterating over a set
1394 # of markers (markerstoiter) which is bigger than the set
1391 # of markers (markerstoiter) which is bigger than the set
1395 # of markers we want to display (markers)
1392 # of markers we want to display (markers)
1396 # this can happen if both --index and --rev options are
1393 # this can happen if both --index and --rev options are
1397 # provided and thus we need to iterate over all of the markers
1394 # provided and thus we need to iterate over all of the markers
1398 # to get the correct indices, but only display the ones that
1395 # to get the correct indices, but only display the ones that
1399 # are relevant to --rev value
1396 # are relevant to --rev value
1400 continue
1397 continue
1401 fm.startitem()
1398 fm.startitem()
1402 ind = i if opts.get('index') else None
1399 ind = i if opts.get('index') else None
1403 cmdutil.showmarker(fm, m, index=ind)
1400 cmdutil.showmarker(fm, m, index=ind)
1404 fm.end()
1401 fm.end()
1405
1402
1406 @command('debugpathcomplete',
1403 @command('debugpathcomplete',
1407 [('f', 'full', None, _('complete an entire path')),
1404 [('f', 'full', None, _('complete an entire path')),
1408 ('n', 'normal', None, _('show only normal files')),
1405 ('n', 'normal', None, _('show only normal files')),
1409 ('a', 'added', None, _('show only added files')),
1406 ('a', 'added', None, _('show only added files')),
1410 ('r', 'removed', None, _('show only removed files'))],
1407 ('r', 'removed', None, _('show only removed files'))],
1411 _('FILESPEC...'))
1408 _('FILESPEC...'))
1412 def debugpathcomplete(ui, repo, *specs, **opts):
1409 def debugpathcomplete(ui, repo, *specs, **opts):
1413 '''complete part or all of a tracked path
1410 '''complete part or all of a tracked path
1414
1411
1415 This command supports shells that offer path name completion. It
1412 This command supports shells that offer path name completion. It
1416 currently completes only files already known to the dirstate.
1413 currently completes only files already known to the dirstate.
1417
1414
1418 Completion extends only to the next path segment unless
1415 Completion extends only to the next path segment unless
1419 --full is specified, in which case entire paths are used.'''
1416 --full is specified, in which case entire paths are used.'''
1420
1417
1421 def complete(path, acceptable):
1418 def complete(path, acceptable):
1422 dirstate = repo.dirstate
1419 dirstate = repo.dirstate
1423 spec = os.path.normpath(os.path.join(pycompat.getcwd(), path))
1420 spec = os.path.normpath(os.path.join(pycompat.getcwd(), path))
1424 rootdir = repo.root + pycompat.ossep
1421 rootdir = repo.root + pycompat.ossep
1425 if spec != repo.root and not spec.startswith(rootdir):
1422 if spec != repo.root and not spec.startswith(rootdir):
1426 return [], []
1423 return [], []
1427 if os.path.isdir(spec):
1424 if os.path.isdir(spec):
1428 spec += '/'
1425 spec += '/'
1429 spec = spec[len(rootdir):]
1426 spec = spec[len(rootdir):]
1430 fixpaths = pycompat.ossep != '/'
1427 fixpaths = pycompat.ossep != '/'
1431 if fixpaths:
1428 if fixpaths:
1432 spec = spec.replace(pycompat.ossep, '/')
1429 spec = spec.replace(pycompat.ossep, '/')
1433 speclen = len(spec)
1430 speclen = len(spec)
1434 fullpaths = opts['full']
1431 fullpaths = opts['full']
1435 files, dirs = set(), set()
1432 files, dirs = set(), set()
1436 adddir, addfile = dirs.add, files.add
1433 adddir, addfile = dirs.add, files.add
1437 for f, st in dirstate.iteritems():
1434 for f, st in dirstate.iteritems():
1438 if f.startswith(spec) and st[0] in acceptable:
1435 if f.startswith(spec) and st[0] in acceptable:
1439 if fixpaths:
1436 if fixpaths:
1440 f = f.replace('/', pycompat.ossep)
1437 f = f.replace('/', pycompat.ossep)
1441 if fullpaths:
1438 if fullpaths:
1442 addfile(f)
1439 addfile(f)
1443 continue
1440 continue
1444 s = f.find(pycompat.ossep, speclen)
1441 s = f.find(pycompat.ossep, speclen)
1445 if s >= 0:
1442 if s >= 0:
1446 adddir(f[:s])
1443 adddir(f[:s])
1447 else:
1444 else:
1448 addfile(f)
1445 addfile(f)
1449 return files, dirs
1446 return files, dirs
1450
1447
1451 acceptable = ''
1448 acceptable = ''
1452 if opts['normal']:
1449 if opts['normal']:
1453 acceptable += 'nm'
1450 acceptable += 'nm'
1454 if opts['added']:
1451 if opts['added']:
1455 acceptable += 'a'
1452 acceptable += 'a'
1456 if opts['removed']:
1453 if opts['removed']:
1457 acceptable += 'r'
1454 acceptable += 'r'
1458 cwd = repo.getcwd()
1455 cwd = repo.getcwd()
1459 if not specs:
1456 if not specs:
1460 specs = ['.']
1457 specs = ['.']
1461
1458
1462 files, dirs = set(), set()
1459 files, dirs = set(), set()
1463 for spec in specs:
1460 for spec in specs:
1464 f, d = complete(spec, acceptable or 'nmar')
1461 f, d = complete(spec, acceptable or 'nmar')
1465 files.update(f)
1462 files.update(f)
1466 dirs.update(d)
1463 dirs.update(d)
1467 files.update(dirs)
1464 files.update(dirs)
1468 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
1465 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
1469 ui.write('\n')
1466 ui.write('\n')
1470
1467
1471 @command('debugpickmergetool',
1468 @command('debugpickmergetool',
1472 [('r', 'rev', '', _('check for files in this revision'), _('REV')),
1469 [('r', 'rev', '', _('check for files in this revision'), _('REV')),
1473 ('', 'changedelete', None, _('emulate merging change and delete')),
1470 ('', 'changedelete', None, _('emulate merging change and delete')),
1474 ] + cmdutil.walkopts + cmdutil.mergetoolopts,
1471 ] + cmdutil.walkopts + cmdutil.mergetoolopts,
1475 _('[PATTERN]...'),
1472 _('[PATTERN]...'),
1476 inferrepo=True)
1473 inferrepo=True)
1477 def debugpickmergetool(ui, repo, *pats, **opts):
1474 def debugpickmergetool(ui, repo, *pats, **opts):
1478 """examine which merge tool is chosen for specified file
1475 """examine which merge tool is chosen for specified file
1479
1476
1480 As described in :hg:`help merge-tools`, Mercurial examines
1477 As described in :hg:`help merge-tools`, Mercurial examines
1481 configurations below in this order to decide which merge tool is
1478 configurations below in this order to decide which merge tool is
1482 chosen for specified file.
1479 chosen for specified file.
1483
1480
1484 1. ``--tool`` option
1481 1. ``--tool`` option
1485 2. ``HGMERGE`` environment variable
1482 2. ``HGMERGE`` environment variable
1486 3. configurations in ``merge-patterns`` section
1483 3. configurations in ``merge-patterns`` section
1487 4. configuration of ``ui.merge``
1484 4. configuration of ``ui.merge``
1488 5. configurations in ``merge-tools`` section
1485 5. configurations in ``merge-tools`` section
1489 6. ``hgmerge`` tool (for historical reason only)
1486 6. ``hgmerge`` tool (for historical reason only)
1490 7. default tool for fallback (``:merge`` or ``:prompt``)
1487 7. default tool for fallback (``:merge`` or ``:prompt``)
1491
1488
1492 This command writes out examination result in the style below::
1489 This command writes out examination result in the style below::
1493
1490
1494 FILE = MERGETOOL
1491 FILE = MERGETOOL
1495
1492
1496 By default, all files known in the first parent context of the
1493 By default, all files known in the first parent context of the
1497 working directory are examined. Use file patterns and/or -I/-X
1494 working directory are examined. Use file patterns and/or -I/-X
1498 options to limit target files. -r/--rev is also useful to examine
1495 options to limit target files. -r/--rev is also useful to examine
1499 files in another context without actual updating to it.
1496 files in another context without actual updating to it.
1500
1497
1501 With --debug, this command shows warning messages while matching
1498 With --debug, this command shows warning messages while matching
1502 against ``merge-patterns`` and so on, too. It is recommended to
1499 against ``merge-patterns`` and so on, too. It is recommended to
1503 use this option with explicit file patterns and/or -I/-X options,
1500 use this option with explicit file patterns and/or -I/-X options,
1504 because this option increases amount of output per file according
1501 because this option increases amount of output per file according
1505 to configurations in hgrc.
1502 to configurations in hgrc.
1506
1503
1507 With -v/--verbose, this command shows configurations below at
1504 With -v/--verbose, this command shows configurations below at
1508 first (only if specified).
1505 first (only if specified).
1509
1506
1510 - ``--tool`` option
1507 - ``--tool`` option
1511 - ``HGMERGE`` environment variable
1508 - ``HGMERGE`` environment variable
1512 - configuration of ``ui.merge``
1509 - configuration of ``ui.merge``
1513
1510
1514 If merge tool is chosen before matching against
1511 If merge tool is chosen before matching against
1515 ``merge-patterns``, this command can't show any helpful
1512 ``merge-patterns``, this command can't show any helpful
1516 information, even with --debug. In such case, information above is
1513 information, even with --debug. In such case, information above is
1517 useful to know why a merge tool is chosen.
1514 useful to know why a merge tool is chosen.
1518 """
1515 """
1519 overrides = {}
1516 overrides = {}
1520 if opts['tool']:
1517 if opts['tool']:
1521 overrides[('ui', 'forcemerge')] = opts['tool']
1518 overrides[('ui', 'forcemerge')] = opts['tool']
1522 ui.note(('with --tool %r\n') % (opts['tool']))
1519 ui.note(('with --tool %r\n') % (opts['tool']))
1523
1520
1524 with ui.configoverride(overrides, 'debugmergepatterns'):
1521 with ui.configoverride(overrides, 'debugmergepatterns'):
1525 hgmerge = encoding.environ.get("HGMERGE")
1522 hgmerge = encoding.environ.get("HGMERGE")
1526 if hgmerge is not None:
1523 if hgmerge is not None:
1527 ui.note(('with HGMERGE=%r\n') % (hgmerge))
1524 ui.note(('with HGMERGE=%r\n') % (hgmerge))
1528 uimerge = ui.config("ui", "merge")
1525 uimerge = ui.config("ui", "merge")
1529 if uimerge:
1526 if uimerge:
1530 ui.note(('with ui.merge=%r\n') % (uimerge))
1527 ui.note(('with ui.merge=%r\n') % (uimerge))
1531
1528
1532 ctx = scmutil.revsingle(repo, opts.get('rev'))
1529 ctx = scmutil.revsingle(repo, opts.get('rev'))
1533 m = scmutil.match(ctx, pats, opts)
1530 m = scmutil.match(ctx, pats, opts)
1534 changedelete = opts['changedelete']
1531 changedelete = opts['changedelete']
1535 for path in ctx.walk(m):
1532 for path in ctx.walk(m):
1536 fctx = ctx[path]
1533 fctx = ctx[path]
1537 try:
1534 try:
1538 if not ui.debugflag:
1535 if not ui.debugflag:
1539 ui.pushbuffer(error=True)
1536 ui.pushbuffer(error=True)
1540 tool, toolpath = filemerge._picktool(repo, ui, path,
1537 tool, toolpath = filemerge._picktool(repo, ui, path,
1541 fctx.isbinary(),
1538 fctx.isbinary(),
1542 'l' in fctx.flags(),
1539 'l' in fctx.flags(),
1543 changedelete)
1540 changedelete)
1544 finally:
1541 finally:
1545 if not ui.debugflag:
1542 if not ui.debugflag:
1546 ui.popbuffer()
1543 ui.popbuffer()
1547 ui.write(('%s = %s\n') % (path, tool))
1544 ui.write(('%s = %s\n') % (path, tool))
1548
1545
1549 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
1546 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
1550 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1547 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1551 '''access the pushkey key/value protocol
1548 '''access the pushkey key/value protocol
1552
1549
1553 With two args, list the keys in the given namespace.
1550 With two args, list the keys in the given namespace.
1554
1551
1555 With five args, set a key to new if it currently is set to old.
1552 With five args, set a key to new if it currently is set to old.
1556 Reports success or failure.
1553 Reports success or failure.
1557 '''
1554 '''
1558
1555
1559 target = hg.peer(ui, {}, repopath)
1556 target = hg.peer(ui, {}, repopath)
1560 if keyinfo:
1557 if keyinfo:
1561 key, old, new = keyinfo
1558 key, old, new = keyinfo
1562 r = target.pushkey(namespace, key, old, new)
1559 r = target.pushkey(namespace, key, old, new)
1563 ui.status(str(r) + '\n')
1560 ui.status(str(r) + '\n')
1564 return not r
1561 return not r
1565 else:
1562 else:
1566 for k, v in sorted(target.listkeys(namespace).iteritems()):
1563 for k, v in sorted(target.listkeys(namespace).iteritems()):
1567 ui.write("%s\t%s\n" % (util.escapestr(k),
1564 ui.write("%s\t%s\n" % (util.escapestr(k),
1568 util.escapestr(v)))
1565 util.escapestr(v)))
1569
1566
1570 @command('debugpvec', [], _('A B'))
1567 @command('debugpvec', [], _('A B'))
1571 def debugpvec(ui, repo, a, b=None):
1568 def debugpvec(ui, repo, a, b=None):
1572 ca = scmutil.revsingle(repo, a)
1569 ca = scmutil.revsingle(repo, a)
1573 cb = scmutil.revsingle(repo, b)
1570 cb = scmutil.revsingle(repo, b)
1574 pa = pvec.ctxpvec(ca)
1571 pa = pvec.ctxpvec(ca)
1575 pb = pvec.ctxpvec(cb)
1572 pb = pvec.ctxpvec(cb)
1576 if pa == pb:
1573 if pa == pb:
1577 rel = "="
1574 rel = "="
1578 elif pa > pb:
1575 elif pa > pb:
1579 rel = ">"
1576 rel = ">"
1580 elif pa < pb:
1577 elif pa < pb:
1581 rel = "<"
1578 rel = "<"
1582 elif pa | pb:
1579 elif pa | pb:
1583 rel = "|"
1580 rel = "|"
1584 ui.write(_("a: %s\n") % pa)
1581 ui.write(_("a: %s\n") % pa)
1585 ui.write(_("b: %s\n") % pb)
1582 ui.write(_("b: %s\n") % pb)
1586 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
1583 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
1587 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
1584 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
1588 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
1585 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
1589 pa.distance(pb), rel))
1586 pa.distance(pb), rel))
1590
1587
1591 @command('debugrebuilddirstate|debugrebuildstate',
1588 @command('debugrebuilddirstate|debugrebuildstate',
1592 [('r', 'rev', '', _('revision to rebuild to'), _('REV')),
1589 [('r', 'rev', '', _('revision to rebuild to'), _('REV')),
1593 ('', 'minimal', None, _('only rebuild files that are inconsistent with '
1590 ('', 'minimal', None, _('only rebuild files that are inconsistent with '
1594 'the working copy parent')),
1591 'the working copy parent')),
1595 ],
1592 ],
1596 _('[-r REV]'))
1593 _('[-r REV]'))
1597 def debugrebuilddirstate(ui, repo, rev, **opts):
1594 def debugrebuilddirstate(ui, repo, rev, **opts):
1598 """rebuild the dirstate as it would look like for the given revision
1595 """rebuild the dirstate as it would look like for the given revision
1599
1596
1600 If no revision is specified the first current parent will be used.
1597 If no revision is specified the first current parent will be used.
1601
1598
1602 The dirstate will be set to the files of the given revision.
1599 The dirstate will be set to the files of the given revision.
1603 The actual working directory content or existing dirstate
1600 The actual working directory content or existing dirstate
1604 information such as adds or removes is not considered.
1601 information such as adds or removes is not considered.
1605
1602
1606 ``minimal`` will only rebuild the dirstate status for files that claim to be
1603 ``minimal`` will only rebuild the dirstate status for files that claim to be
1607 tracked but are not in the parent manifest, or that exist in the parent
1604 tracked but are not in the parent manifest, or that exist in the parent
1608 manifest but are not in the dirstate. It will not change adds, removes, or
1605 manifest but are not in the dirstate. It will not change adds, removes, or
1609 modified files that are in the working copy parent.
1606 modified files that are in the working copy parent.
1610
1607
1611 One use of this command is to make the next :hg:`status` invocation
1608 One use of this command is to make the next :hg:`status` invocation
1612 check the actual file content.
1609 check the actual file content.
1613 """
1610 """
1614 ctx = scmutil.revsingle(repo, rev)
1611 ctx = scmutil.revsingle(repo, rev)
1615 with repo.wlock():
1612 with repo.wlock():
1616 dirstate = repo.dirstate
1613 dirstate = repo.dirstate
1617 changedfiles = None
1614 changedfiles = None
1618 # See command doc for what minimal does.
1615 # See command doc for what minimal does.
1619 if opts.get('minimal'):
1616 if opts.get('minimal'):
1620 manifestfiles = set(ctx.manifest().keys())
1617 manifestfiles = set(ctx.manifest().keys())
1621 dirstatefiles = set(dirstate)
1618 dirstatefiles = set(dirstate)
1622 manifestonly = manifestfiles - dirstatefiles
1619 manifestonly = manifestfiles - dirstatefiles
1623 dsonly = dirstatefiles - manifestfiles
1620 dsonly = dirstatefiles - manifestfiles
1624 dsnotadded = set(f for f in dsonly if dirstate[f] != 'a')
1621 dsnotadded = set(f for f in dsonly if dirstate[f] != 'a')
1625 changedfiles = manifestonly | dsnotadded
1622 changedfiles = manifestonly | dsnotadded
1626
1623
1627 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
1624 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
1628
1625
1629 @command('debugrebuildfncache', [], '')
1626 @command('debugrebuildfncache', [], '')
1630 def debugrebuildfncache(ui, repo):
1627 def debugrebuildfncache(ui, repo):
1631 """rebuild the fncache file"""
1628 """rebuild the fncache file"""
1632 repair.rebuildfncache(ui, repo)
1629 repair.rebuildfncache(ui, repo)
1633
1630
1634 @command('debugrename',
1631 @command('debugrename',
1635 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1632 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1636 _('[-r REV] FILE'))
1633 _('[-r REV] FILE'))
1637 def debugrename(ui, repo, file1, *pats, **opts):
1634 def debugrename(ui, repo, file1, *pats, **opts):
1638 """dump rename information"""
1635 """dump rename information"""
1639
1636
1640 ctx = scmutil.revsingle(repo, opts.get('rev'))
1637 ctx = scmutil.revsingle(repo, opts.get('rev'))
1641 m = scmutil.match(ctx, (file1,) + pats, opts)
1638 m = scmutil.match(ctx, (file1,) + pats, opts)
1642 for abs in ctx.walk(m):
1639 for abs in ctx.walk(m):
1643 fctx = ctx[abs]
1640 fctx = ctx[abs]
1644 o = fctx.filelog().renamed(fctx.filenode())
1641 o = fctx.filelog().renamed(fctx.filenode())
1645 rel = m.rel(abs)
1642 rel = m.rel(abs)
1646 if o:
1643 if o:
1647 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1644 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1648 else:
1645 else:
1649 ui.write(_("%s not renamed\n") % rel)
1646 ui.write(_("%s not renamed\n") % rel)
1650
1647
1651 @command('debugrevlog', cmdutil.debugrevlogopts +
1648 @command('debugrevlog', cmdutil.debugrevlogopts +
1652 [('d', 'dump', False, _('dump index data'))],
1649 [('d', 'dump', False, _('dump index data'))],
1653 _('-c|-m|FILE'),
1650 _('-c|-m|FILE'),
1654 optionalrepo=True)
1651 optionalrepo=True)
1655 def debugrevlog(ui, repo, file_=None, **opts):
1652 def debugrevlog(ui, repo, file_=None, **opts):
1656 """show data and statistics about a revlog"""
1653 """show data and statistics about a revlog"""
1657 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
1654 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
1658
1655
1659 if opts.get("dump"):
1656 if opts.get("dump"):
1660 numrevs = len(r)
1657 numrevs = len(r)
1661 ui.write(("# rev p1rev p2rev start end deltastart base p1 p2"
1658 ui.write(("# rev p1rev p2rev start end deltastart base p1 p2"
1662 " rawsize totalsize compression heads chainlen\n"))
1659 " rawsize totalsize compression heads chainlen\n"))
1663 ts = 0
1660 ts = 0
1664 heads = set()
1661 heads = set()
1665
1662
1666 for rev in xrange(numrevs):
1663 for rev in xrange(numrevs):
1667 dbase = r.deltaparent(rev)
1664 dbase = r.deltaparent(rev)
1668 if dbase == -1:
1665 if dbase == -1:
1669 dbase = rev
1666 dbase = rev
1670 cbase = r.chainbase(rev)
1667 cbase = r.chainbase(rev)
1671 clen = r.chainlen(rev)
1668 clen = r.chainlen(rev)
1672 p1, p2 = r.parentrevs(rev)
1669 p1, p2 = r.parentrevs(rev)
1673 rs = r.rawsize(rev)
1670 rs = r.rawsize(rev)
1674 ts = ts + rs
1671 ts = ts + rs
1675 heads -= set(r.parentrevs(rev))
1672 heads -= set(r.parentrevs(rev))
1676 heads.add(rev)
1673 heads.add(rev)
1677 try:
1674 try:
1678 compression = ts / r.end(rev)
1675 compression = ts / r.end(rev)
1679 except ZeroDivisionError:
1676 except ZeroDivisionError:
1680 compression = 0
1677 compression = 0
1681 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
1678 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
1682 "%11d %5d %8d\n" %
1679 "%11d %5d %8d\n" %
1683 (rev, p1, p2, r.start(rev), r.end(rev),
1680 (rev, p1, p2, r.start(rev), r.end(rev),
1684 r.start(dbase), r.start(cbase),
1681 r.start(dbase), r.start(cbase),
1685 r.start(p1), r.start(p2),
1682 r.start(p1), r.start(p2),
1686 rs, ts, compression, len(heads), clen))
1683 rs, ts, compression, len(heads), clen))
1687 return 0
1684 return 0
1688
1685
1689 v = r.version
1686 v = r.version
1690 format = v & 0xFFFF
1687 format = v & 0xFFFF
1691 flags = []
1688 flags = []
1692 gdelta = False
1689 gdelta = False
1693 if v & revlog.FLAG_INLINE_DATA:
1690 if v & revlog.FLAG_INLINE_DATA:
1694 flags.append('inline')
1691 flags.append('inline')
1695 if v & revlog.FLAG_GENERALDELTA:
1692 if v & revlog.FLAG_GENERALDELTA:
1696 gdelta = True
1693 gdelta = True
1697 flags.append('generaldelta')
1694 flags.append('generaldelta')
1698 if not flags:
1695 if not flags:
1699 flags = ['(none)']
1696 flags = ['(none)']
1700
1697
1701 nummerges = 0
1698 nummerges = 0
1702 numfull = 0
1699 numfull = 0
1703 numprev = 0
1700 numprev = 0
1704 nump1 = 0
1701 nump1 = 0
1705 nump2 = 0
1702 nump2 = 0
1706 numother = 0
1703 numother = 0
1707 nump1prev = 0
1704 nump1prev = 0
1708 nump2prev = 0
1705 nump2prev = 0
1709 chainlengths = []
1706 chainlengths = []
1710
1707
1711 datasize = [None, 0, 0]
1708 datasize = [None, 0, 0]
1712 fullsize = [None, 0, 0]
1709 fullsize = [None, 0, 0]
1713 deltasize = [None, 0, 0]
1710 deltasize = [None, 0, 0]
1714 chunktypecounts = {}
1711 chunktypecounts = {}
1715 chunktypesizes = {}
1712 chunktypesizes = {}
1716
1713
1717 def addsize(size, l):
1714 def addsize(size, l):
1718 if l[0] is None or size < l[0]:
1715 if l[0] is None or size < l[0]:
1719 l[0] = size
1716 l[0] = size
1720 if size > l[1]:
1717 if size > l[1]:
1721 l[1] = size
1718 l[1] = size
1722 l[2] += size
1719 l[2] += size
1723
1720
1724 numrevs = len(r)
1721 numrevs = len(r)
1725 for rev in xrange(numrevs):
1722 for rev in xrange(numrevs):
1726 p1, p2 = r.parentrevs(rev)
1723 p1, p2 = r.parentrevs(rev)
1727 delta = r.deltaparent(rev)
1724 delta = r.deltaparent(rev)
1728 if format > 0:
1725 if format > 0:
1729 addsize(r.rawsize(rev), datasize)
1726 addsize(r.rawsize(rev), datasize)
1730 if p2 != nullrev:
1727 if p2 != nullrev:
1731 nummerges += 1
1728 nummerges += 1
1732 size = r.length(rev)
1729 size = r.length(rev)
1733 if delta == nullrev:
1730 if delta == nullrev:
1734 chainlengths.append(0)
1731 chainlengths.append(0)
1735 numfull += 1
1732 numfull += 1
1736 addsize(size, fullsize)
1733 addsize(size, fullsize)
1737 else:
1734 else:
1738 chainlengths.append(chainlengths[delta] + 1)
1735 chainlengths.append(chainlengths[delta] + 1)
1739 addsize(size, deltasize)
1736 addsize(size, deltasize)
1740 if delta == rev - 1:
1737 if delta == rev - 1:
1741 numprev += 1
1738 numprev += 1
1742 if delta == p1:
1739 if delta == p1:
1743 nump1prev += 1
1740 nump1prev += 1
1744 elif delta == p2:
1741 elif delta == p2:
1745 nump2prev += 1
1742 nump2prev += 1
1746 elif delta == p1:
1743 elif delta == p1:
1747 nump1 += 1
1744 nump1 += 1
1748 elif delta == p2:
1745 elif delta == p2:
1749 nump2 += 1
1746 nump2 += 1
1750 elif delta != nullrev:
1747 elif delta != nullrev:
1751 numother += 1
1748 numother += 1
1752
1749
1753 # Obtain data on the raw chunks in the revlog.
1750 # Obtain data on the raw chunks in the revlog.
1754 segment = r._getsegmentforrevs(rev, rev)[1]
1751 segment = r._getsegmentforrevs(rev, rev)[1]
1755 if segment:
1752 if segment:
1756 chunktype = segment[0]
1753 chunktype = segment[0]
1757 else:
1754 else:
1758 chunktype = 'empty'
1755 chunktype = 'empty'
1759
1756
1760 if chunktype not in chunktypecounts:
1757 if chunktype not in chunktypecounts:
1761 chunktypecounts[chunktype] = 0
1758 chunktypecounts[chunktype] = 0
1762 chunktypesizes[chunktype] = 0
1759 chunktypesizes[chunktype] = 0
1763
1760
1764 chunktypecounts[chunktype] += 1
1761 chunktypecounts[chunktype] += 1
1765 chunktypesizes[chunktype] += size
1762 chunktypesizes[chunktype] += size
1766
1763
1767 # Adjust size min value for empty cases
1764 # Adjust size min value for empty cases
1768 for size in (datasize, fullsize, deltasize):
1765 for size in (datasize, fullsize, deltasize):
1769 if size[0] is None:
1766 if size[0] is None:
1770 size[0] = 0
1767 size[0] = 0
1771
1768
1772 numdeltas = numrevs - numfull
1769 numdeltas = numrevs - numfull
1773 numoprev = numprev - nump1prev - nump2prev
1770 numoprev = numprev - nump1prev - nump2prev
1774 totalrawsize = datasize[2]
1771 totalrawsize = datasize[2]
1775 datasize[2] /= numrevs
1772 datasize[2] /= numrevs
1776 fulltotal = fullsize[2]
1773 fulltotal = fullsize[2]
1777 fullsize[2] /= numfull
1774 fullsize[2] /= numfull
1778 deltatotal = deltasize[2]
1775 deltatotal = deltasize[2]
1779 if numrevs - numfull > 0:
1776 if numrevs - numfull > 0:
1780 deltasize[2] /= numrevs - numfull
1777 deltasize[2] /= numrevs - numfull
1781 totalsize = fulltotal + deltatotal
1778 totalsize = fulltotal + deltatotal
1782 avgchainlen = sum(chainlengths) / numrevs
1779 avgchainlen = sum(chainlengths) / numrevs
1783 maxchainlen = max(chainlengths)
1780 maxchainlen = max(chainlengths)
1784 compratio = 1
1781 compratio = 1
1785 if totalsize:
1782 if totalsize:
1786 compratio = totalrawsize / totalsize
1783 compratio = totalrawsize / totalsize
1787
1784
1788 basedfmtstr = '%%%dd\n'
1785 basedfmtstr = '%%%dd\n'
1789 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
1786 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
1790
1787
1791 def dfmtstr(max):
1788 def dfmtstr(max):
1792 return basedfmtstr % len(str(max))
1789 return basedfmtstr % len(str(max))
1793 def pcfmtstr(max, padding=0):
1790 def pcfmtstr(max, padding=0):
1794 return basepcfmtstr % (len(str(max)), ' ' * padding)
1791 return basepcfmtstr % (len(str(max)), ' ' * padding)
1795
1792
1796 def pcfmt(value, total):
1793 def pcfmt(value, total):
1797 if total:
1794 if total:
1798 return (value, 100 * float(value) / total)
1795 return (value, 100 * float(value) / total)
1799 else:
1796 else:
1800 return value, 100.0
1797 return value, 100.0
1801
1798
1802 ui.write(('format : %d\n') % format)
1799 ui.write(('format : %d\n') % format)
1803 ui.write(('flags : %s\n') % ', '.join(flags))
1800 ui.write(('flags : %s\n') % ', '.join(flags))
1804
1801
1805 ui.write('\n')
1802 ui.write('\n')
1806 fmt = pcfmtstr(totalsize)
1803 fmt = pcfmtstr(totalsize)
1807 fmt2 = dfmtstr(totalsize)
1804 fmt2 = dfmtstr(totalsize)
1808 ui.write(('revisions : ') + fmt2 % numrevs)
1805 ui.write(('revisions : ') + fmt2 % numrevs)
1809 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
1806 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
1810 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
1807 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
1811 ui.write(('revisions : ') + fmt2 % numrevs)
1808 ui.write(('revisions : ') + fmt2 % numrevs)
1812 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
1809 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
1813 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
1810 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
1814 ui.write(('revision size : ') + fmt2 % totalsize)
1811 ui.write(('revision size : ') + fmt2 % totalsize)
1815 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
1812 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
1816 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
1813 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
1817
1814
1818 def fmtchunktype(chunktype):
1815 def fmtchunktype(chunktype):
1819 if chunktype == 'empty':
1816 if chunktype == 'empty':
1820 return ' %s : ' % chunktype
1817 return ' %s : ' % chunktype
1821 elif chunktype in string.ascii_letters:
1818 elif chunktype in string.ascii_letters:
1822 return ' 0x%s (%s) : ' % (hex(chunktype), chunktype)
1819 return ' 0x%s (%s) : ' % (hex(chunktype), chunktype)
1823 else:
1820 else:
1824 return ' 0x%s : ' % hex(chunktype)
1821 return ' 0x%s : ' % hex(chunktype)
1825
1822
1826 ui.write('\n')
1823 ui.write('\n')
1827 ui.write(('chunks : ') + fmt2 % numrevs)
1824 ui.write(('chunks : ') + fmt2 % numrevs)
1828 for chunktype in sorted(chunktypecounts):
1825 for chunktype in sorted(chunktypecounts):
1829 ui.write(fmtchunktype(chunktype))
1826 ui.write(fmtchunktype(chunktype))
1830 ui.write(fmt % pcfmt(chunktypecounts[chunktype], numrevs))
1827 ui.write(fmt % pcfmt(chunktypecounts[chunktype], numrevs))
1831 ui.write(('chunks size : ') + fmt2 % totalsize)
1828 ui.write(('chunks size : ') + fmt2 % totalsize)
1832 for chunktype in sorted(chunktypecounts):
1829 for chunktype in sorted(chunktypecounts):
1833 ui.write(fmtchunktype(chunktype))
1830 ui.write(fmtchunktype(chunktype))
1834 ui.write(fmt % pcfmt(chunktypesizes[chunktype], totalsize))
1831 ui.write(fmt % pcfmt(chunktypesizes[chunktype], totalsize))
1835
1832
1836 ui.write('\n')
1833 ui.write('\n')
1837 fmt = dfmtstr(max(avgchainlen, compratio))
1834 fmt = dfmtstr(max(avgchainlen, compratio))
1838 ui.write(('avg chain length : ') + fmt % avgchainlen)
1835 ui.write(('avg chain length : ') + fmt % avgchainlen)
1839 ui.write(('max chain length : ') + fmt % maxchainlen)
1836 ui.write(('max chain length : ') + fmt % maxchainlen)
1840 ui.write(('compression ratio : ') + fmt % compratio)
1837 ui.write(('compression ratio : ') + fmt % compratio)
1841
1838
1842 if format > 0:
1839 if format > 0:
1843 ui.write('\n')
1840 ui.write('\n')
1844 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
1841 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
1845 % tuple(datasize))
1842 % tuple(datasize))
1846 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
1843 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
1847 % tuple(fullsize))
1844 % tuple(fullsize))
1848 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
1845 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
1849 % tuple(deltasize))
1846 % tuple(deltasize))
1850
1847
1851 if numdeltas > 0:
1848 if numdeltas > 0:
1852 ui.write('\n')
1849 ui.write('\n')
1853 fmt = pcfmtstr(numdeltas)
1850 fmt = pcfmtstr(numdeltas)
1854 fmt2 = pcfmtstr(numdeltas, 4)
1851 fmt2 = pcfmtstr(numdeltas, 4)
1855 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
1852 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
1856 if numprev > 0:
1853 if numprev > 0:
1857 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
1854 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
1858 numprev))
1855 numprev))
1859 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
1856 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
1860 numprev))
1857 numprev))
1861 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
1858 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
1862 numprev))
1859 numprev))
1863 if gdelta:
1860 if gdelta:
1864 ui.write(('deltas against p1 : ')
1861 ui.write(('deltas against p1 : ')
1865 + fmt % pcfmt(nump1, numdeltas))
1862 + fmt % pcfmt(nump1, numdeltas))
1866 ui.write(('deltas against p2 : ')
1863 ui.write(('deltas against p2 : ')
1867 + fmt % pcfmt(nump2, numdeltas))
1864 + fmt % pcfmt(nump2, numdeltas))
1868 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
1865 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
1869 numdeltas))
1866 numdeltas))
1870
1867
1871 @command('debugrevspec',
1868 @command('debugrevspec',
1872 [('', 'optimize', None,
1869 [('', 'optimize', None,
1873 _('print parsed tree after optimizing (DEPRECATED)')),
1870 _('print parsed tree after optimizing (DEPRECATED)')),
1874 ('p', 'show-stage', [],
1871 ('p', 'show-stage', [],
1875 _('print parsed tree at the given stage'), _('NAME')),
1872 _('print parsed tree at the given stage'), _('NAME')),
1876 ('', 'no-optimized', False, _('evaluate tree without optimization')),
1873 ('', 'no-optimized', False, _('evaluate tree without optimization')),
1877 ('', 'verify-optimized', False, _('verify optimized result')),
1874 ('', 'verify-optimized', False, _('verify optimized result')),
1878 ],
1875 ],
1879 ('REVSPEC'))
1876 ('REVSPEC'))
1880 def debugrevspec(ui, repo, expr, **opts):
1877 def debugrevspec(ui, repo, expr, **opts):
1881 """parse and apply a revision specification
1878 """parse and apply a revision specification
1882
1879
1883 Use -p/--show-stage option to print the parsed tree at the given stages.
1880 Use -p/--show-stage option to print the parsed tree at the given stages.
1884 Use -p all to print tree at every stage.
1881 Use -p all to print tree at every stage.
1885
1882
1886 Use --verify-optimized to compare the optimized result with the unoptimized
1883 Use --verify-optimized to compare the optimized result with the unoptimized
1887 one. Returns 1 if the optimized result differs.
1884 one. Returns 1 if the optimized result differs.
1888 """
1885 """
1889 stages = [
1886 stages = [
1890 ('parsed', lambda tree: tree),
1887 ('parsed', lambda tree: tree),
1891 ('expanded', lambda tree: revsetlang.expandaliases(ui, tree)),
1888 ('expanded', lambda tree: revsetlang.expandaliases(ui, tree)),
1892 ('concatenated', revsetlang.foldconcat),
1889 ('concatenated', revsetlang.foldconcat),
1893 ('analyzed', revsetlang.analyze),
1890 ('analyzed', revsetlang.analyze),
1894 ('optimized', revsetlang.optimize),
1891 ('optimized', revsetlang.optimize),
1895 ]
1892 ]
1896 if opts['no_optimized']:
1893 if opts['no_optimized']:
1897 stages = stages[:-1]
1894 stages = stages[:-1]
1898 if opts['verify_optimized'] and opts['no_optimized']:
1895 if opts['verify_optimized'] and opts['no_optimized']:
1899 raise error.Abort(_('cannot use --verify-optimized with '
1896 raise error.Abort(_('cannot use --verify-optimized with '
1900 '--no-optimized'))
1897 '--no-optimized'))
1901 stagenames = set(n for n, f in stages)
1898 stagenames = set(n for n, f in stages)
1902
1899
1903 showalways = set()
1900 showalways = set()
1904 showchanged = set()
1901 showchanged = set()
1905 if ui.verbose and not opts['show_stage']:
1902 if ui.verbose and not opts['show_stage']:
1906 # show parsed tree by --verbose (deprecated)
1903 # show parsed tree by --verbose (deprecated)
1907 showalways.add('parsed')
1904 showalways.add('parsed')
1908 showchanged.update(['expanded', 'concatenated'])
1905 showchanged.update(['expanded', 'concatenated'])
1909 if opts['optimize']:
1906 if opts['optimize']:
1910 showalways.add('optimized')
1907 showalways.add('optimized')
1911 if opts['show_stage'] and opts['optimize']:
1908 if opts['show_stage'] and opts['optimize']:
1912 raise error.Abort(_('cannot use --optimize with --show-stage'))
1909 raise error.Abort(_('cannot use --optimize with --show-stage'))
1913 if opts['show_stage'] == ['all']:
1910 if opts['show_stage'] == ['all']:
1914 showalways.update(stagenames)
1911 showalways.update(stagenames)
1915 else:
1912 else:
1916 for n in opts['show_stage']:
1913 for n in opts['show_stage']:
1917 if n not in stagenames:
1914 if n not in stagenames:
1918 raise error.Abort(_('invalid stage name: %s') % n)
1915 raise error.Abort(_('invalid stage name: %s') % n)
1919 showalways.update(opts['show_stage'])
1916 showalways.update(opts['show_stage'])
1920
1917
1921 treebystage = {}
1918 treebystage = {}
1922 printedtree = None
1919 printedtree = None
1923 tree = revsetlang.parse(expr, lookup=repo.__contains__)
1920 tree = revsetlang.parse(expr, lookup=repo.__contains__)
1924 for n, f in stages:
1921 for n, f in stages:
1925 treebystage[n] = tree = f(tree)
1922 treebystage[n] = tree = f(tree)
1926 if n in showalways or (n in showchanged and tree != printedtree):
1923 if n in showalways or (n in showchanged and tree != printedtree):
1927 if opts['show_stage'] or n != 'parsed':
1924 if opts['show_stage'] or n != 'parsed':
1928 ui.write(("* %s:\n") % n)
1925 ui.write(("* %s:\n") % n)
1929 ui.write(revsetlang.prettyformat(tree), "\n")
1926 ui.write(revsetlang.prettyformat(tree), "\n")
1930 printedtree = tree
1927 printedtree = tree
1931
1928
1932 if opts['verify_optimized']:
1929 if opts['verify_optimized']:
1933 arevs = revset.makematcher(treebystage['analyzed'])(repo)
1930 arevs = revset.makematcher(treebystage['analyzed'])(repo)
1934 brevs = revset.makematcher(treebystage['optimized'])(repo)
1931 brevs = revset.makematcher(treebystage['optimized'])(repo)
1935 if ui.verbose:
1932 if ui.verbose:
1936 ui.note(("* analyzed set:\n"), smartset.prettyformat(arevs), "\n")
1933 ui.note(("* analyzed set:\n"), smartset.prettyformat(arevs), "\n")
1937 ui.note(("* optimized set:\n"), smartset.prettyformat(brevs), "\n")
1934 ui.note(("* optimized set:\n"), smartset.prettyformat(brevs), "\n")
1938 arevs = list(arevs)
1935 arevs = list(arevs)
1939 brevs = list(brevs)
1936 brevs = list(brevs)
1940 if arevs == brevs:
1937 if arevs == brevs:
1941 return 0
1938 return 0
1942 ui.write(('--- analyzed\n'), label='diff.file_a')
1939 ui.write(('--- analyzed\n'), label='diff.file_a')
1943 ui.write(('+++ optimized\n'), label='diff.file_b')
1940 ui.write(('+++ optimized\n'), label='diff.file_b')
1944 sm = difflib.SequenceMatcher(None, arevs, brevs)
1941 sm = difflib.SequenceMatcher(None, arevs, brevs)
1945 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1942 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1946 if tag in ('delete', 'replace'):
1943 if tag in ('delete', 'replace'):
1947 for c in arevs[alo:ahi]:
1944 for c in arevs[alo:ahi]:
1948 ui.write('-%s\n' % c, label='diff.deleted')
1945 ui.write('-%s\n' % c, label='diff.deleted')
1949 if tag in ('insert', 'replace'):
1946 if tag in ('insert', 'replace'):
1950 for c in brevs[blo:bhi]:
1947 for c in brevs[blo:bhi]:
1951 ui.write('+%s\n' % c, label='diff.inserted')
1948 ui.write('+%s\n' % c, label='diff.inserted')
1952 if tag == 'equal':
1949 if tag == 'equal':
1953 for c in arevs[alo:ahi]:
1950 for c in arevs[alo:ahi]:
1954 ui.write(' %s\n' % c)
1951 ui.write(' %s\n' % c)
1955 return 1
1952 return 1
1956
1953
1957 func = revset.makematcher(tree)
1954 func = revset.makematcher(tree)
1958 revs = func(repo)
1955 revs = func(repo)
1959 if ui.verbose:
1956 if ui.verbose:
1960 ui.note(("* set:\n"), smartset.prettyformat(revs), "\n")
1957 ui.note(("* set:\n"), smartset.prettyformat(revs), "\n")
1961 for c in revs:
1958 for c in revs:
1962 ui.write("%s\n" % c)
1959 ui.write("%s\n" % c)
1963
1960
1964 @command('debugsetparents', [], _('REV1 [REV2]'))
1961 @command('debugsetparents', [], _('REV1 [REV2]'))
1965 def debugsetparents(ui, repo, rev1, rev2=None):
1962 def debugsetparents(ui, repo, rev1, rev2=None):
1966 """manually set the parents of the current working directory
1963 """manually set the parents of the current working directory
1967
1964
1968 This is useful for writing repository conversion tools, but should
1965 This is useful for writing repository conversion tools, but should
1969 be used with care. For example, neither the working directory nor the
1966 be used with care. For example, neither the working directory nor the
1970 dirstate is updated, so file status may be incorrect after running this
1967 dirstate is updated, so file status may be incorrect after running this
1971 command.
1968 command.
1972
1969
1973 Returns 0 on success.
1970 Returns 0 on success.
1974 """
1971 """
1975
1972
1976 r1 = scmutil.revsingle(repo, rev1).node()
1973 r1 = scmutil.revsingle(repo, rev1).node()
1977 r2 = scmutil.revsingle(repo, rev2, 'null').node()
1974 r2 = scmutil.revsingle(repo, rev2, 'null').node()
1978
1975
1979 with repo.wlock():
1976 with repo.wlock():
1980 repo.setparents(r1, r2)
1977 repo.setparents(r1, r2)
1981
1978
1982 @command('debugsub',
1979 @command('debugsub',
1983 [('r', 'rev', '',
1980 [('r', 'rev', '',
1984 _('revision to check'), _('REV'))],
1981 _('revision to check'), _('REV'))],
1985 _('[-r REV] [REV]'))
1982 _('[-r REV] [REV]'))
1986 def debugsub(ui, repo, rev=None):
1983 def debugsub(ui, repo, rev=None):
1987 ctx = scmutil.revsingle(repo, rev, None)
1984 ctx = scmutil.revsingle(repo, rev, None)
1988 for k, v in sorted(ctx.substate.items()):
1985 for k, v in sorted(ctx.substate.items()):
1989 ui.write(('path %s\n') % k)
1986 ui.write(('path %s\n') % k)
1990 ui.write((' source %s\n') % v[0])
1987 ui.write((' source %s\n') % v[0])
1991 ui.write((' revision %s\n') % v[1])
1988 ui.write((' revision %s\n') % v[1])
1992
1989
1993 @command('debugsuccessorssets',
1990 @command('debugsuccessorssets',
1994 [],
1991 [],
1995 _('[REV]'))
1992 _('[REV]'))
1996 def debugsuccessorssets(ui, repo, *revs):
1993 def debugsuccessorssets(ui, repo, *revs):
1997 """show set of successors for revision
1994 """show set of successors for revision
1998
1995
1999 A successors set of changeset A is a consistent group of revisions that
1996 A successors set of changeset A is a consistent group of revisions that
2000 succeed A. It contains non-obsolete changesets only.
1997 succeed A. It contains non-obsolete changesets only.
2001
1998
2002 In most cases a changeset A has a single successors set containing a single
1999 In most cases a changeset A has a single successors set containing a single
2003 successor (changeset A replaced by A').
2000 successor (changeset A replaced by A').
2004
2001
2005 A changeset that is made obsolete with no successors are called "pruned".
2002 A changeset that is made obsolete with no successors are called "pruned".
2006 Such changesets have no successors sets at all.
2003 Such changesets have no successors sets at all.
2007
2004
2008 A changeset that has been "split" will have a successors set containing
2005 A changeset that has been "split" will have a successors set containing
2009 more than one successor.
2006 more than one successor.
2010
2007
2011 A changeset that has been rewritten in multiple different ways is called
2008 A changeset that has been rewritten in multiple different ways is called
2012 "divergent". Such changesets have multiple successor sets (each of which
2009 "divergent". Such changesets have multiple successor sets (each of which
2013 may also be split, i.e. have multiple successors).
2010 may also be split, i.e. have multiple successors).
2014
2011
2015 Results are displayed as follows::
2012 Results are displayed as follows::
2016
2013
2017 <rev1>
2014 <rev1>
2018 <successors-1A>
2015 <successors-1A>
2019 <rev2>
2016 <rev2>
2020 <successors-2A>
2017 <successors-2A>
2021 <successors-2B1> <successors-2B2> <successors-2B3>
2018 <successors-2B1> <successors-2B2> <successors-2B3>
2022
2019
2023 Here rev2 has two possible (i.e. divergent) successors sets. The first
2020 Here rev2 has two possible (i.e. divergent) successors sets. The first
2024 holds one element, whereas the second holds three (i.e. the changeset has
2021 holds one element, whereas the second holds three (i.e. the changeset has
2025 been split).
2022 been split).
2026 """
2023 """
2027 # passed to successorssets caching computation from one call to another
2024 # passed to successorssets caching computation from one call to another
2028 cache = {}
2025 cache = {}
2029 ctx2str = str
2026 ctx2str = str
2030 node2str = short
2027 node2str = short
2031 if ui.debug():
2028 if ui.debug():
2032 def ctx2str(ctx):
2029 def ctx2str(ctx):
2033 return ctx.hex()
2030 return ctx.hex()
2034 node2str = hex
2031 node2str = hex
2035 for rev in scmutil.revrange(repo, revs):
2032 for rev in scmutil.revrange(repo, revs):
2036 ctx = repo[rev]
2033 ctx = repo[rev]
2037 ui.write('%s\n'% ctx2str(ctx))
2034 ui.write('%s\n'% ctx2str(ctx))
2038 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
2035 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
2039 if succsset:
2036 if succsset:
2040 ui.write(' ')
2037 ui.write(' ')
2041 ui.write(node2str(succsset[0]))
2038 ui.write(node2str(succsset[0]))
2042 for node in succsset[1:]:
2039 for node in succsset[1:]:
2043 ui.write(' ')
2040 ui.write(' ')
2044 ui.write(node2str(node))
2041 ui.write(node2str(node))
2045 ui.write('\n')
2042 ui.write('\n')
2046
2043
2047 @command('debugtemplate',
2044 @command('debugtemplate',
2048 [('r', 'rev', [], _('apply template on changesets'), _('REV')),
2045 [('r', 'rev', [], _('apply template on changesets'), _('REV')),
2049 ('D', 'define', [], _('define template keyword'), _('KEY=VALUE'))],
2046 ('D', 'define', [], _('define template keyword'), _('KEY=VALUE'))],
2050 _('[-r REV]... [-D KEY=VALUE]... TEMPLATE'),
2047 _('[-r REV]... [-D KEY=VALUE]... TEMPLATE'),
2051 optionalrepo=True)
2048 optionalrepo=True)
2052 def debugtemplate(ui, repo, tmpl, **opts):
2049 def debugtemplate(ui, repo, tmpl, **opts):
2053 """parse and apply a template
2050 """parse and apply a template
2054
2051
2055 If -r/--rev is given, the template is processed as a log template and
2052 If -r/--rev is given, the template is processed as a log template and
2056 applied to the given changesets. Otherwise, it is processed as a generic
2053 applied to the given changesets. Otherwise, it is processed as a generic
2057 template.
2054 template.
2058
2055
2059 Use --verbose to print the parsed tree.
2056 Use --verbose to print the parsed tree.
2060 """
2057 """
2061 revs = None
2058 revs = None
2062 if opts['rev']:
2059 if opts['rev']:
2063 if repo is None:
2060 if repo is None:
2064 raise error.RepoError(_('there is no Mercurial repository here '
2061 raise error.RepoError(_('there is no Mercurial repository here '
2065 '(.hg not found)'))
2062 '(.hg not found)'))
2066 revs = scmutil.revrange(repo, opts['rev'])
2063 revs = scmutil.revrange(repo, opts['rev'])
2067
2064
2068 props = {}
2065 props = {}
2069 for d in opts['define']:
2066 for d in opts['define']:
2070 try:
2067 try:
2071 k, v = (e.strip() for e in d.split('=', 1))
2068 k, v = (e.strip() for e in d.split('=', 1))
2072 if not k or k == 'ui':
2069 if not k or k == 'ui':
2073 raise ValueError
2070 raise ValueError
2074 props[k] = v
2071 props[k] = v
2075 except ValueError:
2072 except ValueError:
2076 raise error.Abort(_('malformed keyword definition: %s') % d)
2073 raise error.Abort(_('malformed keyword definition: %s') % d)
2077
2074
2078 if ui.verbose:
2075 if ui.verbose:
2079 aliases = ui.configitems('templatealias')
2076 aliases = ui.configitems('templatealias')
2080 tree = templater.parse(tmpl)
2077 tree = templater.parse(tmpl)
2081 ui.note(templater.prettyformat(tree), '\n')
2078 ui.note(templater.prettyformat(tree), '\n')
2082 newtree = templater.expandaliases(tree, aliases)
2079 newtree = templater.expandaliases(tree, aliases)
2083 if newtree != tree:
2080 if newtree != tree:
2084 ui.note(("* expanded:\n"), templater.prettyformat(newtree), '\n')
2081 ui.note(("* expanded:\n"), templater.prettyformat(newtree), '\n')
2085
2082
2086 mapfile = None
2083 mapfile = None
2087 if revs is None:
2084 if revs is None:
2088 k = 'debugtemplate'
2085 k = 'debugtemplate'
2089 t = formatter.maketemplater(ui, k, tmpl)
2086 t = formatter.maketemplater(ui, k, tmpl)
2090 ui.write(templater.stringify(t(k, ui=ui, **props)))
2087 ui.write(templater.stringify(t(k, ui=ui, **props)))
2091 else:
2088 else:
2092 displayer = cmdutil.changeset_templater(ui, repo, None, opts, tmpl,
2089 displayer = cmdutil.changeset_templater(ui, repo, None, opts, tmpl,
2093 mapfile, buffered=False)
2090 mapfile, buffered=False)
2094 for r in revs:
2091 for r in revs:
2095 displayer.show(repo[r], **props)
2092 displayer.show(repo[r], **props)
2096 displayer.close()
2093 displayer.close()
2097
2094
2098 @command('debugupdatecaches', [])
2095 @command('debugupdatecaches', [])
2099 def debugupdatecaches(ui, repo, *pats, **opts):
2096 def debugupdatecaches(ui, repo, *pats, **opts):
2100 """warm all known caches in the repository"""
2097 """warm all known caches in the repository"""
2101 with repo.wlock():
2098 with repo.wlock():
2102 with repo.lock():
2099 with repo.lock():
2103 repo.updatecaches()
2100 repo.updatecaches()
2104
2101
2105 @command('debugupgraderepo', [
2102 @command('debugupgraderepo', [
2106 ('o', 'optimize', [], _('extra optimization to perform'), _('NAME')),
2103 ('o', 'optimize', [], _('extra optimization to perform'), _('NAME')),
2107 ('', 'run', False, _('performs an upgrade')),
2104 ('', 'run', False, _('performs an upgrade')),
2108 ])
2105 ])
2109 def debugupgraderepo(ui, repo, run=False, optimize=None):
2106 def debugupgraderepo(ui, repo, run=False, optimize=None):
2110 """upgrade a repository to use different features
2107 """upgrade a repository to use different features
2111
2108
2112 If no arguments are specified, the repository is evaluated for upgrade
2109 If no arguments are specified, the repository is evaluated for upgrade
2113 and a list of problems and potential optimizations is printed.
2110 and a list of problems and potential optimizations is printed.
2114
2111
2115 With ``--run``, a repository upgrade is performed. Behavior of the upgrade
2112 With ``--run``, a repository upgrade is performed. Behavior of the upgrade
2116 can be influenced via additional arguments. More details will be provided
2113 can be influenced via additional arguments. More details will be provided
2117 by the command output when run without ``--run``.
2114 by the command output when run without ``--run``.
2118
2115
2119 During the upgrade, the repository will be locked and no writes will be
2116 During the upgrade, the repository will be locked and no writes will be
2120 allowed.
2117 allowed.
2121
2118
2122 At the end of the upgrade, the repository may not be readable while new
2119 At the end of the upgrade, the repository may not be readable while new
2123 repository data is swapped in. This window will be as long as it takes to
2120 repository data is swapped in. This window will be as long as it takes to
2124 rename some directories inside the ``.hg`` directory. On most machines, this
2121 rename some directories inside the ``.hg`` directory. On most machines, this
2125 should complete almost instantaneously and the chances of a consumer being
2122 should complete almost instantaneously and the chances of a consumer being
2126 unable to access the repository should be low.
2123 unable to access the repository should be low.
2127 """
2124 """
2128 return upgrade.upgraderepo(ui, repo, run=run, optimize=optimize)
2125 return upgrade.upgraderepo(ui, repo, run=run, optimize=optimize)
2129
2126
2130 @command('debugwalk', cmdutil.walkopts, _('[OPTION]... [FILE]...'),
2127 @command('debugwalk', cmdutil.walkopts, _('[OPTION]... [FILE]...'),
2131 inferrepo=True)
2128 inferrepo=True)
2132 def debugwalk(ui, repo, *pats, **opts):
2129 def debugwalk(ui, repo, *pats, **opts):
2133 """show how files match on given patterns"""
2130 """show how files match on given patterns"""
2134 m = scmutil.match(repo[None], pats, opts)
2131 m = scmutil.match(repo[None], pats, opts)
2135 items = list(repo[None].walk(m))
2132 items = list(repo[None].walk(m))
2136 if not items:
2133 if not items:
2137 return
2134 return
2138 f = lambda fn: fn
2135 f = lambda fn: fn
2139 if ui.configbool('ui', 'slash') and pycompat.ossep != '/':
2136 if ui.configbool('ui', 'slash') and pycompat.ossep != '/':
2140 f = lambda fn: util.normpath(fn)
2137 f = lambda fn: util.normpath(fn)
2141 fmt = 'f %%-%ds %%-%ds %%s' % (
2138 fmt = 'f %%-%ds %%-%ds %%s' % (
2142 max([len(abs) for abs in items]),
2139 max([len(abs) for abs in items]),
2143 max([len(m.rel(abs)) for abs in items]))
2140 max([len(m.rel(abs)) for abs in items]))
2144 for abs in items:
2141 for abs in items:
2145 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2142 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2146 ui.write("%s\n" % line.rstrip())
2143 ui.write("%s\n" % line.rstrip())
2147
2144
2148 @command('debugwireargs',
2145 @command('debugwireargs',
2149 [('', 'three', '', 'three'),
2146 [('', 'three', '', 'three'),
2150 ('', 'four', '', 'four'),
2147 ('', 'four', '', 'four'),
2151 ('', 'five', '', 'five'),
2148 ('', 'five', '', 'five'),
2152 ] + cmdutil.remoteopts,
2149 ] + cmdutil.remoteopts,
2153 _('REPO [OPTIONS]... [ONE [TWO]]'),
2150 _('REPO [OPTIONS]... [ONE [TWO]]'),
2154 norepo=True)
2151 norepo=True)
2155 def debugwireargs(ui, repopath, *vals, **opts):
2152 def debugwireargs(ui, repopath, *vals, **opts):
2156 repo = hg.peer(ui, opts, repopath)
2153 repo = hg.peer(ui, opts, repopath)
2157 for opt in cmdutil.remoteopts:
2154 for opt in cmdutil.remoteopts:
2158 del opts[opt[1]]
2155 del opts[opt[1]]
2159 args = {}
2156 args = {}
2160 for k, v in opts.iteritems():
2157 for k, v in opts.iteritems():
2161 if v:
2158 if v:
2162 args[k] = v
2159 args[k] = v
2163 # run twice to check that we don't mess up the stream for the next command
2160 # run twice to check that we don't mess up the stream for the next command
2164 res1 = repo.debugwireargs(*vals, **args)
2161 res1 = repo.debugwireargs(*vals, **args)
2165 res2 = repo.debugwireargs(*vals, **args)
2162 res2 = repo.debugwireargs(*vals, **args)
2166 ui.write("%s\n" % res1)
2163 ui.write("%s\n" % res1)
2167 if res1 != res2:
2164 if res1 != res2:
2168 ui.warn("%s\n" % res2)
2165 ui.warn("%s\n" % res2)
@@ -1,1005 +1,1000 b''
1 # dispatch.py - command dispatching for mercurial
1 # dispatch.py - command dispatching for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
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, print_function
8 from __future__ import absolute_import, print_function
9
9
10 import difflib
10 import difflib
11 import errno
11 import errno
12 import getopt
12 import getopt
13 import os
13 import os
14 import pdb
14 import pdb
15 import re
15 import re
16 import signal
16 import signal
17 import sys
17 import sys
18 import time
18 import time
19 import traceback
19 import traceback
20
20
21
21
22 from .i18n import _
22 from .i18n import _
23
23
24 from . import (
24 from . import (
25 cmdutil,
25 cmdutil,
26 color,
26 color,
27 commands,
27 commands,
28 debugcommands,
29 demandimport,
28 demandimport,
30 encoding,
29 encoding,
31 error,
30 error,
32 extensions,
31 extensions,
33 fancyopts,
32 fancyopts,
34 fileset,
33 fileset,
35 help,
34 help,
36 hg,
35 hg,
37 hook,
36 hook,
38 profiling,
37 profiling,
39 pycompat,
38 pycompat,
40 revset,
39 revset,
41 scmutil,
40 scmutil,
42 templatefilters,
41 templatefilters,
43 templatekw,
42 templatekw,
44 templater,
43 templater,
45 ui as uimod,
44 ui as uimod,
46 util,
45 util,
47 )
46 )
48
47
49 class request(object):
48 class request(object):
50 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
49 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
51 ferr=None):
50 ferr=None):
52 self.args = args
51 self.args = args
53 self.ui = ui
52 self.ui = ui
54 self.repo = repo
53 self.repo = repo
55
54
56 # input/output/error streams
55 # input/output/error streams
57 self.fin = fin
56 self.fin = fin
58 self.fout = fout
57 self.fout = fout
59 self.ferr = ferr
58 self.ferr = ferr
60
59
61 def _runexithandlers(self):
60 def _runexithandlers(self):
62 exc = None
61 exc = None
63 handlers = self.ui._exithandlers
62 handlers = self.ui._exithandlers
64 try:
63 try:
65 while handlers:
64 while handlers:
66 func, args, kwargs = handlers.pop()
65 func, args, kwargs = handlers.pop()
67 try:
66 try:
68 func(*args, **kwargs)
67 func(*args, **kwargs)
69 except: # re-raises below
68 except: # re-raises below
70 if exc is None:
69 if exc is None:
71 exc = sys.exc_info()[1]
70 exc = sys.exc_info()[1]
72 self.ui.warn(('error in exit handlers:\n'))
71 self.ui.warn(('error in exit handlers:\n'))
73 self.ui.traceback(force=True)
72 self.ui.traceback(force=True)
74 finally:
73 finally:
75 if exc is not None:
74 if exc is not None:
76 raise exc
75 raise exc
77
76
78 def run():
77 def run():
79 "run the command in sys.argv"
78 "run the command in sys.argv"
80 req = request(pycompat.sysargv[1:])
79 req = request(pycompat.sysargv[1:])
81 err = None
80 err = None
82 try:
81 try:
83 status = (dispatch(req) or 0) & 255
82 status = (dispatch(req) or 0) & 255
84 except error.StdioError as err:
83 except error.StdioError as err:
85 status = -1
84 status = -1
86 if util.safehasattr(req.ui, 'fout'):
85 if util.safehasattr(req.ui, 'fout'):
87 try:
86 try:
88 req.ui.fout.close()
87 req.ui.fout.close()
89 except IOError as err:
88 except IOError as err:
90 status = -1
89 status = -1
91 if util.safehasattr(req.ui, 'ferr'):
90 if util.safehasattr(req.ui, 'ferr'):
92 if err is not None and err.errno != errno.EPIPE:
91 if err is not None and err.errno != errno.EPIPE:
93 req.ui.ferr.write('abort: %s\n' % err.strerror)
92 req.ui.ferr.write('abort: %s\n' % err.strerror)
94 req.ui.ferr.close()
93 req.ui.ferr.close()
95 sys.exit(status & 255)
94 sys.exit(status & 255)
96
95
97 def _getsimilar(symbols, value):
96 def _getsimilar(symbols, value):
98 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
97 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
99 # The cutoff for similarity here is pretty arbitrary. It should
98 # The cutoff for similarity here is pretty arbitrary. It should
100 # probably be investigated and tweaked.
99 # probably be investigated and tweaked.
101 return [s for s in symbols if sim(s) > 0.6]
100 return [s for s in symbols if sim(s) > 0.6]
102
101
103 def _reportsimilar(write, similar):
102 def _reportsimilar(write, similar):
104 if len(similar) == 1:
103 if len(similar) == 1:
105 write(_("(did you mean %s?)\n") % similar[0])
104 write(_("(did you mean %s?)\n") % similar[0])
106 elif similar:
105 elif similar:
107 ss = ", ".join(sorted(similar))
106 ss = ", ".join(sorted(similar))
108 write(_("(did you mean one of %s?)\n") % ss)
107 write(_("(did you mean one of %s?)\n") % ss)
109
108
110 def _formatparse(write, inst):
109 def _formatparse(write, inst):
111 similar = []
110 similar = []
112 if isinstance(inst, error.UnknownIdentifier):
111 if isinstance(inst, error.UnknownIdentifier):
113 # make sure to check fileset first, as revset can invoke fileset
112 # make sure to check fileset first, as revset can invoke fileset
114 similar = _getsimilar(inst.symbols, inst.function)
113 similar = _getsimilar(inst.symbols, inst.function)
115 if len(inst.args) > 1:
114 if len(inst.args) > 1:
116 write(_("hg: parse error at %s: %s\n") %
115 write(_("hg: parse error at %s: %s\n") %
117 (inst.args[1], inst.args[0]))
116 (inst.args[1], inst.args[0]))
118 if (inst.args[0][0] == ' '):
117 if (inst.args[0][0] == ' '):
119 write(_("unexpected leading whitespace\n"))
118 write(_("unexpected leading whitespace\n"))
120 else:
119 else:
121 write(_("hg: parse error: %s\n") % inst.args[0])
120 write(_("hg: parse error: %s\n") % inst.args[0])
122 _reportsimilar(write, similar)
121 _reportsimilar(write, similar)
123 if inst.hint:
122 if inst.hint:
124 write(_("(%s)\n") % inst.hint)
123 write(_("(%s)\n") % inst.hint)
125
124
126 def _formatargs(args):
125 def _formatargs(args):
127 return ' '.join(util.shellquote(a) for a in args)
126 return ' '.join(util.shellquote(a) for a in args)
128
127
129 def dispatch(req):
128 def dispatch(req):
130 "run the command specified in req.args"
129 "run the command specified in req.args"
131 if req.ferr:
130 if req.ferr:
132 ferr = req.ferr
131 ferr = req.ferr
133 elif req.ui:
132 elif req.ui:
134 ferr = req.ui.ferr
133 ferr = req.ui.ferr
135 else:
134 else:
136 ferr = util.stderr
135 ferr = util.stderr
137
136
138 try:
137 try:
139 if not req.ui:
138 if not req.ui:
140 req.ui = uimod.ui.load()
139 req.ui = uimod.ui.load()
141 if '--traceback' in req.args:
140 if '--traceback' in req.args:
142 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
141 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
143
142
144 # set ui streams from the request
143 # set ui streams from the request
145 if req.fin:
144 if req.fin:
146 req.ui.fin = req.fin
145 req.ui.fin = req.fin
147 if req.fout:
146 if req.fout:
148 req.ui.fout = req.fout
147 req.ui.fout = req.fout
149 if req.ferr:
148 if req.ferr:
150 req.ui.ferr = req.ferr
149 req.ui.ferr = req.ferr
151 except error.Abort as inst:
150 except error.Abort as inst:
152 ferr.write(_("abort: %s\n") % inst)
151 ferr.write(_("abort: %s\n") % inst)
153 if inst.hint:
152 if inst.hint:
154 ferr.write(_("(%s)\n") % inst.hint)
153 ferr.write(_("(%s)\n") % inst.hint)
155 return -1
154 return -1
156 except error.ParseError as inst:
155 except error.ParseError as inst:
157 _formatparse(ferr.write, inst)
156 _formatparse(ferr.write, inst)
158 return -1
157 return -1
159
158
160 msg = _formatargs(req.args)
159 msg = _formatargs(req.args)
161 starttime = util.timer()
160 starttime = util.timer()
162 ret = None
161 ret = None
163 try:
162 try:
164 ret = _runcatch(req)
163 ret = _runcatch(req)
165 except error.ProgrammingError as inst:
164 except error.ProgrammingError as inst:
166 req.ui.warn(_('** ProgrammingError: %s\n') % inst)
165 req.ui.warn(_('** ProgrammingError: %s\n') % inst)
167 if inst.hint:
166 if inst.hint:
168 req.ui.warn(_('** (%s)\n') % inst.hint)
167 req.ui.warn(_('** (%s)\n') % inst.hint)
169 raise
168 raise
170 except KeyboardInterrupt as inst:
169 except KeyboardInterrupt as inst:
171 try:
170 try:
172 if isinstance(inst, error.SignalInterrupt):
171 if isinstance(inst, error.SignalInterrupt):
173 msg = _("killed!\n")
172 msg = _("killed!\n")
174 else:
173 else:
175 msg = _("interrupted!\n")
174 msg = _("interrupted!\n")
176 req.ui.warn(msg)
175 req.ui.warn(msg)
177 except error.SignalInterrupt:
176 except error.SignalInterrupt:
178 # maybe pager would quit without consuming all the output, and
177 # maybe pager would quit without consuming all the output, and
179 # SIGPIPE was raised. we cannot print anything in this case.
178 # SIGPIPE was raised. we cannot print anything in this case.
180 pass
179 pass
181 except IOError as inst:
180 except IOError as inst:
182 if inst.errno != errno.EPIPE:
181 if inst.errno != errno.EPIPE:
183 raise
182 raise
184 ret = -1
183 ret = -1
185 finally:
184 finally:
186 duration = util.timer() - starttime
185 duration = util.timer() - starttime
187 req.ui.flush()
186 req.ui.flush()
188 if req.ui.logblockedtimes:
187 if req.ui.logblockedtimes:
189 req.ui._blockedtimes['command_duration'] = duration * 1000
188 req.ui._blockedtimes['command_duration'] = duration * 1000
190 req.ui.log('uiblocked', 'ui blocked ms', **req.ui._blockedtimes)
189 req.ui.log('uiblocked', 'ui blocked ms', **req.ui._blockedtimes)
191 req.ui.log("commandfinish", "%s exited %d after %0.2f seconds\n",
190 req.ui.log("commandfinish", "%s exited %d after %0.2f seconds\n",
192 msg, ret or 0, duration)
191 msg, ret or 0, duration)
193 try:
192 try:
194 req._runexithandlers()
193 req._runexithandlers()
195 except: # exiting, so no re-raises
194 except: # exiting, so no re-raises
196 ret = ret or -1
195 ret = ret or -1
197 return ret
196 return ret
198
197
199 def _runcatch(req):
198 def _runcatch(req):
200 def catchterm(*args):
199 def catchterm(*args):
201 raise error.SignalInterrupt
200 raise error.SignalInterrupt
202
201
203 ui = req.ui
202 ui = req.ui
204 try:
203 try:
205 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
204 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
206 num = getattr(signal, name, None)
205 num = getattr(signal, name, None)
207 if num:
206 if num:
208 signal.signal(num, catchterm)
207 signal.signal(num, catchterm)
209 except ValueError:
208 except ValueError:
210 pass # happens if called in a thread
209 pass # happens if called in a thread
211
210
212 def _runcatchfunc():
211 def _runcatchfunc():
213 realcmd = None
212 realcmd = None
214 try:
213 try:
215 cmdargs = fancyopts.fancyopts(req.args[:], commands.globalopts, {})
214 cmdargs = fancyopts.fancyopts(req.args[:], commands.globalopts, {})
216 cmd = cmdargs[0]
215 cmd = cmdargs[0]
217 aliases, entry = cmdutil.findcmd(cmd, commands.table, False)
216 aliases, entry = cmdutil.findcmd(cmd, commands.table, False)
218 realcmd = aliases[0]
217 realcmd = aliases[0]
219 except (error.UnknownCommand, error.AmbiguousCommand,
218 except (error.UnknownCommand, error.AmbiguousCommand,
220 IndexError, getopt.GetoptError):
219 IndexError, getopt.GetoptError):
221 # Don't handle this here. We know the command is
220 # Don't handle this here. We know the command is
222 # invalid, but all we're worried about for now is that
221 # invalid, but all we're worried about for now is that
223 # it's not a command that server operators expect to
222 # it's not a command that server operators expect to
224 # be safe to offer to users in a sandbox.
223 # be safe to offer to users in a sandbox.
225 pass
224 pass
226 if realcmd == 'serve' and '--stdio' in cmdargs:
225 if realcmd == 'serve' and '--stdio' in cmdargs:
227 # We want to constrain 'hg serve --stdio' instances pretty
226 # We want to constrain 'hg serve --stdio' instances pretty
228 # closely, as many shared-ssh access tools want to grant
227 # closely, as many shared-ssh access tools want to grant
229 # access to run *only* 'hg -R $repo serve --stdio'. We
228 # access to run *only* 'hg -R $repo serve --stdio'. We
230 # restrict to exactly that set of arguments, and prohibit
229 # restrict to exactly that set of arguments, and prohibit
231 # any repo name that starts with '--' to prevent
230 # any repo name that starts with '--' to prevent
232 # shenanigans wherein a user does something like pass
231 # shenanigans wherein a user does something like pass
233 # --debugger or --config=ui.debugger=1 as a repo
232 # --debugger or --config=ui.debugger=1 as a repo
234 # name. This used to actually run the debugger.
233 # name. This used to actually run the debugger.
235 if (len(req.args) != 4 or
234 if (len(req.args) != 4 or
236 req.args[0] != '-R' or
235 req.args[0] != '-R' or
237 req.args[1].startswith('--') or
236 req.args[1].startswith('--') or
238 req.args[2] != 'serve' or
237 req.args[2] != 'serve' or
239 req.args[3] != '--stdio'):
238 req.args[3] != '--stdio'):
240 raise error.Abort(
239 raise error.Abort(
241 _('potentially unsafe serve --stdio invocation: %r') %
240 _('potentially unsafe serve --stdio invocation: %r') %
242 (req.args,))
241 (req.args,))
243
242
244 try:
243 try:
245 debugger = 'pdb'
244 debugger = 'pdb'
246 debugtrace = {
245 debugtrace = {
247 'pdb' : pdb.set_trace
246 'pdb' : pdb.set_trace
248 }
247 }
249 debugmortem = {
248 debugmortem = {
250 'pdb' : pdb.post_mortem
249 'pdb' : pdb.post_mortem
251 }
250 }
252
251
253 # read --config before doing anything else
252 # read --config before doing anything else
254 # (e.g. to change trust settings for reading .hg/hgrc)
253 # (e.g. to change trust settings for reading .hg/hgrc)
255 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
254 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
256
255
257 if req.repo:
256 if req.repo:
258 # copy configs that were passed on the cmdline (--config) to
257 # copy configs that were passed on the cmdline (--config) to
259 # the repo ui
258 # the repo ui
260 for sec, name, val in cfgs:
259 for sec, name, val in cfgs:
261 req.repo.ui.setconfig(sec, name, val, source='--config')
260 req.repo.ui.setconfig(sec, name, val, source='--config')
262
261
263 # developer config: ui.debugger
262 # developer config: ui.debugger
264 debugger = ui.config("ui", "debugger")
263 debugger = ui.config("ui", "debugger")
265 debugmod = pdb
264 debugmod = pdb
266 if not debugger or ui.plain():
265 if not debugger or ui.plain():
267 # if we are in HGPLAIN mode, then disable custom debugging
266 # if we are in HGPLAIN mode, then disable custom debugging
268 debugger = 'pdb'
267 debugger = 'pdb'
269 elif '--debugger' in req.args:
268 elif '--debugger' in req.args:
270 # This import can be slow for fancy debuggers, so only
269 # This import can be slow for fancy debuggers, so only
271 # do it when absolutely necessary, i.e. when actual
270 # do it when absolutely necessary, i.e. when actual
272 # debugging has been requested
271 # debugging has been requested
273 with demandimport.deactivated():
272 with demandimport.deactivated():
274 try:
273 try:
275 debugmod = __import__(debugger)
274 debugmod = __import__(debugger)
276 except ImportError:
275 except ImportError:
277 pass # Leave debugmod = pdb
276 pass # Leave debugmod = pdb
278
277
279 debugtrace[debugger] = debugmod.set_trace
278 debugtrace[debugger] = debugmod.set_trace
280 debugmortem[debugger] = debugmod.post_mortem
279 debugmortem[debugger] = debugmod.post_mortem
281
280
282 # enter the debugger before command execution
281 # enter the debugger before command execution
283 if '--debugger' in req.args:
282 if '--debugger' in req.args:
284 ui.warn(_("entering debugger - "
283 ui.warn(_("entering debugger - "
285 "type c to continue starting hg or h for help\n"))
284 "type c to continue starting hg or h for help\n"))
286
285
287 if (debugger != 'pdb' and
286 if (debugger != 'pdb' and
288 debugtrace[debugger] == debugtrace['pdb']):
287 debugtrace[debugger] == debugtrace['pdb']):
289 ui.warn(_("%s debugger specified "
288 ui.warn(_("%s debugger specified "
290 "but its module was not found\n") % debugger)
289 "but its module was not found\n") % debugger)
291 with demandimport.deactivated():
290 with demandimport.deactivated():
292 debugtrace[debugger]()
291 debugtrace[debugger]()
293 try:
292 try:
294 return _dispatch(req)
293 return _dispatch(req)
295 finally:
294 finally:
296 ui.flush()
295 ui.flush()
297 except: # re-raises
296 except: # re-raises
298 # enter the debugger when we hit an exception
297 # enter the debugger when we hit an exception
299 if '--debugger' in req.args:
298 if '--debugger' in req.args:
300 traceback.print_exc()
299 traceback.print_exc()
301 debugmortem[debugger](sys.exc_info()[2])
300 debugmortem[debugger](sys.exc_info()[2])
302 raise
301 raise
303
302
304 return _callcatch(ui, _runcatchfunc)
303 return _callcatch(ui, _runcatchfunc)
305
304
306 def _callcatch(ui, func):
305 def _callcatch(ui, func):
307 """like scmutil.callcatch but handles more high-level exceptions about
306 """like scmutil.callcatch but handles more high-level exceptions about
308 config parsing and commands. besides, use handlecommandexception to handle
307 config parsing and commands. besides, use handlecommandexception to handle
309 uncaught exceptions.
308 uncaught exceptions.
310 """
309 """
311 try:
310 try:
312 return scmutil.callcatch(ui, func)
311 return scmutil.callcatch(ui, func)
313 except error.AmbiguousCommand as inst:
312 except error.AmbiguousCommand as inst:
314 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
313 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
315 (inst.args[0], " ".join(inst.args[1])))
314 (inst.args[0], " ".join(inst.args[1])))
316 except error.CommandError as inst:
315 except error.CommandError as inst:
317 if inst.args[0]:
316 if inst.args[0]:
318 ui.pager('help')
317 ui.pager('help')
319 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
318 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
320 commands.help_(ui, inst.args[0], full=False, command=True)
319 commands.help_(ui, inst.args[0], full=False, command=True)
321 else:
320 else:
322 ui.pager('help')
321 ui.pager('help')
323 ui.warn(_("hg: %s\n") % inst.args[1])
322 ui.warn(_("hg: %s\n") % inst.args[1])
324 commands.help_(ui, 'shortlist')
323 commands.help_(ui, 'shortlist')
325 except error.ParseError as inst:
324 except error.ParseError as inst:
326 _formatparse(ui.warn, inst)
325 _formatparse(ui.warn, inst)
327 return -1
326 return -1
328 except error.UnknownCommand as inst:
327 except error.UnknownCommand as inst:
329 nocmdmsg = _("hg: unknown command '%s'\n") % inst.args[0]
328 nocmdmsg = _("hg: unknown command '%s'\n") % inst.args[0]
330 try:
329 try:
331 # check if the command is in a disabled extension
330 # check if the command is in a disabled extension
332 # (but don't check for extensions themselves)
331 # (but don't check for extensions themselves)
333 formatted = help.formattedhelp(ui, inst.args[0], unknowncmd=True)
332 formatted = help.formattedhelp(ui, inst.args[0], unknowncmd=True)
334 ui.warn(nocmdmsg)
333 ui.warn(nocmdmsg)
335 ui.write(formatted)
334 ui.write(formatted)
336 except (error.UnknownCommand, error.Abort):
335 except (error.UnknownCommand, error.Abort):
337 suggested = False
336 suggested = False
338 if len(inst.args) == 2:
337 if len(inst.args) == 2:
339 sim = _getsimilar(inst.args[1], inst.args[0])
338 sim = _getsimilar(inst.args[1], inst.args[0])
340 if sim:
339 if sim:
341 ui.warn(nocmdmsg)
340 ui.warn(nocmdmsg)
342 _reportsimilar(ui.warn, sim)
341 _reportsimilar(ui.warn, sim)
343 suggested = True
342 suggested = True
344 if not suggested:
343 if not suggested:
345 ui.pager('help')
344 ui.pager('help')
346 ui.warn(nocmdmsg)
345 ui.warn(nocmdmsg)
347 commands.help_(ui, 'shortlist')
346 commands.help_(ui, 'shortlist')
348 except IOError:
347 except IOError:
349 raise
348 raise
350 except KeyboardInterrupt:
349 except KeyboardInterrupt:
351 raise
350 raise
352 except: # probably re-raises
351 except: # probably re-raises
353 if not handlecommandexception(ui):
352 if not handlecommandexception(ui):
354 raise
353 raise
355
354
356 return -1
355 return -1
357
356
358 def aliasargs(fn, givenargs):
357 def aliasargs(fn, givenargs):
359 args = getattr(fn, 'args', [])
358 args = getattr(fn, 'args', [])
360 if args:
359 if args:
361 cmd = ' '.join(map(util.shellquote, args))
360 cmd = ' '.join(map(util.shellquote, args))
362
361
363 nums = []
362 nums = []
364 def replacer(m):
363 def replacer(m):
365 num = int(m.group(1)) - 1
364 num = int(m.group(1)) - 1
366 nums.append(num)
365 nums.append(num)
367 if num < len(givenargs):
366 if num < len(givenargs):
368 return givenargs[num]
367 return givenargs[num]
369 raise error.Abort(_('too few arguments for command alias'))
368 raise error.Abort(_('too few arguments for command alias'))
370 cmd = re.sub(br'\$(\d+|\$)', replacer, cmd)
369 cmd = re.sub(br'\$(\d+|\$)', replacer, cmd)
371 givenargs = [x for i, x in enumerate(givenargs)
370 givenargs = [x for i, x in enumerate(givenargs)
372 if i not in nums]
371 if i not in nums]
373 args = pycompat.shlexsplit(cmd)
372 args = pycompat.shlexsplit(cmd)
374 return args + givenargs
373 return args + givenargs
375
374
376 def aliasinterpolate(name, args, cmd):
375 def aliasinterpolate(name, args, cmd):
377 '''interpolate args into cmd for shell aliases
376 '''interpolate args into cmd for shell aliases
378
377
379 This also handles $0, $@ and "$@".
378 This also handles $0, $@ and "$@".
380 '''
379 '''
381 # util.interpolate can't deal with "$@" (with quotes) because it's only
380 # util.interpolate can't deal with "$@" (with quotes) because it's only
382 # built to match prefix + patterns.
381 # built to match prefix + patterns.
383 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
382 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
384 replacemap['$0'] = name
383 replacemap['$0'] = name
385 replacemap['$$'] = '$'
384 replacemap['$$'] = '$'
386 replacemap['$@'] = ' '.join(args)
385 replacemap['$@'] = ' '.join(args)
387 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
386 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
388 # parameters, separated out into words. Emulate the same behavior here by
387 # parameters, separated out into words. Emulate the same behavior here by
389 # quoting the arguments individually. POSIX shells will then typically
388 # quoting the arguments individually. POSIX shells will then typically
390 # tokenize each argument into exactly one word.
389 # tokenize each argument into exactly one word.
391 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
390 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
392 # escape '\$' for regex
391 # escape '\$' for regex
393 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
392 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
394 r = re.compile(regex)
393 r = re.compile(regex)
395 return r.sub(lambda x: replacemap[x.group()], cmd)
394 return r.sub(lambda x: replacemap[x.group()], cmd)
396
395
397 class cmdalias(object):
396 class cmdalias(object):
398 def __init__(self, name, definition, cmdtable, source):
397 def __init__(self, name, definition, cmdtable, source):
399 self.name = self.cmd = name
398 self.name = self.cmd = name
400 self.cmdname = ''
399 self.cmdname = ''
401 self.definition = definition
400 self.definition = definition
402 self.fn = None
401 self.fn = None
403 self.givenargs = []
402 self.givenargs = []
404 self.opts = []
403 self.opts = []
405 self.help = ''
404 self.help = ''
406 self.badalias = None
405 self.badalias = None
407 self.unknowncmd = False
406 self.unknowncmd = False
408 self.source = source
407 self.source = source
409
408
410 try:
409 try:
411 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
410 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
412 for alias, e in cmdtable.iteritems():
411 for alias, e in cmdtable.iteritems():
413 if e is entry:
412 if e is entry:
414 self.cmd = alias
413 self.cmd = alias
415 break
414 break
416 self.shadows = True
415 self.shadows = True
417 except error.UnknownCommand:
416 except error.UnknownCommand:
418 self.shadows = False
417 self.shadows = False
419
418
420 if not self.definition:
419 if not self.definition:
421 self.badalias = _("no definition for alias '%s'") % self.name
420 self.badalias = _("no definition for alias '%s'") % self.name
422 return
421 return
423
422
424 if self.definition.startswith('!'):
423 if self.definition.startswith('!'):
425 self.shell = True
424 self.shell = True
426 def fn(ui, *args):
425 def fn(ui, *args):
427 env = {'HG_ARGS': ' '.join((self.name,) + args)}
426 env = {'HG_ARGS': ' '.join((self.name,) + args)}
428 def _checkvar(m):
427 def _checkvar(m):
429 if m.groups()[0] == '$':
428 if m.groups()[0] == '$':
430 return m.group()
429 return m.group()
431 elif int(m.groups()[0]) <= len(args):
430 elif int(m.groups()[0]) <= len(args):
432 return m.group()
431 return m.group()
433 else:
432 else:
434 ui.debug("No argument found for substitution "
433 ui.debug("No argument found for substitution "
435 "of %i variable in alias '%s' definition."
434 "of %i variable in alias '%s' definition."
436 % (int(m.groups()[0]), self.name))
435 % (int(m.groups()[0]), self.name))
437 return ''
436 return ''
438 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
437 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
439 cmd = aliasinterpolate(self.name, args, cmd)
438 cmd = aliasinterpolate(self.name, args, cmd)
440 return ui.system(cmd, environ=env,
439 return ui.system(cmd, environ=env,
441 blockedtag='alias_%s' % self.name)
440 blockedtag='alias_%s' % self.name)
442 self.fn = fn
441 self.fn = fn
443 return
442 return
444
443
445 try:
444 try:
446 args = pycompat.shlexsplit(self.definition)
445 args = pycompat.shlexsplit(self.definition)
447 except ValueError as inst:
446 except ValueError as inst:
448 self.badalias = (_("error in definition for alias '%s': %s")
447 self.badalias = (_("error in definition for alias '%s': %s")
449 % (self.name, inst))
448 % (self.name, inst))
450 return
449 return
451 self.cmdname = cmd = args.pop(0)
450 self.cmdname = cmd = args.pop(0)
452 self.givenargs = args
451 self.givenargs = args
453
452
454 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
453 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
455 if _earlygetopt([invalidarg], args):
454 if _earlygetopt([invalidarg], args):
456 self.badalias = (_("error in definition for alias '%s': %s may "
455 self.badalias = (_("error in definition for alias '%s': %s may "
457 "only be given on the command line")
456 "only be given on the command line")
458 % (self.name, invalidarg))
457 % (self.name, invalidarg))
459 return
458 return
460
459
461 try:
460 try:
462 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
461 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
463 if len(tableentry) > 2:
462 if len(tableentry) > 2:
464 self.fn, self.opts, self.help = tableentry
463 self.fn, self.opts, self.help = tableentry
465 else:
464 else:
466 self.fn, self.opts = tableentry
465 self.fn, self.opts = tableentry
467
466
468 if self.help.startswith("hg " + cmd):
467 if self.help.startswith("hg " + cmd):
469 # drop prefix in old-style help lines so hg shows the alias
468 # drop prefix in old-style help lines so hg shows the alias
470 self.help = self.help[4 + len(cmd):]
469 self.help = self.help[4 + len(cmd):]
471 self.__doc__ = self.fn.__doc__
470 self.__doc__ = self.fn.__doc__
472
471
473 except error.UnknownCommand:
472 except error.UnknownCommand:
474 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
473 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
475 % (self.name, cmd))
474 % (self.name, cmd))
476 self.unknowncmd = True
475 self.unknowncmd = True
477 except error.AmbiguousCommand:
476 except error.AmbiguousCommand:
478 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
477 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
479 % (self.name, cmd))
478 % (self.name, cmd))
480
479
481 @property
480 @property
482 def args(self):
481 def args(self):
483 args = pycompat.maplist(util.expandpath, self.givenargs)
482 args = pycompat.maplist(util.expandpath, self.givenargs)
484 return aliasargs(self.fn, args)
483 return aliasargs(self.fn, args)
485
484
486 def __getattr__(self, name):
485 def __getattr__(self, name):
487 adefaults = {r'norepo': True,
486 adefaults = {r'norepo': True,
488 r'optionalrepo': False, r'inferrepo': False}
487 r'optionalrepo': False, r'inferrepo': False}
489 if name not in adefaults:
488 if name not in adefaults:
490 raise AttributeError(name)
489 raise AttributeError(name)
491 if self.badalias or util.safehasattr(self, 'shell'):
490 if self.badalias or util.safehasattr(self, 'shell'):
492 return adefaults[name]
491 return adefaults[name]
493 return getattr(self.fn, name)
492 return getattr(self.fn, name)
494
493
495 def __call__(self, ui, *args, **opts):
494 def __call__(self, ui, *args, **opts):
496 if self.badalias:
495 if self.badalias:
497 hint = None
496 hint = None
498 if self.unknowncmd:
497 if self.unknowncmd:
499 try:
498 try:
500 # check if the command is in a disabled extension
499 # check if the command is in a disabled extension
501 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
500 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
502 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
501 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
503 except error.UnknownCommand:
502 except error.UnknownCommand:
504 pass
503 pass
505 raise error.Abort(self.badalias, hint=hint)
504 raise error.Abort(self.badalias, hint=hint)
506 if self.shadows:
505 if self.shadows:
507 ui.debug("alias '%s' shadows command '%s'\n" %
506 ui.debug("alias '%s' shadows command '%s'\n" %
508 (self.name, self.cmdname))
507 (self.name, self.cmdname))
509
508
510 ui.log('commandalias', "alias '%s' expands to '%s'\n",
509 ui.log('commandalias', "alias '%s' expands to '%s'\n",
511 self.name, self.definition)
510 self.name, self.definition)
512 if util.safehasattr(self, 'shell'):
511 if util.safehasattr(self, 'shell'):
513 return self.fn(ui, *args, **opts)
512 return self.fn(ui, *args, **opts)
514 else:
513 else:
515 try:
514 try:
516 return util.checksignature(self.fn)(ui, *args, **opts)
515 return util.checksignature(self.fn)(ui, *args, **opts)
517 except error.SignatureError:
516 except error.SignatureError:
518 args = ' '.join([self.cmdname] + self.args)
517 args = ' '.join([self.cmdname] + self.args)
519 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
518 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
520 raise
519 raise
521
520
522 def addaliases(ui, cmdtable):
521 def addaliases(ui, cmdtable):
523 # aliases are processed after extensions have been loaded, so they
522 # aliases are processed after extensions have been loaded, so they
524 # may use extension commands. Aliases can also use other alias definitions,
523 # may use extension commands. Aliases can also use other alias definitions,
525 # but only if they have been defined prior to the current definition.
524 # but only if they have been defined prior to the current definition.
526 for alias, definition in ui.configitems('alias'):
525 for alias, definition in ui.configitems('alias'):
527 source = ui.configsource('alias', alias)
526 source = ui.configsource('alias', alias)
528 aliasdef = cmdalias(alias, definition, cmdtable, source)
527 aliasdef = cmdalias(alias, definition, cmdtable, source)
529
528
530 try:
529 try:
531 olddef = cmdtable[aliasdef.cmd][0]
530 olddef = cmdtable[aliasdef.cmd][0]
532 if olddef.definition == aliasdef.definition:
531 if olddef.definition == aliasdef.definition:
533 continue
532 continue
534 except (KeyError, AttributeError):
533 except (KeyError, AttributeError):
535 # definition might not exist or it might not be a cmdalias
534 # definition might not exist or it might not be a cmdalias
536 pass
535 pass
537
536
538 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
537 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
539
538
540 def _parse(ui, args):
539 def _parse(ui, args):
541 options = {}
540 options = {}
542 cmdoptions = {}
541 cmdoptions = {}
543
542
544 try:
543 try:
545 args = fancyopts.fancyopts(args, commands.globalopts, options)
544 args = fancyopts.fancyopts(args, commands.globalopts, options)
546 except getopt.GetoptError as inst:
545 except getopt.GetoptError as inst:
547 raise error.CommandError(None, inst)
546 raise error.CommandError(None, inst)
548
547
549 if args:
548 if args:
550 cmd, args = args[0], args[1:]
549 cmd, args = args[0], args[1:]
551 aliases, entry = cmdutil.findcmd(cmd, commands.table,
550 aliases, entry = cmdutil.findcmd(cmd, commands.table,
552 ui.configbool("ui", "strict"))
551 ui.configbool("ui", "strict"))
553 cmd = aliases[0]
552 cmd = aliases[0]
554 args = aliasargs(entry[0], args)
553 args = aliasargs(entry[0], args)
555 defaults = ui.config("defaults", cmd)
554 defaults = ui.config("defaults", cmd)
556 if defaults:
555 if defaults:
557 args = pycompat.maplist(
556 args = pycompat.maplist(
558 util.expandpath, pycompat.shlexsplit(defaults)) + args
557 util.expandpath, pycompat.shlexsplit(defaults)) + args
559 c = list(entry[1])
558 c = list(entry[1])
560 else:
559 else:
561 cmd = None
560 cmd = None
562 c = []
561 c = []
563
562
564 # combine global options into local
563 # combine global options into local
565 for o in commands.globalopts:
564 for o in commands.globalopts:
566 c.append((o[0], o[1], options[o[1]], o[3]))
565 c.append((o[0], o[1], options[o[1]], o[3]))
567
566
568 try:
567 try:
569 args = fancyopts.fancyopts(args, c, cmdoptions, gnu=True)
568 args = fancyopts.fancyopts(args, c, cmdoptions, gnu=True)
570 except getopt.GetoptError as inst:
569 except getopt.GetoptError as inst:
571 raise error.CommandError(cmd, inst)
570 raise error.CommandError(cmd, inst)
572
571
573 # separate global options back out
572 # separate global options back out
574 for o in commands.globalopts:
573 for o in commands.globalopts:
575 n = o[1]
574 n = o[1]
576 options[n] = cmdoptions[n]
575 options[n] = cmdoptions[n]
577 del cmdoptions[n]
576 del cmdoptions[n]
578
577
579 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
578 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
580
579
581 def _parseconfig(ui, config):
580 def _parseconfig(ui, config):
582 """parse the --config options from the command line"""
581 """parse the --config options from the command line"""
583 configs = []
582 configs = []
584
583
585 for cfg in config:
584 for cfg in config:
586 try:
585 try:
587 name, value = [cfgelem.strip()
586 name, value = [cfgelem.strip()
588 for cfgelem in cfg.split('=', 1)]
587 for cfgelem in cfg.split('=', 1)]
589 section, name = name.split('.', 1)
588 section, name = name.split('.', 1)
590 if not section or not name:
589 if not section or not name:
591 raise IndexError
590 raise IndexError
592 ui.setconfig(section, name, value, '--config')
591 ui.setconfig(section, name, value, '--config')
593 configs.append((section, name, value))
592 configs.append((section, name, value))
594 except (IndexError, ValueError):
593 except (IndexError, ValueError):
595 raise error.Abort(_('malformed --config option: %r '
594 raise error.Abort(_('malformed --config option: %r '
596 '(use --config section.name=value)') % cfg)
595 '(use --config section.name=value)') % cfg)
597
596
598 return configs
597 return configs
599
598
600 def _earlygetopt(aliases, args):
599 def _earlygetopt(aliases, args):
601 """Return list of values for an option (or aliases).
600 """Return list of values for an option (or aliases).
602
601
603 The values are listed in the order they appear in args.
602 The values are listed in the order they appear in args.
604 The options and values are removed from args.
603 The options and values are removed from args.
605
604
606 >>> args = ['x', '--cwd', 'foo', 'y']
605 >>> args = ['x', '--cwd', 'foo', 'y']
607 >>> _earlygetopt(['--cwd'], args), args
606 >>> _earlygetopt(['--cwd'], args), args
608 (['foo'], ['x', 'y'])
607 (['foo'], ['x', 'y'])
609
608
610 >>> args = ['x', '--cwd=bar', 'y']
609 >>> args = ['x', '--cwd=bar', 'y']
611 >>> _earlygetopt(['--cwd'], args), args
610 >>> _earlygetopt(['--cwd'], args), args
612 (['bar'], ['x', 'y'])
611 (['bar'], ['x', 'y'])
613
612
614 >>> args = ['x', '-R', 'foo', 'y']
613 >>> args = ['x', '-R', 'foo', 'y']
615 >>> _earlygetopt(['-R'], args), args
614 >>> _earlygetopt(['-R'], args), args
616 (['foo'], ['x', 'y'])
615 (['foo'], ['x', 'y'])
617
616
618 >>> args = ['x', '-Rbar', 'y']
617 >>> args = ['x', '-Rbar', 'y']
619 >>> _earlygetopt(['-R'], args), args
618 >>> _earlygetopt(['-R'], args), args
620 (['bar'], ['x', 'y'])
619 (['bar'], ['x', 'y'])
621 """
620 """
622 try:
621 try:
623 argcount = args.index("--")
622 argcount = args.index("--")
624 except ValueError:
623 except ValueError:
625 argcount = len(args)
624 argcount = len(args)
626 shortopts = [opt for opt in aliases if len(opt) == 2]
625 shortopts = [opt for opt in aliases if len(opt) == 2]
627 values = []
626 values = []
628 pos = 0
627 pos = 0
629 while pos < argcount:
628 while pos < argcount:
630 fullarg = arg = args[pos]
629 fullarg = arg = args[pos]
631 equals = arg.find('=')
630 equals = arg.find('=')
632 if equals > -1:
631 if equals > -1:
633 arg = arg[:equals]
632 arg = arg[:equals]
634 if arg in aliases:
633 if arg in aliases:
635 del args[pos]
634 del args[pos]
636 if equals > -1:
635 if equals > -1:
637 values.append(fullarg[equals + 1:])
636 values.append(fullarg[equals + 1:])
638 argcount -= 1
637 argcount -= 1
639 else:
638 else:
640 if pos + 1 >= argcount:
639 if pos + 1 >= argcount:
641 # ignore and let getopt report an error if there is no value
640 # ignore and let getopt report an error if there is no value
642 break
641 break
643 values.append(args.pop(pos))
642 values.append(args.pop(pos))
644 argcount -= 2
643 argcount -= 2
645 elif arg[:2] in shortopts:
644 elif arg[:2] in shortopts:
646 # short option can have no following space, e.g. hg log -Rfoo
645 # short option can have no following space, e.g. hg log -Rfoo
647 values.append(args.pop(pos)[2:])
646 values.append(args.pop(pos)[2:])
648 argcount -= 1
647 argcount -= 1
649 else:
648 else:
650 pos += 1
649 pos += 1
651 return values
650 return values
652
651
653 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
652 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
654 # run pre-hook, and abort if it fails
653 # run pre-hook, and abort if it fails
655 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
654 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
656 pats=cmdpats, opts=cmdoptions)
655 pats=cmdpats, opts=cmdoptions)
657 try:
656 try:
658 ret = _runcommand(ui, options, cmd, d)
657 ret = _runcommand(ui, options, cmd, d)
659 # run post-hook, passing command result
658 # run post-hook, passing command result
660 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
659 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
661 result=ret, pats=cmdpats, opts=cmdoptions)
660 result=ret, pats=cmdpats, opts=cmdoptions)
662 except Exception:
661 except Exception:
663 # run failure hook and re-raise
662 # run failure hook and re-raise
664 hook.hook(lui, repo, "fail-%s" % cmd, False, args=" ".join(fullargs),
663 hook.hook(lui, repo, "fail-%s" % cmd, False, args=" ".join(fullargs),
665 pats=cmdpats, opts=cmdoptions)
664 pats=cmdpats, opts=cmdoptions)
666 raise
665 raise
667 return ret
666 return ret
668
667
669 def _getlocal(ui, rpath, wd=None):
668 def _getlocal(ui, rpath, wd=None):
670 """Return (path, local ui object) for the given target path.
669 """Return (path, local ui object) for the given target path.
671
670
672 Takes paths in [cwd]/.hg/hgrc into account."
671 Takes paths in [cwd]/.hg/hgrc into account."
673 """
672 """
674 if wd is None:
673 if wd is None:
675 try:
674 try:
676 wd = pycompat.getcwd()
675 wd = pycompat.getcwd()
677 except OSError as e:
676 except OSError as e:
678 raise error.Abort(_("error getting current working directory: %s") %
677 raise error.Abort(_("error getting current working directory: %s") %
679 e.strerror)
678 e.strerror)
680 path = cmdutil.findrepo(wd) or ""
679 path = cmdutil.findrepo(wd) or ""
681 if not path:
680 if not path:
682 lui = ui
681 lui = ui
683 else:
682 else:
684 lui = ui.copy()
683 lui = ui.copy()
685 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
684 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
686
685
687 if rpath and rpath[-1]:
686 if rpath and rpath[-1]:
688 path = lui.expandpath(rpath[-1])
687 path = lui.expandpath(rpath[-1])
689 lui = ui.copy()
688 lui = ui.copy()
690 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
689 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
691
690
692 return path, lui
691 return path, lui
693
692
694 def _checkshellalias(lui, ui, args):
693 def _checkshellalias(lui, ui, args):
695 """Return the function to run the shell alias, if it is required"""
694 """Return the function to run the shell alias, if it is required"""
696 options = {}
695 options = {}
697
696
698 try:
697 try:
699 args = fancyopts.fancyopts(args, commands.globalopts, options)
698 args = fancyopts.fancyopts(args, commands.globalopts, options)
700 except getopt.GetoptError:
699 except getopt.GetoptError:
701 return
700 return
702
701
703 if not args:
702 if not args:
704 return
703 return
705
704
706 cmdtable = commands.table
705 cmdtable = commands.table
707
706
708 cmd = args[0]
707 cmd = args[0]
709 try:
708 try:
710 strict = ui.configbool("ui", "strict")
709 strict = ui.configbool("ui", "strict")
711 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
710 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
712 except (error.AmbiguousCommand, error.UnknownCommand):
711 except (error.AmbiguousCommand, error.UnknownCommand):
713 return
712 return
714
713
715 cmd = aliases[0]
714 cmd = aliases[0]
716 fn = entry[0]
715 fn = entry[0]
717
716
718 if cmd and util.safehasattr(fn, 'shell'):
717 if cmd and util.safehasattr(fn, 'shell'):
719 d = lambda: fn(ui, *args[1:])
718 d = lambda: fn(ui, *args[1:])
720 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
719 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
721 [], {})
720 [], {})
722
721
723 _loaded = set()
722 _loaded = set()
724
723
725 # list of (objname, loadermod, loadername) tuple:
724 # list of (objname, loadermod, loadername) tuple:
726 # - objname is the name of an object in extension module, from which
725 # - objname is the name of an object in extension module, from which
727 # extra information is loaded
726 # extra information is loaded
728 # - loadermod is the module where loader is placed
727 # - loadermod is the module where loader is placed
729 # - loadername is the name of the function, which takes (ui, extensionname,
728 # - loadername is the name of the function, which takes (ui, extensionname,
730 # extraobj) arguments
729 # extraobj) arguments
731 extraloaders = [
730 extraloaders = [
732 ('cmdtable', commands, 'loadcmdtable'),
731 ('cmdtable', commands, 'loadcmdtable'),
733 ('colortable', color, 'loadcolortable'),
732 ('colortable', color, 'loadcolortable'),
734 ('filesetpredicate', fileset, 'loadpredicate'),
733 ('filesetpredicate', fileset, 'loadpredicate'),
735 ('revsetpredicate', revset, 'loadpredicate'),
734 ('revsetpredicate', revset, 'loadpredicate'),
736 ('templatefilter', templatefilters, 'loadfilter'),
735 ('templatefilter', templatefilters, 'loadfilter'),
737 ('templatefunc', templater, 'loadfunction'),
736 ('templatefunc', templater, 'loadfunction'),
738 ('templatekeyword', templatekw, 'loadkeyword'),
737 ('templatekeyword', templatekw, 'loadkeyword'),
739 ]
738 ]
740
739
741 def _dispatch(req):
740 def _dispatch(req):
742 args = req.args
741 args = req.args
743 ui = req.ui
742 ui = req.ui
744
743
745 # check for cwd
744 # check for cwd
746 cwd = _earlygetopt(['--cwd'], args)
745 cwd = _earlygetopt(['--cwd'], args)
747 if cwd:
746 if cwd:
748 os.chdir(cwd[-1])
747 os.chdir(cwd[-1])
749
748
750 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
749 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
751 path, lui = _getlocal(ui, rpath)
750 path, lui = _getlocal(ui, rpath)
752
751
753 # Side-effect of accessing is debugcommands module is guaranteed to be
754 # imported and commands.table is populated.
755 debugcommands.command
756
757 uis = {ui, lui}
752 uis = {ui, lui}
758
753
759 if req.repo:
754 if req.repo:
760 uis.add(req.repo.ui)
755 uis.add(req.repo.ui)
761
756
762 if '--profile' in args:
757 if '--profile' in args:
763 for ui_ in uis:
758 for ui_ in uis:
764 ui_.setconfig('profiling', 'enabled', 'true', '--profile')
759 ui_.setconfig('profiling', 'enabled', 'true', '--profile')
765
760
766 with profiling.maybeprofile(lui):
761 with profiling.maybeprofile(lui):
767 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
762 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
768 # reposetup. Programs like TortoiseHg will call _dispatch several
763 # reposetup. Programs like TortoiseHg will call _dispatch several
769 # times so we keep track of configured extensions in _loaded.
764 # times so we keep track of configured extensions in _loaded.
770 extensions.loadall(lui)
765 extensions.loadall(lui)
771 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
766 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
772 # Propagate any changes to lui.__class__ by extensions
767 # Propagate any changes to lui.__class__ by extensions
773 ui.__class__ = lui.__class__
768 ui.__class__ = lui.__class__
774
769
775 # (uisetup and extsetup are handled in extensions.loadall)
770 # (uisetup and extsetup are handled in extensions.loadall)
776
771
777 for name, module in exts:
772 for name, module in exts:
778 for objname, loadermod, loadername in extraloaders:
773 for objname, loadermod, loadername in extraloaders:
779 extraobj = getattr(module, objname, None)
774 extraobj = getattr(module, objname, None)
780 if extraobj is not None:
775 if extraobj is not None:
781 getattr(loadermod, loadername)(ui, name, extraobj)
776 getattr(loadermod, loadername)(ui, name, extraobj)
782 _loaded.add(name)
777 _loaded.add(name)
783
778
784 # (reposetup is handled in hg.repository)
779 # (reposetup is handled in hg.repository)
785
780
786 addaliases(lui, commands.table)
781 addaliases(lui, commands.table)
787
782
788 # All aliases and commands are completely defined, now.
783 # All aliases and commands are completely defined, now.
789 # Check abbreviation/ambiguity of shell alias.
784 # Check abbreviation/ambiguity of shell alias.
790 shellaliasfn = _checkshellalias(lui, ui, args)
785 shellaliasfn = _checkshellalias(lui, ui, args)
791 if shellaliasfn:
786 if shellaliasfn:
792 return shellaliasfn()
787 return shellaliasfn()
793
788
794 # check for fallback encoding
789 # check for fallback encoding
795 fallback = lui.config('ui', 'fallbackencoding')
790 fallback = lui.config('ui', 'fallbackencoding')
796 if fallback:
791 if fallback:
797 encoding.fallbackencoding = fallback
792 encoding.fallbackencoding = fallback
798
793
799 fullargs = args
794 fullargs = args
800 cmd, func, args, options, cmdoptions = _parse(lui, args)
795 cmd, func, args, options, cmdoptions = _parse(lui, args)
801
796
802 if options["config"]:
797 if options["config"]:
803 raise error.Abort(_("option --config may not be abbreviated!"))
798 raise error.Abort(_("option --config may not be abbreviated!"))
804 if options["cwd"]:
799 if options["cwd"]:
805 raise error.Abort(_("option --cwd may not be abbreviated!"))
800 raise error.Abort(_("option --cwd may not be abbreviated!"))
806 if options["repository"]:
801 if options["repository"]:
807 raise error.Abort(_(
802 raise error.Abort(_(
808 "option -R has to be separated from other options (e.g. not "
803 "option -R has to be separated from other options (e.g. not "
809 "-qR) and --repository may only be abbreviated as --repo!"))
804 "-qR) and --repository may only be abbreviated as --repo!"))
810
805
811 if options["encoding"]:
806 if options["encoding"]:
812 encoding.encoding = options["encoding"]
807 encoding.encoding = options["encoding"]
813 if options["encodingmode"]:
808 if options["encodingmode"]:
814 encoding.encodingmode = options["encodingmode"]
809 encoding.encodingmode = options["encodingmode"]
815 if options["time"]:
810 if options["time"]:
816 def get_times():
811 def get_times():
817 t = os.times()
812 t = os.times()
818 if t[4] == 0.0:
813 if t[4] == 0.0:
819 # Windows leaves this as zero, so use time.clock()
814 # Windows leaves this as zero, so use time.clock()
820 t = (t[0], t[1], t[2], t[3], time.clock())
815 t = (t[0], t[1], t[2], t[3], time.clock())
821 return t
816 return t
822 s = get_times()
817 s = get_times()
823 def print_time():
818 def print_time():
824 t = get_times()
819 t = get_times()
825 ui.warn(
820 ui.warn(
826 _("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
821 _("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
827 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
822 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
828 ui.atexit(print_time)
823 ui.atexit(print_time)
829
824
830 if options['verbose'] or options['debug'] or options['quiet']:
825 if options['verbose'] or options['debug'] or options['quiet']:
831 for opt in ('verbose', 'debug', 'quiet'):
826 for opt in ('verbose', 'debug', 'quiet'):
832 val = str(bool(options[opt]))
827 val = str(bool(options[opt]))
833 if pycompat.ispy3:
828 if pycompat.ispy3:
834 val = val.encode('ascii')
829 val = val.encode('ascii')
835 for ui_ in uis:
830 for ui_ in uis:
836 ui_.setconfig('ui', opt, val, '--' + opt)
831 ui_.setconfig('ui', opt, val, '--' + opt)
837
832
838 if options['traceback']:
833 if options['traceback']:
839 for ui_ in uis:
834 for ui_ in uis:
840 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
835 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
841
836
842 if options['noninteractive']:
837 if options['noninteractive']:
843 for ui_ in uis:
838 for ui_ in uis:
844 ui_.setconfig('ui', 'interactive', 'off', '-y')
839 ui_.setconfig('ui', 'interactive', 'off', '-y')
845
840
846 if util.parsebool(options['pager']):
841 if util.parsebool(options['pager']):
847 ui.pager('internal-always-' + cmd)
842 ui.pager('internal-always-' + cmd)
848 elif options['pager'] != 'auto':
843 elif options['pager'] != 'auto':
849 ui.disablepager()
844 ui.disablepager()
850
845
851 if cmdoptions.get('insecure', False):
846 if cmdoptions.get('insecure', False):
852 for ui_ in uis:
847 for ui_ in uis:
853 ui_.insecureconnections = True
848 ui_.insecureconnections = True
854
849
855 # setup color handling
850 # setup color handling
856 coloropt = options['color']
851 coloropt = options['color']
857 for ui_ in uis:
852 for ui_ in uis:
858 if coloropt:
853 if coloropt:
859 ui_.setconfig('ui', 'color', coloropt, '--color')
854 ui_.setconfig('ui', 'color', coloropt, '--color')
860 color.setup(ui_)
855 color.setup(ui_)
861
856
862 if options['version']:
857 if options['version']:
863 return commands.version_(ui)
858 return commands.version_(ui)
864 if options['help']:
859 if options['help']:
865 return commands.help_(ui, cmd, command=cmd is not None)
860 return commands.help_(ui, cmd, command=cmd is not None)
866 elif not cmd:
861 elif not cmd:
867 return commands.help_(ui, 'shortlist')
862 return commands.help_(ui, 'shortlist')
868
863
869 repo = None
864 repo = None
870 cmdpats = args[:]
865 cmdpats = args[:]
871 if not func.norepo:
866 if not func.norepo:
872 # use the repo from the request only if we don't have -R
867 # use the repo from the request only if we don't have -R
873 if not rpath and not cwd:
868 if not rpath and not cwd:
874 repo = req.repo
869 repo = req.repo
875
870
876 if repo:
871 if repo:
877 # set the descriptors of the repo ui to those of ui
872 # set the descriptors of the repo ui to those of ui
878 repo.ui.fin = ui.fin
873 repo.ui.fin = ui.fin
879 repo.ui.fout = ui.fout
874 repo.ui.fout = ui.fout
880 repo.ui.ferr = ui.ferr
875 repo.ui.ferr = ui.ferr
881 else:
876 else:
882 try:
877 try:
883 repo = hg.repository(ui, path=path)
878 repo = hg.repository(ui, path=path)
884 if not repo.local():
879 if not repo.local():
885 raise error.Abort(_("repository '%s' is not local")
880 raise error.Abort(_("repository '%s' is not local")
886 % path)
881 % path)
887 repo.ui.setconfig("bundle", "mainreporoot", repo.root,
882 repo.ui.setconfig("bundle", "mainreporoot", repo.root,
888 'repo')
883 'repo')
889 except error.RequirementError:
884 except error.RequirementError:
890 raise
885 raise
891 except error.RepoError:
886 except error.RepoError:
892 if rpath and rpath[-1]: # invalid -R path
887 if rpath and rpath[-1]: # invalid -R path
893 raise
888 raise
894 if not func.optionalrepo:
889 if not func.optionalrepo:
895 if func.inferrepo and args and not path:
890 if func.inferrepo and args and not path:
896 # try to infer -R from command args
891 # try to infer -R from command args
897 repos = map(cmdutil.findrepo, args)
892 repos = map(cmdutil.findrepo, args)
898 guess = repos[0]
893 guess = repos[0]
899 if guess and repos.count(guess) == len(repos):
894 if guess and repos.count(guess) == len(repos):
900 req.args = ['--repository', guess] + fullargs
895 req.args = ['--repository', guess] + fullargs
901 return _dispatch(req)
896 return _dispatch(req)
902 if not path:
897 if not path:
903 raise error.RepoError(_("no repository found in"
898 raise error.RepoError(_("no repository found in"
904 " '%s' (.hg not found)")
899 " '%s' (.hg not found)")
905 % pycompat.getcwd())
900 % pycompat.getcwd())
906 raise
901 raise
907 if repo:
902 if repo:
908 ui = repo.ui
903 ui = repo.ui
909 if options['hidden']:
904 if options['hidden']:
910 repo = repo.unfiltered()
905 repo = repo.unfiltered()
911 args.insert(0, repo)
906 args.insert(0, repo)
912 elif rpath:
907 elif rpath:
913 ui.warn(_("warning: --repository ignored\n"))
908 ui.warn(_("warning: --repository ignored\n"))
914
909
915 msg = _formatargs(fullargs)
910 msg = _formatargs(fullargs)
916 ui.log("command", '%s\n', msg)
911 ui.log("command", '%s\n', msg)
917 strcmdopt = pycompat.strkwargs(cmdoptions)
912 strcmdopt = pycompat.strkwargs(cmdoptions)
918 d = lambda: util.checksignature(func)(ui, *args, **strcmdopt)
913 d = lambda: util.checksignature(func)(ui, *args, **strcmdopt)
919 try:
914 try:
920 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
915 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
921 cmdpats, cmdoptions)
916 cmdpats, cmdoptions)
922 finally:
917 finally:
923 if repo and repo != req.repo:
918 if repo and repo != req.repo:
924 repo.close()
919 repo.close()
925
920
926 def _runcommand(ui, options, cmd, cmdfunc):
921 def _runcommand(ui, options, cmd, cmdfunc):
927 """Run a command function, possibly with profiling enabled."""
922 """Run a command function, possibly with profiling enabled."""
928 try:
923 try:
929 return cmdfunc()
924 return cmdfunc()
930 except error.SignatureError:
925 except error.SignatureError:
931 raise error.CommandError(cmd, _('invalid arguments'))
926 raise error.CommandError(cmd, _('invalid arguments'))
932
927
933 def _exceptionwarning(ui):
928 def _exceptionwarning(ui):
934 """Produce a warning message for the current active exception"""
929 """Produce a warning message for the current active exception"""
935
930
936 # For compatibility checking, we discard the portion of the hg
931 # For compatibility checking, we discard the portion of the hg
937 # version after the + on the assumption that if a "normal
932 # version after the + on the assumption that if a "normal
938 # user" is running a build with a + in it the packager
933 # user" is running a build with a + in it the packager
939 # probably built from fairly close to a tag and anyone with a
934 # probably built from fairly close to a tag and anyone with a
940 # 'make local' copy of hg (where the version number can be out
935 # 'make local' copy of hg (where the version number can be out
941 # of date) will be clueful enough to notice the implausible
936 # of date) will be clueful enough to notice the implausible
942 # version number and try updating.
937 # version number and try updating.
943 ct = util.versiontuple(n=2)
938 ct = util.versiontuple(n=2)
944 worst = None, ct, ''
939 worst = None, ct, ''
945 if ui.config('ui', 'supportcontact', None) is None:
940 if ui.config('ui', 'supportcontact', None) is None:
946 for name, mod in extensions.extensions():
941 for name, mod in extensions.extensions():
947 testedwith = getattr(mod, 'testedwith', '')
942 testedwith = getattr(mod, 'testedwith', '')
948 if pycompat.ispy3 and isinstance(testedwith, str):
943 if pycompat.ispy3 and isinstance(testedwith, str):
949 testedwith = testedwith.encode(u'utf-8')
944 testedwith = testedwith.encode(u'utf-8')
950 report = getattr(mod, 'buglink', _('the extension author.'))
945 report = getattr(mod, 'buglink', _('the extension author.'))
951 if not testedwith.strip():
946 if not testedwith.strip():
952 # We found an untested extension. It's likely the culprit.
947 # We found an untested extension. It's likely the culprit.
953 worst = name, 'unknown', report
948 worst = name, 'unknown', report
954 break
949 break
955
950
956 # Never blame on extensions bundled with Mercurial.
951 # Never blame on extensions bundled with Mercurial.
957 if extensions.ismoduleinternal(mod):
952 if extensions.ismoduleinternal(mod):
958 continue
953 continue
959
954
960 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
955 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
961 if ct in tested:
956 if ct in tested:
962 continue
957 continue
963
958
964 lower = [t for t in tested if t < ct]
959 lower = [t for t in tested if t < ct]
965 nearest = max(lower or tested)
960 nearest = max(lower or tested)
966 if worst[0] is None or nearest < worst[1]:
961 if worst[0] is None or nearest < worst[1]:
967 worst = name, nearest, report
962 worst = name, nearest, report
968 if worst[0] is not None:
963 if worst[0] is not None:
969 name, testedwith, report = worst
964 name, testedwith, report = worst
970 if not isinstance(testedwith, (bytes, str)):
965 if not isinstance(testedwith, (bytes, str)):
971 testedwith = '.'.join([str(c) for c in testedwith])
966 testedwith = '.'.join([str(c) for c in testedwith])
972 warning = (_('** Unknown exception encountered with '
967 warning = (_('** Unknown exception encountered with '
973 'possibly-broken third-party extension %s\n'
968 'possibly-broken third-party extension %s\n'
974 '** which supports versions %s of Mercurial.\n'
969 '** which supports versions %s of Mercurial.\n'
975 '** Please disable %s and try your action again.\n'
970 '** Please disable %s and try your action again.\n'
976 '** If that fixes the bug please report it to %s\n')
971 '** If that fixes the bug please report it to %s\n')
977 % (name, testedwith, name, report))
972 % (name, testedwith, name, report))
978 else:
973 else:
979 bugtracker = ui.config('ui', 'supportcontact', None)
974 bugtracker = ui.config('ui', 'supportcontact', None)
980 if bugtracker is None:
975 if bugtracker is None:
981 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker")
976 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker")
982 warning = (_("** unknown exception encountered, "
977 warning = (_("** unknown exception encountered, "
983 "please report by visiting\n** ") + bugtracker + '\n')
978 "please report by visiting\n** ") + bugtracker + '\n')
984 if pycompat.ispy3:
979 if pycompat.ispy3:
985 sysversion = sys.version.encode(u'utf-8')
980 sysversion = sys.version.encode(u'utf-8')
986 else:
981 else:
987 sysversion = sys.version
982 sysversion = sys.version
988 sysversion = sysversion.replace('\n', '')
983 sysversion = sysversion.replace('\n', '')
989 warning += ((_("** Python %s\n") % sysversion) +
984 warning += ((_("** Python %s\n") % sysversion) +
990 (_("** Mercurial Distributed SCM (version %s)\n") %
985 (_("** Mercurial Distributed SCM (version %s)\n") %
991 util.version()) +
986 util.version()) +
992 (_("** Extensions loaded: %s\n") %
987 (_("** Extensions loaded: %s\n") %
993 ", ".join([x[0] for x in extensions.extensions()])))
988 ", ".join([x[0] for x in extensions.extensions()])))
994 return warning
989 return warning
995
990
996 def handlecommandexception(ui):
991 def handlecommandexception(ui):
997 """Produce a warning message for broken commands
992 """Produce a warning message for broken commands
998
993
999 Called when handling an exception; the exception is reraised if
994 Called when handling an exception; the exception is reraised if
1000 this function returns False, ignored otherwise.
995 this function returns False, ignored otherwise.
1001 """
996 """
1002 warning = _exceptionwarning(ui)
997 warning = _exceptionwarning(ui)
1003 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
998 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
1004 ui.warn(warning)
999 ui.warn(warning)
1005 return False # re-raise the exception
1000 return False # re-raise the exception
General Comments 0
You need to be logged in to leave comments. Login now