##// END OF EJS Templates
commands: use byteskwargs() in verify()...
Gregory Szorc -
r42368:90d48c1c default
parent child Browse files
Show More
@@ -1,6256 +1,6258 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 import sys
14 import sys
15
15
16 from .i18n import _
16 from .i18n import _
17 from .node import (
17 from .node import (
18 hex,
18 hex,
19 nullid,
19 nullid,
20 nullrev,
20 nullrev,
21 short,
21 short,
22 wdirhex,
22 wdirhex,
23 wdirrev,
23 wdirrev,
24 )
24 )
25 from . import (
25 from . import (
26 archival,
26 archival,
27 bookmarks,
27 bookmarks,
28 bundle2,
28 bundle2,
29 changegroup,
29 changegroup,
30 cmdutil,
30 cmdutil,
31 copies,
31 copies,
32 debugcommands as debugcommandsmod,
32 debugcommands as debugcommandsmod,
33 destutil,
33 destutil,
34 dirstateguard,
34 dirstateguard,
35 discovery,
35 discovery,
36 encoding,
36 encoding,
37 error,
37 error,
38 exchange,
38 exchange,
39 extensions,
39 extensions,
40 filemerge,
40 filemerge,
41 formatter,
41 formatter,
42 graphmod,
42 graphmod,
43 hbisect,
43 hbisect,
44 help,
44 help,
45 hg,
45 hg,
46 logcmdutil,
46 logcmdutil,
47 merge as mergemod,
47 merge as mergemod,
48 narrowspec,
48 narrowspec,
49 obsolete,
49 obsolete,
50 obsutil,
50 obsutil,
51 patch,
51 patch,
52 phases,
52 phases,
53 pycompat,
53 pycompat,
54 rcutil,
54 rcutil,
55 registrar,
55 registrar,
56 repair,
56 repair,
57 revsetlang,
57 revsetlang,
58 rewriteutil,
58 rewriteutil,
59 scmutil,
59 scmutil,
60 server,
60 server,
61 state as statemod,
61 state as statemod,
62 streamclone,
62 streamclone,
63 tags as tagsmod,
63 tags as tagsmod,
64 ui as uimod,
64 ui as uimod,
65 util,
65 util,
66 verify as verifymod,
66 verify as verifymod,
67 wireprotoserver,
67 wireprotoserver,
68 )
68 )
69 from .utils import (
69 from .utils import (
70 dateutil,
70 dateutil,
71 stringutil,
71 stringutil,
72 )
72 )
73
73
74 table = {}
74 table = {}
75 table.update(debugcommandsmod.command._table)
75 table.update(debugcommandsmod.command._table)
76
76
77 command = registrar.command(table)
77 command = registrar.command(table)
78 INTENT_READONLY = registrar.INTENT_READONLY
78 INTENT_READONLY = registrar.INTENT_READONLY
79
79
80 # common command options
80 # common command options
81
81
82 globalopts = [
82 globalopts = [
83 ('R', 'repository', '',
83 ('R', 'repository', '',
84 _('repository root directory or name of overlay bundle file'),
84 _('repository root directory or name of overlay bundle file'),
85 _('REPO')),
85 _('REPO')),
86 ('', 'cwd', '',
86 ('', 'cwd', '',
87 _('change working directory'), _('DIR')),
87 _('change working directory'), _('DIR')),
88 ('y', 'noninteractive', None,
88 ('y', 'noninteractive', None,
89 _('do not prompt, automatically pick the first choice for all prompts')),
89 _('do not prompt, automatically pick the first choice for all prompts')),
90 ('q', 'quiet', None, _('suppress output')),
90 ('q', 'quiet', None, _('suppress output')),
91 ('v', 'verbose', None, _('enable additional output')),
91 ('v', 'verbose', None, _('enable additional output')),
92 ('', 'color', '',
92 ('', 'color', '',
93 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
93 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
94 # and should not be translated
94 # and should not be translated
95 _("when to colorize (boolean, always, auto, never, or debug)"),
95 _("when to colorize (boolean, always, auto, never, or debug)"),
96 _('TYPE')),
96 _('TYPE')),
97 ('', 'config', [],
97 ('', 'config', [],
98 _('set/override config option (use \'section.name=value\')'),
98 _('set/override config option (use \'section.name=value\')'),
99 _('CONFIG')),
99 _('CONFIG')),
100 ('', 'debug', None, _('enable debugging output')),
100 ('', 'debug', None, _('enable debugging output')),
101 ('', 'debugger', None, _('start debugger')),
101 ('', 'debugger', None, _('start debugger')),
102 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
102 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
103 _('ENCODE')),
103 _('ENCODE')),
104 ('', 'encodingmode', encoding.encodingmode,
104 ('', 'encodingmode', encoding.encodingmode,
105 _('set the charset encoding mode'), _('MODE')),
105 _('set the charset encoding mode'), _('MODE')),
106 ('', 'traceback', None, _('always print a traceback on exception')),
106 ('', 'traceback', None, _('always print a traceback on exception')),
107 ('', 'time', None, _('time how long the command takes')),
107 ('', 'time', None, _('time how long the command takes')),
108 ('', 'profile', None, _('print command execution profile')),
108 ('', 'profile', None, _('print command execution profile')),
109 ('', 'version', None, _('output version information and exit')),
109 ('', 'version', None, _('output version information and exit')),
110 ('h', 'help', None, _('display help and exit')),
110 ('h', 'help', None, _('display help and exit')),
111 ('', 'hidden', False, _('consider hidden changesets')),
111 ('', 'hidden', False, _('consider hidden changesets')),
112 ('', 'pager', 'auto',
112 ('', 'pager', 'auto',
113 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
113 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
114 ]
114 ]
115
115
116 dryrunopts = cmdutil.dryrunopts
116 dryrunopts = cmdutil.dryrunopts
117 remoteopts = cmdutil.remoteopts
117 remoteopts = cmdutil.remoteopts
118 walkopts = cmdutil.walkopts
118 walkopts = cmdutil.walkopts
119 commitopts = cmdutil.commitopts
119 commitopts = cmdutil.commitopts
120 commitopts2 = cmdutil.commitopts2
120 commitopts2 = cmdutil.commitopts2
121 formatteropts = cmdutil.formatteropts
121 formatteropts = cmdutil.formatteropts
122 templateopts = cmdutil.templateopts
122 templateopts = cmdutil.templateopts
123 logopts = cmdutil.logopts
123 logopts = cmdutil.logopts
124 diffopts = cmdutil.diffopts
124 diffopts = cmdutil.diffopts
125 diffwsopts = cmdutil.diffwsopts
125 diffwsopts = cmdutil.diffwsopts
126 diffopts2 = cmdutil.diffopts2
126 diffopts2 = cmdutil.diffopts2
127 mergetoolopts = cmdutil.mergetoolopts
127 mergetoolopts = cmdutil.mergetoolopts
128 similarityopts = cmdutil.similarityopts
128 similarityopts = cmdutil.similarityopts
129 subrepoopts = cmdutil.subrepoopts
129 subrepoopts = cmdutil.subrepoopts
130 debugrevlogopts = cmdutil.debugrevlogopts
130 debugrevlogopts = cmdutil.debugrevlogopts
131
131
132 # Commands start here, listed alphabetically
132 # Commands start here, listed alphabetically
133
133
134 @command('add',
134 @command('add',
135 walkopts + subrepoopts + dryrunopts,
135 walkopts + subrepoopts + dryrunopts,
136 _('[OPTION]... [FILE]...'),
136 _('[OPTION]... [FILE]...'),
137 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
137 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
138 helpbasic=True, inferrepo=True)
138 helpbasic=True, inferrepo=True)
139 def add(ui, repo, *pats, **opts):
139 def add(ui, repo, *pats, **opts):
140 """add the specified files on the next commit
140 """add the specified files on the next commit
141
141
142 Schedule files to be version controlled and added to the
142 Schedule files to be version controlled and added to the
143 repository.
143 repository.
144
144
145 The files will be added to the repository at the next commit. To
145 The files will be added to the repository at the next commit. To
146 undo an add before that, see :hg:`forget`.
146 undo an add before that, see :hg:`forget`.
147
147
148 If no names are given, add all files to the repository (except
148 If no names are given, add all files to the repository (except
149 files matching ``.hgignore``).
149 files matching ``.hgignore``).
150
150
151 .. container:: verbose
151 .. container:: verbose
152
152
153 Examples:
153 Examples:
154
154
155 - New (unknown) files are added
155 - New (unknown) files are added
156 automatically by :hg:`add`::
156 automatically by :hg:`add`::
157
157
158 $ ls
158 $ ls
159 foo.c
159 foo.c
160 $ hg status
160 $ hg status
161 ? foo.c
161 ? foo.c
162 $ hg add
162 $ hg add
163 adding foo.c
163 adding foo.c
164 $ hg status
164 $ hg status
165 A foo.c
165 A foo.c
166
166
167 - Specific files to be added can be specified::
167 - Specific files to be added can be specified::
168
168
169 $ ls
169 $ ls
170 bar.c foo.c
170 bar.c foo.c
171 $ hg status
171 $ hg status
172 ? bar.c
172 ? bar.c
173 ? foo.c
173 ? foo.c
174 $ hg add bar.c
174 $ hg add bar.c
175 $ hg status
175 $ hg status
176 A bar.c
176 A bar.c
177 ? foo.c
177 ? foo.c
178
178
179 Returns 0 if all files are successfully added.
179 Returns 0 if all files are successfully added.
180 """
180 """
181
181
182 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
182 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
183 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
183 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
184 rejected = cmdutil.add(ui, repo, m, "", uipathfn, False, **opts)
184 rejected = cmdutil.add(ui, repo, m, "", uipathfn, False, **opts)
185 return rejected and 1 or 0
185 return rejected and 1 or 0
186
186
187 @command('addremove',
187 @command('addremove',
188 similarityopts + subrepoopts + walkopts + dryrunopts,
188 similarityopts + subrepoopts + walkopts + dryrunopts,
189 _('[OPTION]... [FILE]...'),
189 _('[OPTION]... [FILE]...'),
190 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
190 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
191 inferrepo=True)
191 inferrepo=True)
192 def addremove(ui, repo, *pats, **opts):
192 def addremove(ui, repo, *pats, **opts):
193 """add all new files, delete all missing files
193 """add all new files, delete all missing files
194
194
195 Add all new files and remove all missing files from the
195 Add all new files and remove all missing files from the
196 repository.
196 repository.
197
197
198 Unless names are given, new files are ignored if they match any of
198 Unless names are given, new files are ignored if they match any of
199 the patterns in ``.hgignore``. As with add, these changes take
199 the patterns in ``.hgignore``. As with add, these changes take
200 effect at the next commit.
200 effect at the next commit.
201
201
202 Use the -s/--similarity option to detect renamed files. This
202 Use the -s/--similarity option to detect renamed files. This
203 option takes a percentage between 0 (disabled) and 100 (files must
203 option takes a percentage between 0 (disabled) and 100 (files must
204 be identical) as its parameter. With a parameter greater than 0,
204 be identical) as its parameter. With a parameter greater than 0,
205 this compares every removed file with every added file and records
205 this compares every removed file with every added file and records
206 those similar enough as renames. Detecting renamed files this way
206 those similar enough as renames. Detecting renamed files this way
207 can be expensive. After using this option, :hg:`status -C` can be
207 can be expensive. After using this option, :hg:`status -C` can be
208 used to check which files were identified as moved or renamed. If
208 used to check which files were identified as moved or renamed. If
209 not specified, -s/--similarity defaults to 100 and only renames of
209 not specified, -s/--similarity defaults to 100 and only renames of
210 identical files are detected.
210 identical files are detected.
211
211
212 .. container:: verbose
212 .. container:: verbose
213
213
214 Examples:
214 Examples:
215
215
216 - A number of files (bar.c and foo.c) are new,
216 - A number of files (bar.c and foo.c) are new,
217 while foobar.c has been removed (without using :hg:`remove`)
217 while foobar.c has been removed (without using :hg:`remove`)
218 from the repository::
218 from the repository::
219
219
220 $ ls
220 $ ls
221 bar.c foo.c
221 bar.c foo.c
222 $ hg status
222 $ hg status
223 ! foobar.c
223 ! foobar.c
224 ? bar.c
224 ? bar.c
225 ? foo.c
225 ? foo.c
226 $ hg addremove
226 $ hg addremove
227 adding bar.c
227 adding bar.c
228 adding foo.c
228 adding foo.c
229 removing foobar.c
229 removing foobar.c
230 $ hg status
230 $ hg status
231 A bar.c
231 A bar.c
232 A foo.c
232 A foo.c
233 R foobar.c
233 R foobar.c
234
234
235 - A file foobar.c was moved to foo.c without using :hg:`rename`.
235 - A file foobar.c was moved to foo.c without using :hg:`rename`.
236 Afterwards, it was edited slightly::
236 Afterwards, it was edited slightly::
237
237
238 $ ls
238 $ ls
239 foo.c
239 foo.c
240 $ hg status
240 $ hg status
241 ! foobar.c
241 ! foobar.c
242 ? foo.c
242 ? foo.c
243 $ hg addremove --similarity 90
243 $ hg addremove --similarity 90
244 removing foobar.c
244 removing foobar.c
245 adding foo.c
245 adding foo.c
246 recording removal of foobar.c as rename to foo.c (94% similar)
246 recording removal of foobar.c as rename to foo.c (94% similar)
247 $ hg status -C
247 $ hg status -C
248 A foo.c
248 A foo.c
249 foobar.c
249 foobar.c
250 R foobar.c
250 R foobar.c
251
251
252 Returns 0 if all files are successfully added.
252 Returns 0 if all files are successfully added.
253 """
253 """
254 opts = pycompat.byteskwargs(opts)
254 opts = pycompat.byteskwargs(opts)
255 if not opts.get('similarity'):
255 if not opts.get('similarity'):
256 opts['similarity'] = '100'
256 opts['similarity'] = '100'
257 matcher = scmutil.match(repo[None], pats, opts)
257 matcher = scmutil.match(repo[None], pats, opts)
258 relative = scmutil.anypats(pats, opts)
258 relative = scmutil.anypats(pats, opts)
259 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
259 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
260 return scmutil.addremove(repo, matcher, "", uipathfn, opts)
260 return scmutil.addremove(repo, matcher, "", uipathfn, opts)
261
261
262 @command('annotate|blame',
262 @command('annotate|blame',
263 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
263 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
264 ('', 'follow', None,
264 ('', 'follow', None,
265 _('follow copies/renames and list the filename (DEPRECATED)')),
265 _('follow copies/renames and list the filename (DEPRECATED)')),
266 ('', 'no-follow', None, _("don't follow copies and renames")),
266 ('', 'no-follow', None, _("don't follow copies and renames")),
267 ('a', 'text', None, _('treat all files as text')),
267 ('a', 'text', None, _('treat all files as text')),
268 ('u', 'user', None, _('list the author (long with -v)')),
268 ('u', 'user', None, _('list the author (long with -v)')),
269 ('f', 'file', None, _('list the filename')),
269 ('f', 'file', None, _('list the filename')),
270 ('d', 'date', None, _('list the date (short with -q)')),
270 ('d', 'date', None, _('list the date (short with -q)')),
271 ('n', 'number', None, _('list the revision number (default)')),
271 ('n', 'number', None, _('list the revision number (default)')),
272 ('c', 'changeset', None, _('list the changeset')),
272 ('c', 'changeset', None, _('list the changeset')),
273 ('l', 'line-number', None, _('show line number at the first appearance')),
273 ('l', 'line-number', None, _('show line number at the first appearance')),
274 ('', 'skip', [], _('revision to not display (EXPERIMENTAL)'), _('REV')),
274 ('', 'skip', [], _('revision to not display (EXPERIMENTAL)'), _('REV')),
275 ] + diffwsopts + walkopts + formatteropts,
275 ] + diffwsopts + walkopts + formatteropts,
276 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
276 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
277 helpcategory=command.CATEGORY_FILE_CONTENTS,
277 helpcategory=command.CATEGORY_FILE_CONTENTS,
278 helpbasic=True, inferrepo=True)
278 helpbasic=True, inferrepo=True)
279 def annotate(ui, repo, *pats, **opts):
279 def annotate(ui, repo, *pats, **opts):
280 """show changeset information by line for each file
280 """show changeset information by line for each file
281
281
282 List changes in files, showing the revision id responsible for
282 List changes in files, showing the revision id responsible for
283 each line.
283 each line.
284
284
285 This command is useful for discovering when a change was made and
285 This command is useful for discovering when a change was made and
286 by whom.
286 by whom.
287
287
288 If you include --file, --user, or --date, the revision number is
288 If you include --file, --user, or --date, the revision number is
289 suppressed unless you also include --number.
289 suppressed unless you also include --number.
290
290
291 Without the -a/--text option, annotate will avoid processing files
291 Without the -a/--text option, annotate will avoid processing files
292 it detects as binary. With -a, annotate will annotate the file
292 it detects as binary. With -a, annotate will annotate the file
293 anyway, although the results will probably be neither useful
293 anyway, although the results will probably be neither useful
294 nor desirable.
294 nor desirable.
295
295
296 .. container:: verbose
296 .. container:: verbose
297
297
298 Template:
298 Template:
299
299
300 The following keywords are supported in addition to the common template
300 The following keywords are supported in addition to the common template
301 keywords and functions. See also :hg:`help templates`.
301 keywords and functions. See also :hg:`help templates`.
302
302
303 :lines: List of lines with annotation data.
303 :lines: List of lines with annotation data.
304 :path: String. Repository-absolute path of the specified file.
304 :path: String. Repository-absolute path of the specified file.
305
305
306 And each entry of ``{lines}`` provides the following sub-keywords in
306 And each entry of ``{lines}`` provides the following sub-keywords in
307 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
307 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
308
308
309 :line: String. Line content.
309 :line: String. Line content.
310 :lineno: Integer. Line number at that revision.
310 :lineno: Integer. Line number at that revision.
311 :path: String. Repository-absolute path of the file at that revision.
311 :path: String. Repository-absolute path of the file at that revision.
312
312
313 See :hg:`help templates.operators` for the list expansion syntax.
313 See :hg:`help templates.operators` for the list expansion syntax.
314
314
315 Returns 0 on success.
315 Returns 0 on success.
316 """
316 """
317 opts = pycompat.byteskwargs(opts)
317 opts = pycompat.byteskwargs(opts)
318 if not pats:
318 if not pats:
319 raise error.Abort(_('at least one filename or pattern is required'))
319 raise error.Abort(_('at least one filename or pattern is required'))
320
320
321 if opts.get('follow'):
321 if opts.get('follow'):
322 # --follow is deprecated and now just an alias for -f/--file
322 # --follow is deprecated and now just an alias for -f/--file
323 # to mimic the behavior of Mercurial before version 1.5
323 # to mimic the behavior of Mercurial before version 1.5
324 opts['file'] = True
324 opts['file'] = True
325
325
326 if (not opts.get('user') and not opts.get('changeset')
326 if (not opts.get('user') and not opts.get('changeset')
327 and not opts.get('date') and not opts.get('file')):
327 and not opts.get('date') and not opts.get('file')):
328 opts['number'] = True
328 opts['number'] = True
329
329
330 linenumber = opts.get('line_number') is not None
330 linenumber = opts.get('line_number') is not None
331 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
331 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
332 raise error.Abort(_('at least one of -n/-c is required for -l'))
332 raise error.Abort(_('at least one of -n/-c is required for -l'))
333
333
334 rev = opts.get('rev')
334 rev = opts.get('rev')
335 if rev:
335 if rev:
336 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
336 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
337 ctx = scmutil.revsingle(repo, rev)
337 ctx = scmutil.revsingle(repo, rev)
338
338
339 ui.pager('annotate')
339 ui.pager('annotate')
340 rootfm = ui.formatter('annotate', opts)
340 rootfm = ui.formatter('annotate', opts)
341 if ui.debugflag:
341 if ui.debugflag:
342 shorthex = pycompat.identity
342 shorthex = pycompat.identity
343 else:
343 else:
344 def shorthex(h):
344 def shorthex(h):
345 return h[:12]
345 return h[:12]
346 if ui.quiet:
346 if ui.quiet:
347 datefunc = dateutil.shortdate
347 datefunc = dateutil.shortdate
348 else:
348 else:
349 datefunc = dateutil.datestr
349 datefunc = dateutil.datestr
350 if ctx.rev() is None:
350 if ctx.rev() is None:
351 if opts.get('changeset'):
351 if opts.get('changeset'):
352 # omit "+" suffix which is appended to node hex
352 # omit "+" suffix which is appended to node hex
353 def formatrev(rev):
353 def formatrev(rev):
354 if rev == wdirrev:
354 if rev == wdirrev:
355 return '%d' % ctx.p1().rev()
355 return '%d' % ctx.p1().rev()
356 else:
356 else:
357 return '%d' % rev
357 return '%d' % rev
358 else:
358 else:
359 def formatrev(rev):
359 def formatrev(rev):
360 if rev == wdirrev:
360 if rev == wdirrev:
361 return '%d+' % ctx.p1().rev()
361 return '%d+' % ctx.p1().rev()
362 else:
362 else:
363 return '%d ' % rev
363 return '%d ' % rev
364 def formathex(h):
364 def formathex(h):
365 if h == wdirhex:
365 if h == wdirhex:
366 return '%s+' % shorthex(hex(ctx.p1().node()))
366 return '%s+' % shorthex(hex(ctx.p1().node()))
367 else:
367 else:
368 return '%s ' % shorthex(h)
368 return '%s ' % shorthex(h)
369 else:
369 else:
370 formatrev = b'%d'.__mod__
370 formatrev = b'%d'.__mod__
371 formathex = shorthex
371 formathex = shorthex
372
372
373 opmap = [
373 opmap = [
374 ('user', ' ', lambda x: x.fctx.user(), ui.shortuser),
374 ('user', ' ', lambda x: x.fctx.user(), ui.shortuser),
375 ('rev', ' ', lambda x: scmutil.intrev(x.fctx), formatrev),
375 ('rev', ' ', lambda x: scmutil.intrev(x.fctx), formatrev),
376 ('node', ' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
376 ('node', ' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
377 ('date', ' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
377 ('date', ' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
378 ('path', ' ', lambda x: x.fctx.path(), pycompat.bytestr),
378 ('path', ' ', lambda x: x.fctx.path(), pycompat.bytestr),
379 ('lineno', ':', lambda x: x.lineno, pycompat.bytestr),
379 ('lineno', ':', lambda x: x.lineno, pycompat.bytestr),
380 ]
380 ]
381 opnamemap = {
381 opnamemap = {
382 'rev': 'number',
382 'rev': 'number',
383 'node': 'changeset',
383 'node': 'changeset',
384 'path': 'file',
384 'path': 'file',
385 'lineno': 'line_number',
385 'lineno': 'line_number',
386 }
386 }
387
387
388 if rootfm.isplain():
388 if rootfm.isplain():
389 def makefunc(get, fmt):
389 def makefunc(get, fmt):
390 return lambda x: fmt(get(x))
390 return lambda x: fmt(get(x))
391 else:
391 else:
392 def makefunc(get, fmt):
392 def makefunc(get, fmt):
393 return get
393 return get
394 datahint = rootfm.datahint()
394 datahint = rootfm.datahint()
395 funcmap = [(makefunc(get, fmt), sep) for fn, sep, get, fmt in opmap
395 funcmap = [(makefunc(get, fmt), sep) for fn, sep, get, fmt in opmap
396 if opts.get(opnamemap.get(fn, fn)) or fn in datahint]
396 if opts.get(opnamemap.get(fn, fn)) or fn in datahint]
397 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
397 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
398 fields = ' '.join(fn for fn, sep, get, fmt in opmap
398 fields = ' '.join(fn for fn, sep, get, fmt in opmap
399 if opts.get(opnamemap.get(fn, fn)) or fn in datahint)
399 if opts.get(opnamemap.get(fn, fn)) or fn in datahint)
400
400
401 def bad(x, y):
401 def bad(x, y):
402 raise error.Abort("%s: %s" % (x, y))
402 raise error.Abort("%s: %s" % (x, y))
403
403
404 m = scmutil.match(ctx, pats, opts, badfn=bad)
404 m = scmutil.match(ctx, pats, opts, badfn=bad)
405
405
406 follow = not opts.get('no_follow')
406 follow = not opts.get('no_follow')
407 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
407 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
408 whitespace=True)
408 whitespace=True)
409 skiprevs = opts.get('skip')
409 skiprevs = opts.get('skip')
410 if skiprevs:
410 if skiprevs:
411 skiprevs = scmutil.revrange(repo, skiprevs)
411 skiprevs = scmutil.revrange(repo, skiprevs)
412
412
413 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
413 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
414 for abs in ctx.walk(m):
414 for abs in ctx.walk(m):
415 fctx = ctx[abs]
415 fctx = ctx[abs]
416 rootfm.startitem()
416 rootfm.startitem()
417 rootfm.data(path=abs)
417 rootfm.data(path=abs)
418 if not opts.get('text') and fctx.isbinary():
418 if not opts.get('text') and fctx.isbinary():
419 rootfm.plain(_("%s: binary file\n") % uipathfn(abs))
419 rootfm.plain(_("%s: binary file\n") % uipathfn(abs))
420 continue
420 continue
421
421
422 fm = rootfm.nested('lines', tmpl='{rev}: {line}')
422 fm = rootfm.nested('lines', tmpl='{rev}: {line}')
423 lines = fctx.annotate(follow=follow, skiprevs=skiprevs,
423 lines = fctx.annotate(follow=follow, skiprevs=skiprevs,
424 diffopts=diffopts)
424 diffopts=diffopts)
425 if not lines:
425 if not lines:
426 fm.end()
426 fm.end()
427 continue
427 continue
428 formats = []
428 formats = []
429 pieces = []
429 pieces = []
430
430
431 for f, sep in funcmap:
431 for f, sep in funcmap:
432 l = [f(n) for n in lines]
432 l = [f(n) for n in lines]
433 if fm.isplain():
433 if fm.isplain():
434 sizes = [encoding.colwidth(x) for x in l]
434 sizes = [encoding.colwidth(x) for x in l]
435 ml = max(sizes)
435 ml = max(sizes)
436 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
436 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
437 else:
437 else:
438 formats.append(['%s' for x in l])
438 formats.append(['%s' for x in l])
439 pieces.append(l)
439 pieces.append(l)
440
440
441 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
441 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
442 fm.startitem()
442 fm.startitem()
443 fm.context(fctx=n.fctx)
443 fm.context(fctx=n.fctx)
444 fm.write(fields, "".join(f), *p)
444 fm.write(fields, "".join(f), *p)
445 if n.skip:
445 if n.skip:
446 fmt = "* %s"
446 fmt = "* %s"
447 else:
447 else:
448 fmt = ": %s"
448 fmt = ": %s"
449 fm.write('line', fmt, n.text)
449 fm.write('line', fmt, n.text)
450
450
451 if not lines[-1].text.endswith('\n'):
451 if not lines[-1].text.endswith('\n'):
452 fm.plain('\n')
452 fm.plain('\n')
453 fm.end()
453 fm.end()
454
454
455 rootfm.end()
455 rootfm.end()
456
456
457 @command('archive',
457 @command('archive',
458 [('', 'no-decode', None, _('do not pass files through decoders')),
458 [('', 'no-decode', None, _('do not pass files through decoders')),
459 ('p', 'prefix', '', _('directory prefix for files in archive'),
459 ('p', 'prefix', '', _('directory prefix for files in archive'),
460 _('PREFIX')),
460 _('PREFIX')),
461 ('r', 'rev', '', _('revision to distribute'), _('REV')),
461 ('r', 'rev', '', _('revision to distribute'), _('REV')),
462 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
462 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
463 ] + subrepoopts + walkopts,
463 ] + subrepoopts + walkopts,
464 _('[OPTION]... DEST'),
464 _('[OPTION]... DEST'),
465 helpcategory=command.CATEGORY_IMPORT_EXPORT)
465 helpcategory=command.CATEGORY_IMPORT_EXPORT)
466 def archive(ui, repo, dest, **opts):
466 def archive(ui, repo, dest, **opts):
467 '''create an unversioned archive of a repository revision
467 '''create an unversioned archive of a repository revision
468
468
469 By default, the revision used is the parent of the working
469 By default, the revision used is the parent of the working
470 directory; use -r/--rev to specify a different revision.
470 directory; use -r/--rev to specify a different revision.
471
471
472 The archive type is automatically detected based on file
472 The archive type is automatically detected based on file
473 extension (to override, use -t/--type).
473 extension (to override, use -t/--type).
474
474
475 .. container:: verbose
475 .. container:: verbose
476
476
477 Examples:
477 Examples:
478
478
479 - create a zip file containing the 1.0 release::
479 - create a zip file containing the 1.0 release::
480
480
481 hg archive -r 1.0 project-1.0.zip
481 hg archive -r 1.0 project-1.0.zip
482
482
483 - create a tarball excluding .hg files::
483 - create a tarball excluding .hg files::
484
484
485 hg archive project.tar.gz -X ".hg*"
485 hg archive project.tar.gz -X ".hg*"
486
486
487 Valid types are:
487 Valid types are:
488
488
489 :``files``: a directory full of files (default)
489 :``files``: a directory full of files (default)
490 :``tar``: tar archive, uncompressed
490 :``tar``: tar archive, uncompressed
491 :``tbz2``: tar archive, compressed using bzip2
491 :``tbz2``: tar archive, compressed using bzip2
492 :``tgz``: tar archive, compressed using gzip
492 :``tgz``: tar archive, compressed using gzip
493 :``uzip``: zip archive, uncompressed
493 :``uzip``: zip archive, uncompressed
494 :``zip``: zip archive, compressed using deflate
494 :``zip``: zip archive, compressed using deflate
495
495
496 The exact name of the destination archive or directory is given
496 The exact name of the destination archive or directory is given
497 using a format string; see :hg:`help export` for details.
497 using a format string; see :hg:`help export` for details.
498
498
499 Each member added to an archive file has a directory prefix
499 Each member added to an archive file has a directory prefix
500 prepended. Use -p/--prefix to specify a format string for the
500 prepended. Use -p/--prefix to specify a format string for the
501 prefix. The default is the basename of the archive, with suffixes
501 prefix. The default is the basename of the archive, with suffixes
502 removed.
502 removed.
503
503
504 Returns 0 on success.
504 Returns 0 on success.
505 '''
505 '''
506
506
507 opts = pycompat.byteskwargs(opts)
507 opts = pycompat.byteskwargs(opts)
508 rev = opts.get('rev')
508 rev = opts.get('rev')
509 if rev:
509 if rev:
510 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
510 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
511 ctx = scmutil.revsingle(repo, rev)
511 ctx = scmutil.revsingle(repo, rev)
512 if not ctx:
512 if not ctx:
513 raise error.Abort(_('no working directory: please specify a revision'))
513 raise error.Abort(_('no working directory: please specify a revision'))
514 node = ctx.node()
514 node = ctx.node()
515 dest = cmdutil.makefilename(ctx, dest)
515 dest = cmdutil.makefilename(ctx, dest)
516 if os.path.realpath(dest) == repo.root:
516 if os.path.realpath(dest) == repo.root:
517 raise error.Abort(_('repository root cannot be destination'))
517 raise error.Abort(_('repository root cannot be destination'))
518
518
519 kind = opts.get('type') or archival.guesskind(dest) or 'files'
519 kind = opts.get('type') or archival.guesskind(dest) or 'files'
520 prefix = opts.get('prefix')
520 prefix = opts.get('prefix')
521
521
522 if dest == '-':
522 if dest == '-':
523 if kind == 'files':
523 if kind == 'files':
524 raise error.Abort(_('cannot archive plain files to stdout'))
524 raise error.Abort(_('cannot archive plain files to stdout'))
525 dest = cmdutil.makefileobj(ctx, dest)
525 dest = cmdutil.makefileobj(ctx, dest)
526 if not prefix:
526 if not prefix:
527 prefix = os.path.basename(repo.root) + '-%h'
527 prefix = os.path.basename(repo.root) + '-%h'
528
528
529 prefix = cmdutil.makefilename(ctx, prefix)
529 prefix = cmdutil.makefilename(ctx, prefix)
530 match = scmutil.match(ctx, [], opts)
530 match = scmutil.match(ctx, [], opts)
531 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
531 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
532 match, prefix, subrepos=opts.get('subrepos'))
532 match, prefix, subrepos=opts.get('subrepos'))
533
533
534 @command('backout',
534 @command('backout',
535 [('', 'merge', None, _('merge with old dirstate parent after backout')),
535 [('', 'merge', None, _('merge with old dirstate parent after backout')),
536 ('', 'commit', None,
536 ('', 'commit', None,
537 _('commit if no conflicts were encountered (DEPRECATED)')),
537 _('commit if no conflicts were encountered (DEPRECATED)')),
538 ('', 'no-commit', None, _('do not commit')),
538 ('', 'no-commit', None, _('do not commit')),
539 ('', 'parent', '',
539 ('', 'parent', '',
540 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
540 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
541 ('r', 'rev', '', _('revision to backout'), _('REV')),
541 ('r', 'rev', '', _('revision to backout'), _('REV')),
542 ('e', 'edit', False, _('invoke editor on commit messages')),
542 ('e', 'edit', False, _('invoke editor on commit messages')),
543 ] + mergetoolopts + walkopts + commitopts + commitopts2,
543 ] + mergetoolopts + walkopts + commitopts + commitopts2,
544 _('[OPTION]... [-r] REV'),
544 _('[OPTION]... [-r] REV'),
545 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
545 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
546 def backout(ui, repo, node=None, rev=None, **opts):
546 def backout(ui, repo, node=None, rev=None, **opts):
547 '''reverse effect of earlier changeset
547 '''reverse effect of earlier changeset
548
548
549 Prepare a new changeset with the effect of REV undone in the
549 Prepare a new changeset with the effect of REV undone in the
550 current working directory. If no conflicts were encountered,
550 current working directory. If no conflicts were encountered,
551 it will be committed immediately.
551 it will be committed immediately.
552
552
553 If REV is the parent of the working directory, then this new changeset
553 If REV is the parent of the working directory, then this new changeset
554 is committed automatically (unless --no-commit is specified).
554 is committed automatically (unless --no-commit is specified).
555
555
556 .. note::
556 .. note::
557
557
558 :hg:`backout` cannot be used to fix either an unwanted or
558 :hg:`backout` cannot be used to fix either an unwanted or
559 incorrect merge.
559 incorrect merge.
560
560
561 .. container:: verbose
561 .. container:: verbose
562
562
563 Examples:
563 Examples:
564
564
565 - Reverse the effect of the parent of the working directory.
565 - Reverse the effect of the parent of the working directory.
566 This backout will be committed immediately::
566 This backout will be committed immediately::
567
567
568 hg backout -r .
568 hg backout -r .
569
569
570 - Reverse the effect of previous bad revision 23::
570 - Reverse the effect of previous bad revision 23::
571
571
572 hg backout -r 23
572 hg backout -r 23
573
573
574 - Reverse the effect of previous bad revision 23 and
574 - Reverse the effect of previous bad revision 23 and
575 leave changes uncommitted::
575 leave changes uncommitted::
576
576
577 hg backout -r 23 --no-commit
577 hg backout -r 23 --no-commit
578 hg commit -m "Backout revision 23"
578 hg commit -m "Backout revision 23"
579
579
580 By default, the pending changeset will have one parent,
580 By default, the pending changeset will have one parent,
581 maintaining a linear history. With --merge, the pending
581 maintaining a linear history. With --merge, the pending
582 changeset will instead have two parents: the old parent of the
582 changeset will instead have two parents: the old parent of the
583 working directory and a new child of REV that simply undoes REV.
583 working directory and a new child of REV that simply undoes REV.
584
584
585 Before version 1.7, the behavior without --merge was equivalent
585 Before version 1.7, the behavior without --merge was equivalent
586 to specifying --merge followed by :hg:`update --clean .` to
586 to specifying --merge followed by :hg:`update --clean .` to
587 cancel the merge and leave the child of REV as a head to be
587 cancel the merge and leave the child of REV as a head to be
588 merged separately.
588 merged separately.
589
589
590 See :hg:`help dates` for a list of formats valid for -d/--date.
590 See :hg:`help dates` for a list of formats valid for -d/--date.
591
591
592 See :hg:`help revert` for a way to restore files to the state
592 See :hg:`help revert` for a way to restore files to the state
593 of another revision.
593 of another revision.
594
594
595 Returns 0 on success, 1 if nothing to backout or there are unresolved
595 Returns 0 on success, 1 if nothing to backout or there are unresolved
596 files.
596 files.
597 '''
597 '''
598 with repo.wlock(), repo.lock():
598 with repo.wlock(), repo.lock():
599 return _dobackout(ui, repo, node, rev, **opts)
599 return _dobackout(ui, repo, node, rev, **opts)
600
600
601 def _dobackout(ui, repo, node=None, rev=None, **opts):
601 def _dobackout(ui, repo, node=None, rev=None, **opts):
602 opts = pycompat.byteskwargs(opts)
602 opts = pycompat.byteskwargs(opts)
603 if opts.get('commit') and opts.get('no_commit'):
603 if opts.get('commit') and opts.get('no_commit'):
604 raise error.Abort(_("cannot use --commit with --no-commit"))
604 raise error.Abort(_("cannot use --commit with --no-commit"))
605 if opts.get('merge') and opts.get('no_commit'):
605 if opts.get('merge') and opts.get('no_commit'):
606 raise error.Abort(_("cannot use --merge with --no-commit"))
606 raise error.Abort(_("cannot use --merge with --no-commit"))
607
607
608 if rev and node:
608 if rev and node:
609 raise error.Abort(_("please specify just one revision"))
609 raise error.Abort(_("please specify just one revision"))
610
610
611 if not rev:
611 if not rev:
612 rev = node
612 rev = node
613
613
614 if not rev:
614 if not rev:
615 raise error.Abort(_("please specify a revision to backout"))
615 raise error.Abort(_("please specify a revision to backout"))
616
616
617 date = opts.get('date')
617 date = opts.get('date')
618 if date:
618 if date:
619 opts['date'] = dateutil.parsedate(date)
619 opts['date'] = dateutil.parsedate(date)
620
620
621 cmdutil.checkunfinished(repo)
621 cmdutil.checkunfinished(repo)
622 cmdutil.bailifchanged(repo)
622 cmdutil.bailifchanged(repo)
623 node = scmutil.revsingle(repo, rev).node()
623 node = scmutil.revsingle(repo, rev).node()
624
624
625 op1, op2 = repo.dirstate.parents()
625 op1, op2 = repo.dirstate.parents()
626 if not repo.changelog.isancestor(node, op1):
626 if not repo.changelog.isancestor(node, op1):
627 raise error.Abort(_('cannot backout change that is not an ancestor'))
627 raise error.Abort(_('cannot backout change that is not an ancestor'))
628
628
629 p1, p2 = repo.changelog.parents(node)
629 p1, p2 = repo.changelog.parents(node)
630 if p1 == nullid:
630 if p1 == nullid:
631 raise error.Abort(_('cannot backout a change with no parents'))
631 raise error.Abort(_('cannot backout a change with no parents'))
632 if p2 != nullid:
632 if p2 != nullid:
633 if not opts.get('parent'):
633 if not opts.get('parent'):
634 raise error.Abort(_('cannot backout a merge changeset'))
634 raise error.Abort(_('cannot backout a merge changeset'))
635 p = repo.lookup(opts['parent'])
635 p = repo.lookup(opts['parent'])
636 if p not in (p1, p2):
636 if p not in (p1, p2):
637 raise error.Abort(_('%s is not a parent of %s') %
637 raise error.Abort(_('%s is not a parent of %s') %
638 (short(p), short(node)))
638 (short(p), short(node)))
639 parent = p
639 parent = p
640 else:
640 else:
641 if opts.get('parent'):
641 if opts.get('parent'):
642 raise error.Abort(_('cannot use --parent on non-merge changeset'))
642 raise error.Abort(_('cannot use --parent on non-merge changeset'))
643 parent = p1
643 parent = p1
644
644
645 # the backout should appear on the same branch
645 # the backout should appear on the same branch
646 branch = repo.dirstate.branch()
646 branch = repo.dirstate.branch()
647 bheads = repo.branchheads(branch)
647 bheads = repo.branchheads(branch)
648 rctx = scmutil.revsingle(repo, hex(parent))
648 rctx = scmutil.revsingle(repo, hex(parent))
649 if not opts.get('merge') and op1 != node:
649 if not opts.get('merge') and op1 != node:
650 with dirstateguard.dirstateguard(repo, 'backout'):
650 with dirstateguard.dirstateguard(repo, 'backout'):
651 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
651 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
652 with ui.configoverride(overrides, 'backout'):
652 with ui.configoverride(overrides, 'backout'):
653 stats = mergemod.update(repo, parent, branchmerge=True,
653 stats = mergemod.update(repo, parent, branchmerge=True,
654 force=True, ancestor=node,
654 force=True, ancestor=node,
655 mergeancestor=False)
655 mergeancestor=False)
656 repo.setparents(op1, op2)
656 repo.setparents(op1, op2)
657 hg._showstats(repo, stats)
657 hg._showstats(repo, stats)
658 if stats.unresolvedcount:
658 if stats.unresolvedcount:
659 repo.ui.status(_("use 'hg resolve' to retry unresolved "
659 repo.ui.status(_("use 'hg resolve' to retry unresolved "
660 "file merges\n"))
660 "file merges\n"))
661 return 1
661 return 1
662 else:
662 else:
663 hg.clean(repo, node, show_stats=False)
663 hg.clean(repo, node, show_stats=False)
664 repo.dirstate.setbranch(branch)
664 repo.dirstate.setbranch(branch)
665 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
665 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
666
666
667 if opts.get('no_commit'):
667 if opts.get('no_commit'):
668 msg = _("changeset %s backed out, "
668 msg = _("changeset %s backed out, "
669 "don't forget to commit.\n")
669 "don't forget to commit.\n")
670 ui.status(msg % short(node))
670 ui.status(msg % short(node))
671 return 0
671 return 0
672
672
673 def commitfunc(ui, repo, message, match, opts):
673 def commitfunc(ui, repo, message, match, opts):
674 editform = 'backout'
674 editform = 'backout'
675 e = cmdutil.getcommiteditor(editform=editform,
675 e = cmdutil.getcommiteditor(editform=editform,
676 **pycompat.strkwargs(opts))
676 **pycompat.strkwargs(opts))
677 if not message:
677 if not message:
678 # we don't translate commit messages
678 # we don't translate commit messages
679 message = "Backed out changeset %s" % short(node)
679 message = "Backed out changeset %s" % short(node)
680 e = cmdutil.getcommiteditor(edit=True, editform=editform)
680 e = cmdutil.getcommiteditor(edit=True, editform=editform)
681 return repo.commit(message, opts.get('user'), opts.get('date'),
681 return repo.commit(message, opts.get('user'), opts.get('date'),
682 match, editor=e)
682 match, editor=e)
683 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
683 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
684 if not newnode:
684 if not newnode:
685 ui.status(_("nothing changed\n"))
685 ui.status(_("nothing changed\n"))
686 return 1
686 return 1
687 cmdutil.commitstatus(repo, newnode, branch, bheads)
687 cmdutil.commitstatus(repo, newnode, branch, bheads)
688
688
689 def nice(node):
689 def nice(node):
690 return '%d:%s' % (repo.changelog.rev(node), short(node))
690 return '%d:%s' % (repo.changelog.rev(node), short(node))
691 ui.status(_('changeset %s backs out changeset %s\n') %
691 ui.status(_('changeset %s backs out changeset %s\n') %
692 (nice(repo.changelog.tip()), nice(node)))
692 (nice(repo.changelog.tip()), nice(node)))
693 if opts.get('merge') and op1 != node:
693 if opts.get('merge') and op1 != node:
694 hg.clean(repo, op1, show_stats=False)
694 hg.clean(repo, op1, show_stats=False)
695 ui.status(_('merging with changeset %s\n')
695 ui.status(_('merging with changeset %s\n')
696 % nice(repo.changelog.tip()))
696 % nice(repo.changelog.tip()))
697 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
697 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
698 with ui.configoverride(overrides, 'backout'):
698 with ui.configoverride(overrides, 'backout'):
699 return hg.merge(repo, hex(repo.changelog.tip()))
699 return hg.merge(repo, hex(repo.changelog.tip()))
700 return 0
700 return 0
701
701
702 @command('bisect',
702 @command('bisect',
703 [('r', 'reset', False, _('reset bisect state')),
703 [('r', 'reset', False, _('reset bisect state')),
704 ('g', 'good', False, _('mark changeset good')),
704 ('g', 'good', False, _('mark changeset good')),
705 ('b', 'bad', False, _('mark changeset bad')),
705 ('b', 'bad', False, _('mark changeset bad')),
706 ('s', 'skip', False, _('skip testing changeset')),
706 ('s', 'skip', False, _('skip testing changeset')),
707 ('e', 'extend', False, _('extend the bisect range')),
707 ('e', 'extend', False, _('extend the bisect range')),
708 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
708 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
709 ('U', 'noupdate', False, _('do not update to target'))],
709 ('U', 'noupdate', False, _('do not update to target'))],
710 _("[-gbsr] [-U] [-c CMD] [REV]"),
710 _("[-gbsr] [-U] [-c CMD] [REV]"),
711 helpcategory=command.CATEGORY_CHANGE_NAVIGATION)
711 helpcategory=command.CATEGORY_CHANGE_NAVIGATION)
712 def bisect(ui, repo, rev=None, extra=None, command=None,
712 def bisect(ui, repo, rev=None, extra=None, command=None,
713 reset=None, good=None, bad=None, skip=None, extend=None,
713 reset=None, good=None, bad=None, skip=None, extend=None,
714 noupdate=None):
714 noupdate=None):
715 """subdivision search of changesets
715 """subdivision search of changesets
716
716
717 This command helps to find changesets which introduce problems. To
717 This command helps to find changesets which introduce problems. To
718 use, mark the earliest changeset you know exhibits the problem as
718 use, mark the earliest changeset you know exhibits the problem as
719 bad, then mark the latest changeset which is free from the problem
719 bad, then mark the latest changeset which is free from the problem
720 as good. Bisect will update your working directory to a revision
720 as good. Bisect will update your working directory to a revision
721 for testing (unless the -U/--noupdate option is specified). Once
721 for testing (unless the -U/--noupdate option is specified). Once
722 you have performed tests, mark the working directory as good or
722 you have performed tests, mark the working directory as good or
723 bad, and bisect will either update to another candidate changeset
723 bad, and bisect will either update to another candidate changeset
724 or announce that it has found the bad revision.
724 or announce that it has found the bad revision.
725
725
726 As a shortcut, you can also use the revision argument to mark a
726 As a shortcut, you can also use the revision argument to mark a
727 revision as good or bad without checking it out first.
727 revision as good or bad without checking it out first.
728
728
729 If you supply a command, it will be used for automatic bisection.
729 If you supply a command, it will be used for automatic bisection.
730 The environment variable HG_NODE will contain the ID of the
730 The environment variable HG_NODE will contain the ID of the
731 changeset being tested. The exit status of the command will be
731 changeset being tested. The exit status of the command will be
732 used to mark revisions as good or bad: status 0 means good, 125
732 used to mark revisions as good or bad: status 0 means good, 125
733 means to skip the revision, 127 (command not found) will abort the
733 means to skip the revision, 127 (command not found) will abort the
734 bisection, and any other non-zero exit status means the revision
734 bisection, and any other non-zero exit status means the revision
735 is bad.
735 is bad.
736
736
737 .. container:: verbose
737 .. container:: verbose
738
738
739 Some examples:
739 Some examples:
740
740
741 - start a bisection with known bad revision 34, and good revision 12::
741 - start a bisection with known bad revision 34, and good revision 12::
742
742
743 hg bisect --bad 34
743 hg bisect --bad 34
744 hg bisect --good 12
744 hg bisect --good 12
745
745
746 - advance the current bisection by marking current revision as good or
746 - advance the current bisection by marking current revision as good or
747 bad::
747 bad::
748
748
749 hg bisect --good
749 hg bisect --good
750 hg bisect --bad
750 hg bisect --bad
751
751
752 - mark the current revision, or a known revision, to be skipped (e.g. if
752 - mark the current revision, or a known revision, to be skipped (e.g. if
753 that revision is not usable because of another issue)::
753 that revision is not usable because of another issue)::
754
754
755 hg bisect --skip
755 hg bisect --skip
756 hg bisect --skip 23
756 hg bisect --skip 23
757
757
758 - skip all revisions that do not touch directories ``foo`` or ``bar``::
758 - skip all revisions that do not touch directories ``foo`` or ``bar``::
759
759
760 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
760 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
761
761
762 - forget the current bisection::
762 - forget the current bisection::
763
763
764 hg bisect --reset
764 hg bisect --reset
765
765
766 - use 'make && make tests' to automatically find the first broken
766 - use 'make && make tests' to automatically find the first broken
767 revision::
767 revision::
768
768
769 hg bisect --reset
769 hg bisect --reset
770 hg bisect --bad 34
770 hg bisect --bad 34
771 hg bisect --good 12
771 hg bisect --good 12
772 hg bisect --command "make && make tests"
772 hg bisect --command "make && make tests"
773
773
774 - see all changesets whose states are already known in the current
774 - see all changesets whose states are already known in the current
775 bisection::
775 bisection::
776
776
777 hg log -r "bisect(pruned)"
777 hg log -r "bisect(pruned)"
778
778
779 - see the changeset currently being bisected (especially useful
779 - see the changeset currently being bisected (especially useful
780 if running with -U/--noupdate)::
780 if running with -U/--noupdate)::
781
781
782 hg log -r "bisect(current)"
782 hg log -r "bisect(current)"
783
783
784 - see all changesets that took part in the current bisection::
784 - see all changesets that took part in the current bisection::
785
785
786 hg log -r "bisect(range)"
786 hg log -r "bisect(range)"
787
787
788 - you can even get a nice graph::
788 - you can even get a nice graph::
789
789
790 hg log --graph -r "bisect(range)"
790 hg log --graph -r "bisect(range)"
791
791
792 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
792 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
793
793
794 Returns 0 on success.
794 Returns 0 on success.
795 """
795 """
796 # backward compatibility
796 # backward compatibility
797 if rev in "good bad reset init".split():
797 if rev in "good bad reset init".split():
798 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
798 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
799 cmd, rev, extra = rev, extra, None
799 cmd, rev, extra = rev, extra, None
800 if cmd == "good":
800 if cmd == "good":
801 good = True
801 good = True
802 elif cmd == "bad":
802 elif cmd == "bad":
803 bad = True
803 bad = True
804 else:
804 else:
805 reset = True
805 reset = True
806 elif extra:
806 elif extra:
807 raise error.Abort(_('incompatible arguments'))
807 raise error.Abort(_('incompatible arguments'))
808
808
809 incompatibles = {
809 incompatibles = {
810 '--bad': bad,
810 '--bad': bad,
811 '--command': bool(command),
811 '--command': bool(command),
812 '--extend': extend,
812 '--extend': extend,
813 '--good': good,
813 '--good': good,
814 '--reset': reset,
814 '--reset': reset,
815 '--skip': skip,
815 '--skip': skip,
816 }
816 }
817
817
818 enabled = [x for x in incompatibles if incompatibles[x]]
818 enabled = [x for x in incompatibles if incompatibles[x]]
819
819
820 if len(enabled) > 1:
820 if len(enabled) > 1:
821 raise error.Abort(_('%s and %s are incompatible') %
821 raise error.Abort(_('%s and %s are incompatible') %
822 tuple(sorted(enabled)[0:2]))
822 tuple(sorted(enabled)[0:2]))
823
823
824 if reset:
824 if reset:
825 hbisect.resetstate(repo)
825 hbisect.resetstate(repo)
826 return
826 return
827
827
828 state = hbisect.load_state(repo)
828 state = hbisect.load_state(repo)
829
829
830 # update state
830 # update state
831 if good or bad or skip:
831 if good or bad or skip:
832 if rev:
832 if rev:
833 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
833 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
834 else:
834 else:
835 nodes = [repo.lookup('.')]
835 nodes = [repo.lookup('.')]
836 if good:
836 if good:
837 state['good'] += nodes
837 state['good'] += nodes
838 elif bad:
838 elif bad:
839 state['bad'] += nodes
839 state['bad'] += nodes
840 elif skip:
840 elif skip:
841 state['skip'] += nodes
841 state['skip'] += nodes
842 hbisect.save_state(repo, state)
842 hbisect.save_state(repo, state)
843 if not (state['good'] and state['bad']):
843 if not (state['good'] and state['bad']):
844 return
844 return
845
845
846 def mayupdate(repo, node, show_stats=True):
846 def mayupdate(repo, node, show_stats=True):
847 """common used update sequence"""
847 """common used update sequence"""
848 if noupdate:
848 if noupdate:
849 return
849 return
850 cmdutil.checkunfinished(repo)
850 cmdutil.checkunfinished(repo)
851 cmdutil.bailifchanged(repo)
851 cmdutil.bailifchanged(repo)
852 return hg.clean(repo, node, show_stats=show_stats)
852 return hg.clean(repo, node, show_stats=show_stats)
853
853
854 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
854 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
855
855
856 if command:
856 if command:
857 changesets = 1
857 changesets = 1
858 if noupdate:
858 if noupdate:
859 try:
859 try:
860 node = state['current'][0]
860 node = state['current'][0]
861 except LookupError:
861 except LookupError:
862 raise error.Abort(_('current bisect revision is unknown - '
862 raise error.Abort(_('current bisect revision is unknown - '
863 'start a new bisect to fix'))
863 'start a new bisect to fix'))
864 else:
864 else:
865 node, p2 = repo.dirstate.parents()
865 node, p2 = repo.dirstate.parents()
866 if p2 != nullid:
866 if p2 != nullid:
867 raise error.Abort(_('current bisect revision is a merge'))
867 raise error.Abort(_('current bisect revision is a merge'))
868 if rev:
868 if rev:
869 node = repo[scmutil.revsingle(repo, rev, node)].node()
869 node = repo[scmutil.revsingle(repo, rev, node)].node()
870 try:
870 try:
871 while changesets:
871 while changesets:
872 # update state
872 # update state
873 state['current'] = [node]
873 state['current'] = [node]
874 hbisect.save_state(repo, state)
874 hbisect.save_state(repo, state)
875 status = ui.system(command, environ={'HG_NODE': hex(node)},
875 status = ui.system(command, environ={'HG_NODE': hex(node)},
876 blockedtag='bisect_check')
876 blockedtag='bisect_check')
877 if status == 125:
877 if status == 125:
878 transition = "skip"
878 transition = "skip"
879 elif status == 0:
879 elif status == 0:
880 transition = "good"
880 transition = "good"
881 # status < 0 means process was killed
881 # status < 0 means process was killed
882 elif status == 127:
882 elif status == 127:
883 raise error.Abort(_("failed to execute %s") % command)
883 raise error.Abort(_("failed to execute %s") % command)
884 elif status < 0:
884 elif status < 0:
885 raise error.Abort(_("%s killed") % command)
885 raise error.Abort(_("%s killed") % command)
886 else:
886 else:
887 transition = "bad"
887 transition = "bad"
888 state[transition].append(node)
888 state[transition].append(node)
889 ctx = repo[node]
889 ctx = repo[node]
890 ui.status(_('changeset %d:%s: %s\n') % (ctx.rev(), ctx,
890 ui.status(_('changeset %d:%s: %s\n') % (ctx.rev(), ctx,
891 transition))
891 transition))
892 hbisect.checkstate(state)
892 hbisect.checkstate(state)
893 # bisect
893 # bisect
894 nodes, changesets, bgood = hbisect.bisect(repo, state)
894 nodes, changesets, bgood = hbisect.bisect(repo, state)
895 # update to next check
895 # update to next check
896 node = nodes[0]
896 node = nodes[0]
897 mayupdate(repo, node, show_stats=False)
897 mayupdate(repo, node, show_stats=False)
898 finally:
898 finally:
899 state['current'] = [node]
899 state['current'] = [node]
900 hbisect.save_state(repo, state)
900 hbisect.save_state(repo, state)
901 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
901 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
902 return
902 return
903
903
904 hbisect.checkstate(state)
904 hbisect.checkstate(state)
905
905
906 # actually bisect
906 # actually bisect
907 nodes, changesets, good = hbisect.bisect(repo, state)
907 nodes, changesets, good = hbisect.bisect(repo, state)
908 if extend:
908 if extend:
909 if not changesets:
909 if not changesets:
910 extendnode = hbisect.extendrange(repo, state, nodes, good)
910 extendnode = hbisect.extendrange(repo, state, nodes, good)
911 if extendnode is not None:
911 if extendnode is not None:
912 ui.write(_("Extending search to changeset %d:%s\n")
912 ui.write(_("Extending search to changeset %d:%s\n")
913 % (extendnode.rev(), extendnode))
913 % (extendnode.rev(), extendnode))
914 state['current'] = [extendnode.node()]
914 state['current'] = [extendnode.node()]
915 hbisect.save_state(repo, state)
915 hbisect.save_state(repo, state)
916 return mayupdate(repo, extendnode.node())
916 return mayupdate(repo, extendnode.node())
917 raise error.Abort(_("nothing to extend"))
917 raise error.Abort(_("nothing to extend"))
918
918
919 if changesets == 0:
919 if changesets == 0:
920 hbisect.printresult(ui, repo, state, displayer, nodes, good)
920 hbisect.printresult(ui, repo, state, displayer, nodes, good)
921 else:
921 else:
922 assert len(nodes) == 1 # only a single node can be tested next
922 assert len(nodes) == 1 # only a single node can be tested next
923 node = nodes[0]
923 node = nodes[0]
924 # compute the approximate number of remaining tests
924 # compute the approximate number of remaining tests
925 tests, size = 0, 2
925 tests, size = 0, 2
926 while size <= changesets:
926 while size <= changesets:
927 tests, size = tests + 1, size * 2
927 tests, size = tests + 1, size * 2
928 rev = repo.changelog.rev(node)
928 rev = repo.changelog.rev(node)
929 ui.write(_("Testing changeset %d:%s "
929 ui.write(_("Testing changeset %d:%s "
930 "(%d changesets remaining, ~%d tests)\n")
930 "(%d changesets remaining, ~%d tests)\n")
931 % (rev, short(node), changesets, tests))
931 % (rev, short(node), changesets, tests))
932 state['current'] = [node]
932 state['current'] = [node]
933 hbisect.save_state(repo, state)
933 hbisect.save_state(repo, state)
934 return mayupdate(repo, node)
934 return mayupdate(repo, node)
935
935
936 @command('bookmarks|bookmark',
936 @command('bookmarks|bookmark',
937 [('f', 'force', False, _('force')),
937 [('f', 'force', False, _('force')),
938 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
938 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
939 ('d', 'delete', False, _('delete a given bookmark')),
939 ('d', 'delete', False, _('delete a given bookmark')),
940 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
940 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
941 ('i', 'inactive', False, _('mark a bookmark inactive')),
941 ('i', 'inactive', False, _('mark a bookmark inactive')),
942 ('l', 'list', False, _('list existing bookmarks')),
942 ('l', 'list', False, _('list existing bookmarks')),
943 ] + formatteropts,
943 ] + formatteropts,
944 _('hg bookmarks [OPTIONS]... [NAME]...'),
944 _('hg bookmarks [OPTIONS]... [NAME]...'),
945 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
945 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
946 def bookmark(ui, repo, *names, **opts):
946 def bookmark(ui, repo, *names, **opts):
947 '''create a new bookmark or list existing bookmarks
947 '''create a new bookmark or list existing bookmarks
948
948
949 Bookmarks are labels on changesets to help track lines of development.
949 Bookmarks are labels on changesets to help track lines of development.
950 Bookmarks are unversioned and can be moved, renamed and deleted.
950 Bookmarks are unversioned and can be moved, renamed and deleted.
951 Deleting or moving a bookmark has no effect on the associated changesets.
951 Deleting or moving a bookmark has no effect on the associated changesets.
952
952
953 Creating or updating to a bookmark causes it to be marked as 'active'.
953 Creating or updating to a bookmark causes it to be marked as 'active'.
954 The active bookmark is indicated with a '*'.
954 The active bookmark is indicated with a '*'.
955 When a commit is made, the active bookmark will advance to the new commit.
955 When a commit is made, the active bookmark will advance to the new commit.
956 A plain :hg:`update` will also advance an active bookmark, if possible.
956 A plain :hg:`update` will also advance an active bookmark, if possible.
957 Updating away from a bookmark will cause it to be deactivated.
957 Updating away from a bookmark will cause it to be deactivated.
958
958
959 Bookmarks can be pushed and pulled between repositories (see
959 Bookmarks can be pushed and pulled between repositories (see
960 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
960 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
961 diverged, a new 'divergent bookmark' of the form 'name@path' will
961 diverged, a new 'divergent bookmark' of the form 'name@path' will
962 be created. Using :hg:`merge` will resolve the divergence.
962 be created. Using :hg:`merge` will resolve the divergence.
963
963
964 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
964 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
965 the active bookmark's name.
965 the active bookmark's name.
966
966
967 A bookmark named '@' has the special property that :hg:`clone` will
967 A bookmark named '@' has the special property that :hg:`clone` will
968 check it out by default if it exists.
968 check it out by default if it exists.
969
969
970 .. container:: verbose
970 .. container:: verbose
971
971
972 Template:
972 Template:
973
973
974 The following keywords are supported in addition to the common template
974 The following keywords are supported in addition to the common template
975 keywords and functions such as ``{bookmark}``. See also
975 keywords and functions such as ``{bookmark}``. See also
976 :hg:`help templates`.
976 :hg:`help templates`.
977
977
978 :active: Boolean. True if the bookmark is active.
978 :active: Boolean. True if the bookmark is active.
979
979
980 Examples:
980 Examples:
981
981
982 - create an active bookmark for a new line of development::
982 - create an active bookmark for a new line of development::
983
983
984 hg book new-feature
984 hg book new-feature
985
985
986 - create an inactive bookmark as a place marker::
986 - create an inactive bookmark as a place marker::
987
987
988 hg book -i reviewed
988 hg book -i reviewed
989
989
990 - create an inactive bookmark on another changeset::
990 - create an inactive bookmark on another changeset::
991
991
992 hg book -r .^ tested
992 hg book -r .^ tested
993
993
994 - rename bookmark turkey to dinner::
994 - rename bookmark turkey to dinner::
995
995
996 hg book -m turkey dinner
996 hg book -m turkey dinner
997
997
998 - move the '@' bookmark from another branch::
998 - move the '@' bookmark from another branch::
999
999
1000 hg book -f @
1000 hg book -f @
1001
1001
1002 - print only the active bookmark name::
1002 - print only the active bookmark name::
1003
1003
1004 hg book -ql .
1004 hg book -ql .
1005 '''
1005 '''
1006 opts = pycompat.byteskwargs(opts)
1006 opts = pycompat.byteskwargs(opts)
1007 force = opts.get('force')
1007 force = opts.get('force')
1008 rev = opts.get('rev')
1008 rev = opts.get('rev')
1009 inactive = opts.get('inactive') # meaning add/rename to inactive bookmark
1009 inactive = opts.get('inactive') # meaning add/rename to inactive bookmark
1010
1010
1011 selactions = [k for k in ['delete', 'rename', 'list'] if opts.get(k)]
1011 selactions = [k for k in ['delete', 'rename', 'list'] if opts.get(k)]
1012 if len(selactions) > 1:
1012 if len(selactions) > 1:
1013 raise error.Abort(_('--%s and --%s are incompatible')
1013 raise error.Abort(_('--%s and --%s are incompatible')
1014 % tuple(selactions[:2]))
1014 % tuple(selactions[:2]))
1015 if selactions:
1015 if selactions:
1016 action = selactions[0]
1016 action = selactions[0]
1017 elif names or rev:
1017 elif names or rev:
1018 action = 'add'
1018 action = 'add'
1019 elif inactive:
1019 elif inactive:
1020 action = 'inactive' # meaning deactivate
1020 action = 'inactive' # meaning deactivate
1021 else:
1021 else:
1022 action = 'list'
1022 action = 'list'
1023
1023
1024 if rev and action in {'delete', 'rename', 'list'}:
1024 if rev and action in {'delete', 'rename', 'list'}:
1025 raise error.Abort(_("--rev is incompatible with --%s") % action)
1025 raise error.Abort(_("--rev is incompatible with --%s") % action)
1026 if inactive and action in {'delete', 'list'}:
1026 if inactive and action in {'delete', 'list'}:
1027 raise error.Abort(_("--inactive is incompatible with --%s") % action)
1027 raise error.Abort(_("--inactive is incompatible with --%s") % action)
1028 if not names and action in {'add', 'delete'}:
1028 if not names and action in {'add', 'delete'}:
1029 raise error.Abort(_("bookmark name required"))
1029 raise error.Abort(_("bookmark name required"))
1030
1030
1031 if action in {'add', 'delete', 'rename', 'inactive'}:
1031 if action in {'add', 'delete', 'rename', 'inactive'}:
1032 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
1032 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
1033 if action == 'delete':
1033 if action == 'delete':
1034 names = pycompat.maplist(repo._bookmarks.expandname, names)
1034 names = pycompat.maplist(repo._bookmarks.expandname, names)
1035 bookmarks.delete(repo, tr, names)
1035 bookmarks.delete(repo, tr, names)
1036 elif action == 'rename':
1036 elif action == 'rename':
1037 if not names:
1037 if not names:
1038 raise error.Abort(_("new bookmark name required"))
1038 raise error.Abort(_("new bookmark name required"))
1039 elif len(names) > 1:
1039 elif len(names) > 1:
1040 raise error.Abort(_("only one new bookmark name allowed"))
1040 raise error.Abort(_("only one new bookmark name allowed"))
1041 oldname = repo._bookmarks.expandname(opts['rename'])
1041 oldname = repo._bookmarks.expandname(opts['rename'])
1042 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1042 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1043 elif action == 'add':
1043 elif action == 'add':
1044 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1044 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1045 elif action == 'inactive':
1045 elif action == 'inactive':
1046 if len(repo._bookmarks) == 0:
1046 if len(repo._bookmarks) == 0:
1047 ui.status(_("no bookmarks set\n"))
1047 ui.status(_("no bookmarks set\n"))
1048 elif not repo._activebookmark:
1048 elif not repo._activebookmark:
1049 ui.status(_("no active bookmark\n"))
1049 ui.status(_("no active bookmark\n"))
1050 else:
1050 else:
1051 bookmarks.deactivate(repo)
1051 bookmarks.deactivate(repo)
1052 elif action == 'list':
1052 elif action == 'list':
1053 names = pycompat.maplist(repo._bookmarks.expandname, names)
1053 names = pycompat.maplist(repo._bookmarks.expandname, names)
1054 with ui.formatter('bookmarks', opts) as fm:
1054 with ui.formatter('bookmarks', opts) as fm:
1055 bookmarks.printbookmarks(ui, repo, fm, names)
1055 bookmarks.printbookmarks(ui, repo, fm, names)
1056 else:
1056 else:
1057 raise error.ProgrammingError('invalid action: %s' % action)
1057 raise error.ProgrammingError('invalid action: %s' % action)
1058
1058
1059 @command('branch',
1059 @command('branch',
1060 [('f', 'force', None,
1060 [('f', 'force', None,
1061 _('set branch name even if it shadows an existing branch')),
1061 _('set branch name even if it shadows an existing branch')),
1062 ('C', 'clean', None, _('reset branch name to parent branch name')),
1062 ('C', 'clean', None, _('reset branch name to parent branch name')),
1063 ('r', 'rev', [], _('change branches of the given revs (EXPERIMENTAL)')),
1063 ('r', 'rev', [], _('change branches of the given revs (EXPERIMENTAL)')),
1064 ],
1064 ],
1065 _('[-fC] [NAME]'),
1065 _('[-fC] [NAME]'),
1066 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
1066 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
1067 def branch(ui, repo, label=None, **opts):
1067 def branch(ui, repo, label=None, **opts):
1068 """set or show the current branch name
1068 """set or show the current branch name
1069
1069
1070 .. note::
1070 .. note::
1071
1071
1072 Branch names are permanent and global. Use :hg:`bookmark` to create a
1072 Branch names are permanent and global. Use :hg:`bookmark` to create a
1073 light-weight bookmark instead. See :hg:`help glossary` for more
1073 light-weight bookmark instead. See :hg:`help glossary` for more
1074 information about named branches and bookmarks.
1074 information about named branches and bookmarks.
1075
1075
1076 With no argument, show the current branch name. With one argument,
1076 With no argument, show the current branch name. With one argument,
1077 set the working directory branch name (the branch will not exist
1077 set the working directory branch name (the branch will not exist
1078 in the repository until the next commit). Standard practice
1078 in the repository until the next commit). Standard practice
1079 recommends that primary development take place on the 'default'
1079 recommends that primary development take place on the 'default'
1080 branch.
1080 branch.
1081
1081
1082 Unless -f/--force is specified, branch will not let you set a
1082 Unless -f/--force is specified, branch will not let you set a
1083 branch name that already exists.
1083 branch name that already exists.
1084
1084
1085 Use -C/--clean to reset the working directory branch to that of
1085 Use -C/--clean to reset the working directory branch to that of
1086 the parent of the working directory, negating a previous branch
1086 the parent of the working directory, negating a previous branch
1087 change.
1087 change.
1088
1088
1089 Use the command :hg:`update` to switch to an existing branch. Use
1089 Use the command :hg:`update` to switch to an existing branch. Use
1090 :hg:`commit --close-branch` to mark this branch head as closed.
1090 :hg:`commit --close-branch` to mark this branch head as closed.
1091 When all heads of a branch are closed, the branch will be
1091 When all heads of a branch are closed, the branch will be
1092 considered closed.
1092 considered closed.
1093
1093
1094 Returns 0 on success.
1094 Returns 0 on success.
1095 """
1095 """
1096 opts = pycompat.byteskwargs(opts)
1096 opts = pycompat.byteskwargs(opts)
1097 revs = opts.get('rev')
1097 revs = opts.get('rev')
1098 if label:
1098 if label:
1099 label = label.strip()
1099 label = label.strip()
1100
1100
1101 if not opts.get('clean') and not label:
1101 if not opts.get('clean') and not label:
1102 if revs:
1102 if revs:
1103 raise error.Abort(_("no branch name specified for the revisions"))
1103 raise error.Abort(_("no branch name specified for the revisions"))
1104 ui.write("%s\n" % repo.dirstate.branch())
1104 ui.write("%s\n" % repo.dirstate.branch())
1105 return
1105 return
1106
1106
1107 with repo.wlock():
1107 with repo.wlock():
1108 if opts.get('clean'):
1108 if opts.get('clean'):
1109 label = repo['.'].branch()
1109 label = repo['.'].branch()
1110 repo.dirstate.setbranch(label)
1110 repo.dirstate.setbranch(label)
1111 ui.status(_('reset working directory to branch %s\n') % label)
1111 ui.status(_('reset working directory to branch %s\n') % label)
1112 elif label:
1112 elif label:
1113
1113
1114 scmutil.checknewlabel(repo, label, 'branch')
1114 scmutil.checknewlabel(repo, label, 'branch')
1115 if revs:
1115 if revs:
1116 return cmdutil.changebranch(ui, repo, revs, label)
1116 return cmdutil.changebranch(ui, repo, revs, label)
1117
1117
1118 if not opts.get('force') and label in repo.branchmap():
1118 if not opts.get('force') and label in repo.branchmap():
1119 if label not in [p.branch() for p in repo[None].parents()]:
1119 if label not in [p.branch() for p in repo[None].parents()]:
1120 raise error.Abort(_('a branch of the same name already'
1120 raise error.Abort(_('a branch of the same name already'
1121 ' exists'),
1121 ' exists'),
1122 # i18n: "it" refers to an existing branch
1122 # i18n: "it" refers to an existing branch
1123 hint=_("use 'hg update' to switch to it"))
1123 hint=_("use 'hg update' to switch to it"))
1124
1124
1125 repo.dirstate.setbranch(label)
1125 repo.dirstate.setbranch(label)
1126 ui.status(_('marked working directory as branch %s\n') % label)
1126 ui.status(_('marked working directory as branch %s\n') % label)
1127
1127
1128 # find any open named branches aside from default
1128 # find any open named branches aside from default
1129 for n, h, t, c in repo.branchmap().iterbranches():
1129 for n, h, t, c in repo.branchmap().iterbranches():
1130 if n != "default" and not c:
1130 if n != "default" and not c:
1131 return 0
1131 return 0
1132 ui.status(_('(branches are permanent and global, '
1132 ui.status(_('(branches are permanent and global, '
1133 'did you want a bookmark?)\n'))
1133 'did you want a bookmark?)\n'))
1134
1134
1135 @command('branches',
1135 @command('branches',
1136 [('a', 'active', False,
1136 [('a', 'active', False,
1137 _('show only branches that have unmerged heads (DEPRECATED)')),
1137 _('show only branches that have unmerged heads (DEPRECATED)')),
1138 ('c', 'closed', False, _('show normal and closed branches')),
1138 ('c', 'closed', False, _('show normal and closed branches')),
1139 ('r', 'rev', [], _('show branch name(s) of the given rev'))
1139 ('r', 'rev', [], _('show branch name(s) of the given rev'))
1140 ] + formatteropts,
1140 ] + formatteropts,
1141 _('[-c]'),
1141 _('[-c]'),
1142 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1142 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1143 intents={INTENT_READONLY})
1143 intents={INTENT_READONLY})
1144 def branches(ui, repo, active=False, closed=False, **opts):
1144 def branches(ui, repo, active=False, closed=False, **opts):
1145 """list repository named branches
1145 """list repository named branches
1146
1146
1147 List the repository's named branches, indicating which ones are
1147 List the repository's named branches, indicating which ones are
1148 inactive. If -c/--closed is specified, also list branches which have
1148 inactive. If -c/--closed is specified, also list branches which have
1149 been marked closed (see :hg:`commit --close-branch`).
1149 been marked closed (see :hg:`commit --close-branch`).
1150
1150
1151 Use the command :hg:`update` to switch to an existing branch.
1151 Use the command :hg:`update` to switch to an existing branch.
1152
1152
1153 .. container:: verbose
1153 .. container:: verbose
1154
1154
1155 Template:
1155 Template:
1156
1156
1157 The following keywords are supported in addition to the common template
1157 The following keywords are supported in addition to the common template
1158 keywords and functions such as ``{branch}``. See also
1158 keywords and functions such as ``{branch}``. See also
1159 :hg:`help templates`.
1159 :hg:`help templates`.
1160
1160
1161 :active: Boolean. True if the branch is active.
1161 :active: Boolean. True if the branch is active.
1162 :closed: Boolean. True if the branch is closed.
1162 :closed: Boolean. True if the branch is closed.
1163 :current: Boolean. True if it is the current branch.
1163 :current: Boolean. True if it is the current branch.
1164
1164
1165 Returns 0.
1165 Returns 0.
1166 """
1166 """
1167
1167
1168 opts = pycompat.byteskwargs(opts)
1168 opts = pycompat.byteskwargs(opts)
1169 revs = opts.get('rev')
1169 revs = opts.get('rev')
1170 selectedbranches = None
1170 selectedbranches = None
1171 if revs:
1171 if revs:
1172 revs = scmutil.revrange(repo, revs)
1172 revs = scmutil.revrange(repo, revs)
1173 getbi = repo.revbranchcache().branchinfo
1173 getbi = repo.revbranchcache().branchinfo
1174 selectedbranches = {getbi(r)[0] for r in revs}
1174 selectedbranches = {getbi(r)[0] for r in revs}
1175
1175
1176 ui.pager('branches')
1176 ui.pager('branches')
1177 fm = ui.formatter('branches', opts)
1177 fm = ui.formatter('branches', opts)
1178 hexfunc = fm.hexfunc
1178 hexfunc = fm.hexfunc
1179
1179
1180 allheads = set(repo.heads())
1180 allheads = set(repo.heads())
1181 branches = []
1181 branches = []
1182 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1182 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1183 if selectedbranches is not None and tag not in selectedbranches:
1183 if selectedbranches is not None and tag not in selectedbranches:
1184 continue
1184 continue
1185 isactive = False
1185 isactive = False
1186 if not isclosed:
1186 if not isclosed:
1187 openheads = set(repo.branchmap().iteropen(heads))
1187 openheads = set(repo.branchmap().iteropen(heads))
1188 isactive = bool(openheads & allheads)
1188 isactive = bool(openheads & allheads)
1189 branches.append((tag, repo[tip], isactive, not isclosed))
1189 branches.append((tag, repo[tip], isactive, not isclosed))
1190 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1190 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1191 reverse=True)
1191 reverse=True)
1192
1192
1193 for tag, ctx, isactive, isopen in branches:
1193 for tag, ctx, isactive, isopen in branches:
1194 if active and not isactive:
1194 if active and not isactive:
1195 continue
1195 continue
1196 if isactive:
1196 if isactive:
1197 label = 'branches.active'
1197 label = 'branches.active'
1198 notice = ''
1198 notice = ''
1199 elif not isopen:
1199 elif not isopen:
1200 if not closed:
1200 if not closed:
1201 continue
1201 continue
1202 label = 'branches.closed'
1202 label = 'branches.closed'
1203 notice = _(' (closed)')
1203 notice = _(' (closed)')
1204 else:
1204 else:
1205 label = 'branches.inactive'
1205 label = 'branches.inactive'
1206 notice = _(' (inactive)')
1206 notice = _(' (inactive)')
1207 current = (tag == repo.dirstate.branch())
1207 current = (tag == repo.dirstate.branch())
1208 if current:
1208 if current:
1209 label = 'branches.current'
1209 label = 'branches.current'
1210
1210
1211 fm.startitem()
1211 fm.startitem()
1212 fm.write('branch', '%s', tag, label=label)
1212 fm.write('branch', '%s', tag, label=label)
1213 rev = ctx.rev()
1213 rev = ctx.rev()
1214 padsize = max(31 - len("%d" % rev) - encoding.colwidth(tag), 0)
1214 padsize = max(31 - len("%d" % rev) - encoding.colwidth(tag), 0)
1215 fmt = ' ' * padsize + ' %d:%s'
1215 fmt = ' ' * padsize + ' %d:%s'
1216 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1216 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1217 label='log.changeset changeset.%s' % ctx.phasestr())
1217 label='log.changeset changeset.%s' % ctx.phasestr())
1218 fm.context(ctx=ctx)
1218 fm.context(ctx=ctx)
1219 fm.data(active=isactive, closed=not isopen, current=current)
1219 fm.data(active=isactive, closed=not isopen, current=current)
1220 if not ui.quiet:
1220 if not ui.quiet:
1221 fm.plain(notice)
1221 fm.plain(notice)
1222 fm.plain('\n')
1222 fm.plain('\n')
1223 fm.end()
1223 fm.end()
1224
1224
1225 @command('bundle',
1225 @command('bundle',
1226 [('f', 'force', None, _('run even when the destination is unrelated')),
1226 [('f', 'force', None, _('run even when the destination is unrelated')),
1227 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1227 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1228 _('REV')),
1228 _('REV')),
1229 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1229 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1230 _('BRANCH')),
1230 _('BRANCH')),
1231 ('', 'base', [],
1231 ('', 'base', [],
1232 _('a base changeset assumed to be available at the destination'),
1232 _('a base changeset assumed to be available at the destination'),
1233 _('REV')),
1233 _('REV')),
1234 ('a', 'all', None, _('bundle all changesets in the repository')),
1234 ('a', 'all', None, _('bundle all changesets in the repository')),
1235 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1235 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1236 ] + remoteopts,
1236 ] + remoteopts,
1237 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1237 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1238 helpcategory=command.CATEGORY_IMPORT_EXPORT)
1238 helpcategory=command.CATEGORY_IMPORT_EXPORT)
1239 def bundle(ui, repo, fname, dest=None, **opts):
1239 def bundle(ui, repo, fname, dest=None, **opts):
1240 """create a bundle file
1240 """create a bundle file
1241
1241
1242 Generate a bundle file containing data to be transferred to another
1242 Generate a bundle file containing data to be transferred to another
1243 repository.
1243 repository.
1244
1244
1245 To create a bundle containing all changesets, use -a/--all
1245 To create a bundle containing all changesets, use -a/--all
1246 (or --base null). Otherwise, hg assumes the destination will have
1246 (or --base null). Otherwise, hg assumes the destination will have
1247 all the nodes you specify with --base parameters. Otherwise, hg
1247 all the nodes you specify with --base parameters. Otherwise, hg
1248 will assume the repository has all the nodes in destination, or
1248 will assume the repository has all the nodes in destination, or
1249 default-push/default if no destination is specified, where destination
1249 default-push/default if no destination is specified, where destination
1250 is the repository you provide through DEST option.
1250 is the repository you provide through DEST option.
1251
1251
1252 You can change bundle format with the -t/--type option. See
1252 You can change bundle format with the -t/--type option. See
1253 :hg:`help bundlespec` for documentation on this format. By default,
1253 :hg:`help bundlespec` for documentation on this format. By default,
1254 the most appropriate format is used and compression defaults to
1254 the most appropriate format is used and compression defaults to
1255 bzip2.
1255 bzip2.
1256
1256
1257 The bundle file can then be transferred using conventional means
1257 The bundle file can then be transferred using conventional means
1258 and applied to another repository with the unbundle or pull
1258 and applied to another repository with the unbundle or pull
1259 command. This is useful when direct push and pull are not
1259 command. This is useful when direct push and pull are not
1260 available or when exporting an entire repository is undesirable.
1260 available or when exporting an entire repository is undesirable.
1261
1261
1262 Applying bundles preserves all changeset contents including
1262 Applying bundles preserves all changeset contents including
1263 permissions, copy/rename information, and revision history.
1263 permissions, copy/rename information, and revision history.
1264
1264
1265 Returns 0 on success, 1 if no changes found.
1265 Returns 0 on success, 1 if no changes found.
1266 """
1266 """
1267 opts = pycompat.byteskwargs(opts)
1267 opts = pycompat.byteskwargs(opts)
1268 revs = None
1268 revs = None
1269 if 'rev' in opts:
1269 if 'rev' in opts:
1270 revstrings = opts['rev']
1270 revstrings = opts['rev']
1271 revs = scmutil.revrange(repo, revstrings)
1271 revs = scmutil.revrange(repo, revstrings)
1272 if revstrings and not revs:
1272 if revstrings and not revs:
1273 raise error.Abort(_('no commits to bundle'))
1273 raise error.Abort(_('no commits to bundle'))
1274
1274
1275 bundletype = opts.get('type', 'bzip2').lower()
1275 bundletype = opts.get('type', 'bzip2').lower()
1276 try:
1276 try:
1277 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1277 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1278 except error.UnsupportedBundleSpecification as e:
1278 except error.UnsupportedBundleSpecification as e:
1279 raise error.Abort(pycompat.bytestr(e),
1279 raise error.Abort(pycompat.bytestr(e),
1280 hint=_("see 'hg help bundlespec' for supported "
1280 hint=_("see 'hg help bundlespec' for supported "
1281 "values for --type"))
1281 "values for --type"))
1282 cgversion = bundlespec.contentopts["cg.version"]
1282 cgversion = bundlespec.contentopts["cg.version"]
1283
1283
1284 # Packed bundles are a pseudo bundle format for now.
1284 # Packed bundles are a pseudo bundle format for now.
1285 if cgversion == 's1':
1285 if cgversion == 's1':
1286 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1286 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1287 hint=_("use 'hg debugcreatestreamclonebundle'"))
1287 hint=_("use 'hg debugcreatestreamclonebundle'"))
1288
1288
1289 if opts.get('all'):
1289 if opts.get('all'):
1290 if dest:
1290 if dest:
1291 raise error.Abort(_("--all is incompatible with specifying "
1291 raise error.Abort(_("--all is incompatible with specifying "
1292 "a destination"))
1292 "a destination"))
1293 if opts.get('base'):
1293 if opts.get('base'):
1294 ui.warn(_("ignoring --base because --all was specified\n"))
1294 ui.warn(_("ignoring --base because --all was specified\n"))
1295 base = [nullrev]
1295 base = [nullrev]
1296 else:
1296 else:
1297 base = scmutil.revrange(repo, opts.get('base'))
1297 base = scmutil.revrange(repo, opts.get('base'))
1298 if cgversion not in changegroup.supportedoutgoingversions(repo):
1298 if cgversion not in changegroup.supportedoutgoingversions(repo):
1299 raise error.Abort(_("repository does not support bundle version %s") %
1299 raise error.Abort(_("repository does not support bundle version %s") %
1300 cgversion)
1300 cgversion)
1301
1301
1302 if base:
1302 if base:
1303 if dest:
1303 if dest:
1304 raise error.Abort(_("--base is incompatible with specifying "
1304 raise error.Abort(_("--base is incompatible with specifying "
1305 "a destination"))
1305 "a destination"))
1306 common = [repo[rev].node() for rev in base]
1306 common = [repo[rev].node() for rev in base]
1307 heads = [repo[r].node() for r in revs] if revs else None
1307 heads = [repo[r].node() for r in revs] if revs else None
1308 outgoing = discovery.outgoing(repo, common, heads)
1308 outgoing = discovery.outgoing(repo, common, heads)
1309 else:
1309 else:
1310 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1310 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1311 dest, branches = hg.parseurl(dest, opts.get('branch'))
1311 dest, branches = hg.parseurl(dest, opts.get('branch'))
1312 other = hg.peer(repo, opts, dest)
1312 other = hg.peer(repo, opts, dest)
1313 revs = [repo[r].hex() for r in revs]
1313 revs = [repo[r].hex() for r in revs]
1314 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1314 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1315 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1315 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1316 outgoing = discovery.findcommonoutgoing(repo, other,
1316 outgoing = discovery.findcommonoutgoing(repo, other,
1317 onlyheads=heads,
1317 onlyheads=heads,
1318 force=opts.get('force'),
1318 force=opts.get('force'),
1319 portable=True)
1319 portable=True)
1320
1320
1321 if not outgoing.missing:
1321 if not outgoing.missing:
1322 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1322 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1323 return 1
1323 return 1
1324
1324
1325 if cgversion == '01': #bundle1
1325 if cgversion == '01': #bundle1
1326 bversion = 'HG10' + bundlespec.wirecompression
1326 bversion = 'HG10' + bundlespec.wirecompression
1327 bcompression = None
1327 bcompression = None
1328 elif cgversion in ('02', '03'):
1328 elif cgversion in ('02', '03'):
1329 bversion = 'HG20'
1329 bversion = 'HG20'
1330 bcompression = bundlespec.wirecompression
1330 bcompression = bundlespec.wirecompression
1331 else:
1331 else:
1332 raise error.ProgrammingError(
1332 raise error.ProgrammingError(
1333 'bundle: unexpected changegroup version %s' % cgversion)
1333 'bundle: unexpected changegroup version %s' % cgversion)
1334
1334
1335 # TODO compression options should be derived from bundlespec parsing.
1335 # TODO compression options should be derived from bundlespec parsing.
1336 # This is a temporary hack to allow adjusting bundle compression
1336 # This is a temporary hack to allow adjusting bundle compression
1337 # level without a) formalizing the bundlespec changes to declare it
1337 # level without a) formalizing the bundlespec changes to declare it
1338 # b) introducing a command flag.
1338 # b) introducing a command flag.
1339 compopts = {}
1339 compopts = {}
1340 complevel = ui.configint('experimental',
1340 complevel = ui.configint('experimental',
1341 'bundlecomplevel.' + bundlespec.compression)
1341 'bundlecomplevel.' + bundlespec.compression)
1342 if complevel is None:
1342 if complevel is None:
1343 complevel = ui.configint('experimental', 'bundlecomplevel')
1343 complevel = ui.configint('experimental', 'bundlecomplevel')
1344 if complevel is not None:
1344 if complevel is not None:
1345 compopts['level'] = complevel
1345 compopts['level'] = complevel
1346
1346
1347 # Allow overriding the bundling of obsmarker in phases through
1347 # Allow overriding the bundling of obsmarker in phases through
1348 # configuration while we don't have a bundle version that include them
1348 # configuration while we don't have a bundle version that include them
1349 if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker'):
1349 if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker'):
1350 bundlespec.contentopts['obsolescence'] = True
1350 bundlespec.contentopts['obsolescence'] = True
1351 if repo.ui.configbool('experimental', 'bundle-phases'):
1351 if repo.ui.configbool('experimental', 'bundle-phases'):
1352 bundlespec.contentopts['phases'] = True
1352 bundlespec.contentopts['phases'] = True
1353
1353
1354 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1354 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1355 bundlespec.contentopts, compression=bcompression,
1355 bundlespec.contentopts, compression=bcompression,
1356 compopts=compopts)
1356 compopts=compopts)
1357
1357
1358 @command('cat',
1358 @command('cat',
1359 [('o', 'output', '',
1359 [('o', 'output', '',
1360 _('print output to file with formatted name'), _('FORMAT')),
1360 _('print output to file with formatted name'), _('FORMAT')),
1361 ('r', 'rev', '', _('print the given revision'), _('REV')),
1361 ('r', 'rev', '', _('print the given revision'), _('REV')),
1362 ('', 'decode', None, _('apply any matching decode filter')),
1362 ('', 'decode', None, _('apply any matching decode filter')),
1363 ] + walkopts + formatteropts,
1363 ] + walkopts + formatteropts,
1364 _('[OPTION]... FILE...'),
1364 _('[OPTION]... FILE...'),
1365 helpcategory=command.CATEGORY_FILE_CONTENTS,
1365 helpcategory=command.CATEGORY_FILE_CONTENTS,
1366 inferrepo=True,
1366 inferrepo=True,
1367 intents={INTENT_READONLY})
1367 intents={INTENT_READONLY})
1368 def cat(ui, repo, file1, *pats, **opts):
1368 def cat(ui, repo, file1, *pats, **opts):
1369 """output the current or given revision of files
1369 """output the current or given revision of files
1370
1370
1371 Print the specified files as they were at the given revision. If
1371 Print the specified files as they were at the given revision. If
1372 no revision is given, the parent of the working directory is used.
1372 no revision is given, the parent of the working directory is used.
1373
1373
1374 Output may be to a file, in which case the name of the file is
1374 Output may be to a file, in which case the name of the file is
1375 given using a template string. See :hg:`help templates`. In addition
1375 given using a template string. See :hg:`help templates`. In addition
1376 to the common template keywords, the following formatting rules are
1376 to the common template keywords, the following formatting rules are
1377 supported:
1377 supported:
1378
1378
1379 :``%%``: literal "%" character
1379 :``%%``: literal "%" character
1380 :``%s``: basename of file being printed
1380 :``%s``: basename of file being printed
1381 :``%d``: dirname of file being printed, or '.' if in repository root
1381 :``%d``: dirname of file being printed, or '.' if in repository root
1382 :``%p``: root-relative path name of file being printed
1382 :``%p``: root-relative path name of file being printed
1383 :``%H``: changeset hash (40 hexadecimal digits)
1383 :``%H``: changeset hash (40 hexadecimal digits)
1384 :``%R``: changeset revision number
1384 :``%R``: changeset revision number
1385 :``%h``: short-form changeset hash (12 hexadecimal digits)
1385 :``%h``: short-form changeset hash (12 hexadecimal digits)
1386 :``%r``: zero-padded changeset revision number
1386 :``%r``: zero-padded changeset revision number
1387 :``%b``: basename of the exporting repository
1387 :``%b``: basename of the exporting repository
1388 :``\\``: literal "\\" character
1388 :``\\``: literal "\\" character
1389
1389
1390 .. container:: verbose
1390 .. container:: verbose
1391
1391
1392 Template:
1392 Template:
1393
1393
1394 The following keywords are supported in addition to the common template
1394 The following keywords are supported in addition to the common template
1395 keywords and functions. See also :hg:`help templates`.
1395 keywords and functions. See also :hg:`help templates`.
1396
1396
1397 :data: String. File content.
1397 :data: String. File content.
1398 :path: String. Repository-absolute path of the file.
1398 :path: String. Repository-absolute path of the file.
1399
1399
1400 Returns 0 on success.
1400 Returns 0 on success.
1401 """
1401 """
1402 opts = pycompat.byteskwargs(opts)
1402 opts = pycompat.byteskwargs(opts)
1403 rev = opts.get('rev')
1403 rev = opts.get('rev')
1404 if rev:
1404 if rev:
1405 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
1405 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
1406 ctx = scmutil.revsingle(repo, rev)
1406 ctx = scmutil.revsingle(repo, rev)
1407 m = scmutil.match(ctx, (file1,) + pats, opts)
1407 m = scmutil.match(ctx, (file1,) + pats, opts)
1408 fntemplate = opts.pop('output', '')
1408 fntemplate = opts.pop('output', '')
1409 if cmdutil.isstdiofilename(fntemplate):
1409 if cmdutil.isstdiofilename(fntemplate):
1410 fntemplate = ''
1410 fntemplate = ''
1411
1411
1412 if fntemplate:
1412 if fntemplate:
1413 fm = formatter.nullformatter(ui, 'cat', opts)
1413 fm = formatter.nullformatter(ui, 'cat', opts)
1414 else:
1414 else:
1415 ui.pager('cat')
1415 ui.pager('cat')
1416 fm = ui.formatter('cat', opts)
1416 fm = ui.formatter('cat', opts)
1417 with fm:
1417 with fm:
1418 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '',
1418 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '',
1419 **pycompat.strkwargs(opts))
1419 **pycompat.strkwargs(opts))
1420
1420
1421 @command('clone',
1421 @command('clone',
1422 [('U', 'noupdate', None, _('the clone will include an empty working '
1422 [('U', 'noupdate', None, _('the clone will include an empty working '
1423 'directory (only a repository)')),
1423 'directory (only a repository)')),
1424 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1424 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1425 _('REV')),
1425 _('REV')),
1426 ('r', 'rev', [], _('do not clone everything, but include this changeset'
1426 ('r', 'rev', [], _('do not clone everything, but include this changeset'
1427 ' and its ancestors'), _('REV')),
1427 ' and its ancestors'), _('REV')),
1428 ('b', 'branch', [], _('do not clone everything, but include this branch\'s'
1428 ('b', 'branch', [], _('do not clone everything, but include this branch\'s'
1429 ' changesets and their ancestors'), _('BRANCH')),
1429 ' changesets and their ancestors'), _('BRANCH')),
1430 ('', 'pull', None, _('use pull protocol to copy metadata')),
1430 ('', 'pull', None, _('use pull protocol to copy metadata')),
1431 ('', 'uncompressed', None,
1431 ('', 'uncompressed', None,
1432 _('an alias to --stream (DEPRECATED)')),
1432 _('an alias to --stream (DEPRECATED)')),
1433 ('', 'stream', None,
1433 ('', 'stream', None,
1434 _('clone with minimal data processing')),
1434 _('clone with minimal data processing')),
1435 ] + remoteopts,
1435 ] + remoteopts,
1436 _('[OPTION]... SOURCE [DEST]'),
1436 _('[OPTION]... SOURCE [DEST]'),
1437 helpcategory=command.CATEGORY_REPO_CREATION,
1437 helpcategory=command.CATEGORY_REPO_CREATION,
1438 helpbasic=True, norepo=True)
1438 helpbasic=True, norepo=True)
1439 def clone(ui, source, dest=None, **opts):
1439 def clone(ui, source, dest=None, **opts):
1440 """make a copy of an existing repository
1440 """make a copy of an existing repository
1441
1441
1442 Create a copy of an existing repository in a new directory.
1442 Create a copy of an existing repository in a new directory.
1443
1443
1444 If no destination directory name is specified, it defaults to the
1444 If no destination directory name is specified, it defaults to the
1445 basename of the source.
1445 basename of the source.
1446
1446
1447 The location of the source is added to the new repository's
1447 The location of the source is added to the new repository's
1448 ``.hg/hgrc`` file, as the default to be used for future pulls.
1448 ``.hg/hgrc`` file, as the default to be used for future pulls.
1449
1449
1450 Only local paths and ``ssh://`` URLs are supported as
1450 Only local paths and ``ssh://`` URLs are supported as
1451 destinations. For ``ssh://`` destinations, no working directory or
1451 destinations. For ``ssh://`` destinations, no working directory or
1452 ``.hg/hgrc`` will be created on the remote side.
1452 ``.hg/hgrc`` will be created on the remote side.
1453
1453
1454 If the source repository has a bookmark called '@' set, that
1454 If the source repository has a bookmark called '@' set, that
1455 revision will be checked out in the new repository by default.
1455 revision will be checked out in the new repository by default.
1456
1456
1457 To check out a particular version, use -u/--update, or
1457 To check out a particular version, use -u/--update, or
1458 -U/--noupdate to create a clone with no working directory.
1458 -U/--noupdate to create a clone with no working directory.
1459
1459
1460 To pull only a subset of changesets, specify one or more revisions
1460 To pull only a subset of changesets, specify one or more revisions
1461 identifiers with -r/--rev or branches with -b/--branch. The
1461 identifiers with -r/--rev or branches with -b/--branch. The
1462 resulting clone will contain only the specified changesets and
1462 resulting clone will contain only the specified changesets and
1463 their ancestors. These options (or 'clone src#rev dest') imply
1463 their ancestors. These options (or 'clone src#rev dest') imply
1464 --pull, even for local source repositories.
1464 --pull, even for local source repositories.
1465
1465
1466 In normal clone mode, the remote normalizes repository data into a common
1466 In normal clone mode, the remote normalizes repository data into a common
1467 exchange format and the receiving end translates this data into its local
1467 exchange format and the receiving end translates this data into its local
1468 storage format. --stream activates a different clone mode that essentially
1468 storage format. --stream activates a different clone mode that essentially
1469 copies repository files from the remote with minimal data processing. This
1469 copies repository files from the remote with minimal data processing. This
1470 significantly reduces the CPU cost of a clone both remotely and locally.
1470 significantly reduces the CPU cost of a clone both remotely and locally.
1471 However, it often increases the transferred data size by 30-40%. This can
1471 However, it often increases the transferred data size by 30-40%. This can
1472 result in substantially faster clones where I/O throughput is plentiful,
1472 result in substantially faster clones where I/O throughput is plentiful,
1473 especially for larger repositories. A side-effect of --stream clones is
1473 especially for larger repositories. A side-effect of --stream clones is
1474 that storage settings and requirements on the remote are applied locally:
1474 that storage settings and requirements on the remote are applied locally:
1475 a modern client may inherit legacy or inefficient storage used by the
1475 a modern client may inherit legacy or inefficient storage used by the
1476 remote or a legacy Mercurial client may not be able to clone from a
1476 remote or a legacy Mercurial client may not be able to clone from a
1477 modern Mercurial remote.
1477 modern Mercurial remote.
1478
1478
1479 .. note::
1479 .. note::
1480
1480
1481 Specifying a tag will include the tagged changeset but not the
1481 Specifying a tag will include the tagged changeset but not the
1482 changeset containing the tag.
1482 changeset containing the tag.
1483
1483
1484 .. container:: verbose
1484 .. container:: verbose
1485
1485
1486 For efficiency, hardlinks are used for cloning whenever the
1486 For efficiency, hardlinks are used for cloning whenever the
1487 source and destination are on the same filesystem (note this
1487 source and destination are on the same filesystem (note this
1488 applies only to the repository data, not to the working
1488 applies only to the repository data, not to the working
1489 directory). Some filesystems, such as AFS, implement hardlinking
1489 directory). Some filesystems, such as AFS, implement hardlinking
1490 incorrectly, but do not report errors. In these cases, use the
1490 incorrectly, but do not report errors. In these cases, use the
1491 --pull option to avoid hardlinking.
1491 --pull option to avoid hardlinking.
1492
1492
1493 Mercurial will update the working directory to the first applicable
1493 Mercurial will update the working directory to the first applicable
1494 revision from this list:
1494 revision from this list:
1495
1495
1496 a) null if -U or the source repository has no changesets
1496 a) null if -U or the source repository has no changesets
1497 b) if -u . and the source repository is local, the first parent of
1497 b) if -u . and the source repository is local, the first parent of
1498 the source repository's working directory
1498 the source repository's working directory
1499 c) the changeset specified with -u (if a branch name, this means the
1499 c) the changeset specified with -u (if a branch name, this means the
1500 latest head of that branch)
1500 latest head of that branch)
1501 d) the changeset specified with -r
1501 d) the changeset specified with -r
1502 e) the tipmost head specified with -b
1502 e) the tipmost head specified with -b
1503 f) the tipmost head specified with the url#branch source syntax
1503 f) the tipmost head specified with the url#branch source syntax
1504 g) the revision marked with the '@' bookmark, if present
1504 g) the revision marked with the '@' bookmark, if present
1505 h) the tipmost head of the default branch
1505 h) the tipmost head of the default branch
1506 i) tip
1506 i) tip
1507
1507
1508 When cloning from servers that support it, Mercurial may fetch
1508 When cloning from servers that support it, Mercurial may fetch
1509 pre-generated data from a server-advertised URL or inline from the
1509 pre-generated data from a server-advertised URL or inline from the
1510 same stream. When this is done, hooks operating on incoming changesets
1510 same stream. When this is done, hooks operating on incoming changesets
1511 and changegroups may fire more than once, once for each pre-generated
1511 and changegroups may fire more than once, once for each pre-generated
1512 bundle and as well as for any additional remaining data. In addition,
1512 bundle and as well as for any additional remaining data. In addition,
1513 if an error occurs, the repository may be rolled back to a partial
1513 if an error occurs, the repository may be rolled back to a partial
1514 clone. This behavior may change in future releases.
1514 clone. This behavior may change in future releases.
1515 See :hg:`help -e clonebundles` for more.
1515 See :hg:`help -e clonebundles` for more.
1516
1516
1517 Examples:
1517 Examples:
1518
1518
1519 - clone a remote repository to a new directory named hg/::
1519 - clone a remote repository to a new directory named hg/::
1520
1520
1521 hg clone https://www.mercurial-scm.org/repo/hg/
1521 hg clone https://www.mercurial-scm.org/repo/hg/
1522
1522
1523 - create a lightweight local clone::
1523 - create a lightweight local clone::
1524
1524
1525 hg clone project/ project-feature/
1525 hg clone project/ project-feature/
1526
1526
1527 - clone from an absolute path on an ssh server (note double-slash)::
1527 - clone from an absolute path on an ssh server (note double-slash)::
1528
1528
1529 hg clone ssh://user@server//home/projects/alpha/
1529 hg clone ssh://user@server//home/projects/alpha/
1530
1530
1531 - do a streaming clone while checking out a specified version::
1531 - do a streaming clone while checking out a specified version::
1532
1532
1533 hg clone --stream http://server/repo -u 1.5
1533 hg clone --stream http://server/repo -u 1.5
1534
1534
1535 - create a repository without changesets after a particular revision::
1535 - create a repository without changesets after a particular revision::
1536
1536
1537 hg clone -r 04e544 experimental/ good/
1537 hg clone -r 04e544 experimental/ good/
1538
1538
1539 - clone (and track) a particular named branch::
1539 - clone (and track) a particular named branch::
1540
1540
1541 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1541 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1542
1542
1543 See :hg:`help urls` for details on specifying URLs.
1543 See :hg:`help urls` for details on specifying URLs.
1544
1544
1545 Returns 0 on success.
1545 Returns 0 on success.
1546 """
1546 """
1547 opts = pycompat.byteskwargs(opts)
1547 opts = pycompat.byteskwargs(opts)
1548 if opts.get('noupdate') and opts.get('updaterev'):
1548 if opts.get('noupdate') and opts.get('updaterev'):
1549 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1549 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1550
1550
1551 # --include/--exclude can come from narrow or sparse.
1551 # --include/--exclude can come from narrow or sparse.
1552 includepats, excludepats = None, None
1552 includepats, excludepats = None, None
1553
1553
1554 # hg.clone() differentiates between None and an empty set. So make sure
1554 # hg.clone() differentiates between None and an empty set. So make sure
1555 # patterns are sets if narrow is requested without patterns.
1555 # patterns are sets if narrow is requested without patterns.
1556 if opts.get('narrow'):
1556 if opts.get('narrow'):
1557 includepats = set()
1557 includepats = set()
1558 excludepats = set()
1558 excludepats = set()
1559
1559
1560 if opts.get('include'):
1560 if opts.get('include'):
1561 includepats = narrowspec.parsepatterns(opts.get('include'))
1561 includepats = narrowspec.parsepatterns(opts.get('include'))
1562 if opts.get('exclude'):
1562 if opts.get('exclude'):
1563 excludepats = narrowspec.parsepatterns(opts.get('exclude'))
1563 excludepats = narrowspec.parsepatterns(opts.get('exclude'))
1564
1564
1565 r = hg.clone(ui, opts, source, dest,
1565 r = hg.clone(ui, opts, source, dest,
1566 pull=opts.get('pull'),
1566 pull=opts.get('pull'),
1567 stream=opts.get('stream') or opts.get('uncompressed'),
1567 stream=opts.get('stream') or opts.get('uncompressed'),
1568 revs=opts.get('rev'),
1568 revs=opts.get('rev'),
1569 update=opts.get('updaterev') or not opts.get('noupdate'),
1569 update=opts.get('updaterev') or not opts.get('noupdate'),
1570 branch=opts.get('branch'),
1570 branch=opts.get('branch'),
1571 shareopts=opts.get('shareopts'),
1571 shareopts=opts.get('shareopts'),
1572 storeincludepats=includepats,
1572 storeincludepats=includepats,
1573 storeexcludepats=excludepats,
1573 storeexcludepats=excludepats,
1574 depth=opts.get('depth') or None)
1574 depth=opts.get('depth') or None)
1575
1575
1576 return r is None
1576 return r is None
1577
1577
1578 @command('commit|ci',
1578 @command('commit|ci',
1579 [('A', 'addremove', None,
1579 [('A', 'addremove', None,
1580 _('mark new/missing files as added/removed before committing')),
1580 _('mark new/missing files as added/removed before committing')),
1581 ('', 'close-branch', None,
1581 ('', 'close-branch', None,
1582 _('mark a branch head as closed')),
1582 _('mark a branch head as closed')),
1583 ('', 'amend', None, _('amend the parent of the working directory')),
1583 ('', 'amend', None, _('amend the parent of the working directory')),
1584 ('s', 'secret', None, _('use the secret phase for committing')),
1584 ('s', 'secret', None, _('use the secret phase for committing')),
1585 ('e', 'edit', None, _('invoke editor on commit messages')),
1585 ('e', 'edit', None, _('invoke editor on commit messages')),
1586 ('i', 'interactive', None, _('use interactive mode')),
1586 ('i', 'interactive', None, _('use interactive mode')),
1587 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1587 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1588 _('[OPTION]... [FILE]...'),
1588 _('[OPTION]... [FILE]...'),
1589 helpcategory=command.CATEGORY_COMMITTING, helpbasic=True,
1589 helpcategory=command.CATEGORY_COMMITTING, helpbasic=True,
1590 inferrepo=True)
1590 inferrepo=True)
1591 def commit(ui, repo, *pats, **opts):
1591 def commit(ui, repo, *pats, **opts):
1592 """commit the specified files or all outstanding changes
1592 """commit the specified files or all outstanding changes
1593
1593
1594 Commit changes to the given files into the repository. Unlike a
1594 Commit changes to the given files into the repository. Unlike a
1595 centralized SCM, this operation is a local operation. See
1595 centralized SCM, this operation is a local operation. See
1596 :hg:`push` for a way to actively distribute your changes.
1596 :hg:`push` for a way to actively distribute your changes.
1597
1597
1598 If a list of files is omitted, all changes reported by :hg:`status`
1598 If a list of files is omitted, all changes reported by :hg:`status`
1599 will be committed.
1599 will be committed.
1600
1600
1601 If you are committing the result of a merge, do not provide any
1601 If you are committing the result of a merge, do not provide any
1602 filenames or -I/-X filters.
1602 filenames or -I/-X filters.
1603
1603
1604 If no commit message is specified, Mercurial starts your
1604 If no commit message is specified, Mercurial starts your
1605 configured editor where you can enter a message. In case your
1605 configured editor where you can enter a message. In case your
1606 commit fails, you will find a backup of your message in
1606 commit fails, you will find a backup of your message in
1607 ``.hg/last-message.txt``.
1607 ``.hg/last-message.txt``.
1608
1608
1609 The --close-branch flag can be used to mark the current branch
1609 The --close-branch flag can be used to mark the current branch
1610 head closed. When all heads of a branch are closed, the branch
1610 head closed. When all heads of a branch are closed, the branch
1611 will be considered closed and no longer listed.
1611 will be considered closed and no longer listed.
1612
1612
1613 The --amend flag can be used to amend the parent of the
1613 The --amend flag can be used to amend the parent of the
1614 working directory with a new commit that contains the changes
1614 working directory with a new commit that contains the changes
1615 in the parent in addition to those currently reported by :hg:`status`,
1615 in the parent in addition to those currently reported by :hg:`status`,
1616 if there are any. The old commit is stored in a backup bundle in
1616 if there are any. The old commit is stored in a backup bundle in
1617 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1617 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1618 on how to restore it).
1618 on how to restore it).
1619
1619
1620 Message, user and date are taken from the amended commit unless
1620 Message, user and date are taken from the amended commit unless
1621 specified. When a message isn't specified on the command line,
1621 specified. When a message isn't specified on the command line,
1622 the editor will open with the message of the amended commit.
1622 the editor will open with the message of the amended commit.
1623
1623
1624 It is not possible to amend public changesets (see :hg:`help phases`)
1624 It is not possible to amend public changesets (see :hg:`help phases`)
1625 or changesets that have children.
1625 or changesets that have children.
1626
1626
1627 See :hg:`help dates` for a list of formats valid for -d/--date.
1627 See :hg:`help dates` for a list of formats valid for -d/--date.
1628
1628
1629 Returns 0 on success, 1 if nothing changed.
1629 Returns 0 on success, 1 if nothing changed.
1630
1630
1631 .. container:: verbose
1631 .. container:: verbose
1632
1632
1633 Examples:
1633 Examples:
1634
1634
1635 - commit all files ending in .py::
1635 - commit all files ending in .py::
1636
1636
1637 hg commit --include "set:**.py"
1637 hg commit --include "set:**.py"
1638
1638
1639 - commit all non-binary files::
1639 - commit all non-binary files::
1640
1640
1641 hg commit --exclude "set:binary()"
1641 hg commit --exclude "set:binary()"
1642
1642
1643 - amend the current commit and set the date to now::
1643 - amend the current commit and set the date to now::
1644
1644
1645 hg commit --amend --date now
1645 hg commit --amend --date now
1646 """
1646 """
1647 with repo.wlock(), repo.lock():
1647 with repo.wlock(), repo.lock():
1648 return _docommit(ui, repo, *pats, **opts)
1648 return _docommit(ui, repo, *pats, **opts)
1649
1649
1650 def _docommit(ui, repo, *pats, **opts):
1650 def _docommit(ui, repo, *pats, **opts):
1651 if opts.get(r'interactive'):
1651 if opts.get(r'interactive'):
1652 opts.pop(r'interactive')
1652 opts.pop(r'interactive')
1653 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1653 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1654 cmdutil.recordfilter, *pats,
1654 cmdutil.recordfilter, *pats,
1655 **opts)
1655 **opts)
1656 # ret can be 0 (no changes to record) or the value returned by
1656 # ret can be 0 (no changes to record) or the value returned by
1657 # commit(), 1 if nothing changed or None on success.
1657 # commit(), 1 if nothing changed or None on success.
1658 return 1 if ret == 0 else ret
1658 return 1 if ret == 0 else ret
1659
1659
1660 opts = pycompat.byteskwargs(opts)
1660 opts = pycompat.byteskwargs(opts)
1661 if opts.get('subrepos'):
1661 if opts.get('subrepos'):
1662 if opts.get('amend'):
1662 if opts.get('amend'):
1663 raise error.Abort(_('cannot amend with --subrepos'))
1663 raise error.Abort(_('cannot amend with --subrepos'))
1664 # Let --subrepos on the command line override config setting.
1664 # Let --subrepos on the command line override config setting.
1665 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1665 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1666
1666
1667 cmdutil.checkunfinished(repo, commit=True)
1667 cmdutil.checkunfinished(repo, commit=True)
1668
1668
1669 branch = repo[None].branch()
1669 branch = repo[None].branch()
1670 bheads = repo.branchheads(branch)
1670 bheads = repo.branchheads(branch)
1671
1671
1672 extra = {}
1672 extra = {}
1673 if opts.get('close_branch'):
1673 if opts.get('close_branch'):
1674 extra['close'] = '1'
1674 extra['close'] = '1'
1675
1675
1676 if not bheads:
1676 if not bheads:
1677 raise error.Abort(_('can only close branch heads'))
1677 raise error.Abort(_('can only close branch heads'))
1678 elif opts.get('amend'):
1678 elif opts.get('amend'):
1679 if (repo['.'].p1().branch() != branch and
1679 if (repo['.'].p1().branch() != branch and
1680 repo['.'].p2().branch() != branch):
1680 repo['.'].p2().branch() != branch):
1681 raise error.Abort(_('can only close branch heads'))
1681 raise error.Abort(_('can only close branch heads'))
1682
1682
1683 if opts.get('amend'):
1683 if opts.get('amend'):
1684 if ui.configbool('ui', 'commitsubrepos'):
1684 if ui.configbool('ui', 'commitsubrepos'):
1685 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1685 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1686
1686
1687 old = repo['.']
1687 old = repo['.']
1688 rewriteutil.precheck(repo, [old.rev()], 'amend')
1688 rewriteutil.precheck(repo, [old.rev()], 'amend')
1689
1689
1690 # Currently histedit gets confused if an amend happens while histedit
1690 # Currently histedit gets confused if an amend happens while histedit
1691 # is in progress. Since we have a checkunfinished command, we are
1691 # is in progress. Since we have a checkunfinished command, we are
1692 # temporarily honoring it.
1692 # temporarily honoring it.
1693 #
1693 #
1694 # Note: eventually this guard will be removed. Please do not expect
1694 # Note: eventually this guard will be removed. Please do not expect
1695 # this behavior to remain.
1695 # this behavior to remain.
1696 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1696 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1697 cmdutil.checkunfinished(repo)
1697 cmdutil.checkunfinished(repo)
1698
1698
1699 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
1699 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
1700 if node == old.node():
1700 if node == old.node():
1701 ui.status(_("nothing changed\n"))
1701 ui.status(_("nothing changed\n"))
1702 return 1
1702 return 1
1703 else:
1703 else:
1704 def commitfunc(ui, repo, message, match, opts):
1704 def commitfunc(ui, repo, message, match, opts):
1705 overrides = {}
1705 overrides = {}
1706 if opts.get('secret'):
1706 if opts.get('secret'):
1707 overrides[('phases', 'new-commit')] = 'secret'
1707 overrides[('phases', 'new-commit')] = 'secret'
1708
1708
1709 baseui = repo.baseui
1709 baseui = repo.baseui
1710 with baseui.configoverride(overrides, 'commit'):
1710 with baseui.configoverride(overrides, 'commit'):
1711 with ui.configoverride(overrides, 'commit'):
1711 with ui.configoverride(overrides, 'commit'):
1712 editform = cmdutil.mergeeditform(repo[None],
1712 editform = cmdutil.mergeeditform(repo[None],
1713 'commit.normal')
1713 'commit.normal')
1714 editor = cmdutil.getcommiteditor(
1714 editor = cmdutil.getcommiteditor(
1715 editform=editform, **pycompat.strkwargs(opts))
1715 editform=editform, **pycompat.strkwargs(opts))
1716 return repo.commit(message,
1716 return repo.commit(message,
1717 opts.get('user'),
1717 opts.get('user'),
1718 opts.get('date'),
1718 opts.get('date'),
1719 match,
1719 match,
1720 editor=editor,
1720 editor=editor,
1721 extra=extra)
1721 extra=extra)
1722
1722
1723 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1723 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1724
1724
1725 if not node:
1725 if not node:
1726 stat = cmdutil.postcommitstatus(repo, pats, opts)
1726 stat = cmdutil.postcommitstatus(repo, pats, opts)
1727 if stat[3]:
1727 if stat[3]:
1728 ui.status(_("nothing changed (%d missing files, see "
1728 ui.status(_("nothing changed (%d missing files, see "
1729 "'hg status')\n") % len(stat[3]))
1729 "'hg status')\n") % len(stat[3]))
1730 else:
1730 else:
1731 ui.status(_("nothing changed\n"))
1731 ui.status(_("nothing changed\n"))
1732 return 1
1732 return 1
1733
1733
1734 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1734 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1735
1735
1736 @command('config|showconfig|debugconfig',
1736 @command('config|showconfig|debugconfig',
1737 [('u', 'untrusted', None, _('show untrusted configuration options')),
1737 [('u', 'untrusted', None, _('show untrusted configuration options')),
1738 ('e', 'edit', None, _('edit user config')),
1738 ('e', 'edit', None, _('edit user config')),
1739 ('l', 'local', None, _('edit repository config')),
1739 ('l', 'local', None, _('edit repository config')),
1740 ('g', 'global', None, _('edit global config'))] + formatteropts,
1740 ('g', 'global', None, _('edit global config'))] + formatteropts,
1741 _('[-u] [NAME]...'),
1741 _('[-u] [NAME]...'),
1742 helpcategory=command.CATEGORY_HELP,
1742 helpcategory=command.CATEGORY_HELP,
1743 optionalrepo=True,
1743 optionalrepo=True,
1744 intents={INTENT_READONLY})
1744 intents={INTENT_READONLY})
1745 def config(ui, repo, *values, **opts):
1745 def config(ui, repo, *values, **opts):
1746 """show combined config settings from all hgrc files
1746 """show combined config settings from all hgrc files
1747
1747
1748 With no arguments, print names and values of all config items.
1748 With no arguments, print names and values of all config items.
1749
1749
1750 With one argument of the form section.name, print just the value
1750 With one argument of the form section.name, print just the value
1751 of that config item.
1751 of that config item.
1752
1752
1753 With multiple arguments, print names and values of all config
1753 With multiple arguments, print names and values of all config
1754 items with matching section names or section.names.
1754 items with matching section names or section.names.
1755
1755
1756 With --edit, start an editor on the user-level config file. With
1756 With --edit, start an editor on the user-level config file. With
1757 --global, edit the system-wide config file. With --local, edit the
1757 --global, edit the system-wide config file. With --local, edit the
1758 repository-level config file.
1758 repository-level config file.
1759
1759
1760 With --debug, the source (filename and line number) is printed
1760 With --debug, the source (filename and line number) is printed
1761 for each config item.
1761 for each config item.
1762
1762
1763 See :hg:`help config` for more information about config files.
1763 See :hg:`help config` for more information about config files.
1764
1764
1765 .. container:: verbose
1765 .. container:: verbose
1766
1766
1767 Template:
1767 Template:
1768
1768
1769 The following keywords are supported. See also :hg:`help templates`.
1769 The following keywords are supported. See also :hg:`help templates`.
1770
1770
1771 :name: String. Config name.
1771 :name: String. Config name.
1772 :source: String. Filename and line number where the item is defined.
1772 :source: String. Filename and line number where the item is defined.
1773 :value: String. Config value.
1773 :value: String. Config value.
1774
1774
1775 Returns 0 on success, 1 if NAME does not exist.
1775 Returns 0 on success, 1 if NAME does not exist.
1776
1776
1777 """
1777 """
1778
1778
1779 opts = pycompat.byteskwargs(opts)
1779 opts = pycompat.byteskwargs(opts)
1780 if opts.get('edit') or opts.get('local') or opts.get('global'):
1780 if opts.get('edit') or opts.get('local') or opts.get('global'):
1781 if opts.get('local') and opts.get('global'):
1781 if opts.get('local') and opts.get('global'):
1782 raise error.Abort(_("can't use --local and --global together"))
1782 raise error.Abort(_("can't use --local and --global together"))
1783
1783
1784 if opts.get('local'):
1784 if opts.get('local'):
1785 if not repo:
1785 if not repo:
1786 raise error.Abort(_("can't use --local outside a repository"))
1786 raise error.Abort(_("can't use --local outside a repository"))
1787 paths = [repo.vfs.join('hgrc')]
1787 paths = [repo.vfs.join('hgrc')]
1788 elif opts.get('global'):
1788 elif opts.get('global'):
1789 paths = rcutil.systemrcpath()
1789 paths = rcutil.systemrcpath()
1790 else:
1790 else:
1791 paths = rcutil.userrcpath()
1791 paths = rcutil.userrcpath()
1792
1792
1793 for f in paths:
1793 for f in paths:
1794 if os.path.exists(f):
1794 if os.path.exists(f):
1795 break
1795 break
1796 else:
1796 else:
1797 if opts.get('global'):
1797 if opts.get('global'):
1798 samplehgrc = uimod.samplehgrcs['global']
1798 samplehgrc = uimod.samplehgrcs['global']
1799 elif opts.get('local'):
1799 elif opts.get('local'):
1800 samplehgrc = uimod.samplehgrcs['local']
1800 samplehgrc = uimod.samplehgrcs['local']
1801 else:
1801 else:
1802 samplehgrc = uimod.samplehgrcs['user']
1802 samplehgrc = uimod.samplehgrcs['user']
1803
1803
1804 f = paths[0]
1804 f = paths[0]
1805 fp = open(f, "wb")
1805 fp = open(f, "wb")
1806 fp.write(util.tonativeeol(samplehgrc))
1806 fp.write(util.tonativeeol(samplehgrc))
1807 fp.close()
1807 fp.close()
1808
1808
1809 editor = ui.geteditor()
1809 editor = ui.geteditor()
1810 ui.system("%s \"%s\"" % (editor, f),
1810 ui.system("%s \"%s\"" % (editor, f),
1811 onerr=error.Abort, errprefix=_("edit failed"),
1811 onerr=error.Abort, errprefix=_("edit failed"),
1812 blockedtag='config_edit')
1812 blockedtag='config_edit')
1813 return
1813 return
1814 ui.pager('config')
1814 ui.pager('config')
1815 fm = ui.formatter('config', opts)
1815 fm = ui.formatter('config', opts)
1816 for t, f in rcutil.rccomponents():
1816 for t, f in rcutil.rccomponents():
1817 if t == 'path':
1817 if t == 'path':
1818 ui.debug('read config from: %s\n' % f)
1818 ui.debug('read config from: %s\n' % f)
1819 elif t == 'items':
1819 elif t == 'items':
1820 for section, name, value, source in f:
1820 for section, name, value, source in f:
1821 ui.debug('set config by: %s\n' % source)
1821 ui.debug('set config by: %s\n' % source)
1822 else:
1822 else:
1823 raise error.ProgrammingError('unknown rctype: %s' % t)
1823 raise error.ProgrammingError('unknown rctype: %s' % t)
1824 untrusted = bool(opts.get('untrusted'))
1824 untrusted = bool(opts.get('untrusted'))
1825
1825
1826 selsections = selentries = []
1826 selsections = selentries = []
1827 if values:
1827 if values:
1828 selsections = [v for v in values if '.' not in v]
1828 selsections = [v for v in values if '.' not in v]
1829 selentries = [v for v in values if '.' in v]
1829 selentries = [v for v in values if '.' in v]
1830 uniquesel = (len(selentries) == 1 and not selsections)
1830 uniquesel = (len(selentries) == 1 and not selsections)
1831 selsections = set(selsections)
1831 selsections = set(selsections)
1832 selentries = set(selentries)
1832 selentries = set(selentries)
1833
1833
1834 matched = False
1834 matched = False
1835 for section, name, value in ui.walkconfig(untrusted=untrusted):
1835 for section, name, value in ui.walkconfig(untrusted=untrusted):
1836 source = ui.configsource(section, name, untrusted)
1836 source = ui.configsource(section, name, untrusted)
1837 value = pycompat.bytestr(value)
1837 value = pycompat.bytestr(value)
1838 if fm.isplain():
1838 if fm.isplain():
1839 source = source or 'none'
1839 source = source or 'none'
1840 value = value.replace('\n', '\\n')
1840 value = value.replace('\n', '\\n')
1841 entryname = section + '.' + name
1841 entryname = section + '.' + name
1842 if values and not (section in selsections or entryname in selentries):
1842 if values and not (section in selsections or entryname in selentries):
1843 continue
1843 continue
1844 fm.startitem()
1844 fm.startitem()
1845 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1845 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1846 if uniquesel:
1846 if uniquesel:
1847 fm.data(name=entryname)
1847 fm.data(name=entryname)
1848 fm.write('value', '%s\n', value)
1848 fm.write('value', '%s\n', value)
1849 else:
1849 else:
1850 fm.write('name value', '%s=%s\n', entryname, value)
1850 fm.write('name value', '%s=%s\n', entryname, value)
1851 matched = True
1851 matched = True
1852 fm.end()
1852 fm.end()
1853 if matched:
1853 if matched:
1854 return 0
1854 return 0
1855 return 1
1855 return 1
1856
1856
1857 @command('copy|cp',
1857 @command('copy|cp',
1858 [('A', 'after', None, _('record a copy that has already occurred')),
1858 [('A', 'after', None, _('record a copy that has already occurred')),
1859 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1859 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1860 ] + walkopts + dryrunopts,
1860 ] + walkopts + dryrunopts,
1861 _('[OPTION]... [SOURCE]... DEST'),
1861 _('[OPTION]... [SOURCE]... DEST'),
1862 helpcategory=command.CATEGORY_FILE_CONTENTS)
1862 helpcategory=command.CATEGORY_FILE_CONTENTS)
1863 def copy(ui, repo, *pats, **opts):
1863 def copy(ui, repo, *pats, **opts):
1864 """mark files as copied for the next commit
1864 """mark files as copied for the next commit
1865
1865
1866 Mark dest as having copies of source files. If dest is a
1866 Mark dest as having copies of source files. If dest is a
1867 directory, copies are put in that directory. If dest is a file,
1867 directory, copies are put in that directory. If dest is a file,
1868 the source must be a single file.
1868 the source must be a single file.
1869
1869
1870 By default, this command copies the contents of files as they
1870 By default, this command copies the contents of files as they
1871 exist in the working directory. If invoked with -A/--after, the
1871 exist in the working directory. If invoked with -A/--after, the
1872 operation is recorded, but no copying is performed.
1872 operation is recorded, but no copying is performed.
1873
1873
1874 This command takes effect with the next commit. To undo a copy
1874 This command takes effect with the next commit. To undo a copy
1875 before that, see :hg:`revert`.
1875 before that, see :hg:`revert`.
1876
1876
1877 Returns 0 on success, 1 if errors are encountered.
1877 Returns 0 on success, 1 if errors are encountered.
1878 """
1878 """
1879 opts = pycompat.byteskwargs(opts)
1879 opts = pycompat.byteskwargs(opts)
1880 with repo.wlock(False):
1880 with repo.wlock(False):
1881 return cmdutil.copy(ui, repo, pats, opts)
1881 return cmdutil.copy(ui, repo, pats, opts)
1882
1882
1883 @command(
1883 @command(
1884 'debugcommands', [], _('[COMMAND]'),
1884 'debugcommands', [], _('[COMMAND]'),
1885 helpcategory=command.CATEGORY_HELP,
1885 helpcategory=command.CATEGORY_HELP,
1886 norepo=True)
1886 norepo=True)
1887 def debugcommands(ui, cmd='', *args):
1887 def debugcommands(ui, cmd='', *args):
1888 """list all available commands and options"""
1888 """list all available commands and options"""
1889 for cmd, vals in sorted(table.iteritems()):
1889 for cmd, vals in sorted(table.iteritems()):
1890 cmd = cmd.split('|')[0]
1890 cmd = cmd.split('|')[0]
1891 opts = ', '.join([i[1] for i in vals[1]])
1891 opts = ', '.join([i[1] for i in vals[1]])
1892 ui.write('%s: %s\n' % (cmd, opts))
1892 ui.write('%s: %s\n' % (cmd, opts))
1893
1893
1894 @command('debugcomplete',
1894 @command('debugcomplete',
1895 [('o', 'options', None, _('show the command options'))],
1895 [('o', 'options', None, _('show the command options'))],
1896 _('[-o] CMD'),
1896 _('[-o] CMD'),
1897 helpcategory=command.CATEGORY_HELP,
1897 helpcategory=command.CATEGORY_HELP,
1898 norepo=True)
1898 norepo=True)
1899 def debugcomplete(ui, cmd='', **opts):
1899 def debugcomplete(ui, cmd='', **opts):
1900 """returns the completion list associated with the given command"""
1900 """returns the completion list associated with the given command"""
1901
1901
1902 if opts.get(r'options'):
1902 if opts.get(r'options'):
1903 options = []
1903 options = []
1904 otables = [globalopts]
1904 otables = [globalopts]
1905 if cmd:
1905 if cmd:
1906 aliases, entry = cmdutil.findcmd(cmd, table, False)
1906 aliases, entry = cmdutil.findcmd(cmd, table, False)
1907 otables.append(entry[1])
1907 otables.append(entry[1])
1908 for t in otables:
1908 for t in otables:
1909 for o in t:
1909 for o in t:
1910 if "(DEPRECATED)" in o[3]:
1910 if "(DEPRECATED)" in o[3]:
1911 continue
1911 continue
1912 if o[0]:
1912 if o[0]:
1913 options.append('-%s' % o[0])
1913 options.append('-%s' % o[0])
1914 options.append('--%s' % o[1])
1914 options.append('--%s' % o[1])
1915 ui.write("%s\n" % "\n".join(options))
1915 ui.write("%s\n" % "\n".join(options))
1916 return
1916 return
1917
1917
1918 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1918 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1919 if ui.verbose:
1919 if ui.verbose:
1920 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1920 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1921 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1921 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1922
1922
1923 @command('diff',
1923 @command('diff',
1924 [('r', 'rev', [], _('revision'), _('REV')),
1924 [('r', 'rev', [], _('revision'), _('REV')),
1925 ('c', 'change', '', _('change made by revision'), _('REV'))
1925 ('c', 'change', '', _('change made by revision'), _('REV'))
1926 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1926 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1927 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1927 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1928 helpcategory=command.CATEGORY_FILE_CONTENTS,
1928 helpcategory=command.CATEGORY_FILE_CONTENTS,
1929 helpbasic=True, inferrepo=True, intents={INTENT_READONLY})
1929 helpbasic=True, inferrepo=True, intents={INTENT_READONLY})
1930 def diff(ui, repo, *pats, **opts):
1930 def diff(ui, repo, *pats, **opts):
1931 """diff repository (or selected files)
1931 """diff repository (or selected files)
1932
1932
1933 Show differences between revisions for the specified files.
1933 Show differences between revisions for the specified files.
1934
1934
1935 Differences between files are shown using the unified diff format.
1935 Differences between files are shown using the unified diff format.
1936
1936
1937 .. note::
1937 .. note::
1938
1938
1939 :hg:`diff` may generate unexpected results for merges, as it will
1939 :hg:`diff` may generate unexpected results for merges, as it will
1940 default to comparing against the working directory's first
1940 default to comparing against the working directory's first
1941 parent changeset if no revisions are specified.
1941 parent changeset if no revisions are specified.
1942
1942
1943 When two revision arguments are given, then changes are shown
1943 When two revision arguments are given, then changes are shown
1944 between those revisions. If only one revision is specified then
1944 between those revisions. If only one revision is specified then
1945 that revision is compared to the working directory, and, when no
1945 that revision is compared to the working directory, and, when no
1946 revisions are specified, the working directory files are compared
1946 revisions are specified, the working directory files are compared
1947 to its first parent.
1947 to its first parent.
1948
1948
1949 Alternatively you can specify -c/--change with a revision to see
1949 Alternatively you can specify -c/--change with a revision to see
1950 the changes in that changeset relative to its first parent.
1950 the changes in that changeset relative to its first parent.
1951
1951
1952 Without the -a/--text option, diff will avoid generating diffs of
1952 Without the -a/--text option, diff will avoid generating diffs of
1953 files it detects as binary. With -a, diff will generate a diff
1953 files it detects as binary. With -a, diff will generate a diff
1954 anyway, probably with undesirable results.
1954 anyway, probably with undesirable results.
1955
1955
1956 Use the -g/--git option to generate diffs in the git extended diff
1956 Use the -g/--git option to generate diffs in the git extended diff
1957 format. For more information, read :hg:`help diffs`.
1957 format. For more information, read :hg:`help diffs`.
1958
1958
1959 .. container:: verbose
1959 .. container:: verbose
1960
1960
1961 Examples:
1961 Examples:
1962
1962
1963 - compare a file in the current working directory to its parent::
1963 - compare a file in the current working directory to its parent::
1964
1964
1965 hg diff foo.c
1965 hg diff foo.c
1966
1966
1967 - compare two historical versions of a directory, with rename info::
1967 - compare two historical versions of a directory, with rename info::
1968
1968
1969 hg diff --git -r 1.0:1.2 lib/
1969 hg diff --git -r 1.0:1.2 lib/
1970
1970
1971 - get change stats relative to the last change on some date::
1971 - get change stats relative to the last change on some date::
1972
1972
1973 hg diff --stat -r "date('may 2')"
1973 hg diff --stat -r "date('may 2')"
1974
1974
1975 - diff all newly-added files that contain a keyword::
1975 - diff all newly-added files that contain a keyword::
1976
1976
1977 hg diff "set:added() and grep(GNU)"
1977 hg diff "set:added() and grep(GNU)"
1978
1978
1979 - compare a revision and its parents::
1979 - compare a revision and its parents::
1980
1980
1981 hg diff -c 9353 # compare against first parent
1981 hg diff -c 9353 # compare against first parent
1982 hg diff -r 9353^:9353 # same using revset syntax
1982 hg diff -r 9353^:9353 # same using revset syntax
1983 hg diff -r 9353^2:9353 # compare against the second parent
1983 hg diff -r 9353^2:9353 # compare against the second parent
1984
1984
1985 Returns 0 on success.
1985 Returns 0 on success.
1986 """
1986 """
1987
1987
1988 opts = pycompat.byteskwargs(opts)
1988 opts = pycompat.byteskwargs(opts)
1989 revs = opts.get('rev')
1989 revs = opts.get('rev')
1990 change = opts.get('change')
1990 change = opts.get('change')
1991 stat = opts.get('stat')
1991 stat = opts.get('stat')
1992 reverse = opts.get('reverse')
1992 reverse = opts.get('reverse')
1993
1993
1994 if revs and change:
1994 if revs and change:
1995 msg = _('cannot specify --rev and --change at the same time')
1995 msg = _('cannot specify --rev and --change at the same time')
1996 raise error.Abort(msg)
1996 raise error.Abort(msg)
1997 elif change:
1997 elif change:
1998 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
1998 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
1999 ctx2 = scmutil.revsingle(repo, change, None)
1999 ctx2 = scmutil.revsingle(repo, change, None)
2000 ctx1 = ctx2.p1()
2000 ctx1 = ctx2.p1()
2001 else:
2001 else:
2002 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
2002 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
2003 ctx1, ctx2 = scmutil.revpair(repo, revs)
2003 ctx1, ctx2 = scmutil.revpair(repo, revs)
2004 node1, node2 = ctx1.node(), ctx2.node()
2004 node1, node2 = ctx1.node(), ctx2.node()
2005
2005
2006 if reverse:
2006 if reverse:
2007 node1, node2 = node2, node1
2007 node1, node2 = node2, node1
2008
2008
2009 diffopts = patch.diffallopts(ui, opts)
2009 diffopts = patch.diffallopts(ui, opts)
2010 m = scmutil.match(ctx2, pats, opts)
2010 m = scmutil.match(ctx2, pats, opts)
2011 m = repo.narrowmatch(m)
2011 m = repo.narrowmatch(m)
2012 ui.pager('diff')
2012 ui.pager('diff')
2013 logcmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2013 logcmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2014 listsubrepos=opts.get('subrepos'),
2014 listsubrepos=opts.get('subrepos'),
2015 root=opts.get('root'))
2015 root=opts.get('root'))
2016
2016
2017 @command('export',
2017 @command('export',
2018 [('B', 'bookmark', '',
2018 [('B', 'bookmark', '',
2019 _('export changes only reachable by given bookmark'), _('BOOKMARK')),
2019 _('export changes only reachable by given bookmark'), _('BOOKMARK')),
2020 ('o', 'output', '',
2020 ('o', 'output', '',
2021 _('print output to file with formatted name'), _('FORMAT')),
2021 _('print output to file with formatted name'), _('FORMAT')),
2022 ('', 'switch-parent', None, _('diff against the second parent')),
2022 ('', 'switch-parent', None, _('diff against the second parent')),
2023 ('r', 'rev', [], _('revisions to export'), _('REV')),
2023 ('r', 'rev', [], _('revisions to export'), _('REV')),
2024 ] + diffopts + formatteropts,
2024 ] + diffopts + formatteropts,
2025 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2025 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2026 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2026 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2027 helpbasic=True, intents={INTENT_READONLY})
2027 helpbasic=True, intents={INTENT_READONLY})
2028 def export(ui, repo, *changesets, **opts):
2028 def export(ui, repo, *changesets, **opts):
2029 """dump the header and diffs for one or more changesets
2029 """dump the header and diffs for one or more changesets
2030
2030
2031 Print the changeset header and diffs for one or more revisions.
2031 Print the changeset header and diffs for one or more revisions.
2032 If no revision is given, the parent of the working directory is used.
2032 If no revision is given, the parent of the working directory is used.
2033
2033
2034 The information shown in the changeset header is: author, date,
2034 The information shown in the changeset header is: author, date,
2035 branch name (if non-default), changeset hash, parent(s) and commit
2035 branch name (if non-default), changeset hash, parent(s) and commit
2036 comment.
2036 comment.
2037
2037
2038 .. note::
2038 .. note::
2039
2039
2040 :hg:`export` may generate unexpected diff output for merge
2040 :hg:`export` may generate unexpected diff output for merge
2041 changesets, as it will compare the merge changeset against its
2041 changesets, as it will compare the merge changeset against its
2042 first parent only.
2042 first parent only.
2043
2043
2044 Output may be to a file, in which case the name of the file is
2044 Output may be to a file, in which case the name of the file is
2045 given using a template string. See :hg:`help templates`. In addition
2045 given using a template string. See :hg:`help templates`. In addition
2046 to the common template keywords, the following formatting rules are
2046 to the common template keywords, the following formatting rules are
2047 supported:
2047 supported:
2048
2048
2049 :``%%``: literal "%" character
2049 :``%%``: literal "%" character
2050 :``%H``: changeset hash (40 hexadecimal digits)
2050 :``%H``: changeset hash (40 hexadecimal digits)
2051 :``%N``: number of patches being generated
2051 :``%N``: number of patches being generated
2052 :``%R``: changeset revision number
2052 :``%R``: changeset revision number
2053 :``%b``: basename of the exporting repository
2053 :``%b``: basename of the exporting repository
2054 :``%h``: short-form changeset hash (12 hexadecimal digits)
2054 :``%h``: short-form changeset hash (12 hexadecimal digits)
2055 :``%m``: first line of the commit message (only alphanumeric characters)
2055 :``%m``: first line of the commit message (only alphanumeric characters)
2056 :``%n``: zero-padded sequence number, starting at 1
2056 :``%n``: zero-padded sequence number, starting at 1
2057 :``%r``: zero-padded changeset revision number
2057 :``%r``: zero-padded changeset revision number
2058 :``\\``: literal "\\" character
2058 :``\\``: literal "\\" character
2059
2059
2060 Without the -a/--text option, export will avoid generating diffs
2060 Without the -a/--text option, export will avoid generating diffs
2061 of files it detects as binary. With -a, export will generate a
2061 of files it detects as binary. With -a, export will generate a
2062 diff anyway, probably with undesirable results.
2062 diff anyway, probably with undesirable results.
2063
2063
2064 With -B/--bookmark changesets reachable by the given bookmark are
2064 With -B/--bookmark changesets reachable by the given bookmark are
2065 selected.
2065 selected.
2066
2066
2067 Use the -g/--git option to generate diffs in the git extended diff
2067 Use the -g/--git option to generate diffs in the git extended diff
2068 format. See :hg:`help diffs` for more information.
2068 format. See :hg:`help diffs` for more information.
2069
2069
2070 With the --switch-parent option, the diff will be against the
2070 With the --switch-parent option, the diff will be against the
2071 second parent. It can be useful to review a merge.
2071 second parent. It can be useful to review a merge.
2072
2072
2073 .. container:: verbose
2073 .. container:: verbose
2074
2074
2075 Template:
2075 Template:
2076
2076
2077 The following keywords are supported in addition to the common template
2077 The following keywords are supported in addition to the common template
2078 keywords and functions. See also :hg:`help templates`.
2078 keywords and functions. See also :hg:`help templates`.
2079
2079
2080 :diff: String. Diff content.
2080 :diff: String. Diff content.
2081 :parents: List of strings. Parent nodes of the changeset.
2081 :parents: List of strings. Parent nodes of the changeset.
2082
2082
2083 Examples:
2083 Examples:
2084
2084
2085 - use export and import to transplant a bugfix to the current
2085 - use export and import to transplant a bugfix to the current
2086 branch::
2086 branch::
2087
2087
2088 hg export -r 9353 | hg import -
2088 hg export -r 9353 | hg import -
2089
2089
2090 - export all the changesets between two revisions to a file with
2090 - export all the changesets between two revisions to a file with
2091 rename information::
2091 rename information::
2092
2092
2093 hg export --git -r 123:150 > changes.txt
2093 hg export --git -r 123:150 > changes.txt
2094
2094
2095 - split outgoing changes into a series of patches with
2095 - split outgoing changes into a series of patches with
2096 descriptive names::
2096 descriptive names::
2097
2097
2098 hg export -r "outgoing()" -o "%n-%m.patch"
2098 hg export -r "outgoing()" -o "%n-%m.patch"
2099
2099
2100 Returns 0 on success.
2100 Returns 0 on success.
2101 """
2101 """
2102 opts = pycompat.byteskwargs(opts)
2102 opts = pycompat.byteskwargs(opts)
2103 bookmark = opts.get('bookmark')
2103 bookmark = opts.get('bookmark')
2104 changesets += tuple(opts.get('rev', []))
2104 changesets += tuple(opts.get('rev', []))
2105
2105
2106 if bookmark and changesets:
2106 if bookmark and changesets:
2107 raise error.Abort(_("-r and -B are mutually exclusive"))
2107 raise error.Abort(_("-r and -B are mutually exclusive"))
2108
2108
2109 if bookmark:
2109 if bookmark:
2110 if bookmark not in repo._bookmarks:
2110 if bookmark not in repo._bookmarks:
2111 raise error.Abort(_("bookmark '%s' not found") % bookmark)
2111 raise error.Abort(_("bookmark '%s' not found") % bookmark)
2112
2112
2113 revs = scmutil.bookmarkrevs(repo, bookmark)
2113 revs = scmutil.bookmarkrevs(repo, bookmark)
2114 else:
2114 else:
2115 if not changesets:
2115 if not changesets:
2116 changesets = ['.']
2116 changesets = ['.']
2117
2117
2118 repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn')
2118 repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn')
2119 revs = scmutil.revrange(repo, changesets)
2119 revs = scmutil.revrange(repo, changesets)
2120
2120
2121 if not revs:
2121 if not revs:
2122 raise error.Abort(_("export requires at least one changeset"))
2122 raise error.Abort(_("export requires at least one changeset"))
2123 if len(revs) > 1:
2123 if len(revs) > 1:
2124 ui.note(_('exporting patches:\n'))
2124 ui.note(_('exporting patches:\n'))
2125 else:
2125 else:
2126 ui.note(_('exporting patch:\n'))
2126 ui.note(_('exporting patch:\n'))
2127
2127
2128 fntemplate = opts.get('output')
2128 fntemplate = opts.get('output')
2129 if cmdutil.isstdiofilename(fntemplate):
2129 if cmdutil.isstdiofilename(fntemplate):
2130 fntemplate = ''
2130 fntemplate = ''
2131
2131
2132 if fntemplate:
2132 if fntemplate:
2133 fm = formatter.nullformatter(ui, 'export', opts)
2133 fm = formatter.nullformatter(ui, 'export', opts)
2134 else:
2134 else:
2135 ui.pager('export')
2135 ui.pager('export')
2136 fm = ui.formatter('export', opts)
2136 fm = ui.formatter('export', opts)
2137 with fm:
2137 with fm:
2138 cmdutil.export(repo, revs, fm, fntemplate=fntemplate,
2138 cmdutil.export(repo, revs, fm, fntemplate=fntemplate,
2139 switch_parent=opts.get('switch_parent'),
2139 switch_parent=opts.get('switch_parent'),
2140 opts=patch.diffallopts(ui, opts))
2140 opts=patch.diffallopts(ui, opts))
2141
2141
2142 @command('files',
2142 @command('files',
2143 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
2143 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
2144 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2144 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2145 ] + walkopts + formatteropts + subrepoopts,
2145 ] + walkopts + formatteropts + subrepoopts,
2146 _('[OPTION]... [FILE]...'),
2146 _('[OPTION]... [FILE]...'),
2147 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2147 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2148 intents={INTENT_READONLY})
2148 intents={INTENT_READONLY})
2149 def files(ui, repo, *pats, **opts):
2149 def files(ui, repo, *pats, **opts):
2150 """list tracked files
2150 """list tracked files
2151
2151
2152 Print files under Mercurial control in the working directory or
2152 Print files under Mercurial control in the working directory or
2153 specified revision for given files (excluding removed files).
2153 specified revision for given files (excluding removed files).
2154 Files can be specified as filenames or filesets.
2154 Files can be specified as filenames or filesets.
2155
2155
2156 If no files are given to match, this command prints the names
2156 If no files are given to match, this command prints the names
2157 of all files under Mercurial control.
2157 of all files under Mercurial control.
2158
2158
2159 .. container:: verbose
2159 .. container:: verbose
2160
2160
2161 Template:
2161 Template:
2162
2162
2163 The following keywords are supported in addition to the common template
2163 The following keywords are supported in addition to the common template
2164 keywords and functions. See also :hg:`help templates`.
2164 keywords and functions. See also :hg:`help templates`.
2165
2165
2166 :flags: String. Character denoting file's symlink and executable bits.
2166 :flags: String. Character denoting file's symlink and executable bits.
2167 :path: String. Repository-absolute path of the file.
2167 :path: String. Repository-absolute path of the file.
2168 :size: Integer. Size of the file in bytes.
2168 :size: Integer. Size of the file in bytes.
2169
2169
2170 Examples:
2170 Examples:
2171
2171
2172 - list all files under the current directory::
2172 - list all files under the current directory::
2173
2173
2174 hg files .
2174 hg files .
2175
2175
2176 - shows sizes and flags for current revision::
2176 - shows sizes and flags for current revision::
2177
2177
2178 hg files -vr .
2178 hg files -vr .
2179
2179
2180 - list all files named README::
2180 - list all files named README::
2181
2181
2182 hg files -I "**/README"
2182 hg files -I "**/README"
2183
2183
2184 - list all binary files::
2184 - list all binary files::
2185
2185
2186 hg files "set:binary()"
2186 hg files "set:binary()"
2187
2187
2188 - find files containing a regular expression::
2188 - find files containing a regular expression::
2189
2189
2190 hg files "set:grep('bob')"
2190 hg files "set:grep('bob')"
2191
2191
2192 - search tracked file contents with xargs and grep::
2192 - search tracked file contents with xargs and grep::
2193
2193
2194 hg files -0 | xargs -0 grep foo
2194 hg files -0 | xargs -0 grep foo
2195
2195
2196 See :hg:`help patterns` and :hg:`help filesets` for more information
2196 See :hg:`help patterns` and :hg:`help filesets` for more information
2197 on specifying file patterns.
2197 on specifying file patterns.
2198
2198
2199 Returns 0 if a match is found, 1 otherwise.
2199 Returns 0 if a match is found, 1 otherwise.
2200
2200
2201 """
2201 """
2202
2202
2203 opts = pycompat.byteskwargs(opts)
2203 opts = pycompat.byteskwargs(opts)
2204 rev = opts.get('rev')
2204 rev = opts.get('rev')
2205 if rev:
2205 if rev:
2206 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2206 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2207 ctx = scmutil.revsingle(repo, rev, None)
2207 ctx = scmutil.revsingle(repo, rev, None)
2208
2208
2209 end = '\n'
2209 end = '\n'
2210 if opts.get('print0'):
2210 if opts.get('print0'):
2211 end = '\0'
2211 end = '\0'
2212 fmt = '%s' + end
2212 fmt = '%s' + end
2213
2213
2214 m = scmutil.match(ctx, pats, opts)
2214 m = scmutil.match(ctx, pats, opts)
2215 ui.pager('files')
2215 ui.pager('files')
2216 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2216 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2217 with ui.formatter('files', opts) as fm:
2217 with ui.formatter('files', opts) as fm:
2218 return cmdutil.files(ui, ctx, m, uipathfn, fm, fmt,
2218 return cmdutil.files(ui, ctx, m, uipathfn, fm, fmt,
2219 opts.get('subrepos'))
2219 opts.get('subrepos'))
2220
2220
2221 @command(
2221 @command(
2222 'forget',
2222 'forget',
2223 [('i', 'interactive', None, _('use interactive mode')),
2223 [('i', 'interactive', None, _('use interactive mode')),
2224 ] + walkopts + dryrunopts,
2224 ] + walkopts + dryrunopts,
2225 _('[OPTION]... FILE...'),
2225 _('[OPTION]... FILE...'),
2226 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2226 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2227 helpbasic=True, inferrepo=True)
2227 helpbasic=True, inferrepo=True)
2228 def forget(ui, repo, *pats, **opts):
2228 def forget(ui, repo, *pats, **opts):
2229 """forget the specified files on the next commit
2229 """forget the specified files on the next commit
2230
2230
2231 Mark the specified files so they will no longer be tracked
2231 Mark the specified files so they will no longer be tracked
2232 after the next commit.
2232 after the next commit.
2233
2233
2234 This only removes files from the current branch, not from the
2234 This only removes files from the current branch, not from the
2235 entire project history, and it does not delete them from the
2235 entire project history, and it does not delete them from the
2236 working directory.
2236 working directory.
2237
2237
2238 To delete the file from the working directory, see :hg:`remove`.
2238 To delete the file from the working directory, see :hg:`remove`.
2239
2239
2240 To undo a forget before the next commit, see :hg:`add`.
2240 To undo a forget before the next commit, see :hg:`add`.
2241
2241
2242 .. container:: verbose
2242 .. container:: verbose
2243
2243
2244 Examples:
2244 Examples:
2245
2245
2246 - forget newly-added binary files::
2246 - forget newly-added binary files::
2247
2247
2248 hg forget "set:added() and binary()"
2248 hg forget "set:added() and binary()"
2249
2249
2250 - forget files that would be excluded by .hgignore::
2250 - forget files that would be excluded by .hgignore::
2251
2251
2252 hg forget "set:hgignore()"
2252 hg forget "set:hgignore()"
2253
2253
2254 Returns 0 on success.
2254 Returns 0 on success.
2255 """
2255 """
2256
2256
2257 opts = pycompat.byteskwargs(opts)
2257 opts = pycompat.byteskwargs(opts)
2258 if not pats:
2258 if not pats:
2259 raise error.Abort(_('no files specified'))
2259 raise error.Abort(_('no files specified'))
2260
2260
2261 m = scmutil.match(repo[None], pats, opts)
2261 m = scmutil.match(repo[None], pats, opts)
2262 dryrun, interactive = opts.get('dry_run'), opts.get('interactive')
2262 dryrun, interactive = opts.get('dry_run'), opts.get('interactive')
2263 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2263 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2264 rejected = cmdutil.forget(ui, repo, m, prefix="", uipathfn=uipathfn,
2264 rejected = cmdutil.forget(ui, repo, m, prefix="", uipathfn=uipathfn,
2265 explicitonly=False, dryrun=dryrun,
2265 explicitonly=False, dryrun=dryrun,
2266 interactive=interactive)[0]
2266 interactive=interactive)[0]
2267 return rejected and 1 or 0
2267 return rejected and 1 or 0
2268
2268
2269 @command(
2269 @command(
2270 'graft',
2270 'graft',
2271 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2271 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2272 ('', 'base', '',
2272 ('', 'base', '',
2273 _('base revision when doing the graft merge (ADVANCED)'), _('REV')),
2273 _('base revision when doing the graft merge (ADVANCED)'), _('REV')),
2274 ('c', 'continue', False, _('resume interrupted graft')),
2274 ('c', 'continue', False, _('resume interrupted graft')),
2275 ('', 'stop', False, _('stop interrupted graft')),
2275 ('', 'stop', False, _('stop interrupted graft')),
2276 ('', 'abort', False, _('abort interrupted graft')),
2276 ('', 'abort', False, _('abort interrupted graft')),
2277 ('e', 'edit', False, _('invoke editor on commit messages')),
2277 ('e', 'edit', False, _('invoke editor on commit messages')),
2278 ('', 'log', None, _('append graft info to log message')),
2278 ('', 'log', None, _('append graft info to log message')),
2279 ('', 'no-commit', None,
2279 ('', 'no-commit', None,
2280 _("don't commit, just apply the changes in working directory")),
2280 _("don't commit, just apply the changes in working directory")),
2281 ('f', 'force', False, _('force graft')),
2281 ('f', 'force', False, _('force graft')),
2282 ('D', 'currentdate', False,
2282 ('D', 'currentdate', False,
2283 _('record the current date as commit date')),
2283 _('record the current date as commit date')),
2284 ('U', 'currentuser', False,
2284 ('U', 'currentuser', False,
2285 _('record the current user as committer'))]
2285 _('record the current user as committer'))]
2286 + commitopts2 + mergetoolopts + dryrunopts,
2286 + commitopts2 + mergetoolopts + dryrunopts,
2287 _('[OPTION]... [-r REV]... REV...'),
2287 _('[OPTION]... [-r REV]... REV...'),
2288 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
2288 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
2289 def graft(ui, repo, *revs, **opts):
2289 def graft(ui, repo, *revs, **opts):
2290 '''copy changes from other branches onto the current branch
2290 '''copy changes from other branches onto the current branch
2291
2291
2292 This command uses Mercurial's merge logic to copy individual
2292 This command uses Mercurial's merge logic to copy individual
2293 changes from other branches without merging branches in the
2293 changes from other branches without merging branches in the
2294 history graph. This is sometimes known as 'backporting' or
2294 history graph. This is sometimes known as 'backporting' or
2295 'cherry-picking'. By default, graft will copy user, date, and
2295 'cherry-picking'. By default, graft will copy user, date, and
2296 description from the source changesets.
2296 description from the source changesets.
2297
2297
2298 Changesets that are ancestors of the current revision, that have
2298 Changesets that are ancestors of the current revision, that have
2299 already been grafted, or that are merges will be skipped.
2299 already been grafted, or that are merges will be skipped.
2300
2300
2301 If --log is specified, log messages will have a comment appended
2301 If --log is specified, log messages will have a comment appended
2302 of the form::
2302 of the form::
2303
2303
2304 (grafted from CHANGESETHASH)
2304 (grafted from CHANGESETHASH)
2305
2305
2306 If --force is specified, revisions will be grafted even if they
2306 If --force is specified, revisions will be grafted even if they
2307 are already ancestors of, or have been grafted to, the destination.
2307 are already ancestors of, or have been grafted to, the destination.
2308 This is useful when the revisions have since been backed out.
2308 This is useful when the revisions have since been backed out.
2309
2309
2310 If a graft merge results in conflicts, the graft process is
2310 If a graft merge results in conflicts, the graft process is
2311 interrupted so that the current merge can be manually resolved.
2311 interrupted so that the current merge can be manually resolved.
2312 Once all conflicts are addressed, the graft process can be
2312 Once all conflicts are addressed, the graft process can be
2313 continued with the -c/--continue option.
2313 continued with the -c/--continue option.
2314
2314
2315 The -c/--continue option reapplies all the earlier options.
2315 The -c/--continue option reapplies all the earlier options.
2316
2316
2317 .. container:: verbose
2317 .. container:: verbose
2318
2318
2319 The --base option exposes more of how graft internally uses merge with a
2319 The --base option exposes more of how graft internally uses merge with a
2320 custom base revision. --base can be used to specify another ancestor than
2320 custom base revision. --base can be used to specify another ancestor than
2321 the first and only parent.
2321 the first and only parent.
2322
2322
2323 The command::
2323 The command::
2324
2324
2325 hg graft -r 345 --base 234
2325 hg graft -r 345 --base 234
2326
2326
2327 is thus pretty much the same as::
2327 is thus pretty much the same as::
2328
2328
2329 hg diff -r 234 -r 345 | hg import
2329 hg diff -r 234 -r 345 | hg import
2330
2330
2331 but using merge to resolve conflicts and track moved files.
2331 but using merge to resolve conflicts and track moved files.
2332
2332
2333 The result of a merge can thus be backported as a single commit by
2333 The result of a merge can thus be backported as a single commit by
2334 specifying one of the merge parents as base, and thus effectively
2334 specifying one of the merge parents as base, and thus effectively
2335 grafting the changes from the other side.
2335 grafting the changes from the other side.
2336
2336
2337 It is also possible to collapse multiple changesets and clean up history
2337 It is also possible to collapse multiple changesets and clean up history
2338 by specifying another ancestor as base, much like rebase --collapse
2338 by specifying another ancestor as base, much like rebase --collapse
2339 --keep.
2339 --keep.
2340
2340
2341 The commit message can be tweaked after the fact using commit --amend .
2341 The commit message can be tweaked after the fact using commit --amend .
2342
2342
2343 For using non-ancestors as the base to backout changes, see the backout
2343 For using non-ancestors as the base to backout changes, see the backout
2344 command and the hidden --parent option.
2344 command and the hidden --parent option.
2345
2345
2346 .. container:: verbose
2346 .. container:: verbose
2347
2347
2348 Examples:
2348 Examples:
2349
2349
2350 - copy a single change to the stable branch and edit its description::
2350 - copy a single change to the stable branch and edit its description::
2351
2351
2352 hg update stable
2352 hg update stable
2353 hg graft --edit 9393
2353 hg graft --edit 9393
2354
2354
2355 - graft a range of changesets with one exception, updating dates::
2355 - graft a range of changesets with one exception, updating dates::
2356
2356
2357 hg graft -D "2085::2093 and not 2091"
2357 hg graft -D "2085::2093 and not 2091"
2358
2358
2359 - continue a graft after resolving conflicts::
2359 - continue a graft after resolving conflicts::
2360
2360
2361 hg graft -c
2361 hg graft -c
2362
2362
2363 - show the source of a grafted changeset::
2363 - show the source of a grafted changeset::
2364
2364
2365 hg log --debug -r .
2365 hg log --debug -r .
2366
2366
2367 - show revisions sorted by date::
2367 - show revisions sorted by date::
2368
2368
2369 hg log -r "sort(all(), date)"
2369 hg log -r "sort(all(), date)"
2370
2370
2371 - backport the result of a merge as a single commit::
2371 - backport the result of a merge as a single commit::
2372
2372
2373 hg graft -r 123 --base 123^
2373 hg graft -r 123 --base 123^
2374
2374
2375 - land a feature branch as one changeset::
2375 - land a feature branch as one changeset::
2376
2376
2377 hg up -cr default
2377 hg up -cr default
2378 hg graft -r featureX --base "ancestor('featureX', 'default')"
2378 hg graft -r featureX --base "ancestor('featureX', 'default')"
2379
2379
2380 See :hg:`help revisions` for more about specifying revisions.
2380 See :hg:`help revisions` for more about specifying revisions.
2381
2381
2382 Returns 0 on successful completion.
2382 Returns 0 on successful completion.
2383 '''
2383 '''
2384 with repo.wlock():
2384 with repo.wlock():
2385 return _dograft(ui, repo, *revs, **opts)
2385 return _dograft(ui, repo, *revs, **opts)
2386
2386
2387 def _dograft(ui, repo, *revs, **opts):
2387 def _dograft(ui, repo, *revs, **opts):
2388 opts = pycompat.byteskwargs(opts)
2388 opts = pycompat.byteskwargs(opts)
2389 if revs and opts.get('rev'):
2389 if revs and opts.get('rev'):
2390 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2390 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2391 'revision ordering!\n'))
2391 'revision ordering!\n'))
2392
2392
2393 revs = list(revs)
2393 revs = list(revs)
2394 revs.extend(opts.get('rev'))
2394 revs.extend(opts.get('rev'))
2395 basectx = None
2395 basectx = None
2396 if opts.get('base'):
2396 if opts.get('base'):
2397 basectx = scmutil.revsingle(repo, opts['base'], None)
2397 basectx = scmutil.revsingle(repo, opts['base'], None)
2398 # a dict of data to be stored in state file
2398 # a dict of data to be stored in state file
2399 statedata = {}
2399 statedata = {}
2400 # list of new nodes created by ongoing graft
2400 # list of new nodes created by ongoing graft
2401 statedata['newnodes'] = []
2401 statedata['newnodes'] = []
2402
2402
2403 if opts.get('user') and opts.get('currentuser'):
2403 if opts.get('user') and opts.get('currentuser'):
2404 raise error.Abort(_('--user and --currentuser are mutually exclusive'))
2404 raise error.Abort(_('--user and --currentuser are mutually exclusive'))
2405 if opts.get('date') and opts.get('currentdate'):
2405 if opts.get('date') and opts.get('currentdate'):
2406 raise error.Abort(_('--date and --currentdate are mutually exclusive'))
2406 raise error.Abort(_('--date and --currentdate are mutually exclusive'))
2407 if not opts.get('user') and opts.get('currentuser'):
2407 if not opts.get('user') and opts.get('currentuser'):
2408 opts['user'] = ui.username()
2408 opts['user'] = ui.username()
2409 if not opts.get('date') and opts.get('currentdate'):
2409 if not opts.get('date') and opts.get('currentdate'):
2410 opts['date'] = "%d %d" % dateutil.makedate()
2410 opts['date'] = "%d %d" % dateutil.makedate()
2411
2411
2412 editor = cmdutil.getcommiteditor(editform='graft',
2412 editor = cmdutil.getcommiteditor(editform='graft',
2413 **pycompat.strkwargs(opts))
2413 **pycompat.strkwargs(opts))
2414
2414
2415 cont = False
2415 cont = False
2416 if opts.get('no_commit'):
2416 if opts.get('no_commit'):
2417 if opts.get('edit'):
2417 if opts.get('edit'):
2418 raise error.Abort(_("cannot specify --no-commit and "
2418 raise error.Abort(_("cannot specify --no-commit and "
2419 "--edit together"))
2419 "--edit together"))
2420 if opts.get('currentuser'):
2420 if opts.get('currentuser'):
2421 raise error.Abort(_("cannot specify --no-commit and "
2421 raise error.Abort(_("cannot specify --no-commit and "
2422 "--currentuser together"))
2422 "--currentuser together"))
2423 if opts.get('currentdate'):
2423 if opts.get('currentdate'):
2424 raise error.Abort(_("cannot specify --no-commit and "
2424 raise error.Abort(_("cannot specify --no-commit and "
2425 "--currentdate together"))
2425 "--currentdate together"))
2426 if opts.get('log'):
2426 if opts.get('log'):
2427 raise error.Abort(_("cannot specify --no-commit and "
2427 raise error.Abort(_("cannot specify --no-commit and "
2428 "--log together"))
2428 "--log together"))
2429
2429
2430 graftstate = statemod.cmdstate(repo, 'graftstate')
2430 graftstate = statemod.cmdstate(repo, 'graftstate')
2431
2431
2432 if opts.get('stop'):
2432 if opts.get('stop'):
2433 if opts.get('continue'):
2433 if opts.get('continue'):
2434 raise error.Abort(_("cannot use '--continue' and "
2434 raise error.Abort(_("cannot use '--continue' and "
2435 "'--stop' together"))
2435 "'--stop' together"))
2436 if opts.get('abort'):
2436 if opts.get('abort'):
2437 raise error.Abort(_("cannot use '--abort' and '--stop' together"))
2437 raise error.Abort(_("cannot use '--abort' and '--stop' together"))
2438
2438
2439 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2439 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2440 opts.get('date'), opts.get('currentdate'),
2440 opts.get('date'), opts.get('currentdate'),
2441 opts.get('currentuser'), opts.get('rev'))):
2441 opts.get('currentuser'), opts.get('rev'))):
2442 raise error.Abort(_("cannot specify any other flag with '--stop'"))
2442 raise error.Abort(_("cannot specify any other flag with '--stop'"))
2443 return _stopgraft(ui, repo, graftstate)
2443 return _stopgraft(ui, repo, graftstate)
2444 elif opts.get('abort'):
2444 elif opts.get('abort'):
2445 if opts.get('continue'):
2445 if opts.get('continue'):
2446 raise error.Abort(_("cannot use '--continue' and "
2446 raise error.Abort(_("cannot use '--continue' and "
2447 "'--abort' together"))
2447 "'--abort' together"))
2448 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2448 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2449 opts.get('date'), opts.get('currentdate'),
2449 opts.get('date'), opts.get('currentdate'),
2450 opts.get('currentuser'), opts.get('rev'))):
2450 opts.get('currentuser'), opts.get('rev'))):
2451 raise error.Abort(_("cannot specify any other flag with '--abort'"))
2451 raise error.Abort(_("cannot specify any other flag with '--abort'"))
2452
2452
2453 return _abortgraft(ui, repo, graftstate)
2453 return _abortgraft(ui, repo, graftstate)
2454 elif opts.get('continue'):
2454 elif opts.get('continue'):
2455 cont = True
2455 cont = True
2456 if revs:
2456 if revs:
2457 raise error.Abort(_("can't specify --continue and revisions"))
2457 raise error.Abort(_("can't specify --continue and revisions"))
2458 # read in unfinished revisions
2458 # read in unfinished revisions
2459 if graftstate.exists():
2459 if graftstate.exists():
2460 statedata = _readgraftstate(repo, graftstate)
2460 statedata = _readgraftstate(repo, graftstate)
2461 if statedata.get('date'):
2461 if statedata.get('date'):
2462 opts['date'] = statedata['date']
2462 opts['date'] = statedata['date']
2463 if statedata.get('user'):
2463 if statedata.get('user'):
2464 opts['user'] = statedata['user']
2464 opts['user'] = statedata['user']
2465 if statedata.get('log'):
2465 if statedata.get('log'):
2466 opts['log'] = True
2466 opts['log'] = True
2467 if statedata.get('no_commit'):
2467 if statedata.get('no_commit'):
2468 opts['no_commit'] = statedata.get('no_commit')
2468 opts['no_commit'] = statedata.get('no_commit')
2469 nodes = statedata['nodes']
2469 nodes = statedata['nodes']
2470 revs = [repo[node].rev() for node in nodes]
2470 revs = [repo[node].rev() for node in nodes]
2471 else:
2471 else:
2472 cmdutil.wrongtooltocontinue(repo, _('graft'))
2472 cmdutil.wrongtooltocontinue(repo, _('graft'))
2473 else:
2473 else:
2474 if not revs:
2474 if not revs:
2475 raise error.Abort(_('no revisions specified'))
2475 raise error.Abort(_('no revisions specified'))
2476 cmdutil.checkunfinished(repo)
2476 cmdutil.checkunfinished(repo)
2477 cmdutil.bailifchanged(repo)
2477 cmdutil.bailifchanged(repo)
2478 revs = scmutil.revrange(repo, revs)
2478 revs = scmutil.revrange(repo, revs)
2479
2479
2480 skipped = set()
2480 skipped = set()
2481 if basectx is None:
2481 if basectx is None:
2482 # check for merges
2482 # check for merges
2483 for rev in repo.revs('%ld and merge()', revs):
2483 for rev in repo.revs('%ld and merge()', revs):
2484 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2484 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2485 skipped.add(rev)
2485 skipped.add(rev)
2486 revs = [r for r in revs if r not in skipped]
2486 revs = [r for r in revs if r not in skipped]
2487 if not revs:
2487 if not revs:
2488 return -1
2488 return -1
2489 if basectx is not None and len(revs) != 1:
2489 if basectx is not None and len(revs) != 1:
2490 raise error.Abort(_('only one revision allowed with --base '))
2490 raise error.Abort(_('only one revision allowed with --base '))
2491
2491
2492 # Don't check in the --continue case, in effect retaining --force across
2492 # Don't check in the --continue case, in effect retaining --force across
2493 # --continues. That's because without --force, any revisions we decided to
2493 # --continues. That's because without --force, any revisions we decided to
2494 # skip would have been filtered out here, so they wouldn't have made their
2494 # skip would have been filtered out here, so they wouldn't have made their
2495 # way to the graftstate. With --force, any revisions we would have otherwise
2495 # way to the graftstate. With --force, any revisions we would have otherwise
2496 # skipped would not have been filtered out, and if they hadn't been applied
2496 # skipped would not have been filtered out, and if they hadn't been applied
2497 # already, they'd have been in the graftstate.
2497 # already, they'd have been in the graftstate.
2498 if not (cont or opts.get('force')) and basectx is None:
2498 if not (cont or opts.get('force')) and basectx is None:
2499 # check for ancestors of dest branch
2499 # check for ancestors of dest branch
2500 crev = repo['.'].rev()
2500 crev = repo['.'].rev()
2501 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2501 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2502 # XXX make this lazy in the future
2502 # XXX make this lazy in the future
2503 # don't mutate while iterating, create a copy
2503 # don't mutate while iterating, create a copy
2504 for rev in list(revs):
2504 for rev in list(revs):
2505 if rev in ancestors:
2505 if rev in ancestors:
2506 ui.warn(_('skipping ancestor revision %d:%s\n') %
2506 ui.warn(_('skipping ancestor revision %d:%s\n') %
2507 (rev, repo[rev]))
2507 (rev, repo[rev]))
2508 # XXX remove on list is slow
2508 # XXX remove on list is slow
2509 revs.remove(rev)
2509 revs.remove(rev)
2510 if not revs:
2510 if not revs:
2511 return -1
2511 return -1
2512
2512
2513 # analyze revs for earlier grafts
2513 # analyze revs for earlier grafts
2514 ids = {}
2514 ids = {}
2515 for ctx in repo.set("%ld", revs):
2515 for ctx in repo.set("%ld", revs):
2516 ids[ctx.hex()] = ctx.rev()
2516 ids[ctx.hex()] = ctx.rev()
2517 n = ctx.extra().get('source')
2517 n = ctx.extra().get('source')
2518 if n:
2518 if n:
2519 ids[n] = ctx.rev()
2519 ids[n] = ctx.rev()
2520
2520
2521 # check ancestors for earlier grafts
2521 # check ancestors for earlier grafts
2522 ui.debug('scanning for duplicate grafts\n')
2522 ui.debug('scanning for duplicate grafts\n')
2523
2523
2524 # The only changesets we can be sure doesn't contain grafts of any
2524 # The only changesets we can be sure doesn't contain grafts of any
2525 # revs, are the ones that are common ancestors of *all* revs:
2525 # revs, are the ones that are common ancestors of *all* revs:
2526 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2526 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2527 ctx = repo[rev]
2527 ctx = repo[rev]
2528 n = ctx.extra().get('source')
2528 n = ctx.extra().get('source')
2529 if n in ids:
2529 if n in ids:
2530 try:
2530 try:
2531 r = repo[n].rev()
2531 r = repo[n].rev()
2532 except error.RepoLookupError:
2532 except error.RepoLookupError:
2533 r = None
2533 r = None
2534 if r in revs:
2534 if r in revs:
2535 ui.warn(_('skipping revision %d:%s '
2535 ui.warn(_('skipping revision %d:%s '
2536 '(already grafted to %d:%s)\n')
2536 '(already grafted to %d:%s)\n')
2537 % (r, repo[r], rev, ctx))
2537 % (r, repo[r], rev, ctx))
2538 revs.remove(r)
2538 revs.remove(r)
2539 elif ids[n] in revs:
2539 elif ids[n] in revs:
2540 if r is None:
2540 if r is None:
2541 ui.warn(_('skipping already grafted revision %d:%s '
2541 ui.warn(_('skipping already grafted revision %d:%s '
2542 '(%d:%s also has unknown origin %s)\n')
2542 '(%d:%s also has unknown origin %s)\n')
2543 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2543 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2544 else:
2544 else:
2545 ui.warn(_('skipping already grafted revision %d:%s '
2545 ui.warn(_('skipping already grafted revision %d:%s '
2546 '(%d:%s also has origin %d:%s)\n')
2546 '(%d:%s also has origin %d:%s)\n')
2547 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2547 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2548 revs.remove(ids[n])
2548 revs.remove(ids[n])
2549 elif ctx.hex() in ids:
2549 elif ctx.hex() in ids:
2550 r = ids[ctx.hex()]
2550 r = ids[ctx.hex()]
2551 if r in revs:
2551 if r in revs:
2552 ui.warn(_('skipping already grafted revision %d:%s '
2552 ui.warn(_('skipping already grafted revision %d:%s '
2553 '(was grafted from %d:%s)\n') %
2553 '(was grafted from %d:%s)\n') %
2554 (r, repo[r], rev, ctx))
2554 (r, repo[r], rev, ctx))
2555 revs.remove(r)
2555 revs.remove(r)
2556 if not revs:
2556 if not revs:
2557 return -1
2557 return -1
2558
2558
2559 if opts.get('no_commit'):
2559 if opts.get('no_commit'):
2560 statedata['no_commit'] = True
2560 statedata['no_commit'] = True
2561 for pos, ctx in enumerate(repo.set("%ld", revs)):
2561 for pos, ctx in enumerate(repo.set("%ld", revs)):
2562 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2562 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2563 ctx.description().split('\n', 1)[0])
2563 ctx.description().split('\n', 1)[0])
2564 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2564 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2565 if names:
2565 if names:
2566 desc += ' (%s)' % ' '.join(names)
2566 desc += ' (%s)' % ' '.join(names)
2567 ui.status(_('grafting %s\n') % desc)
2567 ui.status(_('grafting %s\n') % desc)
2568 if opts.get('dry_run'):
2568 if opts.get('dry_run'):
2569 continue
2569 continue
2570
2570
2571 source = ctx.extra().get('source')
2571 source = ctx.extra().get('source')
2572 extra = {}
2572 extra = {}
2573 if source:
2573 if source:
2574 extra['source'] = source
2574 extra['source'] = source
2575 extra['intermediate-source'] = ctx.hex()
2575 extra['intermediate-source'] = ctx.hex()
2576 else:
2576 else:
2577 extra['source'] = ctx.hex()
2577 extra['source'] = ctx.hex()
2578 user = ctx.user()
2578 user = ctx.user()
2579 if opts.get('user'):
2579 if opts.get('user'):
2580 user = opts['user']
2580 user = opts['user']
2581 statedata['user'] = user
2581 statedata['user'] = user
2582 date = ctx.date()
2582 date = ctx.date()
2583 if opts.get('date'):
2583 if opts.get('date'):
2584 date = opts['date']
2584 date = opts['date']
2585 statedata['date'] = date
2585 statedata['date'] = date
2586 message = ctx.description()
2586 message = ctx.description()
2587 if opts.get('log'):
2587 if opts.get('log'):
2588 message += '\n(grafted from %s)' % ctx.hex()
2588 message += '\n(grafted from %s)' % ctx.hex()
2589 statedata['log'] = True
2589 statedata['log'] = True
2590
2590
2591 # we don't merge the first commit when continuing
2591 # we don't merge the first commit when continuing
2592 if not cont:
2592 if not cont:
2593 # perform the graft merge with p1(rev) as 'ancestor'
2593 # perform the graft merge with p1(rev) as 'ancestor'
2594 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
2594 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
2595 base = ctx.p1() if basectx is None else basectx
2595 base = ctx.p1() if basectx is None else basectx
2596 with ui.configoverride(overrides, 'graft'):
2596 with ui.configoverride(overrides, 'graft'):
2597 stats = mergemod.graft(repo, ctx, base, ['local', 'graft'])
2597 stats = mergemod.graft(repo, ctx, base, ['local', 'graft'])
2598 # report any conflicts
2598 # report any conflicts
2599 if stats.unresolvedcount > 0:
2599 if stats.unresolvedcount > 0:
2600 # write out state for --continue
2600 # write out state for --continue
2601 nodes = [repo[rev].hex() for rev in revs[pos:]]
2601 nodes = [repo[rev].hex() for rev in revs[pos:]]
2602 statedata['nodes'] = nodes
2602 statedata['nodes'] = nodes
2603 stateversion = 1
2603 stateversion = 1
2604 graftstate.save(stateversion, statedata)
2604 graftstate.save(stateversion, statedata)
2605 hint = _("use 'hg resolve' and 'hg graft --continue'")
2605 hint = _("use 'hg resolve' and 'hg graft --continue'")
2606 raise error.Abort(
2606 raise error.Abort(
2607 _("unresolved conflicts, can't continue"),
2607 _("unresolved conflicts, can't continue"),
2608 hint=hint)
2608 hint=hint)
2609 else:
2609 else:
2610 cont = False
2610 cont = False
2611
2611
2612 # commit if --no-commit is false
2612 # commit if --no-commit is false
2613 if not opts.get('no_commit'):
2613 if not opts.get('no_commit'):
2614 node = repo.commit(text=message, user=user, date=date, extra=extra,
2614 node = repo.commit(text=message, user=user, date=date, extra=extra,
2615 editor=editor)
2615 editor=editor)
2616 if node is None:
2616 if node is None:
2617 ui.warn(
2617 ui.warn(
2618 _('note: graft of %d:%s created no changes to commit\n') %
2618 _('note: graft of %d:%s created no changes to commit\n') %
2619 (ctx.rev(), ctx))
2619 (ctx.rev(), ctx))
2620 # checking that newnodes exist because old state files won't have it
2620 # checking that newnodes exist because old state files won't have it
2621 elif statedata.get('newnodes') is not None:
2621 elif statedata.get('newnodes') is not None:
2622 statedata['newnodes'].append(node)
2622 statedata['newnodes'].append(node)
2623
2623
2624 # remove state when we complete successfully
2624 # remove state when we complete successfully
2625 if not opts.get('dry_run'):
2625 if not opts.get('dry_run'):
2626 graftstate.delete()
2626 graftstate.delete()
2627
2627
2628 return 0
2628 return 0
2629
2629
2630 def _abortgraft(ui, repo, graftstate):
2630 def _abortgraft(ui, repo, graftstate):
2631 """abort the interrupted graft and rollbacks to the state before interrupted
2631 """abort the interrupted graft and rollbacks to the state before interrupted
2632 graft"""
2632 graft"""
2633 if not graftstate.exists():
2633 if not graftstate.exists():
2634 raise error.Abort(_("no interrupted graft to abort"))
2634 raise error.Abort(_("no interrupted graft to abort"))
2635 statedata = _readgraftstate(repo, graftstate)
2635 statedata = _readgraftstate(repo, graftstate)
2636 newnodes = statedata.get('newnodes')
2636 newnodes = statedata.get('newnodes')
2637 if newnodes is None:
2637 if newnodes is None:
2638 # and old graft state which does not have all the data required to abort
2638 # and old graft state which does not have all the data required to abort
2639 # the graft
2639 # the graft
2640 raise error.Abort(_("cannot abort using an old graftstate"))
2640 raise error.Abort(_("cannot abort using an old graftstate"))
2641
2641
2642 # changeset from which graft operation was started
2642 # changeset from which graft operation was started
2643 if len(newnodes) > 0:
2643 if len(newnodes) > 0:
2644 startctx = repo[newnodes[0]].p1()
2644 startctx = repo[newnodes[0]].p1()
2645 else:
2645 else:
2646 startctx = repo['.']
2646 startctx = repo['.']
2647 # whether to strip or not
2647 # whether to strip or not
2648 cleanup = False
2648 cleanup = False
2649 if newnodes:
2649 if newnodes:
2650 newnodes = [repo[r].rev() for r in newnodes]
2650 newnodes = [repo[r].rev() for r in newnodes]
2651 cleanup = True
2651 cleanup = True
2652 # checking that none of the newnodes turned public or is public
2652 # checking that none of the newnodes turned public or is public
2653 immutable = [c for c in newnodes if not repo[c].mutable()]
2653 immutable = [c for c in newnodes if not repo[c].mutable()]
2654 if immutable:
2654 if immutable:
2655 repo.ui.warn(_("cannot clean up public changesets %s\n")
2655 repo.ui.warn(_("cannot clean up public changesets %s\n")
2656 % ', '.join(bytes(repo[r]) for r in immutable),
2656 % ', '.join(bytes(repo[r]) for r in immutable),
2657 hint=_("see 'hg help phases' for details"))
2657 hint=_("see 'hg help phases' for details"))
2658 cleanup = False
2658 cleanup = False
2659
2659
2660 # checking that no new nodes are created on top of grafted revs
2660 # checking that no new nodes are created on top of grafted revs
2661 desc = set(repo.changelog.descendants(newnodes))
2661 desc = set(repo.changelog.descendants(newnodes))
2662 if desc - set(newnodes):
2662 if desc - set(newnodes):
2663 repo.ui.warn(_("new changesets detected on destination "
2663 repo.ui.warn(_("new changesets detected on destination "
2664 "branch, can't strip\n"))
2664 "branch, can't strip\n"))
2665 cleanup = False
2665 cleanup = False
2666
2666
2667 if cleanup:
2667 if cleanup:
2668 with repo.wlock(), repo.lock():
2668 with repo.wlock(), repo.lock():
2669 hg.updaterepo(repo, startctx.node(), overwrite=True)
2669 hg.updaterepo(repo, startctx.node(), overwrite=True)
2670 # stripping the new nodes created
2670 # stripping the new nodes created
2671 strippoints = [c.node() for c in repo.set("roots(%ld)",
2671 strippoints = [c.node() for c in repo.set("roots(%ld)",
2672 newnodes)]
2672 newnodes)]
2673 repair.strip(repo.ui, repo, strippoints, backup=False)
2673 repair.strip(repo.ui, repo, strippoints, backup=False)
2674
2674
2675 if not cleanup:
2675 if not cleanup:
2676 # we don't update to the startnode if we can't strip
2676 # we don't update to the startnode if we can't strip
2677 startctx = repo['.']
2677 startctx = repo['.']
2678 hg.updaterepo(repo, startctx.node(), overwrite=True)
2678 hg.updaterepo(repo, startctx.node(), overwrite=True)
2679
2679
2680 ui.status(_("graft aborted\n"))
2680 ui.status(_("graft aborted\n"))
2681 ui.status(_("working directory is now at %s\n") % startctx.hex()[:12])
2681 ui.status(_("working directory is now at %s\n") % startctx.hex()[:12])
2682 graftstate.delete()
2682 graftstate.delete()
2683 return 0
2683 return 0
2684
2684
2685 def _readgraftstate(repo, graftstate):
2685 def _readgraftstate(repo, graftstate):
2686 """read the graft state file and return a dict of the data stored in it"""
2686 """read the graft state file and return a dict of the data stored in it"""
2687 try:
2687 try:
2688 return graftstate.read()
2688 return graftstate.read()
2689 except error.CorruptedState:
2689 except error.CorruptedState:
2690 nodes = repo.vfs.read('graftstate').splitlines()
2690 nodes = repo.vfs.read('graftstate').splitlines()
2691 return {'nodes': nodes}
2691 return {'nodes': nodes}
2692
2692
2693 def _stopgraft(ui, repo, graftstate):
2693 def _stopgraft(ui, repo, graftstate):
2694 """stop the interrupted graft"""
2694 """stop the interrupted graft"""
2695 if not graftstate.exists():
2695 if not graftstate.exists():
2696 raise error.Abort(_("no interrupted graft found"))
2696 raise error.Abort(_("no interrupted graft found"))
2697 pctx = repo['.']
2697 pctx = repo['.']
2698 hg.updaterepo(repo, pctx.node(), overwrite=True)
2698 hg.updaterepo(repo, pctx.node(), overwrite=True)
2699 graftstate.delete()
2699 graftstate.delete()
2700 ui.status(_("stopped the interrupted graft\n"))
2700 ui.status(_("stopped the interrupted graft\n"))
2701 ui.status(_("working directory is now at %s\n") % pctx.hex()[:12])
2701 ui.status(_("working directory is now at %s\n") % pctx.hex()[:12])
2702 return 0
2702 return 0
2703
2703
2704 @command('grep',
2704 @command('grep',
2705 [('0', 'print0', None, _('end fields with NUL')),
2705 [('0', 'print0', None, _('end fields with NUL')),
2706 ('', 'all', None, _('print all revisions that match (DEPRECATED) ')),
2706 ('', 'all', None, _('print all revisions that match (DEPRECATED) ')),
2707 ('', 'diff', None, _('print all revisions when the term was introduced '
2707 ('', 'diff', None, _('print all revisions when the term was introduced '
2708 'or removed')),
2708 'or removed')),
2709 ('a', 'text', None, _('treat all files as text')),
2709 ('a', 'text', None, _('treat all files as text')),
2710 ('f', 'follow', None,
2710 ('f', 'follow', None,
2711 _('follow changeset history,'
2711 _('follow changeset history,'
2712 ' or file history across copies and renames')),
2712 ' or file history across copies and renames')),
2713 ('i', 'ignore-case', None, _('ignore case when matching')),
2713 ('i', 'ignore-case', None, _('ignore case when matching')),
2714 ('l', 'files-with-matches', None,
2714 ('l', 'files-with-matches', None,
2715 _('print only filenames and revisions that match')),
2715 _('print only filenames and revisions that match')),
2716 ('n', 'line-number', None, _('print matching line numbers')),
2716 ('n', 'line-number', None, _('print matching line numbers')),
2717 ('r', 'rev', [],
2717 ('r', 'rev', [],
2718 _('only search files changed within revision range'), _('REV')),
2718 _('only search files changed within revision range'), _('REV')),
2719 ('', 'all-files', None,
2719 ('', 'all-files', None,
2720 _('include all files in the changeset while grepping (EXPERIMENTAL)')),
2720 _('include all files in the changeset while grepping (EXPERIMENTAL)')),
2721 ('u', 'user', None, _('list the author (long with -v)')),
2721 ('u', 'user', None, _('list the author (long with -v)')),
2722 ('d', 'date', None, _('list the date (short with -q)')),
2722 ('d', 'date', None, _('list the date (short with -q)')),
2723 ] + formatteropts + walkopts,
2723 ] + formatteropts + walkopts,
2724 _('[OPTION]... PATTERN [FILE]...'),
2724 _('[OPTION]... PATTERN [FILE]...'),
2725 helpcategory=command.CATEGORY_FILE_CONTENTS,
2725 helpcategory=command.CATEGORY_FILE_CONTENTS,
2726 inferrepo=True,
2726 inferrepo=True,
2727 intents={INTENT_READONLY})
2727 intents={INTENT_READONLY})
2728 def grep(ui, repo, pattern, *pats, **opts):
2728 def grep(ui, repo, pattern, *pats, **opts):
2729 """search revision history for a pattern in specified files
2729 """search revision history for a pattern in specified files
2730
2730
2731 Search revision history for a regular expression in the specified
2731 Search revision history for a regular expression in the specified
2732 files or the entire project.
2732 files or the entire project.
2733
2733
2734 By default, grep prints the most recent revision number for each
2734 By default, grep prints the most recent revision number for each
2735 file in which it finds a match. To get it to print every revision
2735 file in which it finds a match. To get it to print every revision
2736 that contains a change in match status ("-" for a match that becomes
2736 that contains a change in match status ("-" for a match that becomes
2737 a non-match, or "+" for a non-match that becomes a match), use the
2737 a non-match, or "+" for a non-match that becomes a match), use the
2738 --diff flag.
2738 --diff flag.
2739
2739
2740 PATTERN can be any Python (roughly Perl-compatible) regular
2740 PATTERN can be any Python (roughly Perl-compatible) regular
2741 expression.
2741 expression.
2742
2742
2743 If no FILEs are specified (and -f/--follow isn't set), all files in
2743 If no FILEs are specified (and -f/--follow isn't set), all files in
2744 the repository are searched, including those that don't exist in the
2744 the repository are searched, including those that don't exist in the
2745 current branch or have been deleted in a prior changeset.
2745 current branch or have been deleted in a prior changeset.
2746
2746
2747 .. container:: verbose
2747 .. container:: verbose
2748
2748
2749 Template:
2749 Template:
2750
2750
2751 The following keywords are supported in addition to the common template
2751 The following keywords are supported in addition to the common template
2752 keywords and functions. See also :hg:`help templates`.
2752 keywords and functions. See also :hg:`help templates`.
2753
2753
2754 :change: String. Character denoting insertion ``+`` or removal ``-``.
2754 :change: String. Character denoting insertion ``+`` or removal ``-``.
2755 Available if ``--diff`` is specified.
2755 Available if ``--diff`` is specified.
2756 :lineno: Integer. Line number of the match.
2756 :lineno: Integer. Line number of the match.
2757 :path: String. Repository-absolute path of the file.
2757 :path: String. Repository-absolute path of the file.
2758 :texts: List of text chunks.
2758 :texts: List of text chunks.
2759
2759
2760 And each entry of ``{texts}`` provides the following sub-keywords.
2760 And each entry of ``{texts}`` provides the following sub-keywords.
2761
2761
2762 :matched: Boolean. True if the chunk matches the specified pattern.
2762 :matched: Boolean. True if the chunk matches the specified pattern.
2763 :text: String. Chunk content.
2763 :text: String. Chunk content.
2764
2764
2765 See :hg:`help templates.operators` for the list expansion syntax.
2765 See :hg:`help templates.operators` for the list expansion syntax.
2766
2766
2767 Returns 0 if a match is found, 1 otherwise.
2767 Returns 0 if a match is found, 1 otherwise.
2768 """
2768 """
2769 opts = pycompat.byteskwargs(opts)
2769 opts = pycompat.byteskwargs(opts)
2770 diff = opts.get('all') or opts.get('diff')
2770 diff = opts.get('all') or opts.get('diff')
2771 all_files = opts.get('all_files')
2771 all_files = opts.get('all_files')
2772 if diff and opts.get('all_files'):
2772 if diff and opts.get('all_files'):
2773 raise error.Abort(_('--diff and --all-files are mutually exclusive'))
2773 raise error.Abort(_('--diff and --all-files are mutually exclusive'))
2774 # TODO: remove "not opts.get('rev')" if --all-files -rMULTIREV gets working
2774 # TODO: remove "not opts.get('rev')" if --all-files -rMULTIREV gets working
2775 if opts.get('all_files') is None and not opts.get('rev') and not diff:
2775 if opts.get('all_files') is None and not opts.get('rev') and not diff:
2776 # experimental config: commands.grep.all-files
2776 # experimental config: commands.grep.all-files
2777 opts['all_files'] = ui.configbool('commands', 'grep.all-files')
2777 opts['all_files'] = ui.configbool('commands', 'grep.all-files')
2778 plaingrep = opts.get('all_files') and not opts.get('rev')
2778 plaingrep = opts.get('all_files') and not opts.get('rev')
2779 if plaingrep:
2779 if plaingrep:
2780 opts['rev'] = ['wdir()']
2780 opts['rev'] = ['wdir()']
2781
2781
2782 reflags = re.M
2782 reflags = re.M
2783 if opts.get('ignore_case'):
2783 if opts.get('ignore_case'):
2784 reflags |= re.I
2784 reflags |= re.I
2785 try:
2785 try:
2786 regexp = util.re.compile(pattern, reflags)
2786 regexp = util.re.compile(pattern, reflags)
2787 except re.error as inst:
2787 except re.error as inst:
2788 ui.warn(_("grep: invalid match pattern: %s\n") % pycompat.bytestr(inst))
2788 ui.warn(_("grep: invalid match pattern: %s\n") % pycompat.bytestr(inst))
2789 return 1
2789 return 1
2790 sep, eol = ':', '\n'
2790 sep, eol = ':', '\n'
2791 if opts.get('print0'):
2791 if opts.get('print0'):
2792 sep = eol = '\0'
2792 sep = eol = '\0'
2793
2793
2794 getfile = util.lrucachefunc(repo.file)
2794 getfile = util.lrucachefunc(repo.file)
2795
2795
2796 def matchlines(body):
2796 def matchlines(body):
2797 begin = 0
2797 begin = 0
2798 linenum = 0
2798 linenum = 0
2799 while begin < len(body):
2799 while begin < len(body):
2800 match = regexp.search(body, begin)
2800 match = regexp.search(body, begin)
2801 if not match:
2801 if not match:
2802 break
2802 break
2803 mstart, mend = match.span()
2803 mstart, mend = match.span()
2804 linenum += body.count('\n', begin, mstart) + 1
2804 linenum += body.count('\n', begin, mstart) + 1
2805 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2805 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2806 begin = body.find('\n', mend) + 1 or len(body) + 1
2806 begin = body.find('\n', mend) + 1 or len(body) + 1
2807 lend = begin - 1
2807 lend = begin - 1
2808 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2808 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2809
2809
2810 class linestate(object):
2810 class linestate(object):
2811 def __init__(self, line, linenum, colstart, colend):
2811 def __init__(self, line, linenum, colstart, colend):
2812 self.line = line
2812 self.line = line
2813 self.linenum = linenum
2813 self.linenum = linenum
2814 self.colstart = colstart
2814 self.colstart = colstart
2815 self.colend = colend
2815 self.colend = colend
2816
2816
2817 def __hash__(self):
2817 def __hash__(self):
2818 return hash((self.linenum, self.line))
2818 return hash((self.linenum, self.line))
2819
2819
2820 def __eq__(self, other):
2820 def __eq__(self, other):
2821 return self.line == other.line
2821 return self.line == other.line
2822
2822
2823 def findpos(self):
2823 def findpos(self):
2824 """Iterate all (start, end) indices of matches"""
2824 """Iterate all (start, end) indices of matches"""
2825 yield self.colstart, self.colend
2825 yield self.colstart, self.colend
2826 p = self.colend
2826 p = self.colend
2827 while p < len(self.line):
2827 while p < len(self.line):
2828 m = regexp.search(self.line, p)
2828 m = regexp.search(self.line, p)
2829 if not m:
2829 if not m:
2830 break
2830 break
2831 yield m.span()
2831 yield m.span()
2832 p = m.end()
2832 p = m.end()
2833
2833
2834 matches = {}
2834 matches = {}
2835 copies = {}
2835 copies = {}
2836 def grepbody(fn, rev, body):
2836 def grepbody(fn, rev, body):
2837 matches[rev].setdefault(fn, [])
2837 matches[rev].setdefault(fn, [])
2838 m = matches[rev][fn]
2838 m = matches[rev][fn]
2839 for lnum, cstart, cend, line in matchlines(body):
2839 for lnum, cstart, cend, line in matchlines(body):
2840 s = linestate(line, lnum, cstart, cend)
2840 s = linestate(line, lnum, cstart, cend)
2841 m.append(s)
2841 m.append(s)
2842
2842
2843 def difflinestates(a, b):
2843 def difflinestates(a, b):
2844 sm = difflib.SequenceMatcher(None, a, b)
2844 sm = difflib.SequenceMatcher(None, a, b)
2845 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2845 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2846 if tag == r'insert':
2846 if tag == r'insert':
2847 for i in pycompat.xrange(blo, bhi):
2847 for i in pycompat.xrange(blo, bhi):
2848 yield ('+', b[i])
2848 yield ('+', b[i])
2849 elif tag == r'delete':
2849 elif tag == r'delete':
2850 for i in pycompat.xrange(alo, ahi):
2850 for i in pycompat.xrange(alo, ahi):
2851 yield ('-', a[i])
2851 yield ('-', a[i])
2852 elif tag == r'replace':
2852 elif tag == r'replace':
2853 for i in pycompat.xrange(alo, ahi):
2853 for i in pycompat.xrange(alo, ahi):
2854 yield ('-', a[i])
2854 yield ('-', a[i])
2855 for i in pycompat.xrange(blo, bhi):
2855 for i in pycompat.xrange(blo, bhi):
2856 yield ('+', b[i])
2856 yield ('+', b[i])
2857
2857
2858 uipathfn = scmutil.getuipathfn(repo)
2858 uipathfn = scmutil.getuipathfn(repo)
2859 def display(fm, fn, ctx, pstates, states):
2859 def display(fm, fn, ctx, pstates, states):
2860 rev = scmutil.intrev(ctx)
2860 rev = scmutil.intrev(ctx)
2861 if fm.isplain():
2861 if fm.isplain():
2862 formatuser = ui.shortuser
2862 formatuser = ui.shortuser
2863 else:
2863 else:
2864 formatuser = pycompat.bytestr
2864 formatuser = pycompat.bytestr
2865 if ui.quiet:
2865 if ui.quiet:
2866 datefmt = '%Y-%m-%d'
2866 datefmt = '%Y-%m-%d'
2867 else:
2867 else:
2868 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2868 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2869 found = False
2869 found = False
2870 @util.cachefunc
2870 @util.cachefunc
2871 def binary():
2871 def binary():
2872 flog = getfile(fn)
2872 flog = getfile(fn)
2873 try:
2873 try:
2874 return stringutil.binary(flog.read(ctx.filenode(fn)))
2874 return stringutil.binary(flog.read(ctx.filenode(fn)))
2875 except error.WdirUnsupported:
2875 except error.WdirUnsupported:
2876 return ctx[fn].isbinary()
2876 return ctx[fn].isbinary()
2877
2877
2878 fieldnamemap = {'linenumber': 'lineno'}
2878 fieldnamemap = {'linenumber': 'lineno'}
2879 if diff:
2879 if diff:
2880 iter = difflinestates(pstates, states)
2880 iter = difflinestates(pstates, states)
2881 else:
2881 else:
2882 iter = [('', l) for l in states]
2882 iter = [('', l) for l in states]
2883 for change, l in iter:
2883 for change, l in iter:
2884 fm.startitem()
2884 fm.startitem()
2885 fm.context(ctx=ctx)
2885 fm.context(ctx=ctx)
2886 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
2886 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
2887 fm.plain(uipathfn(fn), label='grep.filename')
2887 fm.plain(uipathfn(fn), label='grep.filename')
2888
2888
2889 cols = [
2889 cols = [
2890 ('rev', '%d', rev, not plaingrep, ''),
2890 ('rev', '%d', rev, not plaingrep, ''),
2891 ('linenumber', '%d', l.linenum, opts.get('line_number'), ''),
2891 ('linenumber', '%d', l.linenum, opts.get('line_number'), ''),
2892 ]
2892 ]
2893 if diff:
2893 if diff:
2894 cols.append(
2894 cols.append(
2895 ('change', '%s', change, True,
2895 ('change', '%s', change, True,
2896 'grep.inserted ' if change == '+' else 'grep.deleted ')
2896 'grep.inserted ' if change == '+' else 'grep.deleted ')
2897 )
2897 )
2898 cols.extend([
2898 cols.extend([
2899 ('user', '%s', formatuser(ctx.user()), opts.get('user'), ''),
2899 ('user', '%s', formatuser(ctx.user()), opts.get('user'), ''),
2900 ('date', '%s', fm.formatdate(ctx.date(), datefmt),
2900 ('date', '%s', fm.formatdate(ctx.date(), datefmt),
2901 opts.get('date'), ''),
2901 opts.get('date'), ''),
2902 ])
2902 ])
2903 for name, fmt, data, cond, extra_label in cols:
2903 for name, fmt, data, cond, extra_label in cols:
2904 if cond:
2904 if cond:
2905 fm.plain(sep, label='grep.sep')
2905 fm.plain(sep, label='grep.sep')
2906 field = fieldnamemap.get(name, name)
2906 field = fieldnamemap.get(name, name)
2907 label = extra_label + ('grep.%s' % name)
2907 label = extra_label + ('grep.%s' % name)
2908 fm.condwrite(cond, field, fmt, data, label=label)
2908 fm.condwrite(cond, field, fmt, data, label=label)
2909 if not opts.get('files_with_matches'):
2909 if not opts.get('files_with_matches'):
2910 fm.plain(sep, label='grep.sep')
2910 fm.plain(sep, label='grep.sep')
2911 if not opts.get('text') and binary():
2911 if not opts.get('text') and binary():
2912 fm.plain(_(" Binary file matches"))
2912 fm.plain(_(" Binary file matches"))
2913 else:
2913 else:
2914 displaymatches(fm.nested('texts', tmpl='{text}'), l)
2914 displaymatches(fm.nested('texts', tmpl='{text}'), l)
2915 fm.plain(eol)
2915 fm.plain(eol)
2916 found = True
2916 found = True
2917 if opts.get('files_with_matches'):
2917 if opts.get('files_with_matches'):
2918 break
2918 break
2919 return found
2919 return found
2920
2920
2921 def displaymatches(fm, l):
2921 def displaymatches(fm, l):
2922 p = 0
2922 p = 0
2923 for s, e in l.findpos():
2923 for s, e in l.findpos():
2924 if p < s:
2924 if p < s:
2925 fm.startitem()
2925 fm.startitem()
2926 fm.write('text', '%s', l.line[p:s])
2926 fm.write('text', '%s', l.line[p:s])
2927 fm.data(matched=False)
2927 fm.data(matched=False)
2928 fm.startitem()
2928 fm.startitem()
2929 fm.write('text', '%s', l.line[s:e], label='grep.match')
2929 fm.write('text', '%s', l.line[s:e], label='grep.match')
2930 fm.data(matched=True)
2930 fm.data(matched=True)
2931 p = e
2931 p = e
2932 if p < len(l.line):
2932 if p < len(l.line):
2933 fm.startitem()
2933 fm.startitem()
2934 fm.write('text', '%s', l.line[p:])
2934 fm.write('text', '%s', l.line[p:])
2935 fm.data(matched=False)
2935 fm.data(matched=False)
2936 fm.end()
2936 fm.end()
2937
2937
2938 skip = set()
2938 skip = set()
2939 revfiles = {}
2939 revfiles = {}
2940 match = scmutil.match(repo[None], pats, opts)
2940 match = scmutil.match(repo[None], pats, opts)
2941 found = False
2941 found = False
2942 follow = opts.get('follow')
2942 follow = opts.get('follow')
2943
2943
2944 getrenamed = scmutil.getrenamedfn(repo)
2944 getrenamed = scmutil.getrenamedfn(repo)
2945 def prep(ctx, fns):
2945 def prep(ctx, fns):
2946 rev = ctx.rev()
2946 rev = ctx.rev()
2947 pctx = ctx.p1()
2947 pctx = ctx.p1()
2948 parent = pctx.rev()
2948 parent = pctx.rev()
2949 matches.setdefault(rev, {})
2949 matches.setdefault(rev, {})
2950 matches.setdefault(parent, {})
2950 matches.setdefault(parent, {})
2951 files = revfiles.setdefault(rev, [])
2951 files = revfiles.setdefault(rev, [])
2952 for fn in fns:
2952 for fn in fns:
2953 flog = getfile(fn)
2953 flog = getfile(fn)
2954 try:
2954 try:
2955 fnode = ctx.filenode(fn)
2955 fnode = ctx.filenode(fn)
2956 except error.LookupError:
2956 except error.LookupError:
2957 continue
2957 continue
2958
2958
2959 copy = None
2959 copy = None
2960 if follow:
2960 if follow:
2961 copy = getrenamed(fn, rev)
2961 copy = getrenamed(fn, rev)
2962 if copy:
2962 if copy:
2963 copies.setdefault(rev, {})[fn] = copy
2963 copies.setdefault(rev, {})[fn] = copy
2964 if fn in skip:
2964 if fn in skip:
2965 skip.add(copy)
2965 skip.add(copy)
2966 if fn in skip:
2966 if fn in skip:
2967 continue
2967 continue
2968 files.append(fn)
2968 files.append(fn)
2969
2969
2970 if fn not in matches[rev]:
2970 if fn not in matches[rev]:
2971 try:
2971 try:
2972 content = flog.read(fnode)
2972 content = flog.read(fnode)
2973 except error.WdirUnsupported:
2973 except error.WdirUnsupported:
2974 content = ctx[fn].data()
2974 content = ctx[fn].data()
2975 grepbody(fn, rev, content)
2975 grepbody(fn, rev, content)
2976
2976
2977 pfn = copy or fn
2977 pfn = copy or fn
2978 if pfn not in matches[parent]:
2978 if pfn not in matches[parent]:
2979 try:
2979 try:
2980 fnode = pctx.filenode(pfn)
2980 fnode = pctx.filenode(pfn)
2981 grepbody(pfn, parent, flog.read(fnode))
2981 grepbody(pfn, parent, flog.read(fnode))
2982 except error.LookupError:
2982 except error.LookupError:
2983 pass
2983 pass
2984
2984
2985 ui.pager('grep')
2985 ui.pager('grep')
2986 fm = ui.formatter('grep', opts)
2986 fm = ui.formatter('grep', opts)
2987 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2987 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2988 rev = ctx.rev()
2988 rev = ctx.rev()
2989 parent = ctx.p1().rev()
2989 parent = ctx.p1().rev()
2990 for fn in sorted(revfiles.get(rev, [])):
2990 for fn in sorted(revfiles.get(rev, [])):
2991 states = matches[rev][fn]
2991 states = matches[rev][fn]
2992 copy = copies.get(rev, {}).get(fn)
2992 copy = copies.get(rev, {}).get(fn)
2993 if fn in skip:
2993 if fn in skip:
2994 if copy:
2994 if copy:
2995 skip.add(copy)
2995 skip.add(copy)
2996 continue
2996 continue
2997 pstates = matches.get(parent, {}).get(copy or fn, [])
2997 pstates = matches.get(parent, {}).get(copy or fn, [])
2998 if pstates or states:
2998 if pstates or states:
2999 r = display(fm, fn, ctx, pstates, states)
2999 r = display(fm, fn, ctx, pstates, states)
3000 found = found or r
3000 found = found or r
3001 if r and not diff and not all_files:
3001 if r and not diff and not all_files:
3002 skip.add(fn)
3002 skip.add(fn)
3003 if copy:
3003 if copy:
3004 skip.add(copy)
3004 skip.add(copy)
3005 del revfiles[rev]
3005 del revfiles[rev]
3006 # We will keep the matches dict for the duration of the window
3006 # We will keep the matches dict for the duration of the window
3007 # clear the matches dict once the window is over
3007 # clear the matches dict once the window is over
3008 if not revfiles:
3008 if not revfiles:
3009 matches.clear()
3009 matches.clear()
3010 fm.end()
3010 fm.end()
3011
3011
3012 return not found
3012 return not found
3013
3013
3014 @command('heads',
3014 @command('heads',
3015 [('r', 'rev', '',
3015 [('r', 'rev', '',
3016 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3016 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3017 ('t', 'topo', False, _('show topological heads only')),
3017 ('t', 'topo', False, _('show topological heads only')),
3018 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3018 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3019 ('c', 'closed', False, _('show normal and closed branch heads')),
3019 ('c', 'closed', False, _('show normal and closed branch heads')),
3020 ] + templateopts,
3020 ] + templateopts,
3021 _('[-ct] [-r STARTREV] [REV]...'),
3021 _('[-ct] [-r STARTREV] [REV]...'),
3022 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3022 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3023 intents={INTENT_READONLY})
3023 intents={INTENT_READONLY})
3024 def heads(ui, repo, *branchrevs, **opts):
3024 def heads(ui, repo, *branchrevs, **opts):
3025 """show branch heads
3025 """show branch heads
3026
3026
3027 With no arguments, show all open branch heads in the repository.
3027 With no arguments, show all open branch heads in the repository.
3028 Branch heads are changesets that have no descendants on the
3028 Branch heads are changesets that have no descendants on the
3029 same branch. They are where development generally takes place and
3029 same branch. They are where development generally takes place and
3030 are the usual targets for update and merge operations.
3030 are the usual targets for update and merge operations.
3031
3031
3032 If one or more REVs are given, only open branch heads on the
3032 If one or more REVs are given, only open branch heads on the
3033 branches associated with the specified changesets are shown. This
3033 branches associated with the specified changesets are shown. This
3034 means that you can use :hg:`heads .` to see the heads on the
3034 means that you can use :hg:`heads .` to see the heads on the
3035 currently checked-out branch.
3035 currently checked-out branch.
3036
3036
3037 If -c/--closed is specified, also show branch heads marked closed
3037 If -c/--closed is specified, also show branch heads marked closed
3038 (see :hg:`commit --close-branch`).
3038 (see :hg:`commit --close-branch`).
3039
3039
3040 If STARTREV is specified, only those heads that are descendants of
3040 If STARTREV is specified, only those heads that are descendants of
3041 STARTREV will be displayed.
3041 STARTREV will be displayed.
3042
3042
3043 If -t/--topo is specified, named branch mechanics will be ignored and only
3043 If -t/--topo is specified, named branch mechanics will be ignored and only
3044 topological heads (changesets with no children) will be shown.
3044 topological heads (changesets with no children) will be shown.
3045
3045
3046 Returns 0 if matching heads are found, 1 if not.
3046 Returns 0 if matching heads are found, 1 if not.
3047 """
3047 """
3048
3048
3049 opts = pycompat.byteskwargs(opts)
3049 opts = pycompat.byteskwargs(opts)
3050 start = None
3050 start = None
3051 rev = opts.get('rev')
3051 rev = opts.get('rev')
3052 if rev:
3052 if rev:
3053 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3053 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3054 start = scmutil.revsingle(repo, rev, None).node()
3054 start = scmutil.revsingle(repo, rev, None).node()
3055
3055
3056 if opts.get('topo'):
3056 if opts.get('topo'):
3057 heads = [repo[h] for h in repo.heads(start)]
3057 heads = [repo[h] for h in repo.heads(start)]
3058 else:
3058 else:
3059 heads = []
3059 heads = []
3060 for branch in repo.branchmap():
3060 for branch in repo.branchmap():
3061 heads += repo.branchheads(branch, start, opts.get('closed'))
3061 heads += repo.branchheads(branch, start, opts.get('closed'))
3062 heads = [repo[h] for h in heads]
3062 heads = [repo[h] for h in heads]
3063
3063
3064 if branchrevs:
3064 if branchrevs:
3065 branches = set(repo[r].branch()
3065 branches = set(repo[r].branch()
3066 for r in scmutil.revrange(repo, branchrevs))
3066 for r in scmutil.revrange(repo, branchrevs))
3067 heads = [h for h in heads if h.branch() in branches]
3067 heads = [h for h in heads if h.branch() in branches]
3068
3068
3069 if opts.get('active') and branchrevs:
3069 if opts.get('active') and branchrevs:
3070 dagheads = repo.heads(start)
3070 dagheads = repo.heads(start)
3071 heads = [h for h in heads if h.node() in dagheads]
3071 heads = [h for h in heads if h.node() in dagheads]
3072
3072
3073 if branchrevs:
3073 if branchrevs:
3074 haveheads = set(h.branch() for h in heads)
3074 haveheads = set(h.branch() for h in heads)
3075 if branches - haveheads:
3075 if branches - haveheads:
3076 headless = ', '.join(b for b in branches - haveheads)
3076 headless = ', '.join(b for b in branches - haveheads)
3077 msg = _('no open branch heads found on branches %s')
3077 msg = _('no open branch heads found on branches %s')
3078 if opts.get('rev'):
3078 if opts.get('rev'):
3079 msg += _(' (started at %s)') % opts['rev']
3079 msg += _(' (started at %s)') % opts['rev']
3080 ui.warn((msg + '\n') % headless)
3080 ui.warn((msg + '\n') % headless)
3081
3081
3082 if not heads:
3082 if not heads:
3083 return 1
3083 return 1
3084
3084
3085 ui.pager('heads')
3085 ui.pager('heads')
3086 heads = sorted(heads, key=lambda x: -x.rev())
3086 heads = sorted(heads, key=lambda x: -x.rev())
3087 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3087 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3088 for ctx in heads:
3088 for ctx in heads:
3089 displayer.show(ctx)
3089 displayer.show(ctx)
3090 displayer.close()
3090 displayer.close()
3091
3091
3092 @command('help',
3092 @command('help',
3093 [('e', 'extension', None, _('show only help for extensions')),
3093 [('e', 'extension', None, _('show only help for extensions')),
3094 ('c', 'command', None, _('show only help for commands')),
3094 ('c', 'command', None, _('show only help for commands')),
3095 ('k', 'keyword', None, _('show topics matching keyword')),
3095 ('k', 'keyword', None, _('show topics matching keyword')),
3096 ('s', 'system', [],
3096 ('s', 'system', [],
3097 _('show help for specific platform(s)'), _('PLATFORM')),
3097 _('show help for specific platform(s)'), _('PLATFORM')),
3098 ],
3098 ],
3099 _('[-eck] [-s PLATFORM] [TOPIC]'),
3099 _('[-eck] [-s PLATFORM] [TOPIC]'),
3100 helpcategory=command.CATEGORY_HELP,
3100 helpcategory=command.CATEGORY_HELP,
3101 norepo=True,
3101 norepo=True,
3102 intents={INTENT_READONLY})
3102 intents={INTENT_READONLY})
3103 def help_(ui, name=None, **opts):
3103 def help_(ui, name=None, **opts):
3104 """show help for a given topic or a help overview
3104 """show help for a given topic or a help overview
3105
3105
3106 With no arguments, print a list of commands with short help messages.
3106 With no arguments, print a list of commands with short help messages.
3107
3107
3108 Given a topic, extension, or command name, print help for that
3108 Given a topic, extension, or command name, print help for that
3109 topic.
3109 topic.
3110
3110
3111 Returns 0 if successful.
3111 Returns 0 if successful.
3112 """
3112 """
3113
3113
3114 keep = opts.get(r'system') or []
3114 keep = opts.get(r'system') or []
3115 if len(keep) == 0:
3115 if len(keep) == 0:
3116 if pycompat.sysplatform.startswith('win'):
3116 if pycompat.sysplatform.startswith('win'):
3117 keep.append('windows')
3117 keep.append('windows')
3118 elif pycompat.sysplatform == 'OpenVMS':
3118 elif pycompat.sysplatform == 'OpenVMS':
3119 keep.append('vms')
3119 keep.append('vms')
3120 elif pycompat.sysplatform == 'plan9':
3120 elif pycompat.sysplatform == 'plan9':
3121 keep.append('plan9')
3121 keep.append('plan9')
3122 else:
3122 else:
3123 keep.append('unix')
3123 keep.append('unix')
3124 keep.append(pycompat.sysplatform.lower())
3124 keep.append(pycompat.sysplatform.lower())
3125 if ui.verbose:
3125 if ui.verbose:
3126 keep.append('verbose')
3126 keep.append('verbose')
3127
3127
3128 commands = sys.modules[__name__]
3128 commands = sys.modules[__name__]
3129 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3129 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3130 ui.pager('help')
3130 ui.pager('help')
3131 ui.write(formatted)
3131 ui.write(formatted)
3132
3132
3133
3133
3134 @command('identify|id',
3134 @command('identify|id',
3135 [('r', 'rev', '',
3135 [('r', 'rev', '',
3136 _('identify the specified revision'), _('REV')),
3136 _('identify the specified revision'), _('REV')),
3137 ('n', 'num', None, _('show local revision number')),
3137 ('n', 'num', None, _('show local revision number')),
3138 ('i', 'id', None, _('show global revision id')),
3138 ('i', 'id', None, _('show global revision id')),
3139 ('b', 'branch', None, _('show branch')),
3139 ('b', 'branch', None, _('show branch')),
3140 ('t', 'tags', None, _('show tags')),
3140 ('t', 'tags', None, _('show tags')),
3141 ('B', 'bookmarks', None, _('show bookmarks')),
3141 ('B', 'bookmarks', None, _('show bookmarks')),
3142 ] + remoteopts + formatteropts,
3142 ] + remoteopts + formatteropts,
3143 _('[-nibtB] [-r REV] [SOURCE]'),
3143 _('[-nibtB] [-r REV] [SOURCE]'),
3144 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3144 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3145 optionalrepo=True,
3145 optionalrepo=True,
3146 intents={INTENT_READONLY})
3146 intents={INTENT_READONLY})
3147 def identify(ui, repo, source=None, rev=None,
3147 def identify(ui, repo, source=None, rev=None,
3148 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3148 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3149 """identify the working directory or specified revision
3149 """identify the working directory or specified revision
3150
3150
3151 Print a summary identifying the repository state at REV using one or
3151 Print a summary identifying the repository state at REV using one or
3152 two parent hash identifiers, followed by a "+" if the working
3152 two parent hash identifiers, followed by a "+" if the working
3153 directory has uncommitted changes, the branch name (if not default),
3153 directory has uncommitted changes, the branch name (if not default),
3154 a list of tags, and a list of bookmarks.
3154 a list of tags, and a list of bookmarks.
3155
3155
3156 When REV is not given, print a summary of the current state of the
3156 When REV is not given, print a summary of the current state of the
3157 repository including the working directory. Specify -r. to get information
3157 repository including the working directory. Specify -r. to get information
3158 of the working directory parent without scanning uncommitted changes.
3158 of the working directory parent without scanning uncommitted changes.
3159
3159
3160 Specifying a path to a repository root or Mercurial bundle will
3160 Specifying a path to a repository root or Mercurial bundle will
3161 cause lookup to operate on that repository/bundle.
3161 cause lookup to operate on that repository/bundle.
3162
3162
3163 .. container:: verbose
3163 .. container:: verbose
3164
3164
3165 Template:
3165 Template:
3166
3166
3167 The following keywords are supported in addition to the common template
3167 The following keywords are supported in addition to the common template
3168 keywords and functions. See also :hg:`help templates`.
3168 keywords and functions. See also :hg:`help templates`.
3169
3169
3170 :dirty: String. Character ``+`` denoting if the working directory has
3170 :dirty: String. Character ``+`` denoting if the working directory has
3171 uncommitted changes.
3171 uncommitted changes.
3172 :id: String. One or two nodes, optionally followed by ``+``.
3172 :id: String. One or two nodes, optionally followed by ``+``.
3173 :parents: List of strings. Parent nodes of the changeset.
3173 :parents: List of strings. Parent nodes of the changeset.
3174
3174
3175 Examples:
3175 Examples:
3176
3176
3177 - generate a build identifier for the working directory::
3177 - generate a build identifier for the working directory::
3178
3178
3179 hg id --id > build-id.dat
3179 hg id --id > build-id.dat
3180
3180
3181 - find the revision corresponding to a tag::
3181 - find the revision corresponding to a tag::
3182
3182
3183 hg id -n -r 1.3
3183 hg id -n -r 1.3
3184
3184
3185 - check the most recent revision of a remote repository::
3185 - check the most recent revision of a remote repository::
3186
3186
3187 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3187 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3188
3188
3189 See :hg:`log` for generating more information about specific revisions,
3189 See :hg:`log` for generating more information about specific revisions,
3190 including full hash identifiers.
3190 including full hash identifiers.
3191
3191
3192 Returns 0 if successful.
3192 Returns 0 if successful.
3193 """
3193 """
3194
3194
3195 opts = pycompat.byteskwargs(opts)
3195 opts = pycompat.byteskwargs(opts)
3196 if not repo and not source:
3196 if not repo and not source:
3197 raise error.Abort(_("there is no Mercurial repository here "
3197 raise error.Abort(_("there is no Mercurial repository here "
3198 "(.hg not found)"))
3198 "(.hg not found)"))
3199
3199
3200 default = not (num or id or branch or tags or bookmarks)
3200 default = not (num or id or branch or tags or bookmarks)
3201 output = []
3201 output = []
3202 revs = []
3202 revs = []
3203
3203
3204 if source:
3204 if source:
3205 source, branches = hg.parseurl(ui.expandpath(source))
3205 source, branches = hg.parseurl(ui.expandpath(source))
3206 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3206 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3207 repo = peer.local()
3207 repo = peer.local()
3208 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3208 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3209
3209
3210 fm = ui.formatter('identify', opts)
3210 fm = ui.formatter('identify', opts)
3211 fm.startitem()
3211 fm.startitem()
3212
3212
3213 if not repo:
3213 if not repo:
3214 if num or branch or tags:
3214 if num or branch or tags:
3215 raise error.Abort(
3215 raise error.Abort(
3216 _("can't query remote revision number, branch, or tags"))
3216 _("can't query remote revision number, branch, or tags"))
3217 if not rev and revs:
3217 if not rev and revs:
3218 rev = revs[0]
3218 rev = revs[0]
3219 if not rev:
3219 if not rev:
3220 rev = "tip"
3220 rev = "tip"
3221
3221
3222 remoterev = peer.lookup(rev)
3222 remoterev = peer.lookup(rev)
3223 hexrev = fm.hexfunc(remoterev)
3223 hexrev = fm.hexfunc(remoterev)
3224 if default or id:
3224 if default or id:
3225 output = [hexrev]
3225 output = [hexrev]
3226 fm.data(id=hexrev)
3226 fm.data(id=hexrev)
3227
3227
3228 @util.cachefunc
3228 @util.cachefunc
3229 def getbms():
3229 def getbms():
3230 bms = []
3230 bms = []
3231
3231
3232 if 'bookmarks' in peer.listkeys('namespaces'):
3232 if 'bookmarks' in peer.listkeys('namespaces'):
3233 hexremoterev = hex(remoterev)
3233 hexremoterev = hex(remoterev)
3234 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3234 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3235 if bmr == hexremoterev]
3235 if bmr == hexremoterev]
3236
3236
3237 return sorted(bms)
3237 return sorted(bms)
3238
3238
3239 if fm.isplain():
3239 if fm.isplain():
3240 if bookmarks:
3240 if bookmarks:
3241 output.extend(getbms())
3241 output.extend(getbms())
3242 elif default and not ui.quiet:
3242 elif default and not ui.quiet:
3243 # multiple bookmarks for a single parent separated by '/'
3243 # multiple bookmarks for a single parent separated by '/'
3244 bm = '/'.join(getbms())
3244 bm = '/'.join(getbms())
3245 if bm:
3245 if bm:
3246 output.append(bm)
3246 output.append(bm)
3247 else:
3247 else:
3248 fm.data(node=hex(remoterev))
3248 fm.data(node=hex(remoterev))
3249 if bookmarks or 'bookmarks' in fm.datahint():
3249 if bookmarks or 'bookmarks' in fm.datahint():
3250 fm.data(bookmarks=fm.formatlist(getbms(), name='bookmark'))
3250 fm.data(bookmarks=fm.formatlist(getbms(), name='bookmark'))
3251 else:
3251 else:
3252 if rev:
3252 if rev:
3253 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3253 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3254 ctx = scmutil.revsingle(repo, rev, None)
3254 ctx = scmutil.revsingle(repo, rev, None)
3255
3255
3256 if ctx.rev() is None:
3256 if ctx.rev() is None:
3257 ctx = repo[None]
3257 ctx = repo[None]
3258 parents = ctx.parents()
3258 parents = ctx.parents()
3259 taglist = []
3259 taglist = []
3260 for p in parents:
3260 for p in parents:
3261 taglist.extend(p.tags())
3261 taglist.extend(p.tags())
3262
3262
3263 dirty = ""
3263 dirty = ""
3264 if ctx.dirty(missing=True, merge=False, branch=False):
3264 if ctx.dirty(missing=True, merge=False, branch=False):
3265 dirty = '+'
3265 dirty = '+'
3266 fm.data(dirty=dirty)
3266 fm.data(dirty=dirty)
3267
3267
3268 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3268 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3269 if default or id:
3269 if default or id:
3270 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
3270 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
3271 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
3271 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
3272
3272
3273 if num:
3273 if num:
3274 numoutput = ["%d" % p.rev() for p in parents]
3274 numoutput = ["%d" % p.rev() for p in parents]
3275 output.append("%s%s" % ('+'.join(numoutput), dirty))
3275 output.append("%s%s" % ('+'.join(numoutput), dirty))
3276
3276
3277 fm.data(parents=fm.formatlist([fm.hexfunc(p.node())
3277 fm.data(parents=fm.formatlist([fm.hexfunc(p.node())
3278 for p in parents], name='node'))
3278 for p in parents], name='node'))
3279 else:
3279 else:
3280 hexoutput = fm.hexfunc(ctx.node())
3280 hexoutput = fm.hexfunc(ctx.node())
3281 if default or id:
3281 if default or id:
3282 output = [hexoutput]
3282 output = [hexoutput]
3283 fm.data(id=hexoutput)
3283 fm.data(id=hexoutput)
3284
3284
3285 if num:
3285 if num:
3286 output.append(pycompat.bytestr(ctx.rev()))
3286 output.append(pycompat.bytestr(ctx.rev()))
3287 taglist = ctx.tags()
3287 taglist = ctx.tags()
3288
3288
3289 if default and not ui.quiet:
3289 if default and not ui.quiet:
3290 b = ctx.branch()
3290 b = ctx.branch()
3291 if b != 'default':
3291 if b != 'default':
3292 output.append("(%s)" % b)
3292 output.append("(%s)" % b)
3293
3293
3294 # multiple tags for a single parent separated by '/'
3294 # multiple tags for a single parent separated by '/'
3295 t = '/'.join(taglist)
3295 t = '/'.join(taglist)
3296 if t:
3296 if t:
3297 output.append(t)
3297 output.append(t)
3298
3298
3299 # multiple bookmarks for a single parent separated by '/'
3299 # multiple bookmarks for a single parent separated by '/'
3300 bm = '/'.join(ctx.bookmarks())
3300 bm = '/'.join(ctx.bookmarks())
3301 if bm:
3301 if bm:
3302 output.append(bm)
3302 output.append(bm)
3303 else:
3303 else:
3304 if branch:
3304 if branch:
3305 output.append(ctx.branch())
3305 output.append(ctx.branch())
3306
3306
3307 if tags:
3307 if tags:
3308 output.extend(taglist)
3308 output.extend(taglist)
3309
3309
3310 if bookmarks:
3310 if bookmarks:
3311 output.extend(ctx.bookmarks())
3311 output.extend(ctx.bookmarks())
3312
3312
3313 fm.data(node=ctx.hex())
3313 fm.data(node=ctx.hex())
3314 fm.data(branch=ctx.branch())
3314 fm.data(branch=ctx.branch())
3315 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
3315 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
3316 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
3316 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
3317 fm.context(ctx=ctx)
3317 fm.context(ctx=ctx)
3318
3318
3319 fm.plain("%s\n" % ' '.join(output))
3319 fm.plain("%s\n" % ' '.join(output))
3320 fm.end()
3320 fm.end()
3321
3321
3322 @command('import|patch',
3322 @command('import|patch',
3323 [('p', 'strip', 1,
3323 [('p', 'strip', 1,
3324 _('directory strip option for patch. This has the same '
3324 _('directory strip option for patch. This has the same '
3325 'meaning as the corresponding patch option'), _('NUM')),
3325 'meaning as the corresponding patch option'), _('NUM')),
3326 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3326 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3327 ('e', 'edit', False, _('invoke editor on commit messages')),
3327 ('e', 'edit', False, _('invoke editor on commit messages')),
3328 ('f', 'force', None,
3328 ('f', 'force', None,
3329 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
3329 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
3330 ('', 'no-commit', None,
3330 ('', 'no-commit', None,
3331 _("don't commit, just update the working directory")),
3331 _("don't commit, just update the working directory")),
3332 ('', 'bypass', None,
3332 ('', 'bypass', None,
3333 _("apply patch without touching the working directory")),
3333 _("apply patch without touching the working directory")),
3334 ('', 'partial', None,
3334 ('', 'partial', None,
3335 _('commit even if some hunks fail')),
3335 _('commit even if some hunks fail')),
3336 ('', 'exact', None,
3336 ('', 'exact', None,
3337 _('abort if patch would apply lossily')),
3337 _('abort if patch would apply lossily')),
3338 ('', 'prefix', '',
3338 ('', 'prefix', '',
3339 _('apply patch to subdirectory'), _('DIR')),
3339 _('apply patch to subdirectory'), _('DIR')),
3340 ('', 'import-branch', None,
3340 ('', 'import-branch', None,
3341 _('use any branch information in patch (implied by --exact)'))] +
3341 _('use any branch information in patch (implied by --exact)'))] +
3342 commitopts + commitopts2 + similarityopts,
3342 commitopts + commitopts2 + similarityopts,
3343 _('[OPTION]... PATCH...'),
3343 _('[OPTION]... PATCH...'),
3344 helpcategory=command.CATEGORY_IMPORT_EXPORT)
3344 helpcategory=command.CATEGORY_IMPORT_EXPORT)
3345 def import_(ui, repo, patch1=None, *patches, **opts):
3345 def import_(ui, repo, patch1=None, *patches, **opts):
3346 """import an ordered set of patches
3346 """import an ordered set of patches
3347
3347
3348 Import a list of patches and commit them individually (unless
3348 Import a list of patches and commit them individually (unless
3349 --no-commit is specified).
3349 --no-commit is specified).
3350
3350
3351 To read a patch from standard input (stdin), use "-" as the patch
3351 To read a patch from standard input (stdin), use "-" as the patch
3352 name. If a URL is specified, the patch will be downloaded from
3352 name. If a URL is specified, the patch will be downloaded from
3353 there.
3353 there.
3354
3354
3355 Import first applies changes to the working directory (unless
3355 Import first applies changes to the working directory (unless
3356 --bypass is specified), import will abort if there are outstanding
3356 --bypass is specified), import will abort if there are outstanding
3357 changes.
3357 changes.
3358
3358
3359 Use --bypass to apply and commit patches directly to the
3359 Use --bypass to apply and commit patches directly to the
3360 repository, without affecting the working directory. Without
3360 repository, without affecting the working directory. Without
3361 --exact, patches will be applied on top of the working directory
3361 --exact, patches will be applied on top of the working directory
3362 parent revision.
3362 parent revision.
3363
3363
3364 You can import a patch straight from a mail message. Even patches
3364 You can import a patch straight from a mail message. Even patches
3365 as attachments work (to use the body part, it must have type
3365 as attachments work (to use the body part, it must have type
3366 text/plain or text/x-patch). From and Subject headers of email
3366 text/plain or text/x-patch). From and Subject headers of email
3367 message are used as default committer and commit message. All
3367 message are used as default committer and commit message. All
3368 text/plain body parts before first diff are added to the commit
3368 text/plain body parts before first diff are added to the commit
3369 message.
3369 message.
3370
3370
3371 If the imported patch was generated by :hg:`export`, user and
3371 If the imported patch was generated by :hg:`export`, user and
3372 description from patch override values from message headers and
3372 description from patch override values from message headers and
3373 body. Values given on command line with -m/--message and -u/--user
3373 body. Values given on command line with -m/--message and -u/--user
3374 override these.
3374 override these.
3375
3375
3376 If --exact is specified, import will set the working directory to
3376 If --exact is specified, import will set the working directory to
3377 the parent of each patch before applying it, and will abort if the
3377 the parent of each patch before applying it, and will abort if the
3378 resulting changeset has a different ID than the one recorded in
3378 resulting changeset has a different ID than the one recorded in
3379 the patch. This will guard against various ways that portable
3379 the patch. This will guard against various ways that portable
3380 patch formats and mail systems might fail to transfer Mercurial
3380 patch formats and mail systems might fail to transfer Mercurial
3381 data or metadata. See :hg:`bundle` for lossless transmission.
3381 data or metadata. See :hg:`bundle` for lossless transmission.
3382
3382
3383 Use --partial to ensure a changeset will be created from the patch
3383 Use --partial to ensure a changeset will be created from the patch
3384 even if some hunks fail to apply. Hunks that fail to apply will be
3384 even if some hunks fail to apply. Hunks that fail to apply will be
3385 written to a <target-file>.rej file. Conflicts can then be resolved
3385 written to a <target-file>.rej file. Conflicts can then be resolved
3386 by hand before :hg:`commit --amend` is run to update the created
3386 by hand before :hg:`commit --amend` is run to update the created
3387 changeset. This flag exists to let people import patches that
3387 changeset. This flag exists to let people import patches that
3388 partially apply without losing the associated metadata (author,
3388 partially apply without losing the associated metadata (author,
3389 date, description, ...).
3389 date, description, ...).
3390
3390
3391 .. note::
3391 .. note::
3392
3392
3393 When no hunks apply cleanly, :hg:`import --partial` will create
3393 When no hunks apply cleanly, :hg:`import --partial` will create
3394 an empty changeset, importing only the patch metadata.
3394 an empty changeset, importing only the patch metadata.
3395
3395
3396 With -s/--similarity, hg will attempt to discover renames and
3396 With -s/--similarity, hg will attempt to discover renames and
3397 copies in the patch in the same way as :hg:`addremove`.
3397 copies in the patch in the same way as :hg:`addremove`.
3398
3398
3399 It is possible to use external patch programs to perform the patch
3399 It is possible to use external patch programs to perform the patch
3400 by setting the ``ui.patch`` configuration option. For the default
3400 by setting the ``ui.patch`` configuration option. For the default
3401 internal tool, the fuzz can also be configured via ``patch.fuzz``.
3401 internal tool, the fuzz can also be configured via ``patch.fuzz``.
3402 See :hg:`help config` for more information about configuration
3402 See :hg:`help config` for more information about configuration
3403 files and how to use these options.
3403 files and how to use these options.
3404
3404
3405 See :hg:`help dates` for a list of formats valid for -d/--date.
3405 See :hg:`help dates` for a list of formats valid for -d/--date.
3406
3406
3407 .. container:: verbose
3407 .. container:: verbose
3408
3408
3409 Examples:
3409 Examples:
3410
3410
3411 - import a traditional patch from a website and detect renames::
3411 - import a traditional patch from a website and detect renames::
3412
3412
3413 hg import -s 80 http://example.com/bugfix.patch
3413 hg import -s 80 http://example.com/bugfix.patch
3414
3414
3415 - import a changeset from an hgweb server::
3415 - import a changeset from an hgweb server::
3416
3416
3417 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3417 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3418
3418
3419 - import all the patches in an Unix-style mbox::
3419 - import all the patches in an Unix-style mbox::
3420
3420
3421 hg import incoming-patches.mbox
3421 hg import incoming-patches.mbox
3422
3422
3423 - import patches from stdin::
3423 - import patches from stdin::
3424
3424
3425 hg import -
3425 hg import -
3426
3426
3427 - attempt to exactly restore an exported changeset (not always
3427 - attempt to exactly restore an exported changeset (not always
3428 possible)::
3428 possible)::
3429
3429
3430 hg import --exact proposed-fix.patch
3430 hg import --exact proposed-fix.patch
3431
3431
3432 - use an external tool to apply a patch which is too fuzzy for
3432 - use an external tool to apply a patch which is too fuzzy for
3433 the default internal tool.
3433 the default internal tool.
3434
3434
3435 hg import --config ui.patch="patch --merge" fuzzy.patch
3435 hg import --config ui.patch="patch --merge" fuzzy.patch
3436
3436
3437 - change the default fuzzing from 2 to a less strict 7
3437 - change the default fuzzing from 2 to a less strict 7
3438
3438
3439 hg import --config ui.fuzz=7 fuzz.patch
3439 hg import --config ui.fuzz=7 fuzz.patch
3440
3440
3441 Returns 0 on success, 1 on partial success (see --partial).
3441 Returns 0 on success, 1 on partial success (see --partial).
3442 """
3442 """
3443
3443
3444 opts = pycompat.byteskwargs(opts)
3444 opts = pycompat.byteskwargs(opts)
3445 if not patch1:
3445 if not patch1:
3446 raise error.Abort(_('need at least one patch to import'))
3446 raise error.Abort(_('need at least one patch to import'))
3447
3447
3448 patches = (patch1,) + patches
3448 patches = (patch1,) + patches
3449
3449
3450 date = opts.get('date')
3450 date = opts.get('date')
3451 if date:
3451 if date:
3452 opts['date'] = dateutil.parsedate(date)
3452 opts['date'] = dateutil.parsedate(date)
3453
3453
3454 exact = opts.get('exact')
3454 exact = opts.get('exact')
3455 update = not opts.get('bypass')
3455 update = not opts.get('bypass')
3456 if not update and opts.get('no_commit'):
3456 if not update and opts.get('no_commit'):
3457 raise error.Abort(_('cannot use --no-commit with --bypass'))
3457 raise error.Abort(_('cannot use --no-commit with --bypass'))
3458 try:
3458 try:
3459 sim = float(opts.get('similarity') or 0)
3459 sim = float(opts.get('similarity') or 0)
3460 except ValueError:
3460 except ValueError:
3461 raise error.Abort(_('similarity must be a number'))
3461 raise error.Abort(_('similarity must be a number'))
3462 if sim < 0 or sim > 100:
3462 if sim < 0 or sim > 100:
3463 raise error.Abort(_('similarity must be between 0 and 100'))
3463 raise error.Abort(_('similarity must be between 0 and 100'))
3464 if sim and not update:
3464 if sim and not update:
3465 raise error.Abort(_('cannot use --similarity with --bypass'))
3465 raise error.Abort(_('cannot use --similarity with --bypass'))
3466 if exact:
3466 if exact:
3467 if opts.get('edit'):
3467 if opts.get('edit'):
3468 raise error.Abort(_('cannot use --exact with --edit'))
3468 raise error.Abort(_('cannot use --exact with --edit'))
3469 if opts.get('prefix'):
3469 if opts.get('prefix'):
3470 raise error.Abort(_('cannot use --exact with --prefix'))
3470 raise error.Abort(_('cannot use --exact with --prefix'))
3471
3471
3472 base = opts["base"]
3472 base = opts["base"]
3473 msgs = []
3473 msgs = []
3474 ret = 0
3474 ret = 0
3475
3475
3476 with repo.wlock():
3476 with repo.wlock():
3477 if update:
3477 if update:
3478 cmdutil.checkunfinished(repo)
3478 cmdutil.checkunfinished(repo)
3479 if (exact or not opts.get('force')):
3479 if (exact or not opts.get('force')):
3480 cmdutil.bailifchanged(repo)
3480 cmdutil.bailifchanged(repo)
3481
3481
3482 if not opts.get('no_commit'):
3482 if not opts.get('no_commit'):
3483 lock = repo.lock
3483 lock = repo.lock
3484 tr = lambda: repo.transaction('import')
3484 tr = lambda: repo.transaction('import')
3485 dsguard = util.nullcontextmanager
3485 dsguard = util.nullcontextmanager
3486 else:
3486 else:
3487 lock = util.nullcontextmanager
3487 lock = util.nullcontextmanager
3488 tr = util.nullcontextmanager
3488 tr = util.nullcontextmanager
3489 dsguard = lambda: dirstateguard.dirstateguard(repo, 'import')
3489 dsguard = lambda: dirstateguard.dirstateguard(repo, 'import')
3490 with lock(), tr(), dsguard():
3490 with lock(), tr(), dsguard():
3491 parents = repo[None].parents()
3491 parents = repo[None].parents()
3492 for patchurl in patches:
3492 for patchurl in patches:
3493 if patchurl == '-':
3493 if patchurl == '-':
3494 ui.status(_('applying patch from stdin\n'))
3494 ui.status(_('applying patch from stdin\n'))
3495 patchfile = ui.fin
3495 patchfile = ui.fin
3496 patchurl = 'stdin' # for error message
3496 patchurl = 'stdin' # for error message
3497 else:
3497 else:
3498 patchurl = os.path.join(base, patchurl)
3498 patchurl = os.path.join(base, patchurl)
3499 ui.status(_('applying %s\n') % patchurl)
3499 ui.status(_('applying %s\n') % patchurl)
3500 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
3500 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
3501
3501
3502 haspatch = False
3502 haspatch = False
3503 for hunk in patch.split(patchfile):
3503 for hunk in patch.split(patchfile):
3504 with patch.extract(ui, hunk) as patchdata:
3504 with patch.extract(ui, hunk) as patchdata:
3505 msg, node, rej = cmdutil.tryimportone(ui, repo,
3505 msg, node, rej = cmdutil.tryimportone(ui, repo,
3506 patchdata,
3506 patchdata,
3507 parents, opts,
3507 parents, opts,
3508 msgs, hg.clean)
3508 msgs, hg.clean)
3509 if msg:
3509 if msg:
3510 haspatch = True
3510 haspatch = True
3511 ui.note(msg + '\n')
3511 ui.note(msg + '\n')
3512 if update or exact:
3512 if update or exact:
3513 parents = repo[None].parents()
3513 parents = repo[None].parents()
3514 else:
3514 else:
3515 parents = [repo[node]]
3515 parents = [repo[node]]
3516 if rej:
3516 if rej:
3517 ui.write_err(_("patch applied partially\n"))
3517 ui.write_err(_("patch applied partially\n"))
3518 ui.write_err(_("(fix the .rej files and run "
3518 ui.write_err(_("(fix the .rej files and run "
3519 "`hg commit --amend`)\n"))
3519 "`hg commit --amend`)\n"))
3520 ret = 1
3520 ret = 1
3521 break
3521 break
3522
3522
3523 if not haspatch:
3523 if not haspatch:
3524 raise error.Abort(_('%s: no diffs found') % patchurl)
3524 raise error.Abort(_('%s: no diffs found') % patchurl)
3525
3525
3526 if msgs:
3526 if msgs:
3527 repo.savecommitmessage('\n* * *\n'.join(msgs))
3527 repo.savecommitmessage('\n* * *\n'.join(msgs))
3528 return ret
3528 return ret
3529
3529
3530 @command('incoming|in',
3530 @command('incoming|in',
3531 [('f', 'force', None,
3531 [('f', 'force', None,
3532 _('run even if remote repository is unrelated')),
3532 _('run even if remote repository is unrelated')),
3533 ('n', 'newest-first', None, _('show newest record first')),
3533 ('n', 'newest-first', None, _('show newest record first')),
3534 ('', 'bundle', '',
3534 ('', 'bundle', '',
3535 _('file to store the bundles into'), _('FILE')),
3535 _('file to store the bundles into'), _('FILE')),
3536 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3536 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3537 ('B', 'bookmarks', False, _("compare bookmarks")),
3537 ('B', 'bookmarks', False, _("compare bookmarks")),
3538 ('b', 'branch', [],
3538 ('b', 'branch', [],
3539 _('a specific branch you would like to pull'), _('BRANCH')),
3539 _('a specific branch you would like to pull'), _('BRANCH')),
3540 ] + logopts + remoteopts + subrepoopts,
3540 ] + logopts + remoteopts + subrepoopts,
3541 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
3541 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
3542 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
3542 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
3543 def incoming(ui, repo, source="default", **opts):
3543 def incoming(ui, repo, source="default", **opts):
3544 """show new changesets found in source
3544 """show new changesets found in source
3545
3545
3546 Show new changesets found in the specified path/URL or the default
3546 Show new changesets found in the specified path/URL or the default
3547 pull location. These are the changesets that would have been pulled
3547 pull location. These are the changesets that would have been pulled
3548 by :hg:`pull` at the time you issued this command.
3548 by :hg:`pull` at the time you issued this command.
3549
3549
3550 See pull for valid source format details.
3550 See pull for valid source format details.
3551
3551
3552 .. container:: verbose
3552 .. container:: verbose
3553
3553
3554 With -B/--bookmarks, the result of bookmark comparison between
3554 With -B/--bookmarks, the result of bookmark comparison between
3555 local and remote repositories is displayed. With -v/--verbose,
3555 local and remote repositories is displayed. With -v/--verbose,
3556 status is also displayed for each bookmark like below::
3556 status is also displayed for each bookmark like below::
3557
3557
3558 BM1 01234567890a added
3558 BM1 01234567890a added
3559 BM2 1234567890ab advanced
3559 BM2 1234567890ab advanced
3560 BM3 234567890abc diverged
3560 BM3 234567890abc diverged
3561 BM4 34567890abcd changed
3561 BM4 34567890abcd changed
3562
3562
3563 The action taken locally when pulling depends on the
3563 The action taken locally when pulling depends on the
3564 status of each bookmark:
3564 status of each bookmark:
3565
3565
3566 :``added``: pull will create it
3566 :``added``: pull will create it
3567 :``advanced``: pull will update it
3567 :``advanced``: pull will update it
3568 :``diverged``: pull will create a divergent bookmark
3568 :``diverged``: pull will create a divergent bookmark
3569 :``changed``: result depends on remote changesets
3569 :``changed``: result depends on remote changesets
3570
3570
3571 From the point of view of pulling behavior, bookmark
3571 From the point of view of pulling behavior, bookmark
3572 existing only in the remote repository are treated as ``added``,
3572 existing only in the remote repository are treated as ``added``,
3573 even if it is in fact locally deleted.
3573 even if it is in fact locally deleted.
3574
3574
3575 .. container:: verbose
3575 .. container:: verbose
3576
3576
3577 For remote repository, using --bundle avoids downloading the
3577 For remote repository, using --bundle avoids downloading the
3578 changesets twice if the incoming is followed by a pull.
3578 changesets twice if the incoming is followed by a pull.
3579
3579
3580 Examples:
3580 Examples:
3581
3581
3582 - show incoming changes with patches and full description::
3582 - show incoming changes with patches and full description::
3583
3583
3584 hg incoming -vp
3584 hg incoming -vp
3585
3585
3586 - show incoming changes excluding merges, store a bundle::
3586 - show incoming changes excluding merges, store a bundle::
3587
3587
3588 hg in -vpM --bundle incoming.hg
3588 hg in -vpM --bundle incoming.hg
3589 hg pull incoming.hg
3589 hg pull incoming.hg
3590
3590
3591 - briefly list changes inside a bundle::
3591 - briefly list changes inside a bundle::
3592
3592
3593 hg in changes.hg -T "{desc|firstline}\\n"
3593 hg in changes.hg -T "{desc|firstline}\\n"
3594
3594
3595 Returns 0 if there are incoming changes, 1 otherwise.
3595 Returns 0 if there are incoming changes, 1 otherwise.
3596 """
3596 """
3597 opts = pycompat.byteskwargs(opts)
3597 opts = pycompat.byteskwargs(opts)
3598 if opts.get('graph'):
3598 if opts.get('graph'):
3599 logcmdutil.checkunsupportedgraphflags([], opts)
3599 logcmdutil.checkunsupportedgraphflags([], opts)
3600 def display(other, chlist, displayer):
3600 def display(other, chlist, displayer):
3601 revdag = logcmdutil.graphrevs(other, chlist, opts)
3601 revdag = logcmdutil.graphrevs(other, chlist, opts)
3602 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3602 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3603 graphmod.asciiedges)
3603 graphmod.asciiedges)
3604
3604
3605 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3605 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3606 return 0
3606 return 0
3607
3607
3608 if opts.get('bundle') and opts.get('subrepos'):
3608 if opts.get('bundle') and opts.get('subrepos'):
3609 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3609 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3610
3610
3611 if opts.get('bookmarks'):
3611 if opts.get('bookmarks'):
3612 source, branches = hg.parseurl(ui.expandpath(source),
3612 source, branches = hg.parseurl(ui.expandpath(source),
3613 opts.get('branch'))
3613 opts.get('branch'))
3614 other = hg.peer(repo, opts, source)
3614 other = hg.peer(repo, opts, source)
3615 if 'bookmarks' not in other.listkeys('namespaces'):
3615 if 'bookmarks' not in other.listkeys('namespaces'):
3616 ui.warn(_("remote doesn't support bookmarks\n"))
3616 ui.warn(_("remote doesn't support bookmarks\n"))
3617 return 0
3617 return 0
3618 ui.pager('incoming')
3618 ui.pager('incoming')
3619 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3619 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3620 return bookmarks.incoming(ui, repo, other)
3620 return bookmarks.incoming(ui, repo, other)
3621
3621
3622 repo._subtoppath = ui.expandpath(source)
3622 repo._subtoppath = ui.expandpath(source)
3623 try:
3623 try:
3624 return hg.incoming(ui, repo, source, opts)
3624 return hg.incoming(ui, repo, source, opts)
3625 finally:
3625 finally:
3626 del repo._subtoppath
3626 del repo._subtoppath
3627
3627
3628
3628
3629 @command('init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3629 @command('init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3630 helpcategory=command.CATEGORY_REPO_CREATION,
3630 helpcategory=command.CATEGORY_REPO_CREATION,
3631 helpbasic=True, norepo=True)
3631 helpbasic=True, norepo=True)
3632 def init(ui, dest=".", **opts):
3632 def init(ui, dest=".", **opts):
3633 """create a new repository in the given directory
3633 """create a new repository in the given directory
3634
3634
3635 Initialize a new repository in the given directory. If the given
3635 Initialize a new repository in the given directory. If the given
3636 directory does not exist, it will be created.
3636 directory does not exist, it will be created.
3637
3637
3638 If no directory is given, the current directory is used.
3638 If no directory is given, the current directory is used.
3639
3639
3640 It is possible to specify an ``ssh://`` URL as the destination.
3640 It is possible to specify an ``ssh://`` URL as the destination.
3641 See :hg:`help urls` for more information.
3641 See :hg:`help urls` for more information.
3642
3642
3643 Returns 0 on success.
3643 Returns 0 on success.
3644 """
3644 """
3645 opts = pycompat.byteskwargs(opts)
3645 opts = pycompat.byteskwargs(opts)
3646 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3646 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3647
3647
3648 @command('locate',
3648 @command('locate',
3649 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3649 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3650 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3650 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3651 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3651 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3652 ] + walkopts,
3652 ] + walkopts,
3653 _('[OPTION]... [PATTERN]...'),
3653 _('[OPTION]... [PATTERN]...'),
3654 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
3654 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
3655 def locate(ui, repo, *pats, **opts):
3655 def locate(ui, repo, *pats, **opts):
3656 """locate files matching specific patterns (DEPRECATED)
3656 """locate files matching specific patterns (DEPRECATED)
3657
3657
3658 Print files under Mercurial control in the working directory whose
3658 Print files under Mercurial control in the working directory whose
3659 names match the given patterns.
3659 names match the given patterns.
3660
3660
3661 By default, this command searches all directories in the working
3661 By default, this command searches all directories in the working
3662 directory. To search just the current directory and its
3662 directory. To search just the current directory and its
3663 subdirectories, use "--include .".
3663 subdirectories, use "--include .".
3664
3664
3665 If no patterns are given to match, this command prints the names
3665 If no patterns are given to match, this command prints the names
3666 of all files under Mercurial control in the working directory.
3666 of all files under Mercurial control in the working directory.
3667
3667
3668 If you want to feed the output of this command into the "xargs"
3668 If you want to feed the output of this command into the "xargs"
3669 command, use the -0 option to both this command and "xargs". This
3669 command, use the -0 option to both this command and "xargs". This
3670 will avoid the problem of "xargs" treating single filenames that
3670 will avoid the problem of "xargs" treating single filenames that
3671 contain whitespace as multiple filenames.
3671 contain whitespace as multiple filenames.
3672
3672
3673 See :hg:`help files` for a more versatile command.
3673 See :hg:`help files` for a more versatile command.
3674
3674
3675 Returns 0 if a match is found, 1 otherwise.
3675 Returns 0 if a match is found, 1 otherwise.
3676 """
3676 """
3677 opts = pycompat.byteskwargs(opts)
3677 opts = pycompat.byteskwargs(opts)
3678 if opts.get('print0'):
3678 if opts.get('print0'):
3679 end = '\0'
3679 end = '\0'
3680 else:
3680 else:
3681 end = '\n'
3681 end = '\n'
3682 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3682 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3683
3683
3684 ret = 1
3684 ret = 1
3685 m = scmutil.match(ctx, pats, opts, default='relglob',
3685 m = scmutil.match(ctx, pats, opts, default='relglob',
3686 badfn=lambda x, y: False)
3686 badfn=lambda x, y: False)
3687
3687
3688 ui.pager('locate')
3688 ui.pager('locate')
3689 if ctx.rev() is None:
3689 if ctx.rev() is None:
3690 # When run on the working copy, "locate" includes removed files, so
3690 # When run on the working copy, "locate" includes removed files, so
3691 # we get the list of files from the dirstate.
3691 # we get the list of files from the dirstate.
3692 filesgen = sorted(repo.dirstate.matches(m))
3692 filesgen = sorted(repo.dirstate.matches(m))
3693 else:
3693 else:
3694 filesgen = ctx.matches(m)
3694 filesgen = ctx.matches(m)
3695 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
3695 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
3696 for abs in filesgen:
3696 for abs in filesgen:
3697 if opts.get('fullpath'):
3697 if opts.get('fullpath'):
3698 ui.write(repo.wjoin(abs), end)
3698 ui.write(repo.wjoin(abs), end)
3699 else:
3699 else:
3700 ui.write(uipathfn(abs), end)
3700 ui.write(uipathfn(abs), end)
3701 ret = 0
3701 ret = 0
3702
3702
3703 return ret
3703 return ret
3704
3704
3705 @command('log|history',
3705 @command('log|history',
3706 [('f', 'follow', None,
3706 [('f', 'follow', None,
3707 _('follow changeset history, or file history across copies and renames')),
3707 _('follow changeset history, or file history across copies and renames')),
3708 ('', 'follow-first', None,
3708 ('', 'follow-first', None,
3709 _('only follow the first parent of merge changesets (DEPRECATED)')),
3709 _('only follow the first parent of merge changesets (DEPRECATED)')),
3710 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3710 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3711 ('C', 'copies', None, _('show copied files')),
3711 ('C', 'copies', None, _('show copied files')),
3712 ('k', 'keyword', [],
3712 ('k', 'keyword', [],
3713 _('do case-insensitive search for a given text'), _('TEXT')),
3713 _('do case-insensitive search for a given text'), _('TEXT')),
3714 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3714 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3715 ('L', 'line-range', [],
3715 ('L', 'line-range', [],
3716 _('follow line range of specified file (EXPERIMENTAL)'),
3716 _('follow line range of specified file (EXPERIMENTAL)'),
3717 _('FILE,RANGE')),
3717 _('FILE,RANGE')),
3718 ('', 'removed', None, _('include revisions where files were removed')),
3718 ('', 'removed', None, _('include revisions where files were removed')),
3719 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3719 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3720 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3720 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3721 ('', 'only-branch', [],
3721 ('', 'only-branch', [],
3722 _('show only changesets within the given named branch (DEPRECATED)'),
3722 _('show only changesets within the given named branch (DEPRECATED)'),
3723 _('BRANCH')),
3723 _('BRANCH')),
3724 ('b', 'branch', [],
3724 ('b', 'branch', [],
3725 _('show changesets within the given named branch'), _('BRANCH')),
3725 _('show changesets within the given named branch'), _('BRANCH')),
3726 ('P', 'prune', [],
3726 ('P', 'prune', [],
3727 _('do not display revision or any of its ancestors'), _('REV')),
3727 _('do not display revision or any of its ancestors'), _('REV')),
3728 ] + logopts + walkopts,
3728 ] + logopts + walkopts,
3729 _('[OPTION]... [FILE]'),
3729 _('[OPTION]... [FILE]'),
3730 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3730 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3731 helpbasic=True, inferrepo=True,
3731 helpbasic=True, inferrepo=True,
3732 intents={INTENT_READONLY})
3732 intents={INTENT_READONLY})
3733 def log(ui, repo, *pats, **opts):
3733 def log(ui, repo, *pats, **opts):
3734 """show revision history of entire repository or files
3734 """show revision history of entire repository or files
3735
3735
3736 Print the revision history of the specified files or the entire
3736 Print the revision history of the specified files or the entire
3737 project.
3737 project.
3738
3738
3739 If no revision range is specified, the default is ``tip:0`` unless
3739 If no revision range is specified, the default is ``tip:0`` unless
3740 --follow is set, in which case the working directory parent is
3740 --follow is set, in which case the working directory parent is
3741 used as the starting revision.
3741 used as the starting revision.
3742
3742
3743 File history is shown without following rename or copy history of
3743 File history is shown without following rename or copy history of
3744 files. Use -f/--follow with a filename to follow history across
3744 files. Use -f/--follow with a filename to follow history across
3745 renames and copies. --follow without a filename will only show
3745 renames and copies. --follow without a filename will only show
3746 ancestors of the starting revision.
3746 ancestors of the starting revision.
3747
3747
3748 By default this command prints revision number and changeset id,
3748 By default this command prints revision number and changeset id,
3749 tags, non-trivial parents, user, date and time, and a summary for
3749 tags, non-trivial parents, user, date and time, and a summary for
3750 each commit. When the -v/--verbose switch is used, the list of
3750 each commit. When the -v/--verbose switch is used, the list of
3751 changed files and full commit message are shown.
3751 changed files and full commit message are shown.
3752
3752
3753 With --graph the revisions are shown as an ASCII art DAG with the most
3753 With --graph the revisions are shown as an ASCII art DAG with the most
3754 recent changeset at the top.
3754 recent changeset at the top.
3755 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
3755 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
3756 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
3756 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
3757 changeset from the lines below is a parent of the 'o' merge on the same
3757 changeset from the lines below is a parent of the 'o' merge on the same
3758 line.
3758 line.
3759 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3759 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3760 of a '|' indicates one or more revisions in a path are omitted.
3760 of a '|' indicates one or more revisions in a path are omitted.
3761
3761
3762 .. container:: verbose
3762 .. container:: verbose
3763
3763
3764 Use -L/--line-range FILE,M:N options to follow the history of lines
3764 Use -L/--line-range FILE,M:N options to follow the history of lines
3765 from M to N in FILE. With -p/--patch only diff hunks affecting
3765 from M to N in FILE. With -p/--patch only diff hunks affecting
3766 specified line range will be shown. This option requires --follow;
3766 specified line range will be shown. This option requires --follow;
3767 it can be specified multiple times. Currently, this option is not
3767 it can be specified multiple times. Currently, this option is not
3768 compatible with --graph. This option is experimental.
3768 compatible with --graph. This option is experimental.
3769
3769
3770 .. note::
3770 .. note::
3771
3771
3772 :hg:`log --patch` may generate unexpected diff output for merge
3772 :hg:`log --patch` may generate unexpected diff output for merge
3773 changesets, as it will only compare the merge changeset against
3773 changesets, as it will only compare the merge changeset against
3774 its first parent. Also, only files different from BOTH parents
3774 its first parent. Also, only files different from BOTH parents
3775 will appear in files:.
3775 will appear in files:.
3776
3776
3777 .. note::
3777 .. note::
3778
3778
3779 For performance reasons, :hg:`log FILE` may omit duplicate changes
3779 For performance reasons, :hg:`log FILE` may omit duplicate changes
3780 made on branches and will not show removals or mode changes. To
3780 made on branches and will not show removals or mode changes. To
3781 see all such changes, use the --removed switch.
3781 see all such changes, use the --removed switch.
3782
3782
3783 .. container:: verbose
3783 .. container:: verbose
3784
3784
3785 .. note::
3785 .. note::
3786
3786
3787 The history resulting from -L/--line-range options depends on diff
3787 The history resulting from -L/--line-range options depends on diff
3788 options; for instance if white-spaces are ignored, respective changes
3788 options; for instance if white-spaces are ignored, respective changes
3789 with only white-spaces in specified line range will not be listed.
3789 with only white-spaces in specified line range will not be listed.
3790
3790
3791 .. container:: verbose
3791 .. container:: verbose
3792
3792
3793 Some examples:
3793 Some examples:
3794
3794
3795 - changesets with full descriptions and file lists::
3795 - changesets with full descriptions and file lists::
3796
3796
3797 hg log -v
3797 hg log -v
3798
3798
3799 - changesets ancestral to the working directory::
3799 - changesets ancestral to the working directory::
3800
3800
3801 hg log -f
3801 hg log -f
3802
3802
3803 - last 10 commits on the current branch::
3803 - last 10 commits on the current branch::
3804
3804
3805 hg log -l 10 -b .
3805 hg log -l 10 -b .
3806
3806
3807 - changesets showing all modifications of a file, including removals::
3807 - changesets showing all modifications of a file, including removals::
3808
3808
3809 hg log --removed file.c
3809 hg log --removed file.c
3810
3810
3811 - all changesets that touch a directory, with diffs, excluding merges::
3811 - all changesets that touch a directory, with diffs, excluding merges::
3812
3812
3813 hg log -Mp lib/
3813 hg log -Mp lib/
3814
3814
3815 - all revision numbers that match a keyword::
3815 - all revision numbers that match a keyword::
3816
3816
3817 hg log -k bug --template "{rev}\\n"
3817 hg log -k bug --template "{rev}\\n"
3818
3818
3819 - the full hash identifier of the working directory parent::
3819 - the full hash identifier of the working directory parent::
3820
3820
3821 hg log -r . --template "{node}\\n"
3821 hg log -r . --template "{node}\\n"
3822
3822
3823 - list available log templates::
3823 - list available log templates::
3824
3824
3825 hg log -T list
3825 hg log -T list
3826
3826
3827 - check if a given changeset is included in a tagged release::
3827 - check if a given changeset is included in a tagged release::
3828
3828
3829 hg log -r "a21ccf and ancestor(1.9)"
3829 hg log -r "a21ccf and ancestor(1.9)"
3830
3830
3831 - find all changesets by some user in a date range::
3831 - find all changesets by some user in a date range::
3832
3832
3833 hg log -k alice -d "may 2008 to jul 2008"
3833 hg log -k alice -d "may 2008 to jul 2008"
3834
3834
3835 - summary of all changesets after the last tag::
3835 - summary of all changesets after the last tag::
3836
3836
3837 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3837 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3838
3838
3839 - changesets touching lines 13 to 23 for file.c::
3839 - changesets touching lines 13 to 23 for file.c::
3840
3840
3841 hg log -L file.c,13:23
3841 hg log -L file.c,13:23
3842
3842
3843 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
3843 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
3844 main.c with patch::
3844 main.c with patch::
3845
3845
3846 hg log -L file.c,13:23 -L main.c,2:6 -p
3846 hg log -L file.c,13:23 -L main.c,2:6 -p
3847
3847
3848 See :hg:`help dates` for a list of formats valid for -d/--date.
3848 See :hg:`help dates` for a list of formats valid for -d/--date.
3849
3849
3850 See :hg:`help revisions` for more about specifying and ordering
3850 See :hg:`help revisions` for more about specifying and ordering
3851 revisions.
3851 revisions.
3852
3852
3853 See :hg:`help templates` for more about pre-packaged styles and
3853 See :hg:`help templates` for more about pre-packaged styles and
3854 specifying custom templates. The default template used by the log
3854 specifying custom templates. The default template used by the log
3855 command can be customized via the ``ui.logtemplate`` configuration
3855 command can be customized via the ``ui.logtemplate`` configuration
3856 setting.
3856 setting.
3857
3857
3858 Returns 0 on success.
3858 Returns 0 on success.
3859
3859
3860 """
3860 """
3861 opts = pycompat.byteskwargs(opts)
3861 opts = pycompat.byteskwargs(opts)
3862 linerange = opts.get('line_range')
3862 linerange = opts.get('line_range')
3863
3863
3864 if linerange and not opts.get('follow'):
3864 if linerange and not opts.get('follow'):
3865 raise error.Abort(_('--line-range requires --follow'))
3865 raise error.Abort(_('--line-range requires --follow'))
3866
3866
3867 if linerange and pats:
3867 if linerange and pats:
3868 # TODO: take pats as patterns with no line-range filter
3868 # TODO: take pats as patterns with no line-range filter
3869 raise error.Abort(
3869 raise error.Abort(
3870 _('FILE arguments are not compatible with --line-range option')
3870 _('FILE arguments are not compatible with --line-range option')
3871 )
3871 )
3872
3872
3873 repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
3873 repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
3874 revs, differ = logcmdutil.getrevs(repo, pats, opts)
3874 revs, differ = logcmdutil.getrevs(repo, pats, opts)
3875 if linerange:
3875 if linerange:
3876 # TODO: should follow file history from logcmdutil._initialrevs(),
3876 # TODO: should follow file history from logcmdutil._initialrevs(),
3877 # then filter the result by logcmdutil._makerevset() and --limit
3877 # then filter the result by logcmdutil._makerevset() and --limit
3878 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
3878 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
3879
3879
3880 getrenamed = None
3880 getrenamed = None
3881 if opts.get('copies'):
3881 if opts.get('copies'):
3882 endrev = None
3882 endrev = None
3883 if revs:
3883 if revs:
3884 endrev = revs.max() + 1
3884 endrev = revs.max() + 1
3885 getrenamed = scmutil.getrenamedfn(repo, endrev=endrev)
3885 getrenamed = scmutil.getrenamedfn(repo, endrev=endrev)
3886
3886
3887 ui.pager('log')
3887 ui.pager('log')
3888 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, differ,
3888 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, differ,
3889 buffered=True)
3889 buffered=True)
3890 if opts.get('graph'):
3890 if opts.get('graph'):
3891 displayfn = logcmdutil.displaygraphrevs
3891 displayfn = logcmdutil.displaygraphrevs
3892 else:
3892 else:
3893 displayfn = logcmdutil.displayrevs
3893 displayfn = logcmdutil.displayrevs
3894 displayfn(ui, repo, revs, displayer, getrenamed)
3894 displayfn(ui, repo, revs, displayer, getrenamed)
3895
3895
3896 @command('manifest',
3896 @command('manifest',
3897 [('r', 'rev', '', _('revision to display'), _('REV')),
3897 [('r', 'rev', '', _('revision to display'), _('REV')),
3898 ('', 'all', False, _("list files from all revisions"))]
3898 ('', 'all', False, _("list files from all revisions"))]
3899 + formatteropts,
3899 + formatteropts,
3900 _('[-r REV]'),
3900 _('[-r REV]'),
3901 helpcategory=command.CATEGORY_MAINTENANCE,
3901 helpcategory=command.CATEGORY_MAINTENANCE,
3902 intents={INTENT_READONLY})
3902 intents={INTENT_READONLY})
3903 def manifest(ui, repo, node=None, rev=None, **opts):
3903 def manifest(ui, repo, node=None, rev=None, **opts):
3904 """output the current or given revision of the project manifest
3904 """output the current or given revision of the project manifest
3905
3905
3906 Print a list of version controlled files for the given revision.
3906 Print a list of version controlled files for the given revision.
3907 If no revision is given, the first parent of the working directory
3907 If no revision is given, the first parent of the working directory
3908 is used, or the null revision if no revision is checked out.
3908 is used, or the null revision if no revision is checked out.
3909
3909
3910 With -v, print file permissions, symlink and executable bits.
3910 With -v, print file permissions, symlink and executable bits.
3911 With --debug, print file revision hashes.
3911 With --debug, print file revision hashes.
3912
3912
3913 If option --all is specified, the list of all files from all revisions
3913 If option --all is specified, the list of all files from all revisions
3914 is printed. This includes deleted and renamed files.
3914 is printed. This includes deleted and renamed files.
3915
3915
3916 Returns 0 on success.
3916 Returns 0 on success.
3917 """
3917 """
3918 opts = pycompat.byteskwargs(opts)
3918 opts = pycompat.byteskwargs(opts)
3919 fm = ui.formatter('manifest', opts)
3919 fm = ui.formatter('manifest', opts)
3920
3920
3921 if opts.get('all'):
3921 if opts.get('all'):
3922 if rev or node:
3922 if rev or node:
3923 raise error.Abort(_("can't specify a revision with --all"))
3923 raise error.Abort(_("can't specify a revision with --all"))
3924
3924
3925 res = set()
3925 res = set()
3926 for rev in repo:
3926 for rev in repo:
3927 ctx = repo[rev]
3927 ctx = repo[rev]
3928 res |= set(ctx.files())
3928 res |= set(ctx.files())
3929
3929
3930 ui.pager('manifest')
3930 ui.pager('manifest')
3931 for f in sorted(res):
3931 for f in sorted(res):
3932 fm.startitem()
3932 fm.startitem()
3933 fm.write("path", '%s\n', f)
3933 fm.write("path", '%s\n', f)
3934 fm.end()
3934 fm.end()
3935 return
3935 return
3936
3936
3937 if rev and node:
3937 if rev and node:
3938 raise error.Abort(_("please specify just one revision"))
3938 raise error.Abort(_("please specify just one revision"))
3939
3939
3940 if not node:
3940 if not node:
3941 node = rev
3941 node = rev
3942
3942
3943 char = {'l': '@', 'x': '*', '': '', 't': 'd'}
3943 char = {'l': '@', 'x': '*', '': '', 't': 'd'}
3944 mode = {'l': '644', 'x': '755', '': '644', 't': '755'}
3944 mode = {'l': '644', 'x': '755', '': '644', 't': '755'}
3945 if node:
3945 if node:
3946 repo = scmutil.unhidehashlikerevs(repo, [node], 'nowarn')
3946 repo = scmutil.unhidehashlikerevs(repo, [node], 'nowarn')
3947 ctx = scmutil.revsingle(repo, node)
3947 ctx = scmutil.revsingle(repo, node)
3948 mf = ctx.manifest()
3948 mf = ctx.manifest()
3949 ui.pager('manifest')
3949 ui.pager('manifest')
3950 for f in ctx:
3950 for f in ctx:
3951 fm.startitem()
3951 fm.startitem()
3952 fm.context(ctx=ctx)
3952 fm.context(ctx=ctx)
3953 fl = ctx[f].flags()
3953 fl = ctx[f].flags()
3954 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3954 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3955 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3955 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3956 fm.write('path', '%s\n', f)
3956 fm.write('path', '%s\n', f)
3957 fm.end()
3957 fm.end()
3958
3958
3959 @command('merge',
3959 @command('merge',
3960 [('f', 'force', None,
3960 [('f', 'force', None,
3961 _('force a merge including outstanding changes (DEPRECATED)')),
3961 _('force a merge including outstanding changes (DEPRECATED)')),
3962 ('r', 'rev', '', _('revision to merge'), _('REV')),
3962 ('r', 'rev', '', _('revision to merge'), _('REV')),
3963 ('P', 'preview', None,
3963 ('P', 'preview', None,
3964 _('review revisions to merge (no merge is performed)')),
3964 _('review revisions to merge (no merge is performed)')),
3965 ('', 'abort', None, _('abort the ongoing merge')),
3965 ('', 'abort', None, _('abort the ongoing merge')),
3966 ] + mergetoolopts,
3966 ] + mergetoolopts,
3967 _('[-P] [[-r] REV]'),
3967 _('[-P] [[-r] REV]'),
3968 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT, helpbasic=True)
3968 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT, helpbasic=True)
3969 def merge(ui, repo, node=None, **opts):
3969 def merge(ui, repo, node=None, **opts):
3970 """merge another revision into working directory
3970 """merge another revision into working directory
3971
3971
3972 The current working directory is updated with all changes made in
3972 The current working directory is updated with all changes made in
3973 the requested revision since the last common predecessor revision.
3973 the requested revision since the last common predecessor revision.
3974
3974
3975 Files that changed between either parent are marked as changed for
3975 Files that changed between either parent are marked as changed for
3976 the next commit and a commit must be performed before any further
3976 the next commit and a commit must be performed before any further
3977 updates to the repository are allowed. The next commit will have
3977 updates to the repository are allowed. The next commit will have
3978 two parents.
3978 two parents.
3979
3979
3980 ``--tool`` can be used to specify the merge tool used for file
3980 ``--tool`` can be used to specify the merge tool used for file
3981 merges. It overrides the HGMERGE environment variable and your
3981 merges. It overrides the HGMERGE environment variable and your
3982 configuration files. See :hg:`help merge-tools` for options.
3982 configuration files. See :hg:`help merge-tools` for options.
3983
3983
3984 If no revision is specified, the working directory's parent is a
3984 If no revision is specified, the working directory's parent is a
3985 head revision, and the current branch contains exactly one other
3985 head revision, and the current branch contains exactly one other
3986 head, the other head is merged with by default. Otherwise, an
3986 head, the other head is merged with by default. Otherwise, an
3987 explicit revision with which to merge with must be provided.
3987 explicit revision with which to merge with must be provided.
3988
3988
3989 See :hg:`help resolve` for information on handling file conflicts.
3989 See :hg:`help resolve` for information on handling file conflicts.
3990
3990
3991 To undo an uncommitted merge, use :hg:`merge --abort` which
3991 To undo an uncommitted merge, use :hg:`merge --abort` which
3992 will check out a clean copy of the original merge parent, losing
3992 will check out a clean copy of the original merge parent, losing
3993 all changes.
3993 all changes.
3994
3994
3995 Returns 0 on success, 1 if there are unresolved files.
3995 Returns 0 on success, 1 if there are unresolved files.
3996 """
3996 """
3997
3997
3998 opts = pycompat.byteskwargs(opts)
3998 opts = pycompat.byteskwargs(opts)
3999 abort = opts.get('abort')
3999 abort = opts.get('abort')
4000 if abort and repo.dirstate.p2() == nullid:
4000 if abort and repo.dirstate.p2() == nullid:
4001 cmdutil.wrongtooltocontinue(repo, _('merge'))
4001 cmdutil.wrongtooltocontinue(repo, _('merge'))
4002 if abort:
4002 if abort:
4003 if node:
4003 if node:
4004 raise error.Abort(_("cannot specify a node with --abort"))
4004 raise error.Abort(_("cannot specify a node with --abort"))
4005 if opts.get('rev'):
4005 if opts.get('rev'):
4006 raise error.Abort(_("cannot specify both --rev and --abort"))
4006 raise error.Abort(_("cannot specify both --rev and --abort"))
4007 if opts.get('preview'):
4007 if opts.get('preview'):
4008 raise error.Abort(_("cannot specify --preview with --abort"))
4008 raise error.Abort(_("cannot specify --preview with --abort"))
4009 if opts.get('rev') and node:
4009 if opts.get('rev') and node:
4010 raise error.Abort(_("please specify just one revision"))
4010 raise error.Abort(_("please specify just one revision"))
4011 if not node:
4011 if not node:
4012 node = opts.get('rev')
4012 node = opts.get('rev')
4013
4013
4014 if node:
4014 if node:
4015 node = scmutil.revsingle(repo, node).node()
4015 node = scmutil.revsingle(repo, node).node()
4016
4016
4017 if not node and not abort:
4017 if not node and not abort:
4018 node = repo[destutil.destmerge(repo)].node()
4018 node = repo[destutil.destmerge(repo)].node()
4019
4019
4020 if opts.get('preview'):
4020 if opts.get('preview'):
4021 # find nodes that are ancestors of p2 but not of p1
4021 # find nodes that are ancestors of p2 but not of p1
4022 p1 = repo.lookup('.')
4022 p1 = repo.lookup('.')
4023 p2 = node
4023 p2 = node
4024 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4024 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4025
4025
4026 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4026 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4027 for node in nodes:
4027 for node in nodes:
4028 displayer.show(repo[node])
4028 displayer.show(repo[node])
4029 displayer.close()
4029 displayer.close()
4030 return 0
4030 return 0
4031
4031
4032 # ui.forcemerge is an internal variable, do not document
4032 # ui.forcemerge is an internal variable, do not document
4033 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4033 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4034 with ui.configoverride(overrides, 'merge'):
4034 with ui.configoverride(overrides, 'merge'):
4035 force = opts.get('force')
4035 force = opts.get('force')
4036 labels = ['working copy', 'merge rev']
4036 labels = ['working copy', 'merge rev']
4037 return hg.merge(repo, node, force=force, mergeforce=force,
4037 return hg.merge(repo, node, force=force, mergeforce=force,
4038 labels=labels, abort=abort)
4038 labels=labels, abort=abort)
4039
4039
4040 @command('outgoing|out',
4040 @command('outgoing|out',
4041 [('f', 'force', None, _('run even when the destination is unrelated')),
4041 [('f', 'force', None, _('run even when the destination is unrelated')),
4042 ('r', 'rev', [],
4042 ('r', 'rev', [],
4043 _('a changeset intended to be included in the destination'), _('REV')),
4043 _('a changeset intended to be included in the destination'), _('REV')),
4044 ('n', 'newest-first', None, _('show newest record first')),
4044 ('n', 'newest-first', None, _('show newest record first')),
4045 ('B', 'bookmarks', False, _('compare bookmarks')),
4045 ('B', 'bookmarks', False, _('compare bookmarks')),
4046 ('b', 'branch', [], _('a specific branch you would like to push'),
4046 ('b', 'branch', [], _('a specific branch you would like to push'),
4047 _('BRANCH')),
4047 _('BRANCH')),
4048 ] + logopts + remoteopts + subrepoopts,
4048 ] + logopts + remoteopts + subrepoopts,
4049 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4049 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4050 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
4050 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
4051 def outgoing(ui, repo, dest=None, **opts):
4051 def outgoing(ui, repo, dest=None, **opts):
4052 """show changesets not found in the destination
4052 """show changesets not found in the destination
4053
4053
4054 Show changesets not found in the specified destination repository
4054 Show changesets not found in the specified destination repository
4055 or the default push location. These are the changesets that would
4055 or the default push location. These are the changesets that would
4056 be pushed if a push was requested.
4056 be pushed if a push was requested.
4057
4057
4058 See pull for details of valid destination formats.
4058 See pull for details of valid destination formats.
4059
4059
4060 .. container:: verbose
4060 .. container:: verbose
4061
4061
4062 With -B/--bookmarks, the result of bookmark comparison between
4062 With -B/--bookmarks, the result of bookmark comparison between
4063 local and remote repositories is displayed. With -v/--verbose,
4063 local and remote repositories is displayed. With -v/--verbose,
4064 status is also displayed for each bookmark like below::
4064 status is also displayed for each bookmark like below::
4065
4065
4066 BM1 01234567890a added
4066 BM1 01234567890a added
4067 BM2 deleted
4067 BM2 deleted
4068 BM3 234567890abc advanced
4068 BM3 234567890abc advanced
4069 BM4 34567890abcd diverged
4069 BM4 34567890abcd diverged
4070 BM5 4567890abcde changed
4070 BM5 4567890abcde changed
4071
4071
4072 The action taken when pushing depends on the
4072 The action taken when pushing depends on the
4073 status of each bookmark:
4073 status of each bookmark:
4074
4074
4075 :``added``: push with ``-B`` will create it
4075 :``added``: push with ``-B`` will create it
4076 :``deleted``: push with ``-B`` will delete it
4076 :``deleted``: push with ``-B`` will delete it
4077 :``advanced``: push will update it
4077 :``advanced``: push will update it
4078 :``diverged``: push with ``-B`` will update it
4078 :``diverged``: push with ``-B`` will update it
4079 :``changed``: push with ``-B`` will update it
4079 :``changed``: push with ``-B`` will update it
4080
4080
4081 From the point of view of pushing behavior, bookmarks
4081 From the point of view of pushing behavior, bookmarks
4082 existing only in the remote repository are treated as
4082 existing only in the remote repository are treated as
4083 ``deleted``, even if it is in fact added remotely.
4083 ``deleted``, even if it is in fact added remotely.
4084
4084
4085 Returns 0 if there are outgoing changes, 1 otherwise.
4085 Returns 0 if there are outgoing changes, 1 otherwise.
4086 """
4086 """
4087 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4087 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4088 # style URLs, so don't overwrite dest.
4088 # style URLs, so don't overwrite dest.
4089 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4089 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4090 if not path:
4090 if not path:
4091 raise error.Abort(_('default repository not configured!'),
4091 raise error.Abort(_('default repository not configured!'),
4092 hint=_("see 'hg help config.paths'"))
4092 hint=_("see 'hg help config.paths'"))
4093
4093
4094 opts = pycompat.byteskwargs(opts)
4094 opts = pycompat.byteskwargs(opts)
4095 if opts.get('graph'):
4095 if opts.get('graph'):
4096 logcmdutil.checkunsupportedgraphflags([], opts)
4096 logcmdutil.checkunsupportedgraphflags([], opts)
4097 o, other = hg._outgoing(ui, repo, dest, opts)
4097 o, other = hg._outgoing(ui, repo, dest, opts)
4098 if not o:
4098 if not o:
4099 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4099 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4100 return
4100 return
4101
4101
4102 revdag = logcmdutil.graphrevs(repo, o, opts)
4102 revdag = logcmdutil.graphrevs(repo, o, opts)
4103 ui.pager('outgoing')
4103 ui.pager('outgoing')
4104 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
4104 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
4105 logcmdutil.displaygraph(ui, repo, revdag, displayer,
4105 logcmdutil.displaygraph(ui, repo, revdag, displayer,
4106 graphmod.asciiedges)
4106 graphmod.asciiedges)
4107 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4107 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4108 return 0
4108 return 0
4109
4109
4110 if opts.get('bookmarks'):
4110 if opts.get('bookmarks'):
4111 dest = path.pushloc or path.loc
4111 dest = path.pushloc or path.loc
4112 other = hg.peer(repo, opts, dest)
4112 other = hg.peer(repo, opts, dest)
4113 if 'bookmarks' not in other.listkeys('namespaces'):
4113 if 'bookmarks' not in other.listkeys('namespaces'):
4114 ui.warn(_("remote doesn't support bookmarks\n"))
4114 ui.warn(_("remote doesn't support bookmarks\n"))
4115 return 0
4115 return 0
4116 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4116 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4117 ui.pager('outgoing')
4117 ui.pager('outgoing')
4118 return bookmarks.outgoing(ui, repo, other)
4118 return bookmarks.outgoing(ui, repo, other)
4119
4119
4120 repo._subtoppath = path.pushloc or path.loc
4120 repo._subtoppath = path.pushloc or path.loc
4121 try:
4121 try:
4122 return hg.outgoing(ui, repo, dest, opts)
4122 return hg.outgoing(ui, repo, dest, opts)
4123 finally:
4123 finally:
4124 del repo._subtoppath
4124 del repo._subtoppath
4125
4125
4126 @command('parents',
4126 @command('parents',
4127 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4127 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4128 ] + templateopts,
4128 ] + templateopts,
4129 _('[-r REV] [FILE]'),
4129 _('[-r REV] [FILE]'),
4130 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4130 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4131 inferrepo=True)
4131 inferrepo=True)
4132 def parents(ui, repo, file_=None, **opts):
4132 def parents(ui, repo, file_=None, **opts):
4133 """show the parents of the working directory or revision (DEPRECATED)
4133 """show the parents of the working directory or revision (DEPRECATED)
4134
4134
4135 Print the working directory's parent revisions. If a revision is
4135 Print the working directory's parent revisions. If a revision is
4136 given via -r/--rev, the parent of that revision will be printed.
4136 given via -r/--rev, the parent of that revision will be printed.
4137 If a file argument is given, the revision in which the file was
4137 If a file argument is given, the revision in which the file was
4138 last changed (before the working directory revision or the
4138 last changed (before the working directory revision or the
4139 argument to --rev if given) is printed.
4139 argument to --rev if given) is printed.
4140
4140
4141 This command is equivalent to::
4141 This command is equivalent to::
4142
4142
4143 hg log -r "p1()+p2()" or
4143 hg log -r "p1()+p2()" or
4144 hg log -r "p1(REV)+p2(REV)" or
4144 hg log -r "p1(REV)+p2(REV)" or
4145 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
4145 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
4146 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
4146 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
4147
4147
4148 See :hg:`summary` and :hg:`help revsets` for related information.
4148 See :hg:`summary` and :hg:`help revsets` for related information.
4149
4149
4150 Returns 0 on success.
4150 Returns 0 on success.
4151 """
4151 """
4152
4152
4153 opts = pycompat.byteskwargs(opts)
4153 opts = pycompat.byteskwargs(opts)
4154 rev = opts.get('rev')
4154 rev = opts.get('rev')
4155 if rev:
4155 if rev:
4156 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
4156 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
4157 ctx = scmutil.revsingle(repo, rev, None)
4157 ctx = scmutil.revsingle(repo, rev, None)
4158
4158
4159 if file_:
4159 if file_:
4160 m = scmutil.match(ctx, (file_,), opts)
4160 m = scmutil.match(ctx, (file_,), opts)
4161 if m.anypats() or len(m.files()) != 1:
4161 if m.anypats() or len(m.files()) != 1:
4162 raise error.Abort(_('can only specify an explicit filename'))
4162 raise error.Abort(_('can only specify an explicit filename'))
4163 file_ = m.files()[0]
4163 file_ = m.files()[0]
4164 filenodes = []
4164 filenodes = []
4165 for cp in ctx.parents():
4165 for cp in ctx.parents():
4166 if not cp:
4166 if not cp:
4167 continue
4167 continue
4168 try:
4168 try:
4169 filenodes.append(cp.filenode(file_))
4169 filenodes.append(cp.filenode(file_))
4170 except error.LookupError:
4170 except error.LookupError:
4171 pass
4171 pass
4172 if not filenodes:
4172 if not filenodes:
4173 raise error.Abort(_("'%s' not found in manifest!") % file_)
4173 raise error.Abort(_("'%s' not found in manifest!") % file_)
4174 p = []
4174 p = []
4175 for fn in filenodes:
4175 for fn in filenodes:
4176 fctx = repo.filectx(file_, fileid=fn)
4176 fctx = repo.filectx(file_, fileid=fn)
4177 p.append(fctx.node())
4177 p.append(fctx.node())
4178 else:
4178 else:
4179 p = [cp.node() for cp in ctx.parents()]
4179 p = [cp.node() for cp in ctx.parents()]
4180
4180
4181 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4181 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4182 for n in p:
4182 for n in p:
4183 if n != nullid:
4183 if n != nullid:
4184 displayer.show(repo[n])
4184 displayer.show(repo[n])
4185 displayer.close()
4185 displayer.close()
4186
4186
4187 @command('paths', formatteropts, _('[NAME]'),
4187 @command('paths', formatteropts, _('[NAME]'),
4188 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4188 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4189 optionalrepo=True, intents={INTENT_READONLY})
4189 optionalrepo=True, intents={INTENT_READONLY})
4190 def paths(ui, repo, search=None, **opts):
4190 def paths(ui, repo, search=None, **opts):
4191 """show aliases for remote repositories
4191 """show aliases for remote repositories
4192
4192
4193 Show definition of symbolic path name NAME. If no name is given,
4193 Show definition of symbolic path name NAME. If no name is given,
4194 show definition of all available names.
4194 show definition of all available names.
4195
4195
4196 Option -q/--quiet suppresses all output when searching for NAME
4196 Option -q/--quiet suppresses all output when searching for NAME
4197 and shows only the path names when listing all definitions.
4197 and shows only the path names when listing all definitions.
4198
4198
4199 Path names are defined in the [paths] section of your
4199 Path names are defined in the [paths] section of your
4200 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4200 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4201 repository, ``.hg/hgrc`` is used, too.
4201 repository, ``.hg/hgrc`` is used, too.
4202
4202
4203 The path names ``default`` and ``default-push`` have a special
4203 The path names ``default`` and ``default-push`` have a special
4204 meaning. When performing a push or pull operation, they are used
4204 meaning. When performing a push or pull operation, they are used
4205 as fallbacks if no location is specified on the command-line.
4205 as fallbacks if no location is specified on the command-line.
4206 When ``default-push`` is set, it will be used for push and
4206 When ``default-push`` is set, it will be used for push and
4207 ``default`` will be used for pull; otherwise ``default`` is used
4207 ``default`` will be used for pull; otherwise ``default`` is used
4208 as the fallback for both. When cloning a repository, the clone
4208 as the fallback for both. When cloning a repository, the clone
4209 source is written as ``default`` in ``.hg/hgrc``.
4209 source is written as ``default`` in ``.hg/hgrc``.
4210
4210
4211 .. note::
4211 .. note::
4212
4212
4213 ``default`` and ``default-push`` apply to all inbound (e.g.
4213 ``default`` and ``default-push`` apply to all inbound (e.g.
4214 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
4214 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
4215 and :hg:`bundle`) operations.
4215 and :hg:`bundle`) operations.
4216
4216
4217 See :hg:`help urls` for more information.
4217 See :hg:`help urls` for more information.
4218
4218
4219 .. container:: verbose
4219 .. container:: verbose
4220
4220
4221 Template:
4221 Template:
4222
4222
4223 The following keywords are supported. See also :hg:`help templates`.
4223 The following keywords are supported. See also :hg:`help templates`.
4224
4224
4225 :name: String. Symbolic name of the path alias.
4225 :name: String. Symbolic name of the path alias.
4226 :pushurl: String. URL for push operations.
4226 :pushurl: String. URL for push operations.
4227 :url: String. URL or directory path for the other operations.
4227 :url: String. URL or directory path for the other operations.
4228
4228
4229 Returns 0 on success.
4229 Returns 0 on success.
4230 """
4230 """
4231
4231
4232 opts = pycompat.byteskwargs(opts)
4232 opts = pycompat.byteskwargs(opts)
4233 ui.pager('paths')
4233 ui.pager('paths')
4234 if search:
4234 if search:
4235 pathitems = [(name, path) for name, path in ui.paths.iteritems()
4235 pathitems = [(name, path) for name, path in ui.paths.iteritems()
4236 if name == search]
4236 if name == search]
4237 else:
4237 else:
4238 pathitems = sorted(ui.paths.iteritems())
4238 pathitems = sorted(ui.paths.iteritems())
4239
4239
4240 fm = ui.formatter('paths', opts)
4240 fm = ui.formatter('paths', opts)
4241 if fm.isplain():
4241 if fm.isplain():
4242 hidepassword = util.hidepassword
4242 hidepassword = util.hidepassword
4243 else:
4243 else:
4244 hidepassword = bytes
4244 hidepassword = bytes
4245 if ui.quiet:
4245 if ui.quiet:
4246 namefmt = '%s\n'
4246 namefmt = '%s\n'
4247 else:
4247 else:
4248 namefmt = '%s = '
4248 namefmt = '%s = '
4249 showsubopts = not search and not ui.quiet
4249 showsubopts = not search and not ui.quiet
4250
4250
4251 for name, path in pathitems:
4251 for name, path in pathitems:
4252 fm.startitem()
4252 fm.startitem()
4253 fm.condwrite(not search, 'name', namefmt, name)
4253 fm.condwrite(not search, 'name', namefmt, name)
4254 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
4254 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
4255 for subopt, value in sorted(path.suboptions.items()):
4255 for subopt, value in sorted(path.suboptions.items()):
4256 assert subopt not in ('name', 'url')
4256 assert subopt not in ('name', 'url')
4257 if showsubopts:
4257 if showsubopts:
4258 fm.plain('%s:%s = ' % (name, subopt))
4258 fm.plain('%s:%s = ' % (name, subopt))
4259 fm.condwrite(showsubopts, subopt, '%s\n', value)
4259 fm.condwrite(showsubopts, subopt, '%s\n', value)
4260
4260
4261 fm.end()
4261 fm.end()
4262
4262
4263 if search and not pathitems:
4263 if search and not pathitems:
4264 if not ui.quiet:
4264 if not ui.quiet:
4265 ui.warn(_("not found!\n"))
4265 ui.warn(_("not found!\n"))
4266 return 1
4266 return 1
4267 else:
4267 else:
4268 return 0
4268 return 0
4269
4269
4270 @command('phase',
4270 @command('phase',
4271 [('p', 'public', False, _('set changeset phase to public')),
4271 [('p', 'public', False, _('set changeset phase to public')),
4272 ('d', 'draft', False, _('set changeset phase to draft')),
4272 ('d', 'draft', False, _('set changeset phase to draft')),
4273 ('s', 'secret', False, _('set changeset phase to secret')),
4273 ('s', 'secret', False, _('set changeset phase to secret')),
4274 ('f', 'force', False, _('allow to move boundary backward')),
4274 ('f', 'force', False, _('allow to move boundary backward')),
4275 ('r', 'rev', [], _('target revision'), _('REV')),
4275 ('r', 'rev', [], _('target revision'), _('REV')),
4276 ],
4276 ],
4277 _('[-p|-d|-s] [-f] [-r] [REV...]'),
4277 _('[-p|-d|-s] [-f] [-r] [REV...]'),
4278 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
4278 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
4279 def phase(ui, repo, *revs, **opts):
4279 def phase(ui, repo, *revs, **opts):
4280 """set or show the current phase name
4280 """set or show the current phase name
4281
4281
4282 With no argument, show the phase name of the current revision(s).
4282 With no argument, show the phase name of the current revision(s).
4283
4283
4284 With one of -p/--public, -d/--draft or -s/--secret, change the
4284 With one of -p/--public, -d/--draft or -s/--secret, change the
4285 phase value of the specified revisions.
4285 phase value of the specified revisions.
4286
4286
4287 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
4287 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
4288 lower phase to a higher phase. Phases are ordered as follows::
4288 lower phase to a higher phase. Phases are ordered as follows::
4289
4289
4290 public < draft < secret
4290 public < draft < secret
4291
4291
4292 Returns 0 on success, 1 if some phases could not be changed.
4292 Returns 0 on success, 1 if some phases could not be changed.
4293
4293
4294 (For more information about the phases concept, see :hg:`help phases`.)
4294 (For more information about the phases concept, see :hg:`help phases`.)
4295 """
4295 """
4296 opts = pycompat.byteskwargs(opts)
4296 opts = pycompat.byteskwargs(opts)
4297 # search for a unique phase argument
4297 # search for a unique phase argument
4298 targetphase = None
4298 targetphase = None
4299 for idx, name in enumerate(phases.cmdphasenames):
4299 for idx, name in enumerate(phases.cmdphasenames):
4300 if opts[name]:
4300 if opts[name]:
4301 if targetphase is not None:
4301 if targetphase is not None:
4302 raise error.Abort(_('only one phase can be specified'))
4302 raise error.Abort(_('only one phase can be specified'))
4303 targetphase = idx
4303 targetphase = idx
4304
4304
4305 # look for specified revision
4305 # look for specified revision
4306 revs = list(revs)
4306 revs = list(revs)
4307 revs.extend(opts['rev'])
4307 revs.extend(opts['rev'])
4308 if not revs:
4308 if not revs:
4309 # display both parents as the second parent phase can influence
4309 # display both parents as the second parent phase can influence
4310 # the phase of a merge commit
4310 # the phase of a merge commit
4311 revs = [c.rev() for c in repo[None].parents()]
4311 revs = [c.rev() for c in repo[None].parents()]
4312
4312
4313 revs = scmutil.revrange(repo, revs)
4313 revs = scmutil.revrange(repo, revs)
4314
4314
4315 ret = 0
4315 ret = 0
4316 if targetphase is None:
4316 if targetphase is None:
4317 # display
4317 # display
4318 for r in revs:
4318 for r in revs:
4319 ctx = repo[r]
4319 ctx = repo[r]
4320 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4320 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4321 else:
4321 else:
4322 with repo.lock(), repo.transaction("phase") as tr:
4322 with repo.lock(), repo.transaction("phase") as tr:
4323 # set phase
4323 # set phase
4324 if not revs:
4324 if not revs:
4325 raise error.Abort(_('empty revision set'))
4325 raise error.Abort(_('empty revision set'))
4326 nodes = [repo[r].node() for r in revs]
4326 nodes = [repo[r].node() for r in revs]
4327 # moving revision from public to draft may hide them
4327 # moving revision from public to draft may hide them
4328 # We have to check result on an unfiltered repository
4328 # We have to check result on an unfiltered repository
4329 unfi = repo.unfiltered()
4329 unfi = repo.unfiltered()
4330 getphase = unfi._phasecache.phase
4330 getphase = unfi._phasecache.phase
4331 olddata = [getphase(unfi, r) for r in unfi]
4331 olddata = [getphase(unfi, r) for r in unfi]
4332 phases.advanceboundary(repo, tr, targetphase, nodes)
4332 phases.advanceboundary(repo, tr, targetphase, nodes)
4333 if opts['force']:
4333 if opts['force']:
4334 phases.retractboundary(repo, tr, targetphase, nodes)
4334 phases.retractboundary(repo, tr, targetphase, nodes)
4335 getphase = unfi._phasecache.phase
4335 getphase = unfi._phasecache.phase
4336 newdata = [getphase(unfi, r) for r in unfi]
4336 newdata = [getphase(unfi, r) for r in unfi]
4337 changes = sum(newdata[r] != olddata[r] for r in unfi)
4337 changes = sum(newdata[r] != olddata[r] for r in unfi)
4338 cl = unfi.changelog
4338 cl = unfi.changelog
4339 rejected = [n for n in nodes
4339 rejected = [n for n in nodes
4340 if newdata[cl.rev(n)] < targetphase]
4340 if newdata[cl.rev(n)] < targetphase]
4341 if rejected:
4341 if rejected:
4342 ui.warn(_('cannot move %i changesets to a higher '
4342 ui.warn(_('cannot move %i changesets to a higher '
4343 'phase, use --force\n') % len(rejected))
4343 'phase, use --force\n') % len(rejected))
4344 ret = 1
4344 ret = 1
4345 if changes:
4345 if changes:
4346 msg = _('phase changed for %i changesets\n') % changes
4346 msg = _('phase changed for %i changesets\n') % changes
4347 if ret:
4347 if ret:
4348 ui.status(msg)
4348 ui.status(msg)
4349 else:
4349 else:
4350 ui.note(msg)
4350 ui.note(msg)
4351 else:
4351 else:
4352 ui.warn(_('no phases changed\n'))
4352 ui.warn(_('no phases changed\n'))
4353 return ret
4353 return ret
4354
4354
4355 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
4355 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
4356 """Run after a changegroup has been added via pull/unbundle
4356 """Run after a changegroup has been added via pull/unbundle
4357
4357
4358 This takes arguments below:
4358 This takes arguments below:
4359
4359
4360 :modheads: change of heads by pull/unbundle
4360 :modheads: change of heads by pull/unbundle
4361 :optupdate: updating working directory is needed or not
4361 :optupdate: updating working directory is needed or not
4362 :checkout: update destination revision (or None to default destination)
4362 :checkout: update destination revision (or None to default destination)
4363 :brev: a name, which might be a bookmark to be activated after updating
4363 :brev: a name, which might be a bookmark to be activated after updating
4364 """
4364 """
4365 if modheads == 0:
4365 if modheads == 0:
4366 return
4366 return
4367 if optupdate:
4367 if optupdate:
4368 try:
4368 try:
4369 return hg.updatetotally(ui, repo, checkout, brev)
4369 return hg.updatetotally(ui, repo, checkout, brev)
4370 except error.UpdateAbort as inst:
4370 except error.UpdateAbort as inst:
4371 msg = _("not updating: %s") % stringutil.forcebytestr(inst)
4371 msg = _("not updating: %s") % stringutil.forcebytestr(inst)
4372 hint = inst.hint
4372 hint = inst.hint
4373 raise error.UpdateAbort(msg, hint=hint)
4373 raise error.UpdateAbort(msg, hint=hint)
4374 if modheads is not None and modheads > 1:
4374 if modheads is not None and modheads > 1:
4375 currentbranchheads = len(repo.branchheads())
4375 currentbranchheads = len(repo.branchheads())
4376 if currentbranchheads == modheads:
4376 if currentbranchheads == modheads:
4377 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4377 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4378 elif currentbranchheads > 1:
4378 elif currentbranchheads > 1:
4379 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4379 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4380 "merge)\n"))
4380 "merge)\n"))
4381 else:
4381 else:
4382 ui.status(_("(run 'hg heads' to see heads)\n"))
4382 ui.status(_("(run 'hg heads' to see heads)\n"))
4383 elif not ui.configbool('commands', 'update.requiredest'):
4383 elif not ui.configbool('commands', 'update.requiredest'):
4384 ui.status(_("(run 'hg update' to get a working copy)\n"))
4384 ui.status(_("(run 'hg update' to get a working copy)\n"))
4385
4385
4386 @command('pull',
4386 @command('pull',
4387 [('u', 'update', None,
4387 [('u', 'update', None,
4388 _('update to new branch head if new descendants were pulled')),
4388 _('update to new branch head if new descendants were pulled')),
4389 ('f', 'force', None, _('run even when remote repository is unrelated')),
4389 ('f', 'force', None, _('run even when remote repository is unrelated')),
4390 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4390 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4391 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4391 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4392 ('b', 'branch', [], _('a specific branch you would like to pull'),
4392 ('b', 'branch', [], _('a specific branch you would like to pull'),
4393 _('BRANCH')),
4393 _('BRANCH')),
4394 ] + remoteopts,
4394 ] + remoteopts,
4395 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
4395 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
4396 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4396 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4397 helpbasic=True)
4397 helpbasic=True)
4398 def pull(ui, repo, source="default", **opts):
4398 def pull(ui, repo, source="default", **opts):
4399 """pull changes from the specified source
4399 """pull changes from the specified source
4400
4400
4401 Pull changes from a remote repository to a local one.
4401 Pull changes from a remote repository to a local one.
4402
4402
4403 This finds all changes from the repository at the specified path
4403 This finds all changes from the repository at the specified path
4404 or URL and adds them to a local repository (the current one unless
4404 or URL and adds them to a local repository (the current one unless
4405 -R is specified). By default, this does not update the copy of the
4405 -R is specified). By default, this does not update the copy of the
4406 project in the working directory.
4406 project in the working directory.
4407
4407
4408 When cloning from servers that support it, Mercurial may fetch
4408 When cloning from servers that support it, Mercurial may fetch
4409 pre-generated data. When this is done, hooks operating on incoming
4409 pre-generated data. When this is done, hooks operating on incoming
4410 changesets and changegroups may fire more than once, once for each
4410 changesets and changegroups may fire more than once, once for each
4411 pre-generated bundle and as well as for any additional remaining
4411 pre-generated bundle and as well as for any additional remaining
4412 data. See :hg:`help -e clonebundles` for more.
4412 data. See :hg:`help -e clonebundles` for more.
4413
4413
4414 Use :hg:`incoming` if you want to see what would have been added
4414 Use :hg:`incoming` if you want to see what would have been added
4415 by a pull at the time you issued this command. If you then decide
4415 by a pull at the time you issued this command. If you then decide
4416 to add those changes to the repository, you should use :hg:`pull
4416 to add those changes to the repository, you should use :hg:`pull
4417 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4417 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4418
4418
4419 If SOURCE is omitted, the 'default' path will be used.
4419 If SOURCE is omitted, the 'default' path will be used.
4420 See :hg:`help urls` for more information.
4420 See :hg:`help urls` for more information.
4421
4421
4422 Specifying bookmark as ``.`` is equivalent to specifying the active
4422 Specifying bookmark as ``.`` is equivalent to specifying the active
4423 bookmark's name.
4423 bookmark's name.
4424
4424
4425 Returns 0 on success, 1 if an update had unresolved files.
4425 Returns 0 on success, 1 if an update had unresolved files.
4426 """
4426 """
4427
4427
4428 opts = pycompat.byteskwargs(opts)
4428 opts = pycompat.byteskwargs(opts)
4429 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
4429 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
4430 msg = _('update destination required by configuration')
4430 msg = _('update destination required by configuration')
4431 hint = _('use hg pull followed by hg update DEST')
4431 hint = _('use hg pull followed by hg update DEST')
4432 raise error.Abort(msg, hint=hint)
4432 raise error.Abort(msg, hint=hint)
4433
4433
4434 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4434 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4435 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4435 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4436 other = hg.peer(repo, opts, source)
4436 other = hg.peer(repo, opts, source)
4437 try:
4437 try:
4438 revs, checkout = hg.addbranchrevs(repo, other, branches,
4438 revs, checkout = hg.addbranchrevs(repo, other, branches,
4439 opts.get('rev'))
4439 opts.get('rev'))
4440
4440
4441 pullopargs = {}
4441 pullopargs = {}
4442
4442
4443 nodes = None
4443 nodes = None
4444 if opts.get('bookmark') or revs:
4444 if opts.get('bookmark') or revs:
4445 # The list of bookmark used here is the same used to actually update
4445 # The list of bookmark used here is the same used to actually update
4446 # the bookmark names, to avoid the race from issue 4689 and we do
4446 # the bookmark names, to avoid the race from issue 4689 and we do
4447 # all lookup and bookmark queries in one go so they see the same
4447 # all lookup and bookmark queries in one go so they see the same
4448 # version of the server state (issue 4700).
4448 # version of the server state (issue 4700).
4449 nodes = []
4449 nodes = []
4450 fnodes = []
4450 fnodes = []
4451 revs = revs or []
4451 revs = revs or []
4452 if revs and not other.capable('lookup'):
4452 if revs and not other.capable('lookup'):
4453 err = _("other repository doesn't support revision lookup, "
4453 err = _("other repository doesn't support revision lookup, "
4454 "so a rev cannot be specified.")
4454 "so a rev cannot be specified.")
4455 raise error.Abort(err)
4455 raise error.Abort(err)
4456 with other.commandexecutor() as e:
4456 with other.commandexecutor() as e:
4457 fremotebookmarks = e.callcommand('listkeys', {
4457 fremotebookmarks = e.callcommand('listkeys', {
4458 'namespace': 'bookmarks'
4458 'namespace': 'bookmarks'
4459 })
4459 })
4460 for r in revs:
4460 for r in revs:
4461 fnodes.append(e.callcommand('lookup', {'key': r}))
4461 fnodes.append(e.callcommand('lookup', {'key': r}))
4462 remotebookmarks = fremotebookmarks.result()
4462 remotebookmarks = fremotebookmarks.result()
4463 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
4463 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
4464 pullopargs['remotebookmarks'] = remotebookmarks
4464 pullopargs['remotebookmarks'] = remotebookmarks
4465 for b in opts.get('bookmark', []):
4465 for b in opts.get('bookmark', []):
4466 b = repo._bookmarks.expandname(b)
4466 b = repo._bookmarks.expandname(b)
4467 if b not in remotebookmarks:
4467 if b not in remotebookmarks:
4468 raise error.Abort(_('remote bookmark %s not found!') % b)
4468 raise error.Abort(_('remote bookmark %s not found!') % b)
4469 nodes.append(remotebookmarks[b])
4469 nodes.append(remotebookmarks[b])
4470 for i, rev in enumerate(revs):
4470 for i, rev in enumerate(revs):
4471 node = fnodes[i].result()
4471 node = fnodes[i].result()
4472 nodes.append(node)
4472 nodes.append(node)
4473 if rev == checkout:
4473 if rev == checkout:
4474 checkout = node
4474 checkout = node
4475
4475
4476 wlock = util.nullcontextmanager()
4476 wlock = util.nullcontextmanager()
4477 if opts.get('update'):
4477 if opts.get('update'):
4478 wlock = repo.wlock()
4478 wlock = repo.wlock()
4479 with wlock:
4479 with wlock:
4480 pullopargs.update(opts.get('opargs', {}))
4480 pullopargs.update(opts.get('opargs', {}))
4481 modheads = exchange.pull(repo, other, heads=nodes,
4481 modheads = exchange.pull(repo, other, heads=nodes,
4482 force=opts.get('force'),
4482 force=opts.get('force'),
4483 bookmarks=opts.get('bookmark', ()),
4483 bookmarks=opts.get('bookmark', ()),
4484 opargs=pullopargs).cgresult
4484 opargs=pullopargs).cgresult
4485
4485
4486 # brev is a name, which might be a bookmark to be activated at
4486 # brev is a name, which might be a bookmark to be activated at
4487 # the end of the update. In other words, it is an explicit
4487 # the end of the update. In other words, it is an explicit
4488 # destination of the update
4488 # destination of the update
4489 brev = None
4489 brev = None
4490
4490
4491 if checkout:
4491 if checkout:
4492 checkout = repo.unfiltered().changelog.rev(checkout)
4492 checkout = repo.unfiltered().changelog.rev(checkout)
4493
4493
4494 # order below depends on implementation of
4494 # order below depends on implementation of
4495 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4495 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4496 # because 'checkout' is determined without it.
4496 # because 'checkout' is determined without it.
4497 if opts.get('rev'):
4497 if opts.get('rev'):
4498 brev = opts['rev'][0]
4498 brev = opts['rev'][0]
4499 elif opts.get('branch'):
4499 elif opts.get('branch'):
4500 brev = opts['branch'][0]
4500 brev = opts['branch'][0]
4501 else:
4501 else:
4502 brev = branches[0]
4502 brev = branches[0]
4503 repo._subtoppath = source
4503 repo._subtoppath = source
4504 try:
4504 try:
4505 ret = postincoming(ui, repo, modheads, opts.get('update'),
4505 ret = postincoming(ui, repo, modheads, opts.get('update'),
4506 checkout, brev)
4506 checkout, brev)
4507 except error.FilteredRepoLookupError as exc:
4507 except error.FilteredRepoLookupError as exc:
4508 msg = _('cannot update to target: %s') % exc.args[0]
4508 msg = _('cannot update to target: %s') % exc.args[0]
4509 exc.args = (msg,) + exc.args[1:]
4509 exc.args = (msg,) + exc.args[1:]
4510 raise
4510 raise
4511 finally:
4511 finally:
4512 del repo._subtoppath
4512 del repo._subtoppath
4513
4513
4514 finally:
4514 finally:
4515 other.close()
4515 other.close()
4516 return ret
4516 return ret
4517
4517
4518 @command('push',
4518 @command('push',
4519 [('f', 'force', None, _('force push')),
4519 [('f', 'force', None, _('force push')),
4520 ('r', 'rev', [],
4520 ('r', 'rev', [],
4521 _('a changeset intended to be included in the destination'),
4521 _('a changeset intended to be included in the destination'),
4522 _('REV')),
4522 _('REV')),
4523 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4523 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4524 ('b', 'branch', [],
4524 ('b', 'branch', [],
4525 _('a specific branch you would like to push'), _('BRANCH')),
4525 _('a specific branch you would like to push'), _('BRANCH')),
4526 ('', 'new-branch', False, _('allow pushing a new branch')),
4526 ('', 'new-branch', False, _('allow pushing a new branch')),
4527 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4527 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4528 ('', 'publish', False, _('push the changeset as public (EXPERIMENTAL)')),
4528 ('', 'publish', False, _('push the changeset as public (EXPERIMENTAL)')),
4529 ] + remoteopts,
4529 ] + remoteopts,
4530 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
4530 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
4531 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4531 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4532 helpbasic=True)
4532 helpbasic=True)
4533 def push(ui, repo, dest=None, **opts):
4533 def push(ui, repo, dest=None, **opts):
4534 """push changes to the specified destination
4534 """push changes to the specified destination
4535
4535
4536 Push changesets from the local repository to the specified
4536 Push changesets from the local repository to the specified
4537 destination.
4537 destination.
4538
4538
4539 This operation is symmetrical to pull: it is identical to a pull
4539 This operation is symmetrical to pull: it is identical to a pull
4540 in the destination repository from the current one.
4540 in the destination repository from the current one.
4541
4541
4542 By default, push will not allow creation of new heads at the
4542 By default, push will not allow creation of new heads at the
4543 destination, since multiple heads would make it unclear which head
4543 destination, since multiple heads would make it unclear which head
4544 to use. In this situation, it is recommended to pull and merge
4544 to use. In this situation, it is recommended to pull and merge
4545 before pushing.
4545 before pushing.
4546
4546
4547 Use --new-branch if you want to allow push to create a new named
4547 Use --new-branch if you want to allow push to create a new named
4548 branch that is not present at the destination. This allows you to
4548 branch that is not present at the destination. This allows you to
4549 only create a new branch without forcing other changes.
4549 only create a new branch without forcing other changes.
4550
4550
4551 .. note::
4551 .. note::
4552
4552
4553 Extra care should be taken with the -f/--force option,
4553 Extra care should be taken with the -f/--force option,
4554 which will push all new heads on all branches, an action which will
4554 which will push all new heads on all branches, an action which will
4555 almost always cause confusion for collaborators.
4555 almost always cause confusion for collaborators.
4556
4556
4557 If -r/--rev is used, the specified revision and all its ancestors
4557 If -r/--rev is used, the specified revision and all its ancestors
4558 will be pushed to the remote repository.
4558 will be pushed to the remote repository.
4559
4559
4560 If -B/--bookmark is used, the specified bookmarked revision, its
4560 If -B/--bookmark is used, the specified bookmarked revision, its
4561 ancestors, and the bookmark will be pushed to the remote
4561 ancestors, and the bookmark will be pushed to the remote
4562 repository. Specifying ``.`` is equivalent to specifying the active
4562 repository. Specifying ``.`` is equivalent to specifying the active
4563 bookmark's name.
4563 bookmark's name.
4564
4564
4565 Please see :hg:`help urls` for important details about ``ssh://``
4565 Please see :hg:`help urls` for important details about ``ssh://``
4566 URLs. If DESTINATION is omitted, a default path will be used.
4566 URLs. If DESTINATION is omitted, a default path will be used.
4567
4567
4568 .. container:: verbose
4568 .. container:: verbose
4569
4569
4570 The --pushvars option sends strings to the server that become
4570 The --pushvars option sends strings to the server that become
4571 environment variables prepended with ``HG_USERVAR_``. For example,
4571 environment variables prepended with ``HG_USERVAR_``. For example,
4572 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4572 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4573 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4573 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4574
4574
4575 pushvars can provide for user-overridable hooks as well as set debug
4575 pushvars can provide for user-overridable hooks as well as set debug
4576 levels. One example is having a hook that blocks commits containing
4576 levels. One example is having a hook that blocks commits containing
4577 conflict markers, but enables the user to override the hook if the file
4577 conflict markers, but enables the user to override the hook if the file
4578 is using conflict markers for testing purposes or the file format has
4578 is using conflict markers for testing purposes or the file format has
4579 strings that look like conflict markers.
4579 strings that look like conflict markers.
4580
4580
4581 By default, servers will ignore `--pushvars`. To enable it add the
4581 By default, servers will ignore `--pushvars`. To enable it add the
4582 following to your configuration file::
4582 following to your configuration file::
4583
4583
4584 [push]
4584 [push]
4585 pushvars.server = true
4585 pushvars.server = true
4586
4586
4587 Returns 0 if push was successful, 1 if nothing to push.
4587 Returns 0 if push was successful, 1 if nothing to push.
4588 """
4588 """
4589
4589
4590 opts = pycompat.byteskwargs(opts)
4590 opts = pycompat.byteskwargs(opts)
4591 if opts.get('bookmark'):
4591 if opts.get('bookmark'):
4592 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4592 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4593 for b in opts['bookmark']:
4593 for b in opts['bookmark']:
4594 # translate -B options to -r so changesets get pushed
4594 # translate -B options to -r so changesets get pushed
4595 b = repo._bookmarks.expandname(b)
4595 b = repo._bookmarks.expandname(b)
4596 if b in repo._bookmarks:
4596 if b in repo._bookmarks:
4597 opts.setdefault('rev', []).append(b)
4597 opts.setdefault('rev', []).append(b)
4598 else:
4598 else:
4599 # if we try to push a deleted bookmark, translate it to null
4599 # if we try to push a deleted bookmark, translate it to null
4600 # this lets simultaneous -r, -b options continue working
4600 # this lets simultaneous -r, -b options continue working
4601 opts.setdefault('rev', []).append("null")
4601 opts.setdefault('rev', []).append("null")
4602
4602
4603 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4603 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4604 if not path:
4604 if not path:
4605 raise error.Abort(_('default repository not configured!'),
4605 raise error.Abort(_('default repository not configured!'),
4606 hint=_("see 'hg help config.paths'"))
4606 hint=_("see 'hg help config.paths'"))
4607 dest = path.pushloc or path.loc
4607 dest = path.pushloc or path.loc
4608 branches = (path.branch, opts.get('branch') or [])
4608 branches = (path.branch, opts.get('branch') or [])
4609 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4609 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4610 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4610 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4611 other = hg.peer(repo, opts, dest)
4611 other = hg.peer(repo, opts, dest)
4612
4612
4613 if revs:
4613 if revs:
4614 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
4614 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
4615 if not revs:
4615 if not revs:
4616 raise error.Abort(_("specified revisions evaluate to an empty set"),
4616 raise error.Abort(_("specified revisions evaluate to an empty set"),
4617 hint=_("use different revision arguments"))
4617 hint=_("use different revision arguments"))
4618 elif path.pushrev:
4618 elif path.pushrev:
4619 # It doesn't make any sense to specify ancestor revisions. So limit
4619 # It doesn't make any sense to specify ancestor revisions. So limit
4620 # to DAG heads to make discovery simpler.
4620 # to DAG heads to make discovery simpler.
4621 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4621 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4622 revs = scmutil.revrange(repo, [expr])
4622 revs = scmutil.revrange(repo, [expr])
4623 revs = [repo[rev].node() for rev in revs]
4623 revs = [repo[rev].node() for rev in revs]
4624 if not revs:
4624 if not revs:
4625 raise error.Abort(_('default push revset for path evaluates to an '
4625 raise error.Abort(_('default push revset for path evaluates to an '
4626 'empty set'))
4626 'empty set'))
4627
4627
4628 repo._subtoppath = dest
4628 repo._subtoppath = dest
4629 try:
4629 try:
4630 # push subrepos depth-first for coherent ordering
4630 # push subrepos depth-first for coherent ordering
4631 c = repo['.']
4631 c = repo['.']
4632 subs = c.substate # only repos that are committed
4632 subs = c.substate # only repos that are committed
4633 for s in sorted(subs):
4633 for s in sorted(subs):
4634 result = c.sub(s).push(opts)
4634 result = c.sub(s).push(opts)
4635 if result == 0:
4635 if result == 0:
4636 return not result
4636 return not result
4637 finally:
4637 finally:
4638 del repo._subtoppath
4638 del repo._subtoppath
4639
4639
4640 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4640 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4641 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4641 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4642
4642
4643 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4643 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4644 newbranch=opts.get('new_branch'),
4644 newbranch=opts.get('new_branch'),
4645 bookmarks=opts.get('bookmark', ()),
4645 bookmarks=opts.get('bookmark', ()),
4646 publish=opts.get('publish'),
4646 publish=opts.get('publish'),
4647 opargs=opargs)
4647 opargs=opargs)
4648
4648
4649 result = not pushop.cgresult
4649 result = not pushop.cgresult
4650
4650
4651 if pushop.bkresult is not None:
4651 if pushop.bkresult is not None:
4652 if pushop.bkresult == 2:
4652 if pushop.bkresult == 2:
4653 result = 2
4653 result = 2
4654 elif not result and pushop.bkresult:
4654 elif not result and pushop.bkresult:
4655 result = 2
4655 result = 2
4656
4656
4657 return result
4657 return result
4658
4658
4659 @command('recover',
4659 @command('recover',
4660 [('','verify', True, "run `hg verify` after succesful recover"),
4660 [('','verify', True, "run `hg verify` after succesful recover"),
4661 ],
4661 ],
4662 helpcategory=command.CATEGORY_MAINTENANCE)
4662 helpcategory=command.CATEGORY_MAINTENANCE)
4663 def recover(ui, repo, **opts):
4663 def recover(ui, repo, **opts):
4664 """roll back an interrupted transaction
4664 """roll back an interrupted transaction
4665
4665
4666 Recover from an interrupted commit or pull.
4666 Recover from an interrupted commit or pull.
4667
4667
4668 This command tries to fix the repository status after an
4668 This command tries to fix the repository status after an
4669 interrupted operation. It should only be necessary when Mercurial
4669 interrupted operation. It should only be necessary when Mercurial
4670 suggests it.
4670 suggests it.
4671
4671
4672 Returns 0 if successful, 1 if nothing to recover or verify fails.
4672 Returns 0 if successful, 1 if nothing to recover or verify fails.
4673 """
4673 """
4674 ret = repo.recover()
4674 ret = repo.recover()
4675 if ret:
4675 if ret:
4676 if opts['verify']:
4676 if opts['verify']:
4677 return hg.verify(repo)
4677 return hg.verify(repo)
4678 else:
4678 else:
4679 msg = _("(verify step skipped, run `hg verify` to check your "
4679 msg = _("(verify step skipped, run `hg verify` to check your "
4680 "repository content)\n")
4680 "repository content)\n")
4681 ui.warn(msg)
4681 ui.warn(msg)
4682 return 0
4682 return 0
4683 return 1
4683 return 1
4684
4684
4685 @command('remove|rm',
4685 @command('remove|rm',
4686 [('A', 'after', None, _('record delete for missing files')),
4686 [('A', 'after', None, _('record delete for missing files')),
4687 ('f', 'force', None,
4687 ('f', 'force', None,
4688 _('forget added files, delete modified files')),
4688 _('forget added files, delete modified files')),
4689 ] + subrepoopts + walkopts + dryrunopts,
4689 ] + subrepoopts + walkopts + dryrunopts,
4690 _('[OPTION]... FILE...'),
4690 _('[OPTION]... FILE...'),
4691 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4691 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4692 helpbasic=True, inferrepo=True)
4692 helpbasic=True, inferrepo=True)
4693 def remove(ui, repo, *pats, **opts):
4693 def remove(ui, repo, *pats, **opts):
4694 """remove the specified files on the next commit
4694 """remove the specified files on the next commit
4695
4695
4696 Schedule the indicated files for removal from the current branch.
4696 Schedule the indicated files for removal from the current branch.
4697
4697
4698 This command schedules the files to be removed at the next commit.
4698 This command schedules the files to be removed at the next commit.
4699 To undo a remove before that, see :hg:`revert`. To undo added
4699 To undo a remove before that, see :hg:`revert`. To undo added
4700 files, see :hg:`forget`.
4700 files, see :hg:`forget`.
4701
4701
4702 .. container:: verbose
4702 .. container:: verbose
4703
4703
4704 -A/--after can be used to remove only files that have already
4704 -A/--after can be used to remove only files that have already
4705 been deleted, -f/--force can be used to force deletion, and -Af
4705 been deleted, -f/--force can be used to force deletion, and -Af
4706 can be used to remove files from the next revision without
4706 can be used to remove files from the next revision without
4707 deleting them from the working directory.
4707 deleting them from the working directory.
4708
4708
4709 The following table details the behavior of remove for different
4709 The following table details the behavior of remove for different
4710 file states (columns) and option combinations (rows). The file
4710 file states (columns) and option combinations (rows). The file
4711 states are Added [A], Clean [C], Modified [M] and Missing [!]
4711 states are Added [A], Clean [C], Modified [M] and Missing [!]
4712 (as reported by :hg:`status`). The actions are Warn, Remove
4712 (as reported by :hg:`status`). The actions are Warn, Remove
4713 (from branch) and Delete (from disk):
4713 (from branch) and Delete (from disk):
4714
4714
4715 ========= == == == ==
4715 ========= == == == ==
4716 opt/state A C M !
4716 opt/state A C M !
4717 ========= == == == ==
4717 ========= == == == ==
4718 none W RD W R
4718 none W RD W R
4719 -f R RD RD R
4719 -f R RD RD R
4720 -A W W W R
4720 -A W W W R
4721 -Af R R R R
4721 -Af R R R R
4722 ========= == == == ==
4722 ========= == == == ==
4723
4723
4724 .. note::
4724 .. note::
4725
4725
4726 :hg:`remove` never deletes files in Added [A] state from the
4726 :hg:`remove` never deletes files in Added [A] state from the
4727 working directory, not even if ``--force`` is specified.
4727 working directory, not even if ``--force`` is specified.
4728
4728
4729 Returns 0 on success, 1 if any warnings encountered.
4729 Returns 0 on success, 1 if any warnings encountered.
4730 """
4730 """
4731
4731
4732 opts = pycompat.byteskwargs(opts)
4732 opts = pycompat.byteskwargs(opts)
4733 after, force = opts.get('after'), opts.get('force')
4733 after, force = opts.get('after'), opts.get('force')
4734 dryrun = opts.get('dry_run')
4734 dryrun = opts.get('dry_run')
4735 if not pats and not after:
4735 if not pats and not after:
4736 raise error.Abort(_('no files specified'))
4736 raise error.Abort(_('no files specified'))
4737
4737
4738 m = scmutil.match(repo[None], pats, opts)
4738 m = scmutil.match(repo[None], pats, opts)
4739 subrepos = opts.get('subrepos')
4739 subrepos = opts.get('subrepos')
4740 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
4740 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
4741 return cmdutil.remove(ui, repo, m, "", uipathfn, after, force, subrepos,
4741 return cmdutil.remove(ui, repo, m, "", uipathfn, after, force, subrepos,
4742 dryrun=dryrun)
4742 dryrun=dryrun)
4743
4743
4744 @command('rename|move|mv',
4744 @command('rename|move|mv',
4745 [('A', 'after', None, _('record a rename that has already occurred')),
4745 [('A', 'after', None, _('record a rename that has already occurred')),
4746 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4746 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4747 ] + walkopts + dryrunopts,
4747 ] + walkopts + dryrunopts,
4748 _('[OPTION]... SOURCE... DEST'),
4748 _('[OPTION]... SOURCE... DEST'),
4749 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
4749 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
4750 def rename(ui, repo, *pats, **opts):
4750 def rename(ui, repo, *pats, **opts):
4751 """rename files; equivalent of copy + remove
4751 """rename files; equivalent of copy + remove
4752
4752
4753 Mark dest as copies of sources; mark sources for deletion. If dest
4753 Mark dest as copies of sources; mark sources for deletion. If dest
4754 is a directory, copies are put in that directory. If dest is a
4754 is a directory, copies are put in that directory. If dest is a
4755 file, there can only be one source.
4755 file, there can only be one source.
4756
4756
4757 By default, this command copies the contents of files as they
4757 By default, this command copies the contents of files as they
4758 exist in the working directory. If invoked with -A/--after, the
4758 exist in the working directory. If invoked with -A/--after, the
4759 operation is recorded, but no copying is performed.
4759 operation is recorded, but no copying is performed.
4760
4760
4761 This command takes effect at the next commit. To undo a rename
4761 This command takes effect at the next commit. To undo a rename
4762 before that, see :hg:`revert`.
4762 before that, see :hg:`revert`.
4763
4763
4764 Returns 0 on success, 1 if errors are encountered.
4764 Returns 0 on success, 1 if errors are encountered.
4765 """
4765 """
4766 opts = pycompat.byteskwargs(opts)
4766 opts = pycompat.byteskwargs(opts)
4767 with repo.wlock(False):
4767 with repo.wlock(False):
4768 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4768 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4769
4769
4770 @command('resolve',
4770 @command('resolve',
4771 [('a', 'all', None, _('select all unresolved files')),
4771 [('a', 'all', None, _('select all unresolved files')),
4772 ('l', 'list', None, _('list state of files needing merge')),
4772 ('l', 'list', None, _('list state of files needing merge')),
4773 ('m', 'mark', None, _('mark files as resolved')),
4773 ('m', 'mark', None, _('mark files as resolved')),
4774 ('u', 'unmark', None, _('mark files as unresolved')),
4774 ('u', 'unmark', None, _('mark files as unresolved')),
4775 ('n', 'no-status', None, _('hide status prefix')),
4775 ('n', 'no-status', None, _('hide status prefix')),
4776 ('', 're-merge', None, _('re-merge files'))]
4776 ('', 're-merge', None, _('re-merge files'))]
4777 + mergetoolopts + walkopts + formatteropts,
4777 + mergetoolopts + walkopts + formatteropts,
4778 _('[OPTION]... [FILE]...'),
4778 _('[OPTION]... [FILE]...'),
4779 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4779 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4780 inferrepo=True)
4780 inferrepo=True)
4781 def resolve(ui, repo, *pats, **opts):
4781 def resolve(ui, repo, *pats, **opts):
4782 """redo merges or set/view the merge status of files
4782 """redo merges or set/view the merge status of files
4783
4783
4784 Merges with unresolved conflicts are often the result of
4784 Merges with unresolved conflicts are often the result of
4785 non-interactive merging using the ``internal:merge`` configuration
4785 non-interactive merging using the ``internal:merge`` configuration
4786 setting, or a command-line merge tool like ``diff3``. The resolve
4786 setting, or a command-line merge tool like ``diff3``. The resolve
4787 command is used to manage the files involved in a merge, after
4787 command is used to manage the files involved in a merge, after
4788 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4788 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4789 working directory must have two parents). See :hg:`help
4789 working directory must have two parents). See :hg:`help
4790 merge-tools` for information on configuring merge tools.
4790 merge-tools` for information on configuring merge tools.
4791
4791
4792 The resolve command can be used in the following ways:
4792 The resolve command can be used in the following ways:
4793
4793
4794 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
4794 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
4795 the specified files, discarding any previous merge attempts. Re-merging
4795 the specified files, discarding any previous merge attempts. Re-merging
4796 is not performed for files already marked as resolved. Use ``--all/-a``
4796 is not performed for files already marked as resolved. Use ``--all/-a``
4797 to select all unresolved files. ``--tool`` can be used to specify
4797 to select all unresolved files. ``--tool`` can be used to specify
4798 the merge tool used for the given files. It overrides the HGMERGE
4798 the merge tool used for the given files. It overrides the HGMERGE
4799 environment variable and your configuration files. Previous file
4799 environment variable and your configuration files. Previous file
4800 contents are saved with a ``.orig`` suffix.
4800 contents are saved with a ``.orig`` suffix.
4801
4801
4802 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4802 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4803 (e.g. after having manually fixed-up the files). The default is
4803 (e.g. after having manually fixed-up the files). The default is
4804 to mark all unresolved files.
4804 to mark all unresolved files.
4805
4805
4806 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4806 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4807 default is to mark all resolved files.
4807 default is to mark all resolved files.
4808
4808
4809 - :hg:`resolve -l`: list files which had or still have conflicts.
4809 - :hg:`resolve -l`: list files which had or still have conflicts.
4810 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4810 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4811 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4811 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4812 the list. See :hg:`help filesets` for details.
4812 the list. See :hg:`help filesets` for details.
4813
4813
4814 .. note::
4814 .. note::
4815
4815
4816 Mercurial will not let you commit files with unresolved merge
4816 Mercurial will not let you commit files with unresolved merge
4817 conflicts. You must use :hg:`resolve -m ...` before you can
4817 conflicts. You must use :hg:`resolve -m ...` before you can
4818 commit after a conflicting merge.
4818 commit after a conflicting merge.
4819
4819
4820 .. container:: verbose
4820 .. container:: verbose
4821
4821
4822 Template:
4822 Template:
4823
4823
4824 The following keywords are supported in addition to the common template
4824 The following keywords are supported in addition to the common template
4825 keywords and functions. See also :hg:`help templates`.
4825 keywords and functions. See also :hg:`help templates`.
4826
4826
4827 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
4827 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
4828 :path: String. Repository-absolute path of the file.
4828 :path: String. Repository-absolute path of the file.
4829
4829
4830 Returns 0 on success, 1 if any files fail a resolve attempt.
4830 Returns 0 on success, 1 if any files fail a resolve attempt.
4831 """
4831 """
4832
4832
4833 opts = pycompat.byteskwargs(opts)
4833 opts = pycompat.byteskwargs(opts)
4834 confirm = ui.configbool('commands', 'resolve.confirm')
4834 confirm = ui.configbool('commands', 'resolve.confirm')
4835 flaglist = 'all mark unmark list no_status re_merge'.split()
4835 flaglist = 'all mark unmark list no_status re_merge'.split()
4836 all, mark, unmark, show, nostatus, remerge = [
4836 all, mark, unmark, show, nostatus, remerge = [
4837 opts.get(o) for o in flaglist]
4837 opts.get(o) for o in flaglist]
4838
4838
4839 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
4839 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
4840 if actioncount > 1:
4840 if actioncount > 1:
4841 raise error.Abort(_("too many actions specified"))
4841 raise error.Abort(_("too many actions specified"))
4842 elif (actioncount == 0
4842 elif (actioncount == 0
4843 and ui.configbool('commands', 'resolve.explicit-re-merge')):
4843 and ui.configbool('commands', 'resolve.explicit-re-merge')):
4844 hint = _('use --mark, --unmark, --list or --re-merge')
4844 hint = _('use --mark, --unmark, --list or --re-merge')
4845 raise error.Abort(_('no action specified'), hint=hint)
4845 raise error.Abort(_('no action specified'), hint=hint)
4846 if pats and all:
4846 if pats and all:
4847 raise error.Abort(_("can't specify --all and patterns"))
4847 raise error.Abort(_("can't specify --all and patterns"))
4848 if not (all or pats or show or mark or unmark):
4848 if not (all or pats or show or mark or unmark):
4849 raise error.Abort(_('no files or directories specified'),
4849 raise error.Abort(_('no files or directories specified'),
4850 hint=('use --all to re-merge all unresolved files'))
4850 hint=('use --all to re-merge all unresolved files'))
4851
4851
4852 if confirm:
4852 if confirm:
4853 if all:
4853 if all:
4854 if ui.promptchoice(_(b're-merge all unresolved files (yn)?'
4854 if ui.promptchoice(_(b're-merge all unresolved files (yn)?'
4855 b'$$ &Yes $$ &No')):
4855 b'$$ &Yes $$ &No')):
4856 raise error.Abort(_('user quit'))
4856 raise error.Abort(_('user quit'))
4857 if mark and not pats:
4857 if mark and not pats:
4858 if ui.promptchoice(_(b'mark all unresolved files as resolved (yn)?'
4858 if ui.promptchoice(_(b'mark all unresolved files as resolved (yn)?'
4859 b'$$ &Yes $$ &No')):
4859 b'$$ &Yes $$ &No')):
4860 raise error.Abort(_('user quit'))
4860 raise error.Abort(_('user quit'))
4861 if unmark and not pats:
4861 if unmark and not pats:
4862 if ui.promptchoice(_(b'mark all resolved files as unresolved (yn)?'
4862 if ui.promptchoice(_(b'mark all resolved files as unresolved (yn)?'
4863 b'$$ &Yes $$ &No')):
4863 b'$$ &Yes $$ &No')):
4864 raise error.Abort(_('user quit'))
4864 raise error.Abort(_('user quit'))
4865
4865
4866 uipathfn = scmutil.getuipathfn(repo)
4866 uipathfn = scmutil.getuipathfn(repo)
4867
4867
4868 if show:
4868 if show:
4869 ui.pager('resolve')
4869 ui.pager('resolve')
4870 fm = ui.formatter('resolve', opts)
4870 fm = ui.formatter('resolve', opts)
4871 ms = mergemod.mergestate.read(repo)
4871 ms = mergemod.mergestate.read(repo)
4872 wctx = repo[None]
4872 wctx = repo[None]
4873 m = scmutil.match(wctx, pats, opts)
4873 m = scmutil.match(wctx, pats, opts)
4874
4874
4875 # Labels and keys based on merge state. Unresolved path conflicts show
4875 # Labels and keys based on merge state. Unresolved path conflicts show
4876 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4876 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4877 # resolved conflicts.
4877 # resolved conflicts.
4878 mergestateinfo = {
4878 mergestateinfo = {
4879 mergemod.MERGE_RECORD_UNRESOLVED: ('resolve.unresolved', 'U'),
4879 mergemod.MERGE_RECORD_UNRESOLVED: ('resolve.unresolved', 'U'),
4880 mergemod.MERGE_RECORD_RESOLVED: ('resolve.resolved', 'R'),
4880 mergemod.MERGE_RECORD_RESOLVED: ('resolve.resolved', 'R'),
4881 mergemod.MERGE_RECORD_UNRESOLVED_PATH: ('resolve.unresolved', 'P'),
4881 mergemod.MERGE_RECORD_UNRESOLVED_PATH: ('resolve.unresolved', 'P'),
4882 mergemod.MERGE_RECORD_RESOLVED_PATH: ('resolve.resolved', 'R'),
4882 mergemod.MERGE_RECORD_RESOLVED_PATH: ('resolve.resolved', 'R'),
4883 mergemod.MERGE_RECORD_DRIVER_RESOLVED: ('resolve.driverresolved',
4883 mergemod.MERGE_RECORD_DRIVER_RESOLVED: ('resolve.driverresolved',
4884 'D'),
4884 'D'),
4885 }
4885 }
4886
4886
4887 for f in ms:
4887 for f in ms:
4888 if not m(f):
4888 if not m(f):
4889 continue
4889 continue
4890
4890
4891 label, key = mergestateinfo[ms[f]]
4891 label, key = mergestateinfo[ms[f]]
4892 fm.startitem()
4892 fm.startitem()
4893 fm.context(ctx=wctx)
4893 fm.context(ctx=wctx)
4894 fm.condwrite(not nostatus, 'mergestatus', '%s ', key, label=label)
4894 fm.condwrite(not nostatus, 'mergestatus', '%s ', key, label=label)
4895 fm.data(path=f)
4895 fm.data(path=f)
4896 fm.plain('%s\n' % uipathfn(f), label=label)
4896 fm.plain('%s\n' % uipathfn(f), label=label)
4897 fm.end()
4897 fm.end()
4898 return 0
4898 return 0
4899
4899
4900 with repo.wlock():
4900 with repo.wlock():
4901 ms = mergemod.mergestate.read(repo)
4901 ms = mergemod.mergestate.read(repo)
4902
4902
4903 if not (ms.active() or repo.dirstate.p2() != nullid):
4903 if not (ms.active() or repo.dirstate.p2() != nullid):
4904 raise error.Abort(
4904 raise error.Abort(
4905 _('resolve command not applicable when not merging'))
4905 _('resolve command not applicable when not merging'))
4906
4906
4907 wctx = repo[None]
4907 wctx = repo[None]
4908
4908
4909 if (ms.mergedriver
4909 if (ms.mergedriver
4910 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED):
4910 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED):
4911 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4911 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4912 ms.commit()
4912 ms.commit()
4913 # allow mark and unmark to go through
4913 # allow mark and unmark to go through
4914 if not mark and not unmark and not proceed:
4914 if not mark and not unmark and not proceed:
4915 return 1
4915 return 1
4916
4916
4917 m = scmutil.match(wctx, pats, opts)
4917 m = scmutil.match(wctx, pats, opts)
4918 ret = 0
4918 ret = 0
4919 didwork = False
4919 didwork = False
4920 runconclude = False
4920 runconclude = False
4921
4921
4922 tocomplete = []
4922 tocomplete = []
4923 hasconflictmarkers = []
4923 hasconflictmarkers = []
4924 if mark:
4924 if mark:
4925 markcheck = ui.config('commands', 'resolve.mark-check')
4925 markcheck = ui.config('commands', 'resolve.mark-check')
4926 if markcheck not in ['warn', 'abort']:
4926 if markcheck not in ['warn', 'abort']:
4927 # Treat all invalid / unrecognized values as 'none'.
4927 # Treat all invalid / unrecognized values as 'none'.
4928 markcheck = False
4928 markcheck = False
4929 for f in ms:
4929 for f in ms:
4930 if not m(f):
4930 if not m(f):
4931 continue
4931 continue
4932
4932
4933 didwork = True
4933 didwork = True
4934
4934
4935 # don't let driver-resolved files be marked, and run the conclude
4935 # don't let driver-resolved files be marked, and run the conclude
4936 # step if asked to resolve
4936 # step if asked to resolve
4937 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
4937 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
4938 exact = m.exact(f)
4938 exact = m.exact(f)
4939 if mark:
4939 if mark:
4940 if exact:
4940 if exact:
4941 ui.warn(_('not marking %s as it is driver-resolved\n')
4941 ui.warn(_('not marking %s as it is driver-resolved\n')
4942 % uipathfn(f))
4942 % uipathfn(f))
4943 elif unmark:
4943 elif unmark:
4944 if exact:
4944 if exact:
4945 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4945 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4946 % uipathfn(f))
4946 % uipathfn(f))
4947 else:
4947 else:
4948 runconclude = True
4948 runconclude = True
4949 continue
4949 continue
4950
4950
4951 # path conflicts must be resolved manually
4951 # path conflicts must be resolved manually
4952 if ms[f] in (mergemod.MERGE_RECORD_UNRESOLVED_PATH,
4952 if ms[f] in (mergemod.MERGE_RECORD_UNRESOLVED_PATH,
4953 mergemod.MERGE_RECORD_RESOLVED_PATH):
4953 mergemod.MERGE_RECORD_RESOLVED_PATH):
4954 if mark:
4954 if mark:
4955 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
4955 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
4956 elif unmark:
4956 elif unmark:
4957 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
4957 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
4958 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
4958 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
4959 ui.warn(_('%s: path conflict must be resolved manually\n')
4959 ui.warn(_('%s: path conflict must be resolved manually\n')
4960 % uipathfn(f))
4960 % uipathfn(f))
4961 continue
4961 continue
4962
4962
4963 if mark:
4963 if mark:
4964 if markcheck:
4964 if markcheck:
4965 fdata = repo.wvfs.tryread(f)
4965 fdata = repo.wvfs.tryread(f)
4966 if (filemerge.hasconflictmarkers(fdata) and
4966 if (filemerge.hasconflictmarkers(fdata) and
4967 ms[f] != mergemod.MERGE_RECORD_RESOLVED):
4967 ms[f] != mergemod.MERGE_RECORD_RESOLVED):
4968 hasconflictmarkers.append(f)
4968 hasconflictmarkers.append(f)
4969 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
4969 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
4970 elif unmark:
4970 elif unmark:
4971 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
4971 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
4972 else:
4972 else:
4973 # backup pre-resolve (merge uses .orig for its own purposes)
4973 # backup pre-resolve (merge uses .orig for its own purposes)
4974 a = repo.wjoin(f)
4974 a = repo.wjoin(f)
4975 try:
4975 try:
4976 util.copyfile(a, a + ".resolve")
4976 util.copyfile(a, a + ".resolve")
4977 except (IOError, OSError) as inst:
4977 except (IOError, OSError) as inst:
4978 if inst.errno != errno.ENOENT:
4978 if inst.errno != errno.ENOENT:
4979 raise
4979 raise
4980
4980
4981 try:
4981 try:
4982 # preresolve file
4982 # preresolve file
4983 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4983 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4984 with ui.configoverride(overrides, 'resolve'):
4984 with ui.configoverride(overrides, 'resolve'):
4985 complete, r = ms.preresolve(f, wctx)
4985 complete, r = ms.preresolve(f, wctx)
4986 if not complete:
4986 if not complete:
4987 tocomplete.append(f)
4987 tocomplete.append(f)
4988 elif r:
4988 elif r:
4989 ret = 1
4989 ret = 1
4990 finally:
4990 finally:
4991 ms.commit()
4991 ms.commit()
4992
4992
4993 # replace filemerge's .orig file with our resolve file, but only
4993 # replace filemerge's .orig file with our resolve file, but only
4994 # for merges that are complete
4994 # for merges that are complete
4995 if complete:
4995 if complete:
4996 try:
4996 try:
4997 util.rename(a + ".resolve",
4997 util.rename(a + ".resolve",
4998 scmutil.backuppath(ui, repo, f))
4998 scmutil.backuppath(ui, repo, f))
4999 except OSError as inst:
4999 except OSError as inst:
5000 if inst.errno != errno.ENOENT:
5000 if inst.errno != errno.ENOENT:
5001 raise
5001 raise
5002
5002
5003 if hasconflictmarkers:
5003 if hasconflictmarkers:
5004 ui.warn(_('warning: the following files still have conflict '
5004 ui.warn(_('warning: the following files still have conflict '
5005 'markers:\n') + ''.join(' ' + uipathfn(f) + '\n'
5005 'markers:\n') + ''.join(' ' + uipathfn(f) + '\n'
5006 for f in hasconflictmarkers))
5006 for f in hasconflictmarkers))
5007 if markcheck == 'abort' and not all and not pats:
5007 if markcheck == 'abort' and not all and not pats:
5008 raise error.Abort(_('conflict markers detected'),
5008 raise error.Abort(_('conflict markers detected'),
5009 hint=_('use --all to mark anyway'))
5009 hint=_('use --all to mark anyway'))
5010
5010
5011 for f in tocomplete:
5011 for f in tocomplete:
5012 try:
5012 try:
5013 # resolve file
5013 # resolve file
5014 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
5014 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
5015 with ui.configoverride(overrides, 'resolve'):
5015 with ui.configoverride(overrides, 'resolve'):
5016 r = ms.resolve(f, wctx)
5016 r = ms.resolve(f, wctx)
5017 if r:
5017 if r:
5018 ret = 1
5018 ret = 1
5019 finally:
5019 finally:
5020 ms.commit()
5020 ms.commit()
5021
5021
5022 # replace filemerge's .orig file with our resolve file
5022 # replace filemerge's .orig file with our resolve file
5023 a = repo.wjoin(f)
5023 a = repo.wjoin(f)
5024 try:
5024 try:
5025 util.rename(a + ".resolve", scmutil.backuppath(ui, repo, f))
5025 util.rename(a + ".resolve", scmutil.backuppath(ui, repo, f))
5026 except OSError as inst:
5026 except OSError as inst:
5027 if inst.errno != errno.ENOENT:
5027 if inst.errno != errno.ENOENT:
5028 raise
5028 raise
5029
5029
5030 ms.commit()
5030 ms.commit()
5031 ms.recordactions()
5031 ms.recordactions()
5032
5032
5033 if not didwork and pats:
5033 if not didwork and pats:
5034 hint = None
5034 hint = None
5035 if not any([p for p in pats if p.find(':') >= 0]):
5035 if not any([p for p in pats if p.find(':') >= 0]):
5036 pats = ['path:%s' % p for p in pats]
5036 pats = ['path:%s' % p for p in pats]
5037 m = scmutil.match(wctx, pats, opts)
5037 m = scmutil.match(wctx, pats, opts)
5038 for f in ms:
5038 for f in ms:
5039 if not m(f):
5039 if not m(f):
5040 continue
5040 continue
5041 def flag(o):
5041 def flag(o):
5042 if o == 're_merge':
5042 if o == 're_merge':
5043 return '--re-merge '
5043 return '--re-merge '
5044 return '-%s ' % o[0:1]
5044 return '-%s ' % o[0:1]
5045 flags = ''.join([flag(o) for o in flaglist if opts.get(o)])
5045 flags = ''.join([flag(o) for o in flaglist if opts.get(o)])
5046 hint = _("(try: hg resolve %s%s)\n") % (
5046 hint = _("(try: hg resolve %s%s)\n") % (
5047 flags,
5047 flags,
5048 ' '.join(pats))
5048 ' '.join(pats))
5049 break
5049 break
5050 ui.warn(_("arguments do not match paths that need resolving\n"))
5050 ui.warn(_("arguments do not match paths that need resolving\n"))
5051 if hint:
5051 if hint:
5052 ui.warn(hint)
5052 ui.warn(hint)
5053 elif ms.mergedriver and ms.mdstate() != 's':
5053 elif ms.mergedriver and ms.mdstate() != 's':
5054 # run conclude step when either a driver-resolved file is requested
5054 # run conclude step when either a driver-resolved file is requested
5055 # or there are no driver-resolved files
5055 # or there are no driver-resolved files
5056 # we can't use 'ret' to determine whether any files are unresolved
5056 # we can't use 'ret' to determine whether any files are unresolved
5057 # because we might not have tried to resolve some
5057 # because we might not have tried to resolve some
5058 if ((runconclude or not list(ms.driverresolved()))
5058 if ((runconclude or not list(ms.driverresolved()))
5059 and not list(ms.unresolved())):
5059 and not list(ms.unresolved())):
5060 proceed = mergemod.driverconclude(repo, ms, wctx)
5060 proceed = mergemod.driverconclude(repo, ms, wctx)
5061 ms.commit()
5061 ms.commit()
5062 if not proceed:
5062 if not proceed:
5063 return 1
5063 return 1
5064
5064
5065 # Nudge users into finishing an unfinished operation
5065 # Nudge users into finishing an unfinished operation
5066 unresolvedf = list(ms.unresolved())
5066 unresolvedf = list(ms.unresolved())
5067 driverresolvedf = list(ms.driverresolved())
5067 driverresolvedf = list(ms.driverresolved())
5068 if not unresolvedf and not driverresolvedf:
5068 if not unresolvedf and not driverresolvedf:
5069 ui.status(_('(no more unresolved files)\n'))
5069 ui.status(_('(no more unresolved files)\n'))
5070 cmdutil.checkafterresolved(repo)
5070 cmdutil.checkafterresolved(repo)
5071 elif not unresolvedf:
5071 elif not unresolvedf:
5072 ui.status(_('(no more unresolved files -- '
5072 ui.status(_('(no more unresolved files -- '
5073 'run "hg resolve --all" to conclude)\n'))
5073 'run "hg resolve --all" to conclude)\n'))
5074
5074
5075 return ret
5075 return ret
5076
5076
5077 @command('revert',
5077 @command('revert',
5078 [('a', 'all', None, _('revert all changes when no arguments given')),
5078 [('a', 'all', None, _('revert all changes when no arguments given')),
5079 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5079 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5080 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5080 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5081 ('C', 'no-backup', None, _('do not save backup copies of files')),
5081 ('C', 'no-backup', None, _('do not save backup copies of files')),
5082 ('i', 'interactive', None, _('interactively select the changes')),
5082 ('i', 'interactive', None, _('interactively select the changes')),
5083 ] + walkopts + dryrunopts,
5083 ] + walkopts + dryrunopts,
5084 _('[OPTION]... [-r REV] [NAME]...'),
5084 _('[OPTION]... [-r REV] [NAME]...'),
5085 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5085 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5086 def revert(ui, repo, *pats, **opts):
5086 def revert(ui, repo, *pats, **opts):
5087 """restore files to their checkout state
5087 """restore files to their checkout state
5088
5088
5089 .. note::
5089 .. note::
5090
5090
5091 To check out earlier revisions, you should use :hg:`update REV`.
5091 To check out earlier revisions, you should use :hg:`update REV`.
5092 To cancel an uncommitted merge (and lose your changes),
5092 To cancel an uncommitted merge (and lose your changes),
5093 use :hg:`merge --abort`.
5093 use :hg:`merge --abort`.
5094
5094
5095 With no revision specified, revert the specified files or directories
5095 With no revision specified, revert the specified files or directories
5096 to the contents they had in the parent of the working directory.
5096 to the contents they had in the parent of the working directory.
5097 This restores the contents of files to an unmodified
5097 This restores the contents of files to an unmodified
5098 state and unschedules adds, removes, copies, and renames. If the
5098 state and unschedules adds, removes, copies, and renames. If the
5099 working directory has two parents, you must explicitly specify a
5099 working directory has two parents, you must explicitly specify a
5100 revision.
5100 revision.
5101
5101
5102 Using the -r/--rev or -d/--date options, revert the given files or
5102 Using the -r/--rev or -d/--date options, revert the given files or
5103 directories to their states as of a specific revision. Because
5103 directories to their states as of a specific revision. Because
5104 revert does not change the working directory parents, this will
5104 revert does not change the working directory parents, this will
5105 cause these files to appear modified. This can be helpful to "back
5105 cause these files to appear modified. This can be helpful to "back
5106 out" some or all of an earlier change. See :hg:`backout` for a
5106 out" some or all of an earlier change. See :hg:`backout` for a
5107 related method.
5107 related method.
5108
5108
5109 Modified files are saved with a .orig suffix before reverting.
5109 Modified files are saved with a .orig suffix before reverting.
5110 To disable these backups, use --no-backup. It is possible to store
5110 To disable these backups, use --no-backup. It is possible to store
5111 the backup files in a custom directory relative to the root of the
5111 the backup files in a custom directory relative to the root of the
5112 repository by setting the ``ui.origbackuppath`` configuration
5112 repository by setting the ``ui.origbackuppath`` configuration
5113 option.
5113 option.
5114
5114
5115 See :hg:`help dates` for a list of formats valid for -d/--date.
5115 See :hg:`help dates` for a list of formats valid for -d/--date.
5116
5116
5117 See :hg:`help backout` for a way to reverse the effect of an
5117 See :hg:`help backout` for a way to reverse the effect of an
5118 earlier changeset.
5118 earlier changeset.
5119
5119
5120 Returns 0 on success.
5120 Returns 0 on success.
5121 """
5121 """
5122
5122
5123 opts = pycompat.byteskwargs(opts)
5123 opts = pycompat.byteskwargs(opts)
5124 if opts.get("date"):
5124 if opts.get("date"):
5125 if opts.get("rev"):
5125 if opts.get("rev"):
5126 raise error.Abort(_("you can't specify a revision and a date"))
5126 raise error.Abort(_("you can't specify a revision and a date"))
5127 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5127 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5128
5128
5129 parent, p2 = repo.dirstate.parents()
5129 parent, p2 = repo.dirstate.parents()
5130 if not opts.get('rev') and p2 != nullid:
5130 if not opts.get('rev') and p2 != nullid:
5131 # revert after merge is a trap for new users (issue2915)
5131 # revert after merge is a trap for new users (issue2915)
5132 raise error.Abort(_('uncommitted merge with no revision specified'),
5132 raise error.Abort(_('uncommitted merge with no revision specified'),
5133 hint=_("use 'hg update' or see 'hg help revert'"))
5133 hint=_("use 'hg update' or see 'hg help revert'"))
5134
5134
5135 rev = opts.get('rev')
5135 rev = opts.get('rev')
5136 if rev:
5136 if rev:
5137 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
5137 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
5138 ctx = scmutil.revsingle(repo, rev)
5138 ctx = scmutil.revsingle(repo, rev)
5139
5139
5140 if (not (pats or opts.get('include') or opts.get('exclude') or
5140 if (not (pats or opts.get('include') or opts.get('exclude') or
5141 opts.get('all') or opts.get('interactive'))):
5141 opts.get('all') or opts.get('interactive'))):
5142 msg = _("no files or directories specified")
5142 msg = _("no files or directories specified")
5143 if p2 != nullid:
5143 if p2 != nullid:
5144 hint = _("uncommitted merge, use --all to discard all changes,"
5144 hint = _("uncommitted merge, use --all to discard all changes,"
5145 " or 'hg update -C .' to abort the merge")
5145 " or 'hg update -C .' to abort the merge")
5146 raise error.Abort(msg, hint=hint)
5146 raise error.Abort(msg, hint=hint)
5147 dirty = any(repo.status())
5147 dirty = any(repo.status())
5148 node = ctx.node()
5148 node = ctx.node()
5149 if node != parent:
5149 if node != parent:
5150 if dirty:
5150 if dirty:
5151 hint = _("uncommitted changes, use --all to discard all"
5151 hint = _("uncommitted changes, use --all to discard all"
5152 " changes, or 'hg update %d' to update") % ctx.rev()
5152 " changes, or 'hg update %d' to update") % ctx.rev()
5153 else:
5153 else:
5154 hint = _("use --all to revert all files,"
5154 hint = _("use --all to revert all files,"
5155 " or 'hg update %d' to update") % ctx.rev()
5155 " or 'hg update %d' to update") % ctx.rev()
5156 elif dirty:
5156 elif dirty:
5157 hint = _("uncommitted changes, use --all to discard all changes")
5157 hint = _("uncommitted changes, use --all to discard all changes")
5158 else:
5158 else:
5159 hint = _("use --all to revert all files")
5159 hint = _("use --all to revert all files")
5160 raise error.Abort(msg, hint=hint)
5160 raise error.Abort(msg, hint=hint)
5161
5161
5162 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats,
5162 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats,
5163 **pycompat.strkwargs(opts))
5163 **pycompat.strkwargs(opts))
5164
5164
5165 @command(
5165 @command(
5166 'rollback',
5166 'rollback',
5167 dryrunopts + [('f', 'force', False, _('ignore safety measures'))],
5167 dryrunopts + [('f', 'force', False, _('ignore safety measures'))],
5168 helpcategory=command.CATEGORY_MAINTENANCE)
5168 helpcategory=command.CATEGORY_MAINTENANCE)
5169 def rollback(ui, repo, **opts):
5169 def rollback(ui, repo, **opts):
5170 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5170 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5171
5171
5172 Please use :hg:`commit --amend` instead of rollback to correct
5172 Please use :hg:`commit --amend` instead of rollback to correct
5173 mistakes in the last commit.
5173 mistakes in the last commit.
5174
5174
5175 This command should be used with care. There is only one level of
5175 This command should be used with care. There is only one level of
5176 rollback, and there is no way to undo a rollback. It will also
5176 rollback, and there is no way to undo a rollback. It will also
5177 restore the dirstate at the time of the last transaction, losing
5177 restore the dirstate at the time of the last transaction, losing
5178 any dirstate changes since that time. This command does not alter
5178 any dirstate changes since that time. This command does not alter
5179 the working directory.
5179 the working directory.
5180
5180
5181 Transactions are used to encapsulate the effects of all commands
5181 Transactions are used to encapsulate the effects of all commands
5182 that create new changesets or propagate existing changesets into a
5182 that create new changesets or propagate existing changesets into a
5183 repository.
5183 repository.
5184
5184
5185 .. container:: verbose
5185 .. container:: verbose
5186
5186
5187 For example, the following commands are transactional, and their
5187 For example, the following commands are transactional, and their
5188 effects can be rolled back:
5188 effects can be rolled back:
5189
5189
5190 - commit
5190 - commit
5191 - import
5191 - import
5192 - pull
5192 - pull
5193 - push (with this repository as the destination)
5193 - push (with this repository as the destination)
5194 - unbundle
5194 - unbundle
5195
5195
5196 To avoid permanent data loss, rollback will refuse to rollback a
5196 To avoid permanent data loss, rollback will refuse to rollback a
5197 commit transaction if it isn't checked out. Use --force to
5197 commit transaction if it isn't checked out. Use --force to
5198 override this protection.
5198 override this protection.
5199
5199
5200 The rollback command can be entirely disabled by setting the
5200 The rollback command can be entirely disabled by setting the
5201 ``ui.rollback`` configuration setting to false. If you're here
5201 ``ui.rollback`` configuration setting to false. If you're here
5202 because you want to use rollback and it's disabled, you can
5202 because you want to use rollback and it's disabled, you can
5203 re-enable the command by setting ``ui.rollback`` to true.
5203 re-enable the command by setting ``ui.rollback`` to true.
5204
5204
5205 This command is not intended for use on public repositories. Once
5205 This command is not intended for use on public repositories. Once
5206 changes are visible for pull by other users, rolling a transaction
5206 changes are visible for pull by other users, rolling a transaction
5207 back locally is ineffective (someone else may already have pulled
5207 back locally is ineffective (someone else may already have pulled
5208 the changes). Furthermore, a race is possible with readers of the
5208 the changes). Furthermore, a race is possible with readers of the
5209 repository; for example an in-progress pull from the repository
5209 repository; for example an in-progress pull from the repository
5210 may fail if a rollback is performed.
5210 may fail if a rollback is performed.
5211
5211
5212 Returns 0 on success, 1 if no rollback data is available.
5212 Returns 0 on success, 1 if no rollback data is available.
5213 """
5213 """
5214 if not ui.configbool('ui', 'rollback'):
5214 if not ui.configbool('ui', 'rollback'):
5215 raise error.Abort(_('rollback is disabled because it is unsafe'),
5215 raise error.Abort(_('rollback is disabled because it is unsafe'),
5216 hint=('see `hg help -v rollback` for information'))
5216 hint=('see `hg help -v rollback` for information'))
5217 return repo.rollback(dryrun=opts.get(r'dry_run'),
5217 return repo.rollback(dryrun=opts.get(r'dry_run'),
5218 force=opts.get(r'force'))
5218 force=opts.get(r'force'))
5219
5219
5220 @command(
5220 @command(
5221 'root', [], intents={INTENT_READONLY},
5221 'root', [], intents={INTENT_READONLY},
5222 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5222 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5223 def root(ui, repo):
5223 def root(ui, repo):
5224 """print the root (top) of the current working directory
5224 """print the root (top) of the current working directory
5225
5225
5226 Print the root directory of the current repository.
5226 Print the root directory of the current repository.
5227
5227
5228 Returns 0 on success.
5228 Returns 0 on success.
5229 """
5229 """
5230 ui.write(repo.root + "\n")
5230 ui.write(repo.root + "\n")
5231
5231
5232 @command('serve',
5232 @command('serve',
5233 [('A', 'accesslog', '', _('name of access log file to write to'),
5233 [('A', 'accesslog', '', _('name of access log file to write to'),
5234 _('FILE')),
5234 _('FILE')),
5235 ('d', 'daemon', None, _('run server in background')),
5235 ('d', 'daemon', None, _('run server in background')),
5236 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
5236 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
5237 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5237 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5238 # use string type, then we can check if something was passed
5238 # use string type, then we can check if something was passed
5239 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5239 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5240 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5240 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5241 _('ADDR')),
5241 _('ADDR')),
5242 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5242 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5243 _('PREFIX')),
5243 _('PREFIX')),
5244 ('n', 'name', '',
5244 ('n', 'name', '',
5245 _('name to show in web pages (default: working directory)'), _('NAME')),
5245 _('name to show in web pages (default: working directory)'), _('NAME')),
5246 ('', 'web-conf', '',
5246 ('', 'web-conf', '',
5247 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
5247 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
5248 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5248 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5249 _('FILE')),
5249 _('FILE')),
5250 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5250 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5251 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
5251 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
5252 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
5252 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
5253 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5253 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5254 ('', 'style', '', _('template style to use'), _('STYLE')),
5254 ('', 'style', '', _('template style to use'), _('STYLE')),
5255 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5255 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5256 ('', 'certificate', '', _('SSL certificate file'), _('FILE')),
5256 ('', 'certificate', '', _('SSL certificate file'), _('FILE')),
5257 ('', 'print-url', None, _('start and print only the URL'))]
5257 ('', 'print-url', None, _('start and print only the URL'))]
5258 + subrepoopts,
5258 + subrepoopts,
5259 _('[OPTION]...'),
5259 _('[OPTION]...'),
5260 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5260 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5261 helpbasic=True, optionalrepo=True)
5261 helpbasic=True, optionalrepo=True)
5262 def serve(ui, repo, **opts):
5262 def serve(ui, repo, **opts):
5263 """start stand-alone webserver
5263 """start stand-alone webserver
5264
5264
5265 Start a local HTTP repository browser and pull server. You can use
5265 Start a local HTTP repository browser and pull server. You can use
5266 this for ad-hoc sharing and browsing of repositories. It is
5266 this for ad-hoc sharing and browsing of repositories. It is
5267 recommended to use a real web server to serve a repository for
5267 recommended to use a real web server to serve a repository for
5268 longer periods of time.
5268 longer periods of time.
5269
5269
5270 Please note that the server does not implement access control.
5270 Please note that the server does not implement access control.
5271 This means that, by default, anybody can read from the server and
5271 This means that, by default, anybody can read from the server and
5272 nobody can write to it by default. Set the ``web.allow-push``
5272 nobody can write to it by default. Set the ``web.allow-push``
5273 option to ``*`` to allow everybody to push to the server. You
5273 option to ``*`` to allow everybody to push to the server. You
5274 should use a real web server if you need to authenticate users.
5274 should use a real web server if you need to authenticate users.
5275
5275
5276 By default, the server logs accesses to stdout and errors to
5276 By default, the server logs accesses to stdout and errors to
5277 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5277 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5278 files.
5278 files.
5279
5279
5280 To have the server choose a free port number to listen on, specify
5280 To have the server choose a free port number to listen on, specify
5281 a port number of 0; in this case, the server will print the port
5281 a port number of 0; in this case, the server will print the port
5282 number it uses.
5282 number it uses.
5283
5283
5284 Returns 0 on success.
5284 Returns 0 on success.
5285 """
5285 """
5286
5286
5287 opts = pycompat.byteskwargs(opts)
5287 opts = pycompat.byteskwargs(opts)
5288 if opts["stdio"] and opts["cmdserver"]:
5288 if opts["stdio"] and opts["cmdserver"]:
5289 raise error.Abort(_("cannot use --stdio with --cmdserver"))
5289 raise error.Abort(_("cannot use --stdio with --cmdserver"))
5290 if opts["print_url"] and ui.verbose:
5290 if opts["print_url"] and ui.verbose:
5291 raise error.Abort(_("cannot use --print-url with --verbose"))
5291 raise error.Abort(_("cannot use --print-url with --verbose"))
5292
5292
5293 if opts["stdio"]:
5293 if opts["stdio"]:
5294 if repo is None:
5294 if repo is None:
5295 raise error.RepoError(_("there is no Mercurial repository here"
5295 raise error.RepoError(_("there is no Mercurial repository here"
5296 " (.hg not found)"))
5296 " (.hg not found)"))
5297 s = wireprotoserver.sshserver(ui, repo)
5297 s = wireprotoserver.sshserver(ui, repo)
5298 s.serve_forever()
5298 s.serve_forever()
5299
5299
5300 service = server.createservice(ui, repo, opts)
5300 service = server.createservice(ui, repo, opts)
5301 return server.runservice(opts, initfn=service.init, runfn=service.run)
5301 return server.runservice(opts, initfn=service.init, runfn=service.run)
5302
5302
5303 _NOTTERSE = 'nothing'
5303 _NOTTERSE = 'nothing'
5304
5304
5305 @command('status|st',
5305 @command('status|st',
5306 [('A', 'all', None, _('show status of all files')),
5306 [('A', 'all', None, _('show status of all files')),
5307 ('m', 'modified', None, _('show only modified files')),
5307 ('m', 'modified', None, _('show only modified files')),
5308 ('a', 'added', None, _('show only added files')),
5308 ('a', 'added', None, _('show only added files')),
5309 ('r', 'removed', None, _('show only removed files')),
5309 ('r', 'removed', None, _('show only removed files')),
5310 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5310 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5311 ('c', 'clean', None, _('show only files without changes')),
5311 ('c', 'clean', None, _('show only files without changes')),
5312 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5312 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5313 ('i', 'ignored', None, _('show only ignored files')),
5313 ('i', 'ignored', None, _('show only ignored files')),
5314 ('n', 'no-status', None, _('hide status prefix')),
5314 ('n', 'no-status', None, _('hide status prefix')),
5315 ('t', 'terse', _NOTTERSE, _('show the terse output (EXPERIMENTAL)')),
5315 ('t', 'terse', _NOTTERSE, _('show the terse output (EXPERIMENTAL)')),
5316 ('C', 'copies', None, _('show source of copied files')),
5316 ('C', 'copies', None, _('show source of copied files')),
5317 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5317 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5318 ('', 'rev', [], _('show difference from revision'), _('REV')),
5318 ('', 'rev', [], _('show difference from revision'), _('REV')),
5319 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5319 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5320 ] + walkopts + subrepoopts + formatteropts,
5320 ] + walkopts + subrepoopts + formatteropts,
5321 _('[OPTION]... [FILE]...'),
5321 _('[OPTION]... [FILE]...'),
5322 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5322 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5323 helpbasic=True, inferrepo=True,
5323 helpbasic=True, inferrepo=True,
5324 intents={INTENT_READONLY})
5324 intents={INTENT_READONLY})
5325 def status(ui, repo, *pats, **opts):
5325 def status(ui, repo, *pats, **opts):
5326 """show changed files in the working directory
5326 """show changed files in the working directory
5327
5327
5328 Show status of files in the repository. If names are given, only
5328 Show status of files in the repository. If names are given, only
5329 files that match are shown. Files that are clean or ignored or
5329 files that match are shown. Files that are clean or ignored or
5330 the source of a copy/move operation, are not listed unless
5330 the source of a copy/move operation, are not listed unless
5331 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5331 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5332 Unless options described with "show only ..." are given, the
5332 Unless options described with "show only ..." are given, the
5333 options -mardu are used.
5333 options -mardu are used.
5334
5334
5335 Option -q/--quiet hides untracked (unknown and ignored) files
5335 Option -q/--quiet hides untracked (unknown and ignored) files
5336 unless explicitly requested with -u/--unknown or -i/--ignored.
5336 unless explicitly requested with -u/--unknown or -i/--ignored.
5337
5337
5338 .. note::
5338 .. note::
5339
5339
5340 :hg:`status` may appear to disagree with diff if permissions have
5340 :hg:`status` may appear to disagree with diff if permissions have
5341 changed or a merge has occurred. The standard diff format does
5341 changed or a merge has occurred. The standard diff format does
5342 not report permission changes and diff only reports changes
5342 not report permission changes and diff only reports changes
5343 relative to one merge parent.
5343 relative to one merge parent.
5344
5344
5345 If one revision is given, it is used as the base revision.
5345 If one revision is given, it is used as the base revision.
5346 If two revisions are given, the differences between them are
5346 If two revisions are given, the differences between them are
5347 shown. The --change option can also be used as a shortcut to list
5347 shown. The --change option can also be used as a shortcut to list
5348 the changed files of a revision from its first parent.
5348 the changed files of a revision from its first parent.
5349
5349
5350 The codes used to show the status of files are::
5350 The codes used to show the status of files are::
5351
5351
5352 M = modified
5352 M = modified
5353 A = added
5353 A = added
5354 R = removed
5354 R = removed
5355 C = clean
5355 C = clean
5356 ! = missing (deleted by non-hg command, but still tracked)
5356 ! = missing (deleted by non-hg command, but still tracked)
5357 ? = not tracked
5357 ? = not tracked
5358 I = ignored
5358 I = ignored
5359 = origin of the previous file (with --copies)
5359 = origin of the previous file (with --copies)
5360
5360
5361 .. container:: verbose
5361 .. container:: verbose
5362
5362
5363 The -t/--terse option abbreviates the output by showing only the directory
5363 The -t/--terse option abbreviates the output by showing only the directory
5364 name if all the files in it share the same status. The option takes an
5364 name if all the files in it share the same status. The option takes an
5365 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
5365 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
5366 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
5366 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
5367 for 'ignored' and 'c' for clean.
5367 for 'ignored' and 'c' for clean.
5368
5368
5369 It abbreviates only those statuses which are passed. Note that clean and
5369 It abbreviates only those statuses which are passed. Note that clean and
5370 ignored files are not displayed with '--terse ic' unless the -c/--clean
5370 ignored files are not displayed with '--terse ic' unless the -c/--clean
5371 and -i/--ignored options are also used.
5371 and -i/--ignored options are also used.
5372
5372
5373 The -v/--verbose option shows information when the repository is in an
5373 The -v/--verbose option shows information when the repository is in an
5374 unfinished merge, shelve, rebase state etc. You can have this behavior
5374 unfinished merge, shelve, rebase state etc. You can have this behavior
5375 turned on by default by enabling the ``commands.status.verbose`` option.
5375 turned on by default by enabling the ``commands.status.verbose`` option.
5376
5376
5377 You can skip displaying some of these states by setting
5377 You can skip displaying some of these states by setting
5378 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
5378 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
5379 'histedit', 'merge', 'rebase', or 'unshelve'.
5379 'histedit', 'merge', 'rebase', or 'unshelve'.
5380
5380
5381 Template:
5381 Template:
5382
5382
5383 The following keywords are supported in addition to the common template
5383 The following keywords are supported in addition to the common template
5384 keywords and functions. See also :hg:`help templates`.
5384 keywords and functions. See also :hg:`help templates`.
5385
5385
5386 :path: String. Repository-absolute path of the file.
5386 :path: String. Repository-absolute path of the file.
5387 :source: String. Repository-absolute path of the file originated from.
5387 :source: String. Repository-absolute path of the file originated from.
5388 Available if ``--copies`` is specified.
5388 Available if ``--copies`` is specified.
5389 :status: String. Character denoting file's status.
5389 :status: String. Character denoting file's status.
5390
5390
5391 Examples:
5391 Examples:
5392
5392
5393 - show changes in the working directory relative to a
5393 - show changes in the working directory relative to a
5394 changeset::
5394 changeset::
5395
5395
5396 hg status --rev 9353
5396 hg status --rev 9353
5397
5397
5398 - show changes in the working directory relative to the
5398 - show changes in the working directory relative to the
5399 current directory (see :hg:`help patterns` for more information)::
5399 current directory (see :hg:`help patterns` for more information)::
5400
5400
5401 hg status re:
5401 hg status re:
5402
5402
5403 - show all changes including copies in an existing changeset::
5403 - show all changes including copies in an existing changeset::
5404
5404
5405 hg status --copies --change 9353
5405 hg status --copies --change 9353
5406
5406
5407 - get a NUL separated list of added files, suitable for xargs::
5407 - get a NUL separated list of added files, suitable for xargs::
5408
5408
5409 hg status -an0
5409 hg status -an0
5410
5410
5411 - show more information about the repository status, abbreviating
5411 - show more information about the repository status, abbreviating
5412 added, removed, modified, deleted, and untracked paths::
5412 added, removed, modified, deleted, and untracked paths::
5413
5413
5414 hg status -v -t mardu
5414 hg status -v -t mardu
5415
5415
5416 Returns 0 on success.
5416 Returns 0 on success.
5417
5417
5418 """
5418 """
5419
5419
5420 opts = pycompat.byteskwargs(opts)
5420 opts = pycompat.byteskwargs(opts)
5421 revs = opts.get('rev')
5421 revs = opts.get('rev')
5422 change = opts.get('change')
5422 change = opts.get('change')
5423 terse = opts.get('terse')
5423 terse = opts.get('terse')
5424 if terse is _NOTTERSE:
5424 if terse is _NOTTERSE:
5425 if revs:
5425 if revs:
5426 terse = ''
5426 terse = ''
5427 else:
5427 else:
5428 terse = ui.config('commands', 'status.terse')
5428 terse = ui.config('commands', 'status.terse')
5429
5429
5430 if revs and change:
5430 if revs and change:
5431 msg = _('cannot specify --rev and --change at the same time')
5431 msg = _('cannot specify --rev and --change at the same time')
5432 raise error.Abort(msg)
5432 raise error.Abort(msg)
5433 elif revs and terse:
5433 elif revs and terse:
5434 msg = _('cannot use --terse with --rev')
5434 msg = _('cannot use --terse with --rev')
5435 raise error.Abort(msg)
5435 raise error.Abort(msg)
5436 elif change:
5436 elif change:
5437 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
5437 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
5438 ctx2 = scmutil.revsingle(repo, change, None)
5438 ctx2 = scmutil.revsingle(repo, change, None)
5439 ctx1 = ctx2.p1()
5439 ctx1 = ctx2.p1()
5440 else:
5440 else:
5441 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
5441 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
5442 ctx1, ctx2 = scmutil.revpair(repo, revs)
5442 ctx1, ctx2 = scmutil.revpair(repo, revs)
5443
5443
5444 forcerelativevalue = None
5444 forcerelativevalue = None
5445 if ui.hasconfig('commands', 'status.relative'):
5445 if ui.hasconfig('commands', 'status.relative'):
5446 forcerelativevalue = ui.configbool('commands', 'status.relative')
5446 forcerelativevalue = ui.configbool('commands', 'status.relative')
5447 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats),
5447 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats),
5448 forcerelativevalue=forcerelativevalue)
5448 forcerelativevalue=forcerelativevalue)
5449
5449
5450 if opts.get('print0'):
5450 if opts.get('print0'):
5451 end = '\0'
5451 end = '\0'
5452 else:
5452 else:
5453 end = '\n'
5453 end = '\n'
5454 copy = {}
5454 copy = {}
5455 states = 'modified added removed deleted unknown ignored clean'.split()
5455 states = 'modified added removed deleted unknown ignored clean'.split()
5456 show = [k for k in states if opts.get(k)]
5456 show = [k for k in states if opts.get(k)]
5457 if opts.get('all'):
5457 if opts.get('all'):
5458 show += ui.quiet and (states[:4] + ['clean']) or states
5458 show += ui.quiet and (states[:4] + ['clean']) or states
5459
5459
5460 if not show:
5460 if not show:
5461 if ui.quiet:
5461 if ui.quiet:
5462 show = states[:4]
5462 show = states[:4]
5463 else:
5463 else:
5464 show = states[:5]
5464 show = states[:5]
5465
5465
5466 m = scmutil.match(ctx2, pats, opts)
5466 m = scmutil.match(ctx2, pats, opts)
5467 if terse:
5467 if terse:
5468 # we need to compute clean and unknown to terse
5468 # we need to compute clean and unknown to terse
5469 stat = repo.status(ctx1.node(), ctx2.node(), m,
5469 stat = repo.status(ctx1.node(), ctx2.node(), m,
5470 'ignored' in show or 'i' in terse,
5470 'ignored' in show or 'i' in terse,
5471 clean=True, unknown=True,
5471 clean=True, unknown=True,
5472 listsubrepos=opts.get('subrepos'))
5472 listsubrepos=opts.get('subrepos'))
5473
5473
5474 stat = cmdutil.tersedir(stat, terse)
5474 stat = cmdutil.tersedir(stat, terse)
5475 else:
5475 else:
5476 stat = repo.status(ctx1.node(), ctx2.node(), m,
5476 stat = repo.status(ctx1.node(), ctx2.node(), m,
5477 'ignored' in show, 'clean' in show,
5477 'ignored' in show, 'clean' in show,
5478 'unknown' in show, opts.get('subrepos'))
5478 'unknown' in show, opts.get('subrepos'))
5479
5479
5480 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
5480 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
5481
5481
5482 if (opts.get('all') or opts.get('copies')
5482 if (opts.get('all') or opts.get('copies')
5483 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5483 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5484 copy = copies.pathcopies(ctx1, ctx2, m)
5484 copy = copies.pathcopies(ctx1, ctx2, m)
5485
5485
5486 ui.pager('status')
5486 ui.pager('status')
5487 fm = ui.formatter('status', opts)
5487 fm = ui.formatter('status', opts)
5488 fmt = '%s' + end
5488 fmt = '%s' + end
5489 showchar = not opts.get('no_status')
5489 showchar = not opts.get('no_status')
5490
5490
5491 for state, char, files in changestates:
5491 for state, char, files in changestates:
5492 if state in show:
5492 if state in show:
5493 label = 'status.' + state
5493 label = 'status.' + state
5494 for f in files:
5494 for f in files:
5495 fm.startitem()
5495 fm.startitem()
5496 fm.context(ctx=ctx2)
5496 fm.context(ctx=ctx2)
5497 fm.data(path=f)
5497 fm.data(path=f)
5498 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5498 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5499 fm.plain(fmt % uipathfn(f), label=label)
5499 fm.plain(fmt % uipathfn(f), label=label)
5500 if f in copy:
5500 if f in copy:
5501 fm.data(source=copy[f])
5501 fm.data(source=copy[f])
5502 fm.plain((' %s' + end) % uipathfn(copy[f]),
5502 fm.plain((' %s' + end) % uipathfn(copy[f]),
5503 label='status.copied')
5503 label='status.copied')
5504
5504
5505 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
5505 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
5506 and not ui.plain()):
5506 and not ui.plain()):
5507 cmdutil.morestatus(repo, fm)
5507 cmdutil.morestatus(repo, fm)
5508 fm.end()
5508 fm.end()
5509
5509
5510 @command('summary|sum',
5510 @command('summary|sum',
5511 [('', 'remote', None, _('check for push and pull'))],
5511 [('', 'remote', None, _('check for push and pull'))],
5512 '[--remote]',
5512 '[--remote]',
5513 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5513 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5514 helpbasic=True,
5514 helpbasic=True,
5515 intents={INTENT_READONLY})
5515 intents={INTENT_READONLY})
5516 def summary(ui, repo, **opts):
5516 def summary(ui, repo, **opts):
5517 """summarize working directory state
5517 """summarize working directory state
5518
5518
5519 This generates a brief summary of the working directory state,
5519 This generates a brief summary of the working directory state,
5520 including parents, branch, commit status, phase and available updates.
5520 including parents, branch, commit status, phase and available updates.
5521
5521
5522 With the --remote option, this will check the default paths for
5522 With the --remote option, this will check the default paths for
5523 incoming and outgoing changes. This can be time-consuming.
5523 incoming and outgoing changes. This can be time-consuming.
5524
5524
5525 Returns 0 on success.
5525 Returns 0 on success.
5526 """
5526 """
5527
5527
5528 opts = pycompat.byteskwargs(opts)
5528 opts = pycompat.byteskwargs(opts)
5529 ui.pager('summary')
5529 ui.pager('summary')
5530 ctx = repo[None]
5530 ctx = repo[None]
5531 parents = ctx.parents()
5531 parents = ctx.parents()
5532 pnode = parents[0].node()
5532 pnode = parents[0].node()
5533 marks = []
5533 marks = []
5534
5534
5535 try:
5535 try:
5536 ms = mergemod.mergestate.read(repo)
5536 ms = mergemod.mergestate.read(repo)
5537 except error.UnsupportedMergeRecords as e:
5537 except error.UnsupportedMergeRecords as e:
5538 s = ' '.join(e.recordtypes)
5538 s = ' '.join(e.recordtypes)
5539 ui.warn(
5539 ui.warn(
5540 _('warning: merge state has unsupported record types: %s\n') % s)
5540 _('warning: merge state has unsupported record types: %s\n') % s)
5541 unresolved = []
5541 unresolved = []
5542 else:
5542 else:
5543 unresolved = list(ms.unresolved())
5543 unresolved = list(ms.unresolved())
5544
5544
5545 for p in parents:
5545 for p in parents:
5546 # label with log.changeset (instead of log.parent) since this
5546 # label with log.changeset (instead of log.parent) since this
5547 # shows a working directory parent *changeset*:
5547 # shows a working directory parent *changeset*:
5548 # i18n: column positioning for "hg summary"
5548 # i18n: column positioning for "hg summary"
5549 ui.write(_('parent: %d:%s ') % (p.rev(), p),
5549 ui.write(_('parent: %d:%s ') % (p.rev(), p),
5550 label=logcmdutil.changesetlabels(p))
5550 label=logcmdutil.changesetlabels(p))
5551 ui.write(' '.join(p.tags()), label='log.tag')
5551 ui.write(' '.join(p.tags()), label='log.tag')
5552 if p.bookmarks():
5552 if p.bookmarks():
5553 marks.extend(p.bookmarks())
5553 marks.extend(p.bookmarks())
5554 if p.rev() == -1:
5554 if p.rev() == -1:
5555 if not len(repo):
5555 if not len(repo):
5556 ui.write(_(' (empty repository)'))
5556 ui.write(_(' (empty repository)'))
5557 else:
5557 else:
5558 ui.write(_(' (no revision checked out)'))
5558 ui.write(_(' (no revision checked out)'))
5559 if p.obsolete():
5559 if p.obsolete():
5560 ui.write(_(' (obsolete)'))
5560 ui.write(_(' (obsolete)'))
5561 if p.isunstable():
5561 if p.isunstable():
5562 instabilities = (ui.label(instability, 'trouble.%s' % instability)
5562 instabilities = (ui.label(instability, 'trouble.%s' % instability)
5563 for instability in p.instabilities())
5563 for instability in p.instabilities())
5564 ui.write(' ('
5564 ui.write(' ('
5565 + ', '.join(instabilities)
5565 + ', '.join(instabilities)
5566 + ')')
5566 + ')')
5567 ui.write('\n')
5567 ui.write('\n')
5568 if p.description():
5568 if p.description():
5569 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5569 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5570 label='log.summary')
5570 label='log.summary')
5571
5571
5572 branch = ctx.branch()
5572 branch = ctx.branch()
5573 bheads = repo.branchheads(branch)
5573 bheads = repo.branchheads(branch)
5574 # i18n: column positioning for "hg summary"
5574 # i18n: column positioning for "hg summary"
5575 m = _('branch: %s\n') % branch
5575 m = _('branch: %s\n') % branch
5576 if branch != 'default':
5576 if branch != 'default':
5577 ui.write(m, label='log.branch')
5577 ui.write(m, label='log.branch')
5578 else:
5578 else:
5579 ui.status(m, label='log.branch')
5579 ui.status(m, label='log.branch')
5580
5580
5581 if marks:
5581 if marks:
5582 active = repo._activebookmark
5582 active = repo._activebookmark
5583 # i18n: column positioning for "hg summary"
5583 # i18n: column positioning for "hg summary"
5584 ui.write(_('bookmarks:'), label='log.bookmark')
5584 ui.write(_('bookmarks:'), label='log.bookmark')
5585 if active is not None:
5585 if active is not None:
5586 if active in marks:
5586 if active in marks:
5587 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
5587 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
5588 marks.remove(active)
5588 marks.remove(active)
5589 else:
5589 else:
5590 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
5590 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
5591 for m in marks:
5591 for m in marks:
5592 ui.write(' ' + m, label='log.bookmark')
5592 ui.write(' ' + m, label='log.bookmark')
5593 ui.write('\n', label='log.bookmark')
5593 ui.write('\n', label='log.bookmark')
5594
5594
5595 status = repo.status(unknown=True)
5595 status = repo.status(unknown=True)
5596
5596
5597 c = repo.dirstate.copies()
5597 c = repo.dirstate.copies()
5598 copied, renamed = [], []
5598 copied, renamed = [], []
5599 for d, s in c.iteritems():
5599 for d, s in c.iteritems():
5600 if s in status.removed:
5600 if s in status.removed:
5601 status.removed.remove(s)
5601 status.removed.remove(s)
5602 renamed.append(d)
5602 renamed.append(d)
5603 else:
5603 else:
5604 copied.append(d)
5604 copied.append(d)
5605 if d in status.added:
5605 if d in status.added:
5606 status.added.remove(d)
5606 status.added.remove(d)
5607
5607
5608 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5608 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5609
5609
5610 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5610 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5611 (ui.label(_('%d added'), 'status.added'), status.added),
5611 (ui.label(_('%d added'), 'status.added'), status.added),
5612 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5612 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5613 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5613 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5614 (ui.label(_('%d copied'), 'status.copied'), copied),
5614 (ui.label(_('%d copied'), 'status.copied'), copied),
5615 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5615 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5616 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5616 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5617 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5617 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5618 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5618 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5619 t = []
5619 t = []
5620 for l, s in labels:
5620 for l, s in labels:
5621 if s:
5621 if s:
5622 t.append(l % len(s))
5622 t.append(l % len(s))
5623
5623
5624 t = ', '.join(t)
5624 t = ', '.join(t)
5625 cleanworkdir = False
5625 cleanworkdir = False
5626
5626
5627 if repo.vfs.exists('graftstate'):
5627 if repo.vfs.exists('graftstate'):
5628 t += _(' (graft in progress)')
5628 t += _(' (graft in progress)')
5629 if repo.vfs.exists('updatestate'):
5629 if repo.vfs.exists('updatestate'):
5630 t += _(' (interrupted update)')
5630 t += _(' (interrupted update)')
5631 elif len(parents) > 1:
5631 elif len(parents) > 1:
5632 t += _(' (merge)')
5632 t += _(' (merge)')
5633 elif branch != parents[0].branch():
5633 elif branch != parents[0].branch():
5634 t += _(' (new branch)')
5634 t += _(' (new branch)')
5635 elif (parents[0].closesbranch() and
5635 elif (parents[0].closesbranch() and
5636 pnode in repo.branchheads(branch, closed=True)):
5636 pnode in repo.branchheads(branch, closed=True)):
5637 t += _(' (head closed)')
5637 t += _(' (head closed)')
5638 elif not (status.modified or status.added or status.removed or renamed or
5638 elif not (status.modified or status.added or status.removed or renamed or
5639 copied or subs):
5639 copied or subs):
5640 t += _(' (clean)')
5640 t += _(' (clean)')
5641 cleanworkdir = True
5641 cleanworkdir = True
5642 elif pnode not in bheads:
5642 elif pnode not in bheads:
5643 t += _(' (new branch head)')
5643 t += _(' (new branch head)')
5644
5644
5645 if parents:
5645 if parents:
5646 pendingphase = max(p.phase() for p in parents)
5646 pendingphase = max(p.phase() for p in parents)
5647 else:
5647 else:
5648 pendingphase = phases.public
5648 pendingphase = phases.public
5649
5649
5650 if pendingphase > phases.newcommitphase(ui):
5650 if pendingphase > phases.newcommitphase(ui):
5651 t += ' (%s)' % phases.phasenames[pendingphase]
5651 t += ' (%s)' % phases.phasenames[pendingphase]
5652
5652
5653 if cleanworkdir:
5653 if cleanworkdir:
5654 # i18n: column positioning for "hg summary"
5654 # i18n: column positioning for "hg summary"
5655 ui.status(_('commit: %s\n') % t.strip())
5655 ui.status(_('commit: %s\n') % t.strip())
5656 else:
5656 else:
5657 # i18n: column positioning for "hg summary"
5657 # i18n: column positioning for "hg summary"
5658 ui.write(_('commit: %s\n') % t.strip())
5658 ui.write(_('commit: %s\n') % t.strip())
5659
5659
5660 # all ancestors of branch heads - all ancestors of parent = new csets
5660 # all ancestors of branch heads - all ancestors of parent = new csets
5661 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5661 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5662 bheads))
5662 bheads))
5663
5663
5664 if new == 0:
5664 if new == 0:
5665 # i18n: column positioning for "hg summary"
5665 # i18n: column positioning for "hg summary"
5666 ui.status(_('update: (current)\n'))
5666 ui.status(_('update: (current)\n'))
5667 elif pnode not in bheads:
5667 elif pnode not in bheads:
5668 # i18n: column positioning for "hg summary"
5668 # i18n: column positioning for "hg summary"
5669 ui.write(_('update: %d new changesets (update)\n') % new)
5669 ui.write(_('update: %d new changesets (update)\n') % new)
5670 else:
5670 else:
5671 # i18n: column positioning for "hg summary"
5671 # i18n: column positioning for "hg summary"
5672 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5672 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5673 (new, len(bheads)))
5673 (new, len(bheads)))
5674
5674
5675 t = []
5675 t = []
5676 draft = len(repo.revs('draft()'))
5676 draft = len(repo.revs('draft()'))
5677 if draft:
5677 if draft:
5678 t.append(_('%d draft') % draft)
5678 t.append(_('%d draft') % draft)
5679 secret = len(repo.revs('secret()'))
5679 secret = len(repo.revs('secret()'))
5680 if secret:
5680 if secret:
5681 t.append(_('%d secret') % secret)
5681 t.append(_('%d secret') % secret)
5682
5682
5683 if draft or secret:
5683 if draft or secret:
5684 ui.status(_('phases: %s\n') % ', '.join(t))
5684 ui.status(_('phases: %s\n') % ', '.join(t))
5685
5685
5686 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5686 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5687 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5687 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5688 numtrouble = len(repo.revs(trouble + "()"))
5688 numtrouble = len(repo.revs(trouble + "()"))
5689 # We write all the possibilities to ease translation
5689 # We write all the possibilities to ease translation
5690 troublemsg = {
5690 troublemsg = {
5691 "orphan": _("orphan: %d changesets"),
5691 "orphan": _("orphan: %d changesets"),
5692 "contentdivergent": _("content-divergent: %d changesets"),
5692 "contentdivergent": _("content-divergent: %d changesets"),
5693 "phasedivergent": _("phase-divergent: %d changesets"),
5693 "phasedivergent": _("phase-divergent: %d changesets"),
5694 }
5694 }
5695 if numtrouble > 0:
5695 if numtrouble > 0:
5696 ui.status(troublemsg[trouble] % numtrouble + "\n")
5696 ui.status(troublemsg[trouble] % numtrouble + "\n")
5697
5697
5698 cmdutil.summaryhooks(ui, repo)
5698 cmdutil.summaryhooks(ui, repo)
5699
5699
5700 if opts.get('remote'):
5700 if opts.get('remote'):
5701 needsincoming, needsoutgoing = True, True
5701 needsincoming, needsoutgoing = True, True
5702 else:
5702 else:
5703 needsincoming, needsoutgoing = False, False
5703 needsincoming, needsoutgoing = False, False
5704 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5704 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5705 if i:
5705 if i:
5706 needsincoming = True
5706 needsincoming = True
5707 if o:
5707 if o:
5708 needsoutgoing = True
5708 needsoutgoing = True
5709 if not needsincoming and not needsoutgoing:
5709 if not needsincoming and not needsoutgoing:
5710 return
5710 return
5711
5711
5712 def getincoming():
5712 def getincoming():
5713 source, branches = hg.parseurl(ui.expandpath('default'))
5713 source, branches = hg.parseurl(ui.expandpath('default'))
5714 sbranch = branches[0]
5714 sbranch = branches[0]
5715 try:
5715 try:
5716 other = hg.peer(repo, {}, source)
5716 other = hg.peer(repo, {}, source)
5717 except error.RepoError:
5717 except error.RepoError:
5718 if opts.get('remote'):
5718 if opts.get('remote'):
5719 raise
5719 raise
5720 return source, sbranch, None, None, None
5720 return source, sbranch, None, None, None
5721 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5721 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5722 if revs:
5722 if revs:
5723 revs = [other.lookup(rev) for rev in revs]
5723 revs = [other.lookup(rev) for rev in revs]
5724 ui.debug('comparing with %s\n' % util.hidepassword(source))
5724 ui.debug('comparing with %s\n' % util.hidepassword(source))
5725 repo.ui.pushbuffer()
5725 repo.ui.pushbuffer()
5726 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5726 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5727 repo.ui.popbuffer()
5727 repo.ui.popbuffer()
5728 return source, sbranch, other, commoninc, commoninc[1]
5728 return source, sbranch, other, commoninc, commoninc[1]
5729
5729
5730 if needsincoming:
5730 if needsincoming:
5731 source, sbranch, sother, commoninc, incoming = getincoming()
5731 source, sbranch, sother, commoninc, incoming = getincoming()
5732 else:
5732 else:
5733 source = sbranch = sother = commoninc = incoming = None
5733 source = sbranch = sother = commoninc = incoming = None
5734
5734
5735 def getoutgoing():
5735 def getoutgoing():
5736 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5736 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5737 dbranch = branches[0]
5737 dbranch = branches[0]
5738 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5738 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5739 if source != dest:
5739 if source != dest:
5740 try:
5740 try:
5741 dother = hg.peer(repo, {}, dest)
5741 dother = hg.peer(repo, {}, dest)
5742 except error.RepoError:
5742 except error.RepoError:
5743 if opts.get('remote'):
5743 if opts.get('remote'):
5744 raise
5744 raise
5745 return dest, dbranch, None, None
5745 return dest, dbranch, None, None
5746 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5746 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5747 elif sother is None:
5747 elif sother is None:
5748 # there is no explicit destination peer, but source one is invalid
5748 # there is no explicit destination peer, but source one is invalid
5749 return dest, dbranch, None, None
5749 return dest, dbranch, None, None
5750 else:
5750 else:
5751 dother = sother
5751 dother = sother
5752 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5752 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5753 common = None
5753 common = None
5754 else:
5754 else:
5755 common = commoninc
5755 common = commoninc
5756 if revs:
5756 if revs:
5757 revs = [repo.lookup(rev) for rev in revs]
5757 revs = [repo.lookup(rev) for rev in revs]
5758 repo.ui.pushbuffer()
5758 repo.ui.pushbuffer()
5759 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5759 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5760 commoninc=common)
5760 commoninc=common)
5761 repo.ui.popbuffer()
5761 repo.ui.popbuffer()
5762 return dest, dbranch, dother, outgoing
5762 return dest, dbranch, dother, outgoing
5763
5763
5764 if needsoutgoing:
5764 if needsoutgoing:
5765 dest, dbranch, dother, outgoing = getoutgoing()
5765 dest, dbranch, dother, outgoing = getoutgoing()
5766 else:
5766 else:
5767 dest = dbranch = dother = outgoing = None
5767 dest = dbranch = dother = outgoing = None
5768
5768
5769 if opts.get('remote'):
5769 if opts.get('remote'):
5770 t = []
5770 t = []
5771 if incoming:
5771 if incoming:
5772 t.append(_('1 or more incoming'))
5772 t.append(_('1 or more incoming'))
5773 o = outgoing.missing
5773 o = outgoing.missing
5774 if o:
5774 if o:
5775 t.append(_('%d outgoing') % len(o))
5775 t.append(_('%d outgoing') % len(o))
5776 other = dother or sother
5776 other = dother or sother
5777 if 'bookmarks' in other.listkeys('namespaces'):
5777 if 'bookmarks' in other.listkeys('namespaces'):
5778 counts = bookmarks.summary(repo, other)
5778 counts = bookmarks.summary(repo, other)
5779 if counts[0] > 0:
5779 if counts[0] > 0:
5780 t.append(_('%d incoming bookmarks') % counts[0])
5780 t.append(_('%d incoming bookmarks') % counts[0])
5781 if counts[1] > 0:
5781 if counts[1] > 0:
5782 t.append(_('%d outgoing bookmarks') % counts[1])
5782 t.append(_('%d outgoing bookmarks') % counts[1])
5783
5783
5784 if t:
5784 if t:
5785 # i18n: column positioning for "hg summary"
5785 # i18n: column positioning for "hg summary"
5786 ui.write(_('remote: %s\n') % (', '.join(t)))
5786 ui.write(_('remote: %s\n') % (', '.join(t)))
5787 else:
5787 else:
5788 # i18n: column positioning for "hg summary"
5788 # i18n: column positioning for "hg summary"
5789 ui.status(_('remote: (synced)\n'))
5789 ui.status(_('remote: (synced)\n'))
5790
5790
5791 cmdutil.summaryremotehooks(ui, repo, opts,
5791 cmdutil.summaryremotehooks(ui, repo, opts,
5792 ((source, sbranch, sother, commoninc),
5792 ((source, sbranch, sother, commoninc),
5793 (dest, dbranch, dother, outgoing)))
5793 (dest, dbranch, dother, outgoing)))
5794
5794
5795 @command('tag',
5795 @command('tag',
5796 [('f', 'force', None, _('force tag')),
5796 [('f', 'force', None, _('force tag')),
5797 ('l', 'local', None, _('make the tag local')),
5797 ('l', 'local', None, _('make the tag local')),
5798 ('r', 'rev', '', _('revision to tag'), _('REV')),
5798 ('r', 'rev', '', _('revision to tag'), _('REV')),
5799 ('', 'remove', None, _('remove a tag')),
5799 ('', 'remove', None, _('remove a tag')),
5800 # -l/--local is already there, commitopts cannot be used
5800 # -l/--local is already there, commitopts cannot be used
5801 ('e', 'edit', None, _('invoke editor on commit messages')),
5801 ('e', 'edit', None, _('invoke editor on commit messages')),
5802 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5802 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5803 ] + commitopts2,
5803 ] + commitopts2,
5804 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
5804 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
5805 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
5805 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
5806 def tag(ui, repo, name1, *names, **opts):
5806 def tag(ui, repo, name1, *names, **opts):
5807 """add one or more tags for the current or given revision
5807 """add one or more tags for the current or given revision
5808
5808
5809 Name a particular revision using <name>.
5809 Name a particular revision using <name>.
5810
5810
5811 Tags are used to name particular revisions of the repository and are
5811 Tags are used to name particular revisions of the repository and are
5812 very useful to compare different revisions, to go back to significant
5812 very useful to compare different revisions, to go back to significant
5813 earlier versions or to mark branch points as releases, etc. Changing
5813 earlier versions or to mark branch points as releases, etc. Changing
5814 an existing tag is normally disallowed; use -f/--force to override.
5814 an existing tag is normally disallowed; use -f/--force to override.
5815
5815
5816 If no revision is given, the parent of the working directory is
5816 If no revision is given, the parent of the working directory is
5817 used.
5817 used.
5818
5818
5819 To facilitate version control, distribution, and merging of tags,
5819 To facilitate version control, distribution, and merging of tags,
5820 they are stored as a file named ".hgtags" which is managed similarly
5820 they are stored as a file named ".hgtags" which is managed similarly
5821 to other project files and can be hand-edited if necessary. This
5821 to other project files and can be hand-edited if necessary. This
5822 also means that tagging creates a new commit. The file
5822 also means that tagging creates a new commit. The file
5823 ".hg/localtags" is used for local tags (not shared among
5823 ".hg/localtags" is used for local tags (not shared among
5824 repositories).
5824 repositories).
5825
5825
5826 Tag commits are usually made at the head of a branch. If the parent
5826 Tag commits are usually made at the head of a branch. If the parent
5827 of the working directory is not a branch head, :hg:`tag` aborts; use
5827 of the working directory is not a branch head, :hg:`tag` aborts; use
5828 -f/--force to force the tag commit to be based on a non-head
5828 -f/--force to force the tag commit to be based on a non-head
5829 changeset.
5829 changeset.
5830
5830
5831 See :hg:`help dates` for a list of formats valid for -d/--date.
5831 See :hg:`help dates` for a list of formats valid for -d/--date.
5832
5832
5833 Since tag names have priority over branch names during revision
5833 Since tag names have priority over branch names during revision
5834 lookup, using an existing branch name as a tag name is discouraged.
5834 lookup, using an existing branch name as a tag name is discouraged.
5835
5835
5836 Returns 0 on success.
5836 Returns 0 on success.
5837 """
5837 """
5838 opts = pycompat.byteskwargs(opts)
5838 opts = pycompat.byteskwargs(opts)
5839 with repo.wlock(), repo.lock():
5839 with repo.wlock(), repo.lock():
5840 rev_ = "."
5840 rev_ = "."
5841 names = [t.strip() for t in (name1,) + names]
5841 names = [t.strip() for t in (name1,) + names]
5842 if len(names) != len(set(names)):
5842 if len(names) != len(set(names)):
5843 raise error.Abort(_('tag names must be unique'))
5843 raise error.Abort(_('tag names must be unique'))
5844 for n in names:
5844 for n in names:
5845 scmutil.checknewlabel(repo, n, 'tag')
5845 scmutil.checknewlabel(repo, n, 'tag')
5846 if not n:
5846 if not n:
5847 raise error.Abort(_('tag names cannot consist entirely of '
5847 raise error.Abort(_('tag names cannot consist entirely of '
5848 'whitespace'))
5848 'whitespace'))
5849 if opts.get('rev') and opts.get('remove'):
5849 if opts.get('rev') and opts.get('remove'):
5850 raise error.Abort(_("--rev and --remove are incompatible"))
5850 raise error.Abort(_("--rev and --remove are incompatible"))
5851 if opts.get('rev'):
5851 if opts.get('rev'):
5852 rev_ = opts['rev']
5852 rev_ = opts['rev']
5853 message = opts.get('message')
5853 message = opts.get('message')
5854 if opts.get('remove'):
5854 if opts.get('remove'):
5855 if opts.get('local'):
5855 if opts.get('local'):
5856 expectedtype = 'local'
5856 expectedtype = 'local'
5857 else:
5857 else:
5858 expectedtype = 'global'
5858 expectedtype = 'global'
5859
5859
5860 for n in names:
5860 for n in names:
5861 if repo.tagtype(n) == 'global':
5861 if repo.tagtype(n) == 'global':
5862 alltags = tagsmod.findglobaltags(ui, repo)
5862 alltags = tagsmod.findglobaltags(ui, repo)
5863 if alltags[n][0] == nullid:
5863 if alltags[n][0] == nullid:
5864 raise error.Abort(_("tag '%s' is already removed") % n)
5864 raise error.Abort(_("tag '%s' is already removed") % n)
5865 if not repo.tagtype(n):
5865 if not repo.tagtype(n):
5866 raise error.Abort(_("tag '%s' does not exist") % n)
5866 raise error.Abort(_("tag '%s' does not exist") % n)
5867 if repo.tagtype(n) != expectedtype:
5867 if repo.tagtype(n) != expectedtype:
5868 if expectedtype == 'global':
5868 if expectedtype == 'global':
5869 raise error.Abort(_("tag '%s' is not a global tag") % n)
5869 raise error.Abort(_("tag '%s' is not a global tag") % n)
5870 else:
5870 else:
5871 raise error.Abort(_("tag '%s' is not a local tag") % n)
5871 raise error.Abort(_("tag '%s' is not a local tag") % n)
5872 rev_ = 'null'
5872 rev_ = 'null'
5873 if not message:
5873 if not message:
5874 # we don't translate commit messages
5874 # we don't translate commit messages
5875 message = 'Removed tag %s' % ', '.join(names)
5875 message = 'Removed tag %s' % ', '.join(names)
5876 elif not opts.get('force'):
5876 elif not opts.get('force'):
5877 for n in names:
5877 for n in names:
5878 if n in repo.tags():
5878 if n in repo.tags():
5879 raise error.Abort(_("tag '%s' already exists "
5879 raise error.Abort(_("tag '%s' already exists "
5880 "(use -f to force)") % n)
5880 "(use -f to force)") % n)
5881 if not opts.get('local'):
5881 if not opts.get('local'):
5882 p1, p2 = repo.dirstate.parents()
5882 p1, p2 = repo.dirstate.parents()
5883 if p2 != nullid:
5883 if p2 != nullid:
5884 raise error.Abort(_('uncommitted merge'))
5884 raise error.Abort(_('uncommitted merge'))
5885 bheads = repo.branchheads()
5885 bheads = repo.branchheads()
5886 if not opts.get('force') and bheads and p1 not in bheads:
5886 if not opts.get('force') and bheads and p1 not in bheads:
5887 raise error.Abort(_('working directory is not at a branch head '
5887 raise error.Abort(_('working directory is not at a branch head '
5888 '(use -f to force)'))
5888 '(use -f to force)'))
5889 node = scmutil.revsingle(repo, rev_).node()
5889 node = scmutil.revsingle(repo, rev_).node()
5890
5890
5891 if not message:
5891 if not message:
5892 # we don't translate commit messages
5892 # we don't translate commit messages
5893 message = ('Added tag %s for changeset %s' %
5893 message = ('Added tag %s for changeset %s' %
5894 (', '.join(names), short(node)))
5894 (', '.join(names), short(node)))
5895
5895
5896 date = opts.get('date')
5896 date = opts.get('date')
5897 if date:
5897 if date:
5898 date = dateutil.parsedate(date)
5898 date = dateutil.parsedate(date)
5899
5899
5900 if opts.get('remove'):
5900 if opts.get('remove'):
5901 editform = 'tag.remove'
5901 editform = 'tag.remove'
5902 else:
5902 else:
5903 editform = 'tag.add'
5903 editform = 'tag.add'
5904 editor = cmdutil.getcommiteditor(editform=editform,
5904 editor = cmdutil.getcommiteditor(editform=editform,
5905 **pycompat.strkwargs(opts))
5905 **pycompat.strkwargs(opts))
5906
5906
5907 # don't allow tagging the null rev
5907 # don't allow tagging the null rev
5908 if (not opts.get('remove') and
5908 if (not opts.get('remove') and
5909 scmutil.revsingle(repo, rev_).rev() == nullrev):
5909 scmutil.revsingle(repo, rev_).rev() == nullrev):
5910 raise error.Abort(_("cannot tag null revision"))
5910 raise error.Abort(_("cannot tag null revision"))
5911
5911
5912 tagsmod.tag(repo, names, node, message, opts.get('local'),
5912 tagsmod.tag(repo, names, node, message, opts.get('local'),
5913 opts.get('user'), date, editor=editor)
5913 opts.get('user'), date, editor=editor)
5914
5914
5915 @command(
5915 @command(
5916 'tags', formatteropts, '',
5916 'tags', formatteropts, '',
5917 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5917 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5918 intents={INTENT_READONLY})
5918 intents={INTENT_READONLY})
5919 def tags(ui, repo, **opts):
5919 def tags(ui, repo, **opts):
5920 """list repository tags
5920 """list repository tags
5921
5921
5922 This lists both regular and local tags. When the -v/--verbose
5922 This lists both regular and local tags. When the -v/--verbose
5923 switch is used, a third column "local" is printed for local tags.
5923 switch is used, a third column "local" is printed for local tags.
5924 When the -q/--quiet switch is used, only the tag name is printed.
5924 When the -q/--quiet switch is used, only the tag name is printed.
5925
5925
5926 .. container:: verbose
5926 .. container:: verbose
5927
5927
5928 Template:
5928 Template:
5929
5929
5930 The following keywords are supported in addition to the common template
5930 The following keywords are supported in addition to the common template
5931 keywords and functions such as ``{tag}``. See also
5931 keywords and functions such as ``{tag}``. See also
5932 :hg:`help templates`.
5932 :hg:`help templates`.
5933
5933
5934 :type: String. ``local`` for local tags.
5934 :type: String. ``local`` for local tags.
5935
5935
5936 Returns 0 on success.
5936 Returns 0 on success.
5937 """
5937 """
5938
5938
5939 opts = pycompat.byteskwargs(opts)
5939 opts = pycompat.byteskwargs(opts)
5940 ui.pager('tags')
5940 ui.pager('tags')
5941 fm = ui.formatter('tags', opts)
5941 fm = ui.formatter('tags', opts)
5942 hexfunc = fm.hexfunc
5942 hexfunc = fm.hexfunc
5943
5943
5944 for t, n in reversed(repo.tagslist()):
5944 for t, n in reversed(repo.tagslist()):
5945 hn = hexfunc(n)
5945 hn = hexfunc(n)
5946 label = 'tags.normal'
5946 label = 'tags.normal'
5947 tagtype = ''
5947 tagtype = ''
5948 if repo.tagtype(t) == 'local':
5948 if repo.tagtype(t) == 'local':
5949 label = 'tags.local'
5949 label = 'tags.local'
5950 tagtype = 'local'
5950 tagtype = 'local'
5951
5951
5952 fm.startitem()
5952 fm.startitem()
5953 fm.context(repo=repo)
5953 fm.context(repo=repo)
5954 fm.write('tag', '%s', t, label=label)
5954 fm.write('tag', '%s', t, label=label)
5955 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5955 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5956 fm.condwrite(not ui.quiet, 'rev node', fmt,
5956 fm.condwrite(not ui.quiet, 'rev node', fmt,
5957 repo.changelog.rev(n), hn, label=label)
5957 repo.changelog.rev(n), hn, label=label)
5958 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5958 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5959 tagtype, label=label)
5959 tagtype, label=label)
5960 fm.plain('\n')
5960 fm.plain('\n')
5961 fm.end()
5961 fm.end()
5962
5962
5963 @command('tip',
5963 @command('tip',
5964 [('p', 'patch', None, _('show patch')),
5964 [('p', 'patch', None, _('show patch')),
5965 ('g', 'git', None, _('use git extended diff format')),
5965 ('g', 'git', None, _('use git extended diff format')),
5966 ] + templateopts,
5966 ] + templateopts,
5967 _('[-p] [-g]'),
5967 _('[-p] [-g]'),
5968 helpcategory=command.CATEGORY_CHANGE_NAVIGATION)
5968 helpcategory=command.CATEGORY_CHANGE_NAVIGATION)
5969 def tip(ui, repo, **opts):
5969 def tip(ui, repo, **opts):
5970 """show the tip revision (DEPRECATED)
5970 """show the tip revision (DEPRECATED)
5971
5971
5972 The tip revision (usually just called the tip) is the changeset
5972 The tip revision (usually just called the tip) is the changeset
5973 most recently added to the repository (and therefore the most
5973 most recently added to the repository (and therefore the most
5974 recently changed head).
5974 recently changed head).
5975
5975
5976 If you have just made a commit, that commit will be the tip. If
5976 If you have just made a commit, that commit will be the tip. If
5977 you have just pulled changes from another repository, the tip of
5977 you have just pulled changes from another repository, the tip of
5978 that repository becomes the current tip. The "tip" tag is special
5978 that repository becomes the current tip. The "tip" tag is special
5979 and cannot be renamed or assigned to a different changeset.
5979 and cannot be renamed or assigned to a different changeset.
5980
5980
5981 This command is deprecated, please use :hg:`heads` instead.
5981 This command is deprecated, please use :hg:`heads` instead.
5982
5982
5983 Returns 0 on success.
5983 Returns 0 on success.
5984 """
5984 """
5985 opts = pycompat.byteskwargs(opts)
5985 opts = pycompat.byteskwargs(opts)
5986 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5986 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5987 displayer.show(repo['tip'])
5987 displayer.show(repo['tip'])
5988 displayer.close()
5988 displayer.close()
5989
5989
5990 @command('unbundle',
5990 @command('unbundle',
5991 [('u', 'update', None,
5991 [('u', 'update', None,
5992 _('update to new branch head if changesets were unbundled'))],
5992 _('update to new branch head if changesets were unbundled'))],
5993 _('[-u] FILE...'),
5993 _('[-u] FILE...'),
5994 helpcategory=command.CATEGORY_IMPORT_EXPORT)
5994 helpcategory=command.CATEGORY_IMPORT_EXPORT)
5995 def unbundle(ui, repo, fname1, *fnames, **opts):
5995 def unbundle(ui, repo, fname1, *fnames, **opts):
5996 """apply one or more bundle files
5996 """apply one or more bundle files
5997
5997
5998 Apply one or more bundle files generated by :hg:`bundle`.
5998 Apply one or more bundle files generated by :hg:`bundle`.
5999
5999
6000 Returns 0 on success, 1 if an update has unresolved files.
6000 Returns 0 on success, 1 if an update has unresolved files.
6001 """
6001 """
6002 fnames = (fname1,) + fnames
6002 fnames = (fname1,) + fnames
6003
6003
6004 with repo.lock():
6004 with repo.lock():
6005 for fname in fnames:
6005 for fname in fnames:
6006 f = hg.openpath(ui, fname)
6006 f = hg.openpath(ui, fname)
6007 gen = exchange.readbundle(ui, f, fname)
6007 gen = exchange.readbundle(ui, f, fname)
6008 if isinstance(gen, streamclone.streamcloneapplier):
6008 if isinstance(gen, streamclone.streamcloneapplier):
6009 raise error.Abort(
6009 raise error.Abort(
6010 _('packed bundles cannot be applied with '
6010 _('packed bundles cannot be applied with '
6011 '"hg unbundle"'),
6011 '"hg unbundle"'),
6012 hint=_('use "hg debugapplystreamclonebundle"'))
6012 hint=_('use "hg debugapplystreamclonebundle"'))
6013 url = 'bundle:' + fname
6013 url = 'bundle:' + fname
6014 try:
6014 try:
6015 txnname = 'unbundle'
6015 txnname = 'unbundle'
6016 if not isinstance(gen, bundle2.unbundle20):
6016 if not isinstance(gen, bundle2.unbundle20):
6017 txnname = 'unbundle\n%s' % util.hidepassword(url)
6017 txnname = 'unbundle\n%s' % util.hidepassword(url)
6018 with repo.transaction(txnname) as tr:
6018 with repo.transaction(txnname) as tr:
6019 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
6019 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
6020 url=url)
6020 url=url)
6021 except error.BundleUnknownFeatureError as exc:
6021 except error.BundleUnknownFeatureError as exc:
6022 raise error.Abort(
6022 raise error.Abort(
6023 _('%s: unknown bundle feature, %s') % (fname, exc),
6023 _('%s: unknown bundle feature, %s') % (fname, exc),
6024 hint=_("see https://mercurial-scm.org/"
6024 hint=_("see https://mercurial-scm.org/"
6025 "wiki/BundleFeature for more "
6025 "wiki/BundleFeature for more "
6026 "information"))
6026 "information"))
6027 modheads = bundle2.combinechangegroupresults(op)
6027 modheads = bundle2.combinechangegroupresults(op)
6028
6028
6029 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
6029 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
6030
6030
6031 @command('update|up|checkout|co',
6031 @command('update|up|checkout|co',
6032 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6032 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6033 ('c', 'check', None, _('require clean working directory')),
6033 ('c', 'check', None, _('require clean working directory')),
6034 ('m', 'merge', None, _('merge uncommitted changes')),
6034 ('m', 'merge', None, _('merge uncommitted changes')),
6035 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6035 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6036 ('r', 'rev', '', _('revision'), _('REV'))
6036 ('r', 'rev', '', _('revision'), _('REV'))
6037 ] + mergetoolopts,
6037 ] + mergetoolopts,
6038 _('[-C|-c|-m] [-d DATE] [[-r] REV]'),
6038 _('[-C|-c|-m] [-d DATE] [[-r] REV]'),
6039 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6039 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6040 helpbasic=True)
6040 helpbasic=True)
6041 def update(ui, repo, node=None, **opts):
6041 def update(ui, repo, node=None, **opts):
6042 """update working directory (or switch revisions)
6042 """update working directory (or switch revisions)
6043
6043
6044 Update the repository's working directory to the specified
6044 Update the repository's working directory to the specified
6045 changeset. If no changeset is specified, update to the tip of the
6045 changeset. If no changeset is specified, update to the tip of the
6046 current named branch and move the active bookmark (see :hg:`help
6046 current named branch and move the active bookmark (see :hg:`help
6047 bookmarks`).
6047 bookmarks`).
6048
6048
6049 Update sets the working directory's parent revision to the specified
6049 Update sets the working directory's parent revision to the specified
6050 changeset (see :hg:`help parents`).
6050 changeset (see :hg:`help parents`).
6051
6051
6052 If the changeset is not a descendant or ancestor of the working
6052 If the changeset is not a descendant or ancestor of the working
6053 directory's parent and there are uncommitted changes, the update is
6053 directory's parent and there are uncommitted changes, the update is
6054 aborted. With the -c/--check option, the working directory is checked
6054 aborted. With the -c/--check option, the working directory is checked
6055 for uncommitted changes; if none are found, the working directory is
6055 for uncommitted changes; if none are found, the working directory is
6056 updated to the specified changeset.
6056 updated to the specified changeset.
6057
6057
6058 .. container:: verbose
6058 .. container:: verbose
6059
6059
6060 The -C/--clean, -c/--check, and -m/--merge options control what
6060 The -C/--clean, -c/--check, and -m/--merge options control what
6061 happens if the working directory contains uncommitted changes.
6061 happens if the working directory contains uncommitted changes.
6062 At most of one of them can be specified.
6062 At most of one of them can be specified.
6063
6063
6064 1. If no option is specified, and if
6064 1. If no option is specified, and if
6065 the requested changeset is an ancestor or descendant of
6065 the requested changeset is an ancestor or descendant of
6066 the working directory's parent, the uncommitted changes
6066 the working directory's parent, the uncommitted changes
6067 are merged into the requested changeset and the merged
6067 are merged into the requested changeset and the merged
6068 result is left uncommitted. If the requested changeset is
6068 result is left uncommitted. If the requested changeset is
6069 not an ancestor or descendant (that is, it is on another
6069 not an ancestor or descendant (that is, it is on another
6070 branch), the update is aborted and the uncommitted changes
6070 branch), the update is aborted and the uncommitted changes
6071 are preserved.
6071 are preserved.
6072
6072
6073 2. With the -m/--merge option, the update is allowed even if the
6073 2. With the -m/--merge option, the update is allowed even if the
6074 requested changeset is not an ancestor or descendant of
6074 requested changeset is not an ancestor or descendant of
6075 the working directory's parent.
6075 the working directory's parent.
6076
6076
6077 3. With the -c/--check option, the update is aborted and the
6077 3. With the -c/--check option, the update is aborted and the
6078 uncommitted changes are preserved.
6078 uncommitted changes are preserved.
6079
6079
6080 4. With the -C/--clean option, uncommitted changes are discarded and
6080 4. With the -C/--clean option, uncommitted changes are discarded and
6081 the working directory is updated to the requested changeset.
6081 the working directory is updated to the requested changeset.
6082
6082
6083 To cancel an uncommitted merge (and lose your changes), use
6083 To cancel an uncommitted merge (and lose your changes), use
6084 :hg:`merge --abort`.
6084 :hg:`merge --abort`.
6085
6085
6086 Use null as the changeset to remove the working directory (like
6086 Use null as the changeset to remove the working directory (like
6087 :hg:`clone -U`).
6087 :hg:`clone -U`).
6088
6088
6089 If you want to revert just one file to an older revision, use
6089 If you want to revert just one file to an older revision, use
6090 :hg:`revert [-r REV] NAME`.
6090 :hg:`revert [-r REV] NAME`.
6091
6091
6092 See :hg:`help dates` for a list of formats valid for -d/--date.
6092 See :hg:`help dates` for a list of formats valid for -d/--date.
6093
6093
6094 Returns 0 on success, 1 if there are unresolved files.
6094 Returns 0 on success, 1 if there are unresolved files.
6095 """
6095 """
6096 rev = opts.get(r'rev')
6096 rev = opts.get(r'rev')
6097 date = opts.get(r'date')
6097 date = opts.get(r'date')
6098 clean = opts.get(r'clean')
6098 clean = opts.get(r'clean')
6099 check = opts.get(r'check')
6099 check = opts.get(r'check')
6100 merge = opts.get(r'merge')
6100 merge = opts.get(r'merge')
6101 if rev and node:
6101 if rev and node:
6102 raise error.Abort(_("please specify just one revision"))
6102 raise error.Abort(_("please specify just one revision"))
6103
6103
6104 if ui.configbool('commands', 'update.requiredest'):
6104 if ui.configbool('commands', 'update.requiredest'):
6105 if not node and not rev and not date:
6105 if not node and not rev and not date:
6106 raise error.Abort(_('you must specify a destination'),
6106 raise error.Abort(_('you must specify a destination'),
6107 hint=_('for example: hg update ".::"'))
6107 hint=_('for example: hg update ".::"'))
6108
6108
6109 if rev is None or rev == '':
6109 if rev is None or rev == '':
6110 rev = node
6110 rev = node
6111
6111
6112 if date and rev is not None:
6112 if date and rev is not None:
6113 raise error.Abort(_("you can't specify a revision and a date"))
6113 raise error.Abort(_("you can't specify a revision and a date"))
6114
6114
6115 if len([x for x in (clean, check, merge) if x]) > 1:
6115 if len([x for x in (clean, check, merge) if x]) > 1:
6116 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
6116 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
6117 "or -m/--merge"))
6117 "or -m/--merge"))
6118
6118
6119 updatecheck = None
6119 updatecheck = None
6120 if check:
6120 if check:
6121 updatecheck = 'abort'
6121 updatecheck = 'abort'
6122 elif merge:
6122 elif merge:
6123 updatecheck = 'none'
6123 updatecheck = 'none'
6124
6124
6125 with repo.wlock():
6125 with repo.wlock():
6126 cmdutil.clearunfinished(repo)
6126 cmdutil.clearunfinished(repo)
6127
6127
6128 if date:
6128 if date:
6129 rev = cmdutil.finddate(ui, repo, date)
6129 rev = cmdutil.finddate(ui, repo, date)
6130
6130
6131 # if we defined a bookmark, we have to remember the original name
6131 # if we defined a bookmark, we have to remember the original name
6132 brev = rev
6132 brev = rev
6133 if rev:
6133 if rev:
6134 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
6134 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
6135 ctx = scmutil.revsingle(repo, rev, default=None)
6135 ctx = scmutil.revsingle(repo, rev, default=None)
6136 rev = ctx.rev()
6136 rev = ctx.rev()
6137 hidden = ctx.hidden()
6137 hidden = ctx.hidden()
6138 overrides = {('ui', 'forcemerge'): opts.get(r'tool', '')}
6138 overrides = {('ui', 'forcemerge'): opts.get(r'tool', '')}
6139 with ui.configoverride(overrides, 'update'):
6139 with ui.configoverride(overrides, 'update'):
6140 ret = hg.updatetotally(ui, repo, rev, brev, clean=clean,
6140 ret = hg.updatetotally(ui, repo, rev, brev, clean=clean,
6141 updatecheck=updatecheck)
6141 updatecheck=updatecheck)
6142 if hidden:
6142 if hidden:
6143 ctxstr = ctx.hex()[:12]
6143 ctxstr = ctx.hex()[:12]
6144 ui.warn(_("updated to hidden changeset %s\n") % ctxstr)
6144 ui.warn(_("updated to hidden changeset %s\n") % ctxstr)
6145
6145
6146 if ctx.obsolete():
6146 if ctx.obsolete():
6147 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
6147 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
6148 ui.warn("(%s)\n" % obsfatemsg)
6148 ui.warn("(%s)\n" % obsfatemsg)
6149 return ret
6149 return ret
6150
6150
6151 @command('verify',
6151 @command('verify',
6152 [('', 'full', False, 'perform more checks (EXPERIMENTAL)')],
6152 [('', 'full', False, 'perform more checks (EXPERIMENTAL)')],
6153 helpcategory=command.CATEGORY_MAINTENANCE)
6153 helpcategory=command.CATEGORY_MAINTENANCE)
6154 def verify(ui, repo, **opts):
6154 def verify(ui, repo, **opts):
6155 """verify the integrity of the repository
6155 """verify the integrity of the repository
6156
6156
6157 Verify the integrity of the current repository.
6157 Verify the integrity of the current repository.
6158
6158
6159 This will perform an extensive check of the repository's
6159 This will perform an extensive check of the repository's
6160 integrity, validating the hashes and checksums of each entry in
6160 integrity, validating the hashes and checksums of each entry in
6161 the changelog, manifest, and tracked files, as well as the
6161 the changelog, manifest, and tracked files, as well as the
6162 integrity of their crosslinks and indices.
6162 integrity of their crosslinks and indices.
6163
6163
6164 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
6164 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
6165 for more information about recovery from corruption of the
6165 for more information about recovery from corruption of the
6166 repository.
6166 repository.
6167
6167
6168 Returns 0 on success, 1 if errors are encountered.
6168 Returns 0 on success, 1 if errors are encountered.
6169 """
6169 """
6170 opts = pycompat.byteskwargs(opts)
6171
6170 level = None
6172 level = None
6171 if opts['full']:
6173 if opts['full']:
6172 level = verifymod.VERIFY_FULL
6174 level = verifymod.VERIFY_FULL
6173 return hg.verify(repo, level)
6175 return hg.verify(repo, level)
6174
6176
6175 @command(
6177 @command(
6176 'version', [] + formatteropts, helpcategory=command.CATEGORY_HELP,
6178 'version', [] + formatteropts, helpcategory=command.CATEGORY_HELP,
6177 norepo=True, intents={INTENT_READONLY})
6179 norepo=True, intents={INTENT_READONLY})
6178 def version_(ui, **opts):
6180 def version_(ui, **opts):
6179 """output version and copyright information
6181 """output version and copyright information
6180
6182
6181 .. container:: verbose
6183 .. container:: verbose
6182
6184
6183 Template:
6185 Template:
6184
6186
6185 The following keywords are supported. See also :hg:`help templates`.
6187 The following keywords are supported. See also :hg:`help templates`.
6186
6188
6187 :extensions: List of extensions.
6189 :extensions: List of extensions.
6188 :ver: String. Version number.
6190 :ver: String. Version number.
6189
6191
6190 And each entry of ``{extensions}`` provides the following sub-keywords
6192 And each entry of ``{extensions}`` provides the following sub-keywords
6191 in addition to ``{ver}``.
6193 in addition to ``{ver}``.
6192
6194
6193 :bundled: Boolean. True if included in the release.
6195 :bundled: Boolean. True if included in the release.
6194 :name: String. Extension name.
6196 :name: String. Extension name.
6195 """
6197 """
6196 opts = pycompat.byteskwargs(opts)
6198 opts = pycompat.byteskwargs(opts)
6197 if ui.verbose:
6199 if ui.verbose:
6198 ui.pager('version')
6200 ui.pager('version')
6199 fm = ui.formatter("version", opts)
6201 fm = ui.formatter("version", opts)
6200 fm.startitem()
6202 fm.startitem()
6201 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
6203 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
6202 util.version())
6204 util.version())
6203 license = _(
6205 license = _(
6204 "(see https://mercurial-scm.org for more information)\n"
6206 "(see https://mercurial-scm.org for more information)\n"
6205 "\nCopyright (C) 2005-2019 Matt Mackall and others\n"
6207 "\nCopyright (C) 2005-2019 Matt Mackall and others\n"
6206 "This is free software; see the source for copying conditions. "
6208 "This is free software; see the source for copying conditions. "
6207 "There is NO\nwarranty; "
6209 "There is NO\nwarranty; "
6208 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6210 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6209 )
6211 )
6210 if not ui.quiet:
6212 if not ui.quiet:
6211 fm.plain(license)
6213 fm.plain(license)
6212
6214
6213 if ui.verbose:
6215 if ui.verbose:
6214 fm.plain(_("\nEnabled extensions:\n\n"))
6216 fm.plain(_("\nEnabled extensions:\n\n"))
6215 # format names and versions into columns
6217 # format names and versions into columns
6216 names = []
6218 names = []
6217 vers = []
6219 vers = []
6218 isinternals = []
6220 isinternals = []
6219 for name, module in extensions.extensions():
6221 for name, module in extensions.extensions():
6220 names.append(name)
6222 names.append(name)
6221 vers.append(extensions.moduleversion(module) or None)
6223 vers.append(extensions.moduleversion(module) or None)
6222 isinternals.append(extensions.ismoduleinternal(module))
6224 isinternals.append(extensions.ismoduleinternal(module))
6223 fn = fm.nested("extensions", tmpl='{name}\n')
6225 fn = fm.nested("extensions", tmpl='{name}\n')
6224 if names:
6226 if names:
6225 namefmt = " %%-%ds " % max(len(n) for n in names)
6227 namefmt = " %%-%ds " % max(len(n) for n in names)
6226 places = [_("external"), _("internal")]
6228 places = [_("external"), _("internal")]
6227 for n, v, p in zip(names, vers, isinternals):
6229 for n, v, p in zip(names, vers, isinternals):
6228 fn.startitem()
6230 fn.startitem()
6229 fn.condwrite(ui.verbose, "name", namefmt, n)
6231 fn.condwrite(ui.verbose, "name", namefmt, n)
6230 if ui.verbose:
6232 if ui.verbose:
6231 fn.plain("%s " % places[p])
6233 fn.plain("%s " % places[p])
6232 fn.data(bundled=p)
6234 fn.data(bundled=p)
6233 fn.condwrite(ui.verbose and v, "ver", "%s", v)
6235 fn.condwrite(ui.verbose and v, "ver", "%s", v)
6234 if ui.verbose:
6236 if ui.verbose:
6235 fn.plain("\n")
6237 fn.plain("\n")
6236 fn.end()
6238 fn.end()
6237 fm.end()
6239 fm.end()
6238
6240
6239 def loadcmdtable(ui, name, cmdtable):
6241 def loadcmdtable(ui, name, cmdtable):
6240 """Load command functions from specified cmdtable
6242 """Load command functions from specified cmdtable
6241 """
6243 """
6242 cmdtable = cmdtable.copy()
6244 cmdtable = cmdtable.copy()
6243 for cmd in list(cmdtable):
6245 for cmd in list(cmdtable):
6244 if not cmd.startswith('^'):
6246 if not cmd.startswith('^'):
6245 continue
6247 continue
6246 ui.deprecwarn("old-style command registration '%s' in extension '%s'"
6248 ui.deprecwarn("old-style command registration '%s' in extension '%s'"
6247 % (cmd, name), '4.8')
6249 % (cmd, name), '4.8')
6248 entry = cmdtable.pop(cmd)
6250 entry = cmdtable.pop(cmd)
6249 entry[0].helpbasic = True
6251 entry[0].helpbasic = True
6250 cmdtable[cmd[1:]] = entry
6252 cmdtable[cmd[1:]] = entry
6251
6253
6252 overrides = [cmd for cmd in cmdtable if cmd in table]
6254 overrides = [cmd for cmd in cmdtable if cmd in table]
6253 if overrides:
6255 if overrides:
6254 ui.warn(_("extension '%s' overrides commands: %s\n")
6256 ui.warn(_("extension '%s' overrides commands: %s\n")
6255 % (name, " ".join(overrides)))
6257 % (name, " ".join(overrides)))
6256 table.update(cmdtable)
6258 table.update(cmdtable)
General Comments 0
You need to be logged in to leave comments. Login now