##// END OF EJS Templates
commands: drop support for legacy ^cmd registration (API)
Matt Harbison -
r42523:fa4b13e8 default
parent child Browse files
Show More
@@ -1,6265 +1,6255 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 branch == repo['.'].branch() and repo['.'].node() not in bheads:
1678 elif branch == repo['.'].branch() and repo['.'].node() not in bheads:
1679 raise error.Abort(_('can only close branch heads'))
1679 raise error.Abort(_('can only close branch heads'))
1680 elif opts.get('amend'):
1680 elif opts.get('amend'):
1681 if (repo['.'].p1().branch() != branch and
1681 if (repo['.'].p1().branch() != branch and
1682 repo['.'].p2().branch() != branch):
1682 repo['.'].p2().branch() != branch):
1683 raise error.Abort(_('can only close branch heads'))
1683 raise error.Abort(_('can only close branch heads'))
1684
1684
1685 if opts.get('amend'):
1685 if opts.get('amend'):
1686 if ui.configbool('ui', 'commitsubrepos'):
1686 if ui.configbool('ui', 'commitsubrepos'):
1687 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1687 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1688
1688
1689 old = repo['.']
1689 old = repo['.']
1690 rewriteutil.precheck(repo, [old.rev()], 'amend')
1690 rewriteutil.precheck(repo, [old.rev()], 'amend')
1691
1691
1692 # Currently histedit gets confused if an amend happens while histedit
1692 # Currently histedit gets confused if an amend happens while histedit
1693 # is in progress. Since we have a checkunfinished command, we are
1693 # is in progress. Since we have a checkunfinished command, we are
1694 # temporarily honoring it.
1694 # temporarily honoring it.
1695 #
1695 #
1696 # Note: eventually this guard will be removed. Please do not expect
1696 # Note: eventually this guard will be removed. Please do not expect
1697 # this behavior to remain.
1697 # this behavior to remain.
1698 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1698 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1699 cmdutil.checkunfinished(repo)
1699 cmdutil.checkunfinished(repo)
1700
1700
1701 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
1701 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
1702 if node == old.node():
1702 if node == old.node():
1703 ui.status(_("nothing changed\n"))
1703 ui.status(_("nothing changed\n"))
1704 return 1
1704 return 1
1705 else:
1705 else:
1706 def commitfunc(ui, repo, message, match, opts):
1706 def commitfunc(ui, repo, message, match, opts):
1707 overrides = {}
1707 overrides = {}
1708 if opts.get('secret'):
1708 if opts.get('secret'):
1709 overrides[('phases', 'new-commit')] = 'secret'
1709 overrides[('phases', 'new-commit')] = 'secret'
1710
1710
1711 baseui = repo.baseui
1711 baseui = repo.baseui
1712 with baseui.configoverride(overrides, 'commit'):
1712 with baseui.configoverride(overrides, 'commit'):
1713 with ui.configoverride(overrides, 'commit'):
1713 with ui.configoverride(overrides, 'commit'):
1714 editform = cmdutil.mergeeditform(repo[None],
1714 editform = cmdutil.mergeeditform(repo[None],
1715 'commit.normal')
1715 'commit.normal')
1716 editor = cmdutil.getcommiteditor(
1716 editor = cmdutil.getcommiteditor(
1717 editform=editform, **pycompat.strkwargs(opts))
1717 editform=editform, **pycompat.strkwargs(opts))
1718 return repo.commit(message,
1718 return repo.commit(message,
1719 opts.get('user'),
1719 opts.get('user'),
1720 opts.get('date'),
1720 opts.get('date'),
1721 match,
1721 match,
1722 editor=editor,
1722 editor=editor,
1723 extra=extra)
1723 extra=extra)
1724
1724
1725 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1725 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1726
1726
1727 if not node:
1727 if not node:
1728 stat = cmdutil.postcommitstatus(repo, pats, opts)
1728 stat = cmdutil.postcommitstatus(repo, pats, opts)
1729 if stat[3]:
1729 if stat[3]:
1730 ui.status(_("nothing changed (%d missing files, see "
1730 ui.status(_("nothing changed (%d missing files, see "
1731 "'hg status')\n") % len(stat[3]))
1731 "'hg status')\n") % len(stat[3]))
1732 else:
1732 else:
1733 ui.status(_("nothing changed\n"))
1733 ui.status(_("nothing changed\n"))
1734 return 1
1734 return 1
1735
1735
1736 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1736 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1737
1737
1738 if not ui.quiet and ui.configbool('commands', 'commit.post-status'):
1738 if not ui.quiet and ui.configbool('commands', 'commit.post-status'):
1739 status(ui, repo, modified=True, added=True, removed=True, deleted=True,
1739 status(ui, repo, modified=True, added=True, removed=True, deleted=True,
1740 unknown=True, subrepos=opts.get('subrepos'))
1740 unknown=True, subrepos=opts.get('subrepos'))
1741
1741
1742 @command('config|showconfig|debugconfig',
1742 @command('config|showconfig|debugconfig',
1743 [('u', 'untrusted', None, _('show untrusted configuration options')),
1743 [('u', 'untrusted', None, _('show untrusted configuration options')),
1744 ('e', 'edit', None, _('edit user config')),
1744 ('e', 'edit', None, _('edit user config')),
1745 ('l', 'local', None, _('edit repository config')),
1745 ('l', 'local', None, _('edit repository config')),
1746 ('g', 'global', None, _('edit global config'))] + formatteropts,
1746 ('g', 'global', None, _('edit global config'))] + formatteropts,
1747 _('[-u] [NAME]...'),
1747 _('[-u] [NAME]...'),
1748 helpcategory=command.CATEGORY_HELP,
1748 helpcategory=command.CATEGORY_HELP,
1749 optionalrepo=True,
1749 optionalrepo=True,
1750 intents={INTENT_READONLY})
1750 intents={INTENT_READONLY})
1751 def config(ui, repo, *values, **opts):
1751 def config(ui, repo, *values, **opts):
1752 """show combined config settings from all hgrc files
1752 """show combined config settings from all hgrc files
1753
1753
1754 With no arguments, print names and values of all config items.
1754 With no arguments, print names and values of all config items.
1755
1755
1756 With one argument of the form section.name, print just the value
1756 With one argument of the form section.name, print just the value
1757 of that config item.
1757 of that config item.
1758
1758
1759 With multiple arguments, print names and values of all config
1759 With multiple arguments, print names and values of all config
1760 items with matching section names or section.names.
1760 items with matching section names or section.names.
1761
1761
1762 With --edit, start an editor on the user-level config file. With
1762 With --edit, start an editor on the user-level config file. With
1763 --global, edit the system-wide config file. With --local, edit the
1763 --global, edit the system-wide config file. With --local, edit the
1764 repository-level config file.
1764 repository-level config file.
1765
1765
1766 With --debug, the source (filename and line number) is printed
1766 With --debug, the source (filename and line number) is printed
1767 for each config item.
1767 for each config item.
1768
1768
1769 See :hg:`help config` for more information about config files.
1769 See :hg:`help config` for more information about config files.
1770
1770
1771 .. container:: verbose
1771 .. container:: verbose
1772
1772
1773 Template:
1773 Template:
1774
1774
1775 The following keywords are supported. See also :hg:`help templates`.
1775 The following keywords are supported. See also :hg:`help templates`.
1776
1776
1777 :name: String. Config name.
1777 :name: String. Config name.
1778 :source: String. Filename and line number where the item is defined.
1778 :source: String. Filename and line number where the item is defined.
1779 :value: String. Config value.
1779 :value: String. Config value.
1780
1780
1781 Returns 0 on success, 1 if NAME does not exist.
1781 Returns 0 on success, 1 if NAME does not exist.
1782
1782
1783 """
1783 """
1784
1784
1785 opts = pycompat.byteskwargs(opts)
1785 opts = pycompat.byteskwargs(opts)
1786 if opts.get('edit') or opts.get('local') or opts.get('global'):
1786 if opts.get('edit') or opts.get('local') or opts.get('global'):
1787 if opts.get('local') and opts.get('global'):
1787 if opts.get('local') and opts.get('global'):
1788 raise error.Abort(_("can't use --local and --global together"))
1788 raise error.Abort(_("can't use --local and --global together"))
1789
1789
1790 if opts.get('local'):
1790 if opts.get('local'):
1791 if not repo:
1791 if not repo:
1792 raise error.Abort(_("can't use --local outside a repository"))
1792 raise error.Abort(_("can't use --local outside a repository"))
1793 paths = [repo.vfs.join('hgrc')]
1793 paths = [repo.vfs.join('hgrc')]
1794 elif opts.get('global'):
1794 elif opts.get('global'):
1795 paths = rcutil.systemrcpath()
1795 paths = rcutil.systemrcpath()
1796 else:
1796 else:
1797 paths = rcutil.userrcpath()
1797 paths = rcutil.userrcpath()
1798
1798
1799 for f in paths:
1799 for f in paths:
1800 if os.path.exists(f):
1800 if os.path.exists(f):
1801 break
1801 break
1802 else:
1802 else:
1803 if opts.get('global'):
1803 if opts.get('global'):
1804 samplehgrc = uimod.samplehgrcs['global']
1804 samplehgrc = uimod.samplehgrcs['global']
1805 elif opts.get('local'):
1805 elif opts.get('local'):
1806 samplehgrc = uimod.samplehgrcs['local']
1806 samplehgrc = uimod.samplehgrcs['local']
1807 else:
1807 else:
1808 samplehgrc = uimod.samplehgrcs['user']
1808 samplehgrc = uimod.samplehgrcs['user']
1809
1809
1810 f = paths[0]
1810 f = paths[0]
1811 fp = open(f, "wb")
1811 fp = open(f, "wb")
1812 fp.write(util.tonativeeol(samplehgrc))
1812 fp.write(util.tonativeeol(samplehgrc))
1813 fp.close()
1813 fp.close()
1814
1814
1815 editor = ui.geteditor()
1815 editor = ui.geteditor()
1816 ui.system("%s \"%s\"" % (editor, f),
1816 ui.system("%s \"%s\"" % (editor, f),
1817 onerr=error.Abort, errprefix=_("edit failed"),
1817 onerr=error.Abort, errprefix=_("edit failed"),
1818 blockedtag='config_edit')
1818 blockedtag='config_edit')
1819 return
1819 return
1820 ui.pager('config')
1820 ui.pager('config')
1821 fm = ui.formatter('config', opts)
1821 fm = ui.formatter('config', opts)
1822 for t, f in rcutil.rccomponents():
1822 for t, f in rcutil.rccomponents():
1823 if t == 'path':
1823 if t == 'path':
1824 ui.debug('read config from: %s\n' % f)
1824 ui.debug('read config from: %s\n' % f)
1825 elif t == 'items':
1825 elif t == 'items':
1826 for section, name, value, source in f:
1826 for section, name, value, source in f:
1827 ui.debug('set config by: %s\n' % source)
1827 ui.debug('set config by: %s\n' % source)
1828 else:
1828 else:
1829 raise error.ProgrammingError('unknown rctype: %s' % t)
1829 raise error.ProgrammingError('unknown rctype: %s' % t)
1830 untrusted = bool(opts.get('untrusted'))
1830 untrusted = bool(opts.get('untrusted'))
1831
1831
1832 selsections = selentries = []
1832 selsections = selentries = []
1833 if values:
1833 if values:
1834 selsections = [v for v in values if '.' not in v]
1834 selsections = [v for v in values if '.' not in v]
1835 selentries = [v for v in values if '.' in v]
1835 selentries = [v for v in values if '.' in v]
1836 uniquesel = (len(selentries) == 1 and not selsections)
1836 uniquesel = (len(selentries) == 1 and not selsections)
1837 selsections = set(selsections)
1837 selsections = set(selsections)
1838 selentries = set(selentries)
1838 selentries = set(selentries)
1839
1839
1840 matched = False
1840 matched = False
1841 for section, name, value in ui.walkconfig(untrusted=untrusted):
1841 for section, name, value in ui.walkconfig(untrusted=untrusted):
1842 source = ui.configsource(section, name, untrusted)
1842 source = ui.configsource(section, name, untrusted)
1843 value = pycompat.bytestr(value)
1843 value = pycompat.bytestr(value)
1844 if fm.isplain():
1844 if fm.isplain():
1845 source = source or 'none'
1845 source = source or 'none'
1846 value = value.replace('\n', '\\n')
1846 value = value.replace('\n', '\\n')
1847 entryname = section + '.' + name
1847 entryname = section + '.' + name
1848 if values and not (section in selsections or entryname in selentries):
1848 if values and not (section in selsections or entryname in selentries):
1849 continue
1849 continue
1850 fm.startitem()
1850 fm.startitem()
1851 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1851 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1852 if uniquesel:
1852 if uniquesel:
1853 fm.data(name=entryname)
1853 fm.data(name=entryname)
1854 fm.write('value', '%s\n', value)
1854 fm.write('value', '%s\n', value)
1855 else:
1855 else:
1856 fm.write('name value', '%s=%s\n', entryname, value)
1856 fm.write('name value', '%s=%s\n', entryname, value)
1857 matched = True
1857 matched = True
1858 fm.end()
1858 fm.end()
1859 if matched:
1859 if matched:
1860 return 0
1860 return 0
1861 return 1
1861 return 1
1862
1862
1863 @command('copy|cp',
1863 @command('copy|cp',
1864 [('A', 'after', None, _('record a copy that has already occurred')),
1864 [('A', 'after', None, _('record a copy that has already occurred')),
1865 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1865 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1866 ] + walkopts + dryrunopts,
1866 ] + walkopts + dryrunopts,
1867 _('[OPTION]... [SOURCE]... DEST'),
1867 _('[OPTION]... [SOURCE]... DEST'),
1868 helpcategory=command.CATEGORY_FILE_CONTENTS)
1868 helpcategory=command.CATEGORY_FILE_CONTENTS)
1869 def copy(ui, repo, *pats, **opts):
1869 def copy(ui, repo, *pats, **opts):
1870 """mark files as copied for the next commit
1870 """mark files as copied for the next commit
1871
1871
1872 Mark dest as having copies of source files. If dest is a
1872 Mark dest as having copies of source files. If dest is a
1873 directory, copies are put in that directory. If dest is a file,
1873 directory, copies are put in that directory. If dest is a file,
1874 the source must be a single file.
1874 the source must be a single file.
1875
1875
1876 By default, this command copies the contents of files as they
1876 By default, this command copies the contents of files as they
1877 exist in the working directory. If invoked with -A/--after, the
1877 exist in the working directory. If invoked with -A/--after, the
1878 operation is recorded, but no copying is performed.
1878 operation is recorded, but no copying is performed.
1879
1879
1880 This command takes effect with the next commit. To undo a copy
1880 This command takes effect with the next commit. To undo a copy
1881 before that, see :hg:`revert`.
1881 before that, see :hg:`revert`.
1882
1882
1883 Returns 0 on success, 1 if errors are encountered.
1883 Returns 0 on success, 1 if errors are encountered.
1884 """
1884 """
1885 opts = pycompat.byteskwargs(opts)
1885 opts = pycompat.byteskwargs(opts)
1886 with repo.wlock(False):
1886 with repo.wlock(False):
1887 return cmdutil.copy(ui, repo, pats, opts)
1887 return cmdutil.copy(ui, repo, pats, opts)
1888
1888
1889 @command(
1889 @command(
1890 'debugcommands', [], _('[COMMAND]'),
1890 'debugcommands', [], _('[COMMAND]'),
1891 helpcategory=command.CATEGORY_HELP,
1891 helpcategory=command.CATEGORY_HELP,
1892 norepo=True)
1892 norepo=True)
1893 def debugcommands(ui, cmd='', *args):
1893 def debugcommands(ui, cmd='', *args):
1894 """list all available commands and options"""
1894 """list all available commands and options"""
1895 for cmd, vals in sorted(table.iteritems()):
1895 for cmd, vals in sorted(table.iteritems()):
1896 cmd = cmd.split('|')[0]
1896 cmd = cmd.split('|')[0]
1897 opts = ', '.join([i[1] for i in vals[1]])
1897 opts = ', '.join([i[1] for i in vals[1]])
1898 ui.write('%s: %s\n' % (cmd, opts))
1898 ui.write('%s: %s\n' % (cmd, opts))
1899
1899
1900 @command('debugcomplete',
1900 @command('debugcomplete',
1901 [('o', 'options', None, _('show the command options'))],
1901 [('o', 'options', None, _('show the command options'))],
1902 _('[-o] CMD'),
1902 _('[-o] CMD'),
1903 helpcategory=command.CATEGORY_HELP,
1903 helpcategory=command.CATEGORY_HELP,
1904 norepo=True)
1904 norepo=True)
1905 def debugcomplete(ui, cmd='', **opts):
1905 def debugcomplete(ui, cmd='', **opts):
1906 """returns the completion list associated with the given command"""
1906 """returns the completion list associated with the given command"""
1907
1907
1908 if opts.get(r'options'):
1908 if opts.get(r'options'):
1909 options = []
1909 options = []
1910 otables = [globalopts]
1910 otables = [globalopts]
1911 if cmd:
1911 if cmd:
1912 aliases, entry = cmdutil.findcmd(cmd, table, False)
1912 aliases, entry = cmdutil.findcmd(cmd, table, False)
1913 otables.append(entry[1])
1913 otables.append(entry[1])
1914 for t in otables:
1914 for t in otables:
1915 for o in t:
1915 for o in t:
1916 if "(DEPRECATED)" in o[3]:
1916 if "(DEPRECATED)" in o[3]:
1917 continue
1917 continue
1918 if o[0]:
1918 if o[0]:
1919 options.append('-%s' % o[0])
1919 options.append('-%s' % o[0])
1920 options.append('--%s' % o[1])
1920 options.append('--%s' % o[1])
1921 ui.write("%s\n" % "\n".join(options))
1921 ui.write("%s\n" % "\n".join(options))
1922 return
1922 return
1923
1923
1924 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1924 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1925 if ui.verbose:
1925 if ui.verbose:
1926 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1926 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1927 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1927 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1928
1928
1929 @command('diff',
1929 @command('diff',
1930 [('r', 'rev', [], _('revision'), _('REV')),
1930 [('r', 'rev', [], _('revision'), _('REV')),
1931 ('c', 'change', '', _('change made by revision'), _('REV'))
1931 ('c', 'change', '', _('change made by revision'), _('REV'))
1932 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1932 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1933 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1933 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1934 helpcategory=command.CATEGORY_FILE_CONTENTS,
1934 helpcategory=command.CATEGORY_FILE_CONTENTS,
1935 helpbasic=True, inferrepo=True, intents={INTENT_READONLY})
1935 helpbasic=True, inferrepo=True, intents={INTENT_READONLY})
1936 def diff(ui, repo, *pats, **opts):
1936 def diff(ui, repo, *pats, **opts):
1937 """diff repository (or selected files)
1937 """diff repository (or selected files)
1938
1938
1939 Show differences between revisions for the specified files.
1939 Show differences between revisions for the specified files.
1940
1940
1941 Differences between files are shown using the unified diff format.
1941 Differences between files are shown using the unified diff format.
1942
1942
1943 .. note::
1943 .. note::
1944
1944
1945 :hg:`diff` may generate unexpected results for merges, as it will
1945 :hg:`diff` may generate unexpected results for merges, as it will
1946 default to comparing against the working directory's first
1946 default to comparing against the working directory's first
1947 parent changeset if no revisions are specified.
1947 parent changeset if no revisions are specified.
1948
1948
1949 When two revision arguments are given, then changes are shown
1949 When two revision arguments are given, then changes are shown
1950 between those revisions. If only one revision is specified then
1950 between those revisions. If only one revision is specified then
1951 that revision is compared to the working directory, and, when no
1951 that revision is compared to the working directory, and, when no
1952 revisions are specified, the working directory files are compared
1952 revisions are specified, the working directory files are compared
1953 to its first parent.
1953 to its first parent.
1954
1954
1955 Alternatively you can specify -c/--change with a revision to see
1955 Alternatively you can specify -c/--change with a revision to see
1956 the changes in that changeset relative to its first parent.
1956 the changes in that changeset relative to its first parent.
1957
1957
1958 Without the -a/--text option, diff will avoid generating diffs of
1958 Without the -a/--text option, diff will avoid generating diffs of
1959 files it detects as binary. With -a, diff will generate a diff
1959 files it detects as binary. With -a, diff will generate a diff
1960 anyway, probably with undesirable results.
1960 anyway, probably with undesirable results.
1961
1961
1962 Use the -g/--git option to generate diffs in the git extended diff
1962 Use the -g/--git option to generate diffs in the git extended diff
1963 format. For more information, read :hg:`help diffs`.
1963 format. For more information, read :hg:`help diffs`.
1964
1964
1965 .. container:: verbose
1965 .. container:: verbose
1966
1966
1967 Examples:
1967 Examples:
1968
1968
1969 - compare a file in the current working directory to its parent::
1969 - compare a file in the current working directory to its parent::
1970
1970
1971 hg diff foo.c
1971 hg diff foo.c
1972
1972
1973 - compare two historical versions of a directory, with rename info::
1973 - compare two historical versions of a directory, with rename info::
1974
1974
1975 hg diff --git -r 1.0:1.2 lib/
1975 hg diff --git -r 1.0:1.2 lib/
1976
1976
1977 - get change stats relative to the last change on some date::
1977 - get change stats relative to the last change on some date::
1978
1978
1979 hg diff --stat -r "date('may 2')"
1979 hg diff --stat -r "date('may 2')"
1980
1980
1981 - diff all newly-added files that contain a keyword::
1981 - diff all newly-added files that contain a keyword::
1982
1982
1983 hg diff "set:added() and grep(GNU)"
1983 hg diff "set:added() and grep(GNU)"
1984
1984
1985 - compare a revision and its parents::
1985 - compare a revision and its parents::
1986
1986
1987 hg diff -c 9353 # compare against first parent
1987 hg diff -c 9353 # compare against first parent
1988 hg diff -r 9353^:9353 # same using revset syntax
1988 hg diff -r 9353^:9353 # same using revset syntax
1989 hg diff -r 9353^2:9353 # compare against the second parent
1989 hg diff -r 9353^2:9353 # compare against the second parent
1990
1990
1991 Returns 0 on success.
1991 Returns 0 on success.
1992 """
1992 """
1993
1993
1994 opts = pycompat.byteskwargs(opts)
1994 opts = pycompat.byteskwargs(opts)
1995 revs = opts.get('rev')
1995 revs = opts.get('rev')
1996 change = opts.get('change')
1996 change = opts.get('change')
1997 stat = opts.get('stat')
1997 stat = opts.get('stat')
1998 reverse = opts.get('reverse')
1998 reverse = opts.get('reverse')
1999
1999
2000 if revs and change:
2000 if revs and change:
2001 msg = _('cannot specify --rev and --change at the same time')
2001 msg = _('cannot specify --rev and --change at the same time')
2002 raise error.Abort(msg)
2002 raise error.Abort(msg)
2003 elif change:
2003 elif change:
2004 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
2004 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
2005 ctx2 = scmutil.revsingle(repo, change, None)
2005 ctx2 = scmutil.revsingle(repo, change, None)
2006 ctx1 = ctx2.p1()
2006 ctx1 = ctx2.p1()
2007 else:
2007 else:
2008 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
2008 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
2009 ctx1, ctx2 = scmutil.revpair(repo, revs)
2009 ctx1, ctx2 = scmutil.revpair(repo, revs)
2010 node1, node2 = ctx1.node(), ctx2.node()
2010 node1, node2 = ctx1.node(), ctx2.node()
2011
2011
2012 if reverse:
2012 if reverse:
2013 node1, node2 = node2, node1
2013 node1, node2 = node2, node1
2014
2014
2015 diffopts = patch.diffallopts(ui, opts)
2015 diffopts = patch.diffallopts(ui, opts)
2016 m = scmutil.match(ctx2, pats, opts)
2016 m = scmutil.match(ctx2, pats, opts)
2017 m = repo.narrowmatch(m)
2017 m = repo.narrowmatch(m)
2018 ui.pager('diff')
2018 ui.pager('diff')
2019 logcmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2019 logcmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2020 listsubrepos=opts.get('subrepos'),
2020 listsubrepos=opts.get('subrepos'),
2021 root=opts.get('root'))
2021 root=opts.get('root'))
2022
2022
2023 @command('export',
2023 @command('export',
2024 [('B', 'bookmark', '',
2024 [('B', 'bookmark', '',
2025 _('export changes only reachable by given bookmark'), _('BOOKMARK')),
2025 _('export changes only reachable by given bookmark'), _('BOOKMARK')),
2026 ('o', 'output', '',
2026 ('o', 'output', '',
2027 _('print output to file with formatted name'), _('FORMAT')),
2027 _('print output to file with formatted name'), _('FORMAT')),
2028 ('', 'switch-parent', None, _('diff against the second parent')),
2028 ('', 'switch-parent', None, _('diff against the second parent')),
2029 ('r', 'rev', [], _('revisions to export'), _('REV')),
2029 ('r', 'rev', [], _('revisions to export'), _('REV')),
2030 ] + diffopts + formatteropts,
2030 ] + diffopts + formatteropts,
2031 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2031 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2032 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2032 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2033 helpbasic=True, intents={INTENT_READONLY})
2033 helpbasic=True, intents={INTENT_READONLY})
2034 def export(ui, repo, *changesets, **opts):
2034 def export(ui, repo, *changesets, **opts):
2035 """dump the header and diffs for one or more changesets
2035 """dump the header and diffs for one or more changesets
2036
2036
2037 Print the changeset header and diffs for one or more revisions.
2037 Print the changeset header and diffs for one or more revisions.
2038 If no revision is given, the parent of the working directory is used.
2038 If no revision is given, the parent of the working directory is used.
2039
2039
2040 The information shown in the changeset header is: author, date,
2040 The information shown in the changeset header is: author, date,
2041 branch name (if non-default), changeset hash, parent(s) and commit
2041 branch name (if non-default), changeset hash, parent(s) and commit
2042 comment.
2042 comment.
2043
2043
2044 .. note::
2044 .. note::
2045
2045
2046 :hg:`export` may generate unexpected diff output for merge
2046 :hg:`export` may generate unexpected diff output for merge
2047 changesets, as it will compare the merge changeset against its
2047 changesets, as it will compare the merge changeset against its
2048 first parent only.
2048 first parent only.
2049
2049
2050 Output may be to a file, in which case the name of the file is
2050 Output may be to a file, in which case the name of the file is
2051 given using a template string. See :hg:`help templates`. In addition
2051 given using a template string. See :hg:`help templates`. In addition
2052 to the common template keywords, the following formatting rules are
2052 to the common template keywords, the following formatting rules are
2053 supported:
2053 supported:
2054
2054
2055 :``%%``: literal "%" character
2055 :``%%``: literal "%" character
2056 :``%H``: changeset hash (40 hexadecimal digits)
2056 :``%H``: changeset hash (40 hexadecimal digits)
2057 :``%N``: number of patches being generated
2057 :``%N``: number of patches being generated
2058 :``%R``: changeset revision number
2058 :``%R``: changeset revision number
2059 :``%b``: basename of the exporting repository
2059 :``%b``: basename of the exporting repository
2060 :``%h``: short-form changeset hash (12 hexadecimal digits)
2060 :``%h``: short-form changeset hash (12 hexadecimal digits)
2061 :``%m``: first line of the commit message (only alphanumeric characters)
2061 :``%m``: first line of the commit message (only alphanumeric characters)
2062 :``%n``: zero-padded sequence number, starting at 1
2062 :``%n``: zero-padded sequence number, starting at 1
2063 :``%r``: zero-padded changeset revision number
2063 :``%r``: zero-padded changeset revision number
2064 :``\\``: literal "\\" character
2064 :``\\``: literal "\\" character
2065
2065
2066 Without the -a/--text option, export will avoid generating diffs
2066 Without the -a/--text option, export will avoid generating diffs
2067 of files it detects as binary. With -a, export will generate a
2067 of files it detects as binary. With -a, export will generate a
2068 diff anyway, probably with undesirable results.
2068 diff anyway, probably with undesirable results.
2069
2069
2070 With -B/--bookmark changesets reachable by the given bookmark are
2070 With -B/--bookmark changesets reachable by the given bookmark are
2071 selected.
2071 selected.
2072
2072
2073 Use the -g/--git option to generate diffs in the git extended diff
2073 Use the -g/--git option to generate diffs in the git extended diff
2074 format. See :hg:`help diffs` for more information.
2074 format. See :hg:`help diffs` for more information.
2075
2075
2076 With the --switch-parent option, the diff will be against the
2076 With the --switch-parent option, the diff will be against the
2077 second parent. It can be useful to review a merge.
2077 second parent. It can be useful to review a merge.
2078
2078
2079 .. container:: verbose
2079 .. container:: verbose
2080
2080
2081 Template:
2081 Template:
2082
2082
2083 The following keywords are supported in addition to the common template
2083 The following keywords are supported in addition to the common template
2084 keywords and functions. See also :hg:`help templates`.
2084 keywords and functions. See also :hg:`help templates`.
2085
2085
2086 :diff: String. Diff content.
2086 :diff: String. Diff content.
2087 :parents: List of strings. Parent nodes of the changeset.
2087 :parents: List of strings. Parent nodes of the changeset.
2088
2088
2089 Examples:
2089 Examples:
2090
2090
2091 - use export and import to transplant a bugfix to the current
2091 - use export and import to transplant a bugfix to the current
2092 branch::
2092 branch::
2093
2093
2094 hg export -r 9353 | hg import -
2094 hg export -r 9353 | hg import -
2095
2095
2096 - export all the changesets between two revisions to a file with
2096 - export all the changesets between two revisions to a file with
2097 rename information::
2097 rename information::
2098
2098
2099 hg export --git -r 123:150 > changes.txt
2099 hg export --git -r 123:150 > changes.txt
2100
2100
2101 - split outgoing changes into a series of patches with
2101 - split outgoing changes into a series of patches with
2102 descriptive names::
2102 descriptive names::
2103
2103
2104 hg export -r "outgoing()" -o "%n-%m.patch"
2104 hg export -r "outgoing()" -o "%n-%m.patch"
2105
2105
2106 Returns 0 on success.
2106 Returns 0 on success.
2107 """
2107 """
2108 opts = pycompat.byteskwargs(opts)
2108 opts = pycompat.byteskwargs(opts)
2109 bookmark = opts.get('bookmark')
2109 bookmark = opts.get('bookmark')
2110 changesets += tuple(opts.get('rev', []))
2110 changesets += tuple(opts.get('rev', []))
2111
2111
2112 if bookmark and changesets:
2112 if bookmark and changesets:
2113 raise error.Abort(_("-r and -B are mutually exclusive"))
2113 raise error.Abort(_("-r and -B are mutually exclusive"))
2114
2114
2115 if bookmark:
2115 if bookmark:
2116 if bookmark not in repo._bookmarks:
2116 if bookmark not in repo._bookmarks:
2117 raise error.Abort(_("bookmark '%s' not found") % bookmark)
2117 raise error.Abort(_("bookmark '%s' not found") % bookmark)
2118
2118
2119 revs = scmutil.bookmarkrevs(repo, bookmark)
2119 revs = scmutil.bookmarkrevs(repo, bookmark)
2120 else:
2120 else:
2121 if not changesets:
2121 if not changesets:
2122 changesets = ['.']
2122 changesets = ['.']
2123
2123
2124 repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn')
2124 repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn')
2125 revs = scmutil.revrange(repo, changesets)
2125 revs = scmutil.revrange(repo, changesets)
2126
2126
2127 if not revs:
2127 if not revs:
2128 raise error.Abort(_("export requires at least one changeset"))
2128 raise error.Abort(_("export requires at least one changeset"))
2129 if len(revs) > 1:
2129 if len(revs) > 1:
2130 ui.note(_('exporting patches:\n'))
2130 ui.note(_('exporting patches:\n'))
2131 else:
2131 else:
2132 ui.note(_('exporting patch:\n'))
2132 ui.note(_('exporting patch:\n'))
2133
2133
2134 fntemplate = opts.get('output')
2134 fntemplate = opts.get('output')
2135 if cmdutil.isstdiofilename(fntemplate):
2135 if cmdutil.isstdiofilename(fntemplate):
2136 fntemplate = ''
2136 fntemplate = ''
2137
2137
2138 if fntemplate:
2138 if fntemplate:
2139 fm = formatter.nullformatter(ui, 'export', opts)
2139 fm = formatter.nullformatter(ui, 'export', opts)
2140 else:
2140 else:
2141 ui.pager('export')
2141 ui.pager('export')
2142 fm = ui.formatter('export', opts)
2142 fm = ui.formatter('export', opts)
2143 with fm:
2143 with fm:
2144 cmdutil.export(repo, revs, fm, fntemplate=fntemplate,
2144 cmdutil.export(repo, revs, fm, fntemplate=fntemplate,
2145 switch_parent=opts.get('switch_parent'),
2145 switch_parent=opts.get('switch_parent'),
2146 opts=patch.diffallopts(ui, opts))
2146 opts=patch.diffallopts(ui, opts))
2147
2147
2148 @command('files',
2148 @command('files',
2149 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
2149 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
2150 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2150 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2151 ] + walkopts + formatteropts + subrepoopts,
2151 ] + walkopts + formatteropts + subrepoopts,
2152 _('[OPTION]... [FILE]...'),
2152 _('[OPTION]... [FILE]...'),
2153 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2153 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2154 intents={INTENT_READONLY})
2154 intents={INTENT_READONLY})
2155 def files(ui, repo, *pats, **opts):
2155 def files(ui, repo, *pats, **opts):
2156 """list tracked files
2156 """list tracked files
2157
2157
2158 Print files under Mercurial control in the working directory or
2158 Print files under Mercurial control in the working directory or
2159 specified revision for given files (excluding removed files).
2159 specified revision for given files (excluding removed files).
2160 Files can be specified as filenames or filesets.
2160 Files can be specified as filenames or filesets.
2161
2161
2162 If no files are given to match, this command prints the names
2162 If no files are given to match, this command prints the names
2163 of all files under Mercurial control.
2163 of all files under Mercurial control.
2164
2164
2165 .. container:: verbose
2165 .. container:: verbose
2166
2166
2167 Template:
2167 Template:
2168
2168
2169 The following keywords are supported in addition to the common template
2169 The following keywords are supported in addition to the common template
2170 keywords and functions. See also :hg:`help templates`.
2170 keywords and functions. See also :hg:`help templates`.
2171
2171
2172 :flags: String. Character denoting file's symlink and executable bits.
2172 :flags: String. Character denoting file's symlink and executable bits.
2173 :path: String. Repository-absolute path of the file.
2173 :path: String. Repository-absolute path of the file.
2174 :size: Integer. Size of the file in bytes.
2174 :size: Integer. Size of the file in bytes.
2175
2175
2176 Examples:
2176 Examples:
2177
2177
2178 - list all files under the current directory::
2178 - list all files under the current directory::
2179
2179
2180 hg files .
2180 hg files .
2181
2181
2182 - shows sizes and flags for current revision::
2182 - shows sizes and flags for current revision::
2183
2183
2184 hg files -vr .
2184 hg files -vr .
2185
2185
2186 - list all files named README::
2186 - list all files named README::
2187
2187
2188 hg files -I "**/README"
2188 hg files -I "**/README"
2189
2189
2190 - list all binary files::
2190 - list all binary files::
2191
2191
2192 hg files "set:binary()"
2192 hg files "set:binary()"
2193
2193
2194 - find files containing a regular expression::
2194 - find files containing a regular expression::
2195
2195
2196 hg files "set:grep('bob')"
2196 hg files "set:grep('bob')"
2197
2197
2198 - search tracked file contents with xargs and grep::
2198 - search tracked file contents with xargs and grep::
2199
2199
2200 hg files -0 | xargs -0 grep foo
2200 hg files -0 | xargs -0 grep foo
2201
2201
2202 See :hg:`help patterns` and :hg:`help filesets` for more information
2202 See :hg:`help patterns` and :hg:`help filesets` for more information
2203 on specifying file patterns.
2203 on specifying file patterns.
2204
2204
2205 Returns 0 if a match is found, 1 otherwise.
2205 Returns 0 if a match is found, 1 otherwise.
2206
2206
2207 """
2207 """
2208
2208
2209 opts = pycompat.byteskwargs(opts)
2209 opts = pycompat.byteskwargs(opts)
2210 rev = opts.get('rev')
2210 rev = opts.get('rev')
2211 if rev:
2211 if rev:
2212 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2212 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2213 ctx = scmutil.revsingle(repo, rev, None)
2213 ctx = scmutil.revsingle(repo, rev, None)
2214
2214
2215 end = '\n'
2215 end = '\n'
2216 if opts.get('print0'):
2216 if opts.get('print0'):
2217 end = '\0'
2217 end = '\0'
2218 fmt = '%s' + end
2218 fmt = '%s' + end
2219
2219
2220 m = scmutil.match(ctx, pats, opts)
2220 m = scmutil.match(ctx, pats, opts)
2221 ui.pager('files')
2221 ui.pager('files')
2222 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2222 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2223 with ui.formatter('files', opts) as fm:
2223 with ui.formatter('files', opts) as fm:
2224 return cmdutil.files(ui, ctx, m, uipathfn, fm, fmt,
2224 return cmdutil.files(ui, ctx, m, uipathfn, fm, fmt,
2225 opts.get('subrepos'))
2225 opts.get('subrepos'))
2226
2226
2227 @command(
2227 @command(
2228 'forget',
2228 'forget',
2229 [('i', 'interactive', None, _('use interactive mode')),
2229 [('i', 'interactive', None, _('use interactive mode')),
2230 ] + walkopts + dryrunopts,
2230 ] + walkopts + dryrunopts,
2231 _('[OPTION]... FILE...'),
2231 _('[OPTION]... FILE...'),
2232 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2232 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2233 helpbasic=True, inferrepo=True)
2233 helpbasic=True, inferrepo=True)
2234 def forget(ui, repo, *pats, **opts):
2234 def forget(ui, repo, *pats, **opts):
2235 """forget the specified files on the next commit
2235 """forget the specified files on the next commit
2236
2236
2237 Mark the specified files so they will no longer be tracked
2237 Mark the specified files so they will no longer be tracked
2238 after the next commit.
2238 after the next commit.
2239
2239
2240 This only removes files from the current branch, not from the
2240 This only removes files from the current branch, not from the
2241 entire project history, and it does not delete them from the
2241 entire project history, and it does not delete them from the
2242 working directory.
2242 working directory.
2243
2243
2244 To delete the file from the working directory, see :hg:`remove`.
2244 To delete the file from the working directory, see :hg:`remove`.
2245
2245
2246 To undo a forget before the next commit, see :hg:`add`.
2246 To undo a forget before the next commit, see :hg:`add`.
2247
2247
2248 .. container:: verbose
2248 .. container:: verbose
2249
2249
2250 Examples:
2250 Examples:
2251
2251
2252 - forget newly-added binary files::
2252 - forget newly-added binary files::
2253
2253
2254 hg forget "set:added() and binary()"
2254 hg forget "set:added() and binary()"
2255
2255
2256 - forget files that would be excluded by .hgignore::
2256 - forget files that would be excluded by .hgignore::
2257
2257
2258 hg forget "set:hgignore()"
2258 hg forget "set:hgignore()"
2259
2259
2260 Returns 0 on success.
2260 Returns 0 on success.
2261 """
2261 """
2262
2262
2263 opts = pycompat.byteskwargs(opts)
2263 opts = pycompat.byteskwargs(opts)
2264 if not pats:
2264 if not pats:
2265 raise error.Abort(_('no files specified'))
2265 raise error.Abort(_('no files specified'))
2266
2266
2267 m = scmutil.match(repo[None], pats, opts)
2267 m = scmutil.match(repo[None], pats, opts)
2268 dryrun, interactive = opts.get('dry_run'), opts.get('interactive')
2268 dryrun, interactive = opts.get('dry_run'), opts.get('interactive')
2269 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2269 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2270 rejected = cmdutil.forget(ui, repo, m, prefix="", uipathfn=uipathfn,
2270 rejected = cmdutil.forget(ui, repo, m, prefix="", uipathfn=uipathfn,
2271 explicitonly=False, dryrun=dryrun,
2271 explicitonly=False, dryrun=dryrun,
2272 interactive=interactive)[0]
2272 interactive=interactive)[0]
2273 return rejected and 1 or 0
2273 return rejected and 1 or 0
2274
2274
2275 @command(
2275 @command(
2276 'graft',
2276 'graft',
2277 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2277 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2278 ('', 'base', '',
2278 ('', 'base', '',
2279 _('base revision when doing the graft merge (ADVANCED)'), _('REV')),
2279 _('base revision when doing the graft merge (ADVANCED)'), _('REV')),
2280 ('c', 'continue', False, _('resume interrupted graft')),
2280 ('c', 'continue', False, _('resume interrupted graft')),
2281 ('', 'stop', False, _('stop interrupted graft')),
2281 ('', 'stop', False, _('stop interrupted graft')),
2282 ('', 'abort', False, _('abort interrupted graft')),
2282 ('', 'abort', False, _('abort interrupted graft')),
2283 ('e', 'edit', False, _('invoke editor on commit messages')),
2283 ('e', 'edit', False, _('invoke editor on commit messages')),
2284 ('', 'log', None, _('append graft info to log message')),
2284 ('', 'log', None, _('append graft info to log message')),
2285 ('', 'no-commit', None,
2285 ('', 'no-commit', None,
2286 _("don't commit, just apply the changes in working directory")),
2286 _("don't commit, just apply the changes in working directory")),
2287 ('f', 'force', False, _('force graft')),
2287 ('f', 'force', False, _('force graft')),
2288 ('D', 'currentdate', False,
2288 ('D', 'currentdate', False,
2289 _('record the current date as commit date')),
2289 _('record the current date as commit date')),
2290 ('U', 'currentuser', False,
2290 ('U', 'currentuser', False,
2291 _('record the current user as committer'))]
2291 _('record the current user as committer'))]
2292 + commitopts2 + mergetoolopts + dryrunopts,
2292 + commitopts2 + mergetoolopts + dryrunopts,
2293 _('[OPTION]... [-r REV]... REV...'),
2293 _('[OPTION]... [-r REV]... REV...'),
2294 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
2294 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
2295 def graft(ui, repo, *revs, **opts):
2295 def graft(ui, repo, *revs, **opts):
2296 '''copy changes from other branches onto the current branch
2296 '''copy changes from other branches onto the current branch
2297
2297
2298 This command uses Mercurial's merge logic to copy individual
2298 This command uses Mercurial's merge logic to copy individual
2299 changes from other branches without merging branches in the
2299 changes from other branches without merging branches in the
2300 history graph. This is sometimes known as 'backporting' or
2300 history graph. This is sometimes known as 'backporting' or
2301 'cherry-picking'. By default, graft will copy user, date, and
2301 'cherry-picking'. By default, graft will copy user, date, and
2302 description from the source changesets.
2302 description from the source changesets.
2303
2303
2304 Changesets that are ancestors of the current revision, that have
2304 Changesets that are ancestors of the current revision, that have
2305 already been grafted, or that are merges will be skipped.
2305 already been grafted, or that are merges will be skipped.
2306
2306
2307 If --log is specified, log messages will have a comment appended
2307 If --log is specified, log messages will have a comment appended
2308 of the form::
2308 of the form::
2309
2309
2310 (grafted from CHANGESETHASH)
2310 (grafted from CHANGESETHASH)
2311
2311
2312 If --force is specified, revisions will be grafted even if they
2312 If --force is specified, revisions will be grafted even if they
2313 are already ancestors of, or have been grafted to, the destination.
2313 are already ancestors of, or have been grafted to, the destination.
2314 This is useful when the revisions have since been backed out.
2314 This is useful when the revisions have since been backed out.
2315
2315
2316 If a graft merge results in conflicts, the graft process is
2316 If a graft merge results in conflicts, the graft process is
2317 interrupted so that the current merge can be manually resolved.
2317 interrupted so that the current merge can be manually resolved.
2318 Once all conflicts are addressed, the graft process can be
2318 Once all conflicts are addressed, the graft process can be
2319 continued with the -c/--continue option.
2319 continued with the -c/--continue option.
2320
2320
2321 The -c/--continue option reapplies all the earlier options.
2321 The -c/--continue option reapplies all the earlier options.
2322
2322
2323 .. container:: verbose
2323 .. container:: verbose
2324
2324
2325 The --base option exposes more of how graft internally uses merge with a
2325 The --base option exposes more of how graft internally uses merge with a
2326 custom base revision. --base can be used to specify another ancestor than
2326 custom base revision. --base can be used to specify another ancestor than
2327 the first and only parent.
2327 the first and only parent.
2328
2328
2329 The command::
2329 The command::
2330
2330
2331 hg graft -r 345 --base 234
2331 hg graft -r 345 --base 234
2332
2332
2333 is thus pretty much the same as::
2333 is thus pretty much the same as::
2334
2334
2335 hg diff -r 234 -r 345 | hg import
2335 hg diff -r 234 -r 345 | hg import
2336
2336
2337 but using merge to resolve conflicts and track moved files.
2337 but using merge to resolve conflicts and track moved files.
2338
2338
2339 The result of a merge can thus be backported as a single commit by
2339 The result of a merge can thus be backported as a single commit by
2340 specifying one of the merge parents as base, and thus effectively
2340 specifying one of the merge parents as base, and thus effectively
2341 grafting the changes from the other side.
2341 grafting the changes from the other side.
2342
2342
2343 It is also possible to collapse multiple changesets and clean up history
2343 It is also possible to collapse multiple changesets and clean up history
2344 by specifying another ancestor as base, much like rebase --collapse
2344 by specifying another ancestor as base, much like rebase --collapse
2345 --keep.
2345 --keep.
2346
2346
2347 The commit message can be tweaked after the fact using commit --amend .
2347 The commit message can be tweaked after the fact using commit --amend .
2348
2348
2349 For using non-ancestors as the base to backout changes, see the backout
2349 For using non-ancestors as the base to backout changes, see the backout
2350 command and the hidden --parent option.
2350 command and the hidden --parent option.
2351
2351
2352 .. container:: verbose
2352 .. container:: verbose
2353
2353
2354 Examples:
2354 Examples:
2355
2355
2356 - copy a single change to the stable branch and edit its description::
2356 - copy a single change to the stable branch and edit its description::
2357
2357
2358 hg update stable
2358 hg update stable
2359 hg graft --edit 9393
2359 hg graft --edit 9393
2360
2360
2361 - graft a range of changesets with one exception, updating dates::
2361 - graft a range of changesets with one exception, updating dates::
2362
2362
2363 hg graft -D "2085::2093 and not 2091"
2363 hg graft -D "2085::2093 and not 2091"
2364
2364
2365 - continue a graft after resolving conflicts::
2365 - continue a graft after resolving conflicts::
2366
2366
2367 hg graft -c
2367 hg graft -c
2368
2368
2369 - show the source of a grafted changeset::
2369 - show the source of a grafted changeset::
2370
2370
2371 hg log --debug -r .
2371 hg log --debug -r .
2372
2372
2373 - show revisions sorted by date::
2373 - show revisions sorted by date::
2374
2374
2375 hg log -r "sort(all(), date)"
2375 hg log -r "sort(all(), date)"
2376
2376
2377 - backport the result of a merge as a single commit::
2377 - backport the result of a merge as a single commit::
2378
2378
2379 hg graft -r 123 --base 123^
2379 hg graft -r 123 --base 123^
2380
2380
2381 - land a feature branch as one changeset::
2381 - land a feature branch as one changeset::
2382
2382
2383 hg up -cr default
2383 hg up -cr default
2384 hg graft -r featureX --base "ancestor('featureX', 'default')"
2384 hg graft -r featureX --base "ancestor('featureX', 'default')"
2385
2385
2386 See :hg:`help revisions` for more about specifying revisions.
2386 See :hg:`help revisions` for more about specifying revisions.
2387
2387
2388 Returns 0 on successful completion.
2388 Returns 0 on successful completion.
2389 '''
2389 '''
2390 with repo.wlock():
2390 with repo.wlock():
2391 return _dograft(ui, repo, *revs, **opts)
2391 return _dograft(ui, repo, *revs, **opts)
2392
2392
2393 def _dograft(ui, repo, *revs, **opts):
2393 def _dograft(ui, repo, *revs, **opts):
2394 opts = pycompat.byteskwargs(opts)
2394 opts = pycompat.byteskwargs(opts)
2395 if revs and opts.get('rev'):
2395 if revs and opts.get('rev'):
2396 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2396 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2397 'revision ordering!\n'))
2397 'revision ordering!\n'))
2398
2398
2399 revs = list(revs)
2399 revs = list(revs)
2400 revs.extend(opts.get('rev'))
2400 revs.extend(opts.get('rev'))
2401 basectx = None
2401 basectx = None
2402 if opts.get('base'):
2402 if opts.get('base'):
2403 basectx = scmutil.revsingle(repo, opts['base'], None)
2403 basectx = scmutil.revsingle(repo, opts['base'], None)
2404 # a dict of data to be stored in state file
2404 # a dict of data to be stored in state file
2405 statedata = {}
2405 statedata = {}
2406 # list of new nodes created by ongoing graft
2406 # list of new nodes created by ongoing graft
2407 statedata['newnodes'] = []
2407 statedata['newnodes'] = []
2408
2408
2409 if opts.get('user') and opts.get('currentuser'):
2409 if opts.get('user') and opts.get('currentuser'):
2410 raise error.Abort(_('--user and --currentuser are mutually exclusive'))
2410 raise error.Abort(_('--user and --currentuser are mutually exclusive'))
2411 if opts.get('date') and opts.get('currentdate'):
2411 if opts.get('date') and opts.get('currentdate'):
2412 raise error.Abort(_('--date and --currentdate are mutually exclusive'))
2412 raise error.Abort(_('--date and --currentdate are mutually exclusive'))
2413 if not opts.get('user') and opts.get('currentuser'):
2413 if not opts.get('user') and opts.get('currentuser'):
2414 opts['user'] = ui.username()
2414 opts['user'] = ui.username()
2415 if not opts.get('date') and opts.get('currentdate'):
2415 if not opts.get('date') and opts.get('currentdate'):
2416 opts['date'] = "%d %d" % dateutil.makedate()
2416 opts['date'] = "%d %d" % dateutil.makedate()
2417
2417
2418 editor = cmdutil.getcommiteditor(editform='graft',
2418 editor = cmdutil.getcommiteditor(editform='graft',
2419 **pycompat.strkwargs(opts))
2419 **pycompat.strkwargs(opts))
2420
2420
2421 cont = False
2421 cont = False
2422 if opts.get('no_commit'):
2422 if opts.get('no_commit'):
2423 if opts.get('edit'):
2423 if opts.get('edit'):
2424 raise error.Abort(_("cannot specify --no-commit and "
2424 raise error.Abort(_("cannot specify --no-commit and "
2425 "--edit together"))
2425 "--edit together"))
2426 if opts.get('currentuser'):
2426 if opts.get('currentuser'):
2427 raise error.Abort(_("cannot specify --no-commit and "
2427 raise error.Abort(_("cannot specify --no-commit and "
2428 "--currentuser together"))
2428 "--currentuser together"))
2429 if opts.get('currentdate'):
2429 if opts.get('currentdate'):
2430 raise error.Abort(_("cannot specify --no-commit and "
2430 raise error.Abort(_("cannot specify --no-commit and "
2431 "--currentdate together"))
2431 "--currentdate together"))
2432 if opts.get('log'):
2432 if opts.get('log'):
2433 raise error.Abort(_("cannot specify --no-commit and "
2433 raise error.Abort(_("cannot specify --no-commit and "
2434 "--log together"))
2434 "--log together"))
2435
2435
2436 graftstate = statemod.cmdstate(repo, 'graftstate')
2436 graftstate = statemod.cmdstate(repo, 'graftstate')
2437
2437
2438 if opts.get('stop'):
2438 if opts.get('stop'):
2439 if opts.get('continue'):
2439 if opts.get('continue'):
2440 raise error.Abort(_("cannot use '--continue' and "
2440 raise error.Abort(_("cannot use '--continue' and "
2441 "'--stop' together"))
2441 "'--stop' together"))
2442 if opts.get('abort'):
2442 if opts.get('abort'):
2443 raise error.Abort(_("cannot use '--abort' and '--stop' together"))
2443 raise error.Abort(_("cannot use '--abort' and '--stop' together"))
2444
2444
2445 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2445 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2446 opts.get('date'), opts.get('currentdate'),
2446 opts.get('date'), opts.get('currentdate'),
2447 opts.get('currentuser'), opts.get('rev'))):
2447 opts.get('currentuser'), opts.get('rev'))):
2448 raise error.Abort(_("cannot specify any other flag with '--stop'"))
2448 raise error.Abort(_("cannot specify any other flag with '--stop'"))
2449 return _stopgraft(ui, repo, graftstate)
2449 return _stopgraft(ui, repo, graftstate)
2450 elif opts.get('abort'):
2450 elif opts.get('abort'):
2451 if opts.get('continue'):
2451 if opts.get('continue'):
2452 raise error.Abort(_("cannot use '--continue' and "
2452 raise error.Abort(_("cannot use '--continue' and "
2453 "'--abort' together"))
2453 "'--abort' together"))
2454 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2454 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2455 opts.get('date'), opts.get('currentdate'),
2455 opts.get('date'), opts.get('currentdate'),
2456 opts.get('currentuser'), opts.get('rev'))):
2456 opts.get('currentuser'), opts.get('rev'))):
2457 raise error.Abort(_("cannot specify any other flag with '--abort'"))
2457 raise error.Abort(_("cannot specify any other flag with '--abort'"))
2458
2458
2459 return _abortgraft(ui, repo, graftstate)
2459 return _abortgraft(ui, repo, graftstate)
2460 elif opts.get('continue'):
2460 elif opts.get('continue'):
2461 cont = True
2461 cont = True
2462 if revs:
2462 if revs:
2463 raise error.Abort(_("can't specify --continue and revisions"))
2463 raise error.Abort(_("can't specify --continue and revisions"))
2464 # read in unfinished revisions
2464 # read in unfinished revisions
2465 if graftstate.exists():
2465 if graftstate.exists():
2466 statedata = _readgraftstate(repo, graftstate)
2466 statedata = _readgraftstate(repo, graftstate)
2467 if statedata.get('date'):
2467 if statedata.get('date'):
2468 opts['date'] = statedata['date']
2468 opts['date'] = statedata['date']
2469 if statedata.get('user'):
2469 if statedata.get('user'):
2470 opts['user'] = statedata['user']
2470 opts['user'] = statedata['user']
2471 if statedata.get('log'):
2471 if statedata.get('log'):
2472 opts['log'] = True
2472 opts['log'] = True
2473 if statedata.get('no_commit'):
2473 if statedata.get('no_commit'):
2474 opts['no_commit'] = statedata.get('no_commit')
2474 opts['no_commit'] = statedata.get('no_commit')
2475 nodes = statedata['nodes']
2475 nodes = statedata['nodes']
2476 revs = [repo[node].rev() for node in nodes]
2476 revs = [repo[node].rev() for node in nodes]
2477 else:
2477 else:
2478 cmdutil.wrongtooltocontinue(repo, _('graft'))
2478 cmdutil.wrongtooltocontinue(repo, _('graft'))
2479 else:
2479 else:
2480 if not revs:
2480 if not revs:
2481 raise error.Abort(_('no revisions specified'))
2481 raise error.Abort(_('no revisions specified'))
2482 cmdutil.checkunfinished(repo)
2482 cmdutil.checkunfinished(repo)
2483 cmdutil.bailifchanged(repo)
2483 cmdutil.bailifchanged(repo)
2484 revs = scmutil.revrange(repo, revs)
2484 revs = scmutil.revrange(repo, revs)
2485
2485
2486 skipped = set()
2486 skipped = set()
2487 if basectx is None:
2487 if basectx is None:
2488 # check for merges
2488 # check for merges
2489 for rev in repo.revs('%ld and merge()', revs):
2489 for rev in repo.revs('%ld and merge()', revs):
2490 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2490 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2491 skipped.add(rev)
2491 skipped.add(rev)
2492 revs = [r for r in revs if r not in skipped]
2492 revs = [r for r in revs if r not in skipped]
2493 if not revs:
2493 if not revs:
2494 return -1
2494 return -1
2495 if basectx is not None and len(revs) != 1:
2495 if basectx is not None and len(revs) != 1:
2496 raise error.Abort(_('only one revision allowed with --base '))
2496 raise error.Abort(_('only one revision allowed with --base '))
2497
2497
2498 # Don't check in the --continue case, in effect retaining --force across
2498 # Don't check in the --continue case, in effect retaining --force across
2499 # --continues. That's because without --force, any revisions we decided to
2499 # --continues. That's because without --force, any revisions we decided to
2500 # skip would have been filtered out here, so they wouldn't have made their
2500 # skip would have been filtered out here, so they wouldn't have made their
2501 # way to the graftstate. With --force, any revisions we would have otherwise
2501 # way to the graftstate. With --force, any revisions we would have otherwise
2502 # skipped would not have been filtered out, and if they hadn't been applied
2502 # skipped would not have been filtered out, and if they hadn't been applied
2503 # already, they'd have been in the graftstate.
2503 # already, they'd have been in the graftstate.
2504 if not (cont or opts.get('force')) and basectx is None:
2504 if not (cont or opts.get('force')) and basectx is None:
2505 # check for ancestors of dest branch
2505 # check for ancestors of dest branch
2506 crev = repo['.'].rev()
2506 crev = repo['.'].rev()
2507 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2507 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2508 # XXX make this lazy in the future
2508 # XXX make this lazy in the future
2509 # don't mutate while iterating, create a copy
2509 # don't mutate while iterating, create a copy
2510 for rev in list(revs):
2510 for rev in list(revs):
2511 if rev in ancestors:
2511 if rev in ancestors:
2512 ui.warn(_('skipping ancestor revision %d:%s\n') %
2512 ui.warn(_('skipping ancestor revision %d:%s\n') %
2513 (rev, repo[rev]))
2513 (rev, repo[rev]))
2514 # XXX remove on list is slow
2514 # XXX remove on list is slow
2515 revs.remove(rev)
2515 revs.remove(rev)
2516 if not revs:
2516 if not revs:
2517 return -1
2517 return -1
2518
2518
2519 # analyze revs for earlier grafts
2519 # analyze revs for earlier grafts
2520 ids = {}
2520 ids = {}
2521 for ctx in repo.set("%ld", revs):
2521 for ctx in repo.set("%ld", revs):
2522 ids[ctx.hex()] = ctx.rev()
2522 ids[ctx.hex()] = ctx.rev()
2523 n = ctx.extra().get('source')
2523 n = ctx.extra().get('source')
2524 if n:
2524 if n:
2525 ids[n] = ctx.rev()
2525 ids[n] = ctx.rev()
2526
2526
2527 # check ancestors for earlier grafts
2527 # check ancestors for earlier grafts
2528 ui.debug('scanning for duplicate grafts\n')
2528 ui.debug('scanning for duplicate grafts\n')
2529
2529
2530 # The only changesets we can be sure doesn't contain grafts of any
2530 # The only changesets we can be sure doesn't contain grafts of any
2531 # revs, are the ones that are common ancestors of *all* revs:
2531 # revs, are the ones that are common ancestors of *all* revs:
2532 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2532 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2533 ctx = repo[rev]
2533 ctx = repo[rev]
2534 n = ctx.extra().get('source')
2534 n = ctx.extra().get('source')
2535 if n in ids:
2535 if n in ids:
2536 try:
2536 try:
2537 r = repo[n].rev()
2537 r = repo[n].rev()
2538 except error.RepoLookupError:
2538 except error.RepoLookupError:
2539 r = None
2539 r = None
2540 if r in revs:
2540 if r in revs:
2541 ui.warn(_('skipping revision %d:%s '
2541 ui.warn(_('skipping revision %d:%s '
2542 '(already grafted to %d:%s)\n')
2542 '(already grafted to %d:%s)\n')
2543 % (r, repo[r], rev, ctx))
2543 % (r, repo[r], rev, ctx))
2544 revs.remove(r)
2544 revs.remove(r)
2545 elif ids[n] in revs:
2545 elif ids[n] in revs:
2546 if r is None:
2546 if r is None:
2547 ui.warn(_('skipping already grafted revision %d:%s '
2547 ui.warn(_('skipping already grafted revision %d:%s '
2548 '(%d:%s also has unknown origin %s)\n')
2548 '(%d:%s also has unknown origin %s)\n')
2549 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2549 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2550 else:
2550 else:
2551 ui.warn(_('skipping already grafted revision %d:%s '
2551 ui.warn(_('skipping already grafted revision %d:%s '
2552 '(%d:%s also has origin %d:%s)\n')
2552 '(%d:%s also has origin %d:%s)\n')
2553 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2553 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2554 revs.remove(ids[n])
2554 revs.remove(ids[n])
2555 elif ctx.hex() in ids:
2555 elif ctx.hex() in ids:
2556 r = ids[ctx.hex()]
2556 r = ids[ctx.hex()]
2557 if r in revs:
2557 if r in revs:
2558 ui.warn(_('skipping already grafted revision %d:%s '
2558 ui.warn(_('skipping already grafted revision %d:%s '
2559 '(was grafted from %d:%s)\n') %
2559 '(was grafted from %d:%s)\n') %
2560 (r, repo[r], rev, ctx))
2560 (r, repo[r], rev, ctx))
2561 revs.remove(r)
2561 revs.remove(r)
2562 if not revs:
2562 if not revs:
2563 return -1
2563 return -1
2564
2564
2565 if opts.get('no_commit'):
2565 if opts.get('no_commit'):
2566 statedata['no_commit'] = True
2566 statedata['no_commit'] = True
2567 for pos, ctx in enumerate(repo.set("%ld", revs)):
2567 for pos, ctx in enumerate(repo.set("%ld", revs)):
2568 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2568 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2569 ctx.description().split('\n', 1)[0])
2569 ctx.description().split('\n', 1)[0])
2570 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2570 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2571 if names:
2571 if names:
2572 desc += ' (%s)' % ' '.join(names)
2572 desc += ' (%s)' % ' '.join(names)
2573 ui.status(_('grafting %s\n') % desc)
2573 ui.status(_('grafting %s\n') % desc)
2574 if opts.get('dry_run'):
2574 if opts.get('dry_run'):
2575 continue
2575 continue
2576
2576
2577 source = ctx.extra().get('source')
2577 source = ctx.extra().get('source')
2578 extra = {}
2578 extra = {}
2579 if source:
2579 if source:
2580 extra['source'] = source
2580 extra['source'] = source
2581 extra['intermediate-source'] = ctx.hex()
2581 extra['intermediate-source'] = ctx.hex()
2582 else:
2582 else:
2583 extra['source'] = ctx.hex()
2583 extra['source'] = ctx.hex()
2584 user = ctx.user()
2584 user = ctx.user()
2585 if opts.get('user'):
2585 if opts.get('user'):
2586 user = opts['user']
2586 user = opts['user']
2587 statedata['user'] = user
2587 statedata['user'] = user
2588 date = ctx.date()
2588 date = ctx.date()
2589 if opts.get('date'):
2589 if opts.get('date'):
2590 date = opts['date']
2590 date = opts['date']
2591 statedata['date'] = date
2591 statedata['date'] = date
2592 message = ctx.description()
2592 message = ctx.description()
2593 if opts.get('log'):
2593 if opts.get('log'):
2594 message += '\n(grafted from %s)' % ctx.hex()
2594 message += '\n(grafted from %s)' % ctx.hex()
2595 statedata['log'] = True
2595 statedata['log'] = True
2596
2596
2597 # we don't merge the first commit when continuing
2597 # we don't merge the first commit when continuing
2598 if not cont:
2598 if not cont:
2599 # perform the graft merge with p1(rev) as 'ancestor'
2599 # perform the graft merge with p1(rev) as 'ancestor'
2600 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
2600 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
2601 base = ctx.p1() if basectx is None else basectx
2601 base = ctx.p1() if basectx is None else basectx
2602 with ui.configoverride(overrides, 'graft'):
2602 with ui.configoverride(overrides, 'graft'):
2603 stats = mergemod.graft(repo, ctx, base, ['local', 'graft'])
2603 stats = mergemod.graft(repo, ctx, base, ['local', 'graft'])
2604 # report any conflicts
2604 # report any conflicts
2605 if stats.unresolvedcount > 0:
2605 if stats.unresolvedcount > 0:
2606 # write out state for --continue
2606 # write out state for --continue
2607 nodes = [repo[rev].hex() for rev in revs[pos:]]
2607 nodes = [repo[rev].hex() for rev in revs[pos:]]
2608 statedata['nodes'] = nodes
2608 statedata['nodes'] = nodes
2609 stateversion = 1
2609 stateversion = 1
2610 graftstate.save(stateversion, statedata)
2610 graftstate.save(stateversion, statedata)
2611 hint = _("use 'hg resolve' and 'hg graft --continue'")
2611 hint = _("use 'hg resolve' and 'hg graft --continue'")
2612 raise error.Abort(
2612 raise error.Abort(
2613 _("unresolved conflicts, can't continue"),
2613 _("unresolved conflicts, can't continue"),
2614 hint=hint)
2614 hint=hint)
2615 else:
2615 else:
2616 cont = False
2616 cont = False
2617
2617
2618 # commit if --no-commit is false
2618 # commit if --no-commit is false
2619 if not opts.get('no_commit'):
2619 if not opts.get('no_commit'):
2620 node = repo.commit(text=message, user=user, date=date, extra=extra,
2620 node = repo.commit(text=message, user=user, date=date, extra=extra,
2621 editor=editor)
2621 editor=editor)
2622 if node is None:
2622 if node is None:
2623 ui.warn(
2623 ui.warn(
2624 _('note: graft of %d:%s created no changes to commit\n') %
2624 _('note: graft of %d:%s created no changes to commit\n') %
2625 (ctx.rev(), ctx))
2625 (ctx.rev(), ctx))
2626 # checking that newnodes exist because old state files won't have it
2626 # checking that newnodes exist because old state files won't have it
2627 elif statedata.get('newnodes') is not None:
2627 elif statedata.get('newnodes') is not None:
2628 statedata['newnodes'].append(node)
2628 statedata['newnodes'].append(node)
2629
2629
2630 # remove state when we complete successfully
2630 # remove state when we complete successfully
2631 if not opts.get('dry_run'):
2631 if not opts.get('dry_run'):
2632 graftstate.delete()
2632 graftstate.delete()
2633
2633
2634 return 0
2634 return 0
2635
2635
2636 def _abortgraft(ui, repo, graftstate):
2636 def _abortgraft(ui, repo, graftstate):
2637 """abort the interrupted graft and rollbacks to the state before interrupted
2637 """abort the interrupted graft and rollbacks to the state before interrupted
2638 graft"""
2638 graft"""
2639 if not graftstate.exists():
2639 if not graftstate.exists():
2640 raise error.Abort(_("no interrupted graft to abort"))
2640 raise error.Abort(_("no interrupted graft to abort"))
2641 statedata = _readgraftstate(repo, graftstate)
2641 statedata = _readgraftstate(repo, graftstate)
2642 newnodes = statedata.get('newnodes')
2642 newnodes = statedata.get('newnodes')
2643 if newnodes is None:
2643 if newnodes is None:
2644 # and old graft state which does not have all the data required to abort
2644 # and old graft state which does not have all the data required to abort
2645 # the graft
2645 # the graft
2646 raise error.Abort(_("cannot abort using an old graftstate"))
2646 raise error.Abort(_("cannot abort using an old graftstate"))
2647
2647
2648 # changeset from which graft operation was started
2648 # changeset from which graft operation was started
2649 if len(newnodes) > 0:
2649 if len(newnodes) > 0:
2650 startctx = repo[newnodes[0]].p1()
2650 startctx = repo[newnodes[0]].p1()
2651 else:
2651 else:
2652 startctx = repo['.']
2652 startctx = repo['.']
2653 # whether to strip or not
2653 # whether to strip or not
2654 cleanup = False
2654 cleanup = False
2655 if newnodes:
2655 if newnodes:
2656 newnodes = [repo[r].rev() for r in newnodes]
2656 newnodes = [repo[r].rev() for r in newnodes]
2657 cleanup = True
2657 cleanup = True
2658 # checking that none of the newnodes turned public or is public
2658 # checking that none of the newnodes turned public or is public
2659 immutable = [c for c in newnodes if not repo[c].mutable()]
2659 immutable = [c for c in newnodes if not repo[c].mutable()]
2660 if immutable:
2660 if immutable:
2661 repo.ui.warn(_("cannot clean up public changesets %s\n")
2661 repo.ui.warn(_("cannot clean up public changesets %s\n")
2662 % ', '.join(bytes(repo[r]) for r in immutable),
2662 % ', '.join(bytes(repo[r]) for r in immutable),
2663 hint=_("see 'hg help phases' for details"))
2663 hint=_("see 'hg help phases' for details"))
2664 cleanup = False
2664 cleanup = False
2665
2665
2666 # checking that no new nodes are created on top of grafted revs
2666 # checking that no new nodes are created on top of grafted revs
2667 desc = set(repo.changelog.descendants(newnodes))
2667 desc = set(repo.changelog.descendants(newnodes))
2668 if desc - set(newnodes):
2668 if desc - set(newnodes):
2669 repo.ui.warn(_("new changesets detected on destination "
2669 repo.ui.warn(_("new changesets detected on destination "
2670 "branch, can't strip\n"))
2670 "branch, can't strip\n"))
2671 cleanup = False
2671 cleanup = False
2672
2672
2673 if cleanup:
2673 if cleanup:
2674 with repo.wlock(), repo.lock():
2674 with repo.wlock(), repo.lock():
2675 hg.updaterepo(repo, startctx.node(), overwrite=True)
2675 hg.updaterepo(repo, startctx.node(), overwrite=True)
2676 # stripping the new nodes created
2676 # stripping the new nodes created
2677 strippoints = [c.node() for c in repo.set("roots(%ld)",
2677 strippoints = [c.node() for c in repo.set("roots(%ld)",
2678 newnodes)]
2678 newnodes)]
2679 repair.strip(repo.ui, repo, strippoints, backup=False)
2679 repair.strip(repo.ui, repo, strippoints, backup=False)
2680
2680
2681 if not cleanup:
2681 if not cleanup:
2682 # we don't update to the startnode if we can't strip
2682 # we don't update to the startnode if we can't strip
2683 startctx = repo['.']
2683 startctx = repo['.']
2684 hg.updaterepo(repo, startctx.node(), overwrite=True)
2684 hg.updaterepo(repo, startctx.node(), overwrite=True)
2685
2685
2686 ui.status(_("graft aborted\n"))
2686 ui.status(_("graft aborted\n"))
2687 ui.status(_("working directory is now at %s\n") % startctx.hex()[:12])
2687 ui.status(_("working directory is now at %s\n") % startctx.hex()[:12])
2688 graftstate.delete()
2688 graftstate.delete()
2689 return 0
2689 return 0
2690
2690
2691 def _readgraftstate(repo, graftstate):
2691 def _readgraftstate(repo, graftstate):
2692 """read the graft state file and return a dict of the data stored in it"""
2692 """read the graft state file and return a dict of the data stored in it"""
2693 try:
2693 try:
2694 return graftstate.read()
2694 return graftstate.read()
2695 except error.CorruptedState:
2695 except error.CorruptedState:
2696 nodes = repo.vfs.read('graftstate').splitlines()
2696 nodes = repo.vfs.read('graftstate').splitlines()
2697 return {'nodes': nodes}
2697 return {'nodes': nodes}
2698
2698
2699 def _stopgraft(ui, repo, graftstate):
2699 def _stopgraft(ui, repo, graftstate):
2700 """stop the interrupted graft"""
2700 """stop the interrupted graft"""
2701 if not graftstate.exists():
2701 if not graftstate.exists():
2702 raise error.Abort(_("no interrupted graft found"))
2702 raise error.Abort(_("no interrupted graft found"))
2703 pctx = repo['.']
2703 pctx = repo['.']
2704 hg.updaterepo(repo, pctx.node(), overwrite=True)
2704 hg.updaterepo(repo, pctx.node(), overwrite=True)
2705 graftstate.delete()
2705 graftstate.delete()
2706 ui.status(_("stopped the interrupted graft\n"))
2706 ui.status(_("stopped the interrupted graft\n"))
2707 ui.status(_("working directory is now at %s\n") % pctx.hex()[:12])
2707 ui.status(_("working directory is now at %s\n") % pctx.hex()[:12])
2708 return 0
2708 return 0
2709
2709
2710 @command('grep',
2710 @command('grep',
2711 [('0', 'print0', None, _('end fields with NUL')),
2711 [('0', 'print0', None, _('end fields with NUL')),
2712 ('', 'all', None, _('print all revisions that match (DEPRECATED) ')),
2712 ('', 'all', None, _('print all revisions that match (DEPRECATED) ')),
2713 ('', 'diff', None, _('print all revisions when the term was introduced '
2713 ('', 'diff', None, _('print all revisions when the term was introduced '
2714 'or removed')),
2714 'or removed')),
2715 ('a', 'text', None, _('treat all files as text')),
2715 ('a', 'text', None, _('treat all files as text')),
2716 ('f', 'follow', None,
2716 ('f', 'follow', None,
2717 _('follow changeset history,'
2717 _('follow changeset history,'
2718 ' or file history across copies and renames')),
2718 ' or file history across copies and renames')),
2719 ('i', 'ignore-case', None, _('ignore case when matching')),
2719 ('i', 'ignore-case', None, _('ignore case when matching')),
2720 ('l', 'files-with-matches', None,
2720 ('l', 'files-with-matches', None,
2721 _('print only filenames and revisions that match')),
2721 _('print only filenames and revisions that match')),
2722 ('n', 'line-number', None, _('print matching line numbers')),
2722 ('n', 'line-number', None, _('print matching line numbers')),
2723 ('r', 'rev', [],
2723 ('r', 'rev', [],
2724 _('only search files changed within revision range'), _('REV')),
2724 _('only search files changed within revision range'), _('REV')),
2725 ('', 'all-files', None,
2725 ('', 'all-files', None,
2726 _('include all files in the changeset while grepping (EXPERIMENTAL)')),
2726 _('include all files in the changeset while grepping (EXPERIMENTAL)')),
2727 ('u', 'user', None, _('list the author (long with -v)')),
2727 ('u', 'user', None, _('list the author (long with -v)')),
2728 ('d', 'date', None, _('list the date (short with -q)')),
2728 ('d', 'date', None, _('list the date (short with -q)')),
2729 ] + formatteropts + walkopts,
2729 ] + formatteropts + walkopts,
2730 _('[OPTION]... PATTERN [FILE]...'),
2730 _('[OPTION]... PATTERN [FILE]...'),
2731 helpcategory=command.CATEGORY_FILE_CONTENTS,
2731 helpcategory=command.CATEGORY_FILE_CONTENTS,
2732 inferrepo=True,
2732 inferrepo=True,
2733 intents={INTENT_READONLY})
2733 intents={INTENT_READONLY})
2734 def grep(ui, repo, pattern, *pats, **opts):
2734 def grep(ui, repo, pattern, *pats, **opts):
2735 """search revision history for a pattern in specified files
2735 """search revision history for a pattern in specified files
2736
2736
2737 Search revision history for a regular expression in the specified
2737 Search revision history for a regular expression in the specified
2738 files or the entire project.
2738 files or the entire project.
2739
2739
2740 By default, grep prints the most recent revision number for each
2740 By default, grep prints the most recent revision number for each
2741 file in which it finds a match. To get it to print every revision
2741 file in which it finds a match. To get it to print every revision
2742 that contains a change in match status ("-" for a match that becomes
2742 that contains a change in match status ("-" for a match that becomes
2743 a non-match, or "+" for a non-match that becomes a match), use the
2743 a non-match, or "+" for a non-match that becomes a match), use the
2744 --diff flag.
2744 --diff flag.
2745
2745
2746 PATTERN can be any Python (roughly Perl-compatible) regular
2746 PATTERN can be any Python (roughly Perl-compatible) regular
2747 expression.
2747 expression.
2748
2748
2749 If no FILEs are specified (and -f/--follow isn't set), all files in
2749 If no FILEs are specified (and -f/--follow isn't set), all files in
2750 the repository are searched, including those that don't exist in the
2750 the repository are searched, including those that don't exist in the
2751 current branch or have been deleted in a prior changeset.
2751 current branch or have been deleted in a prior changeset.
2752
2752
2753 .. container:: verbose
2753 .. container:: verbose
2754
2754
2755 Template:
2755 Template:
2756
2756
2757 The following keywords are supported in addition to the common template
2757 The following keywords are supported in addition to the common template
2758 keywords and functions. See also :hg:`help templates`.
2758 keywords and functions. See also :hg:`help templates`.
2759
2759
2760 :change: String. Character denoting insertion ``+`` or removal ``-``.
2760 :change: String. Character denoting insertion ``+`` or removal ``-``.
2761 Available if ``--diff`` is specified.
2761 Available if ``--diff`` is specified.
2762 :lineno: Integer. Line number of the match.
2762 :lineno: Integer. Line number of the match.
2763 :path: String. Repository-absolute path of the file.
2763 :path: String. Repository-absolute path of the file.
2764 :texts: List of text chunks.
2764 :texts: List of text chunks.
2765
2765
2766 And each entry of ``{texts}`` provides the following sub-keywords.
2766 And each entry of ``{texts}`` provides the following sub-keywords.
2767
2767
2768 :matched: Boolean. True if the chunk matches the specified pattern.
2768 :matched: Boolean. True if the chunk matches the specified pattern.
2769 :text: String. Chunk content.
2769 :text: String. Chunk content.
2770
2770
2771 See :hg:`help templates.operators` for the list expansion syntax.
2771 See :hg:`help templates.operators` for the list expansion syntax.
2772
2772
2773 Returns 0 if a match is found, 1 otherwise.
2773 Returns 0 if a match is found, 1 otherwise.
2774 """
2774 """
2775 opts = pycompat.byteskwargs(opts)
2775 opts = pycompat.byteskwargs(opts)
2776 diff = opts.get('all') or opts.get('diff')
2776 diff = opts.get('all') or opts.get('diff')
2777 all_files = opts.get('all_files')
2777 all_files = opts.get('all_files')
2778 if diff and opts.get('all_files'):
2778 if diff and opts.get('all_files'):
2779 raise error.Abort(_('--diff and --all-files are mutually exclusive'))
2779 raise error.Abort(_('--diff and --all-files are mutually exclusive'))
2780 # TODO: remove "not opts.get('rev')" if --all-files -rMULTIREV gets working
2780 # TODO: remove "not opts.get('rev')" if --all-files -rMULTIREV gets working
2781 if opts.get('all_files') is None and not opts.get('rev') and not diff:
2781 if opts.get('all_files') is None and not opts.get('rev') and not diff:
2782 # experimental config: commands.grep.all-files
2782 # experimental config: commands.grep.all-files
2783 opts['all_files'] = ui.configbool('commands', 'grep.all-files')
2783 opts['all_files'] = ui.configbool('commands', 'grep.all-files')
2784 plaingrep = opts.get('all_files') and not opts.get('rev')
2784 plaingrep = opts.get('all_files') and not opts.get('rev')
2785 if plaingrep:
2785 if plaingrep:
2786 opts['rev'] = ['wdir()']
2786 opts['rev'] = ['wdir()']
2787
2787
2788 reflags = re.M
2788 reflags = re.M
2789 if opts.get('ignore_case'):
2789 if opts.get('ignore_case'):
2790 reflags |= re.I
2790 reflags |= re.I
2791 try:
2791 try:
2792 regexp = util.re.compile(pattern, reflags)
2792 regexp = util.re.compile(pattern, reflags)
2793 except re.error as inst:
2793 except re.error as inst:
2794 ui.warn(_("grep: invalid match pattern: %s\n") % pycompat.bytestr(inst))
2794 ui.warn(_("grep: invalid match pattern: %s\n") % pycompat.bytestr(inst))
2795 return 1
2795 return 1
2796 sep, eol = ':', '\n'
2796 sep, eol = ':', '\n'
2797 if opts.get('print0'):
2797 if opts.get('print0'):
2798 sep = eol = '\0'
2798 sep = eol = '\0'
2799
2799
2800 getfile = util.lrucachefunc(repo.file)
2800 getfile = util.lrucachefunc(repo.file)
2801
2801
2802 def matchlines(body):
2802 def matchlines(body):
2803 begin = 0
2803 begin = 0
2804 linenum = 0
2804 linenum = 0
2805 while begin < len(body):
2805 while begin < len(body):
2806 match = regexp.search(body, begin)
2806 match = regexp.search(body, begin)
2807 if not match:
2807 if not match:
2808 break
2808 break
2809 mstart, mend = match.span()
2809 mstart, mend = match.span()
2810 linenum += body.count('\n', begin, mstart) + 1
2810 linenum += body.count('\n', begin, mstart) + 1
2811 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2811 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2812 begin = body.find('\n', mend) + 1 or len(body) + 1
2812 begin = body.find('\n', mend) + 1 or len(body) + 1
2813 lend = begin - 1
2813 lend = begin - 1
2814 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2814 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2815
2815
2816 class linestate(object):
2816 class linestate(object):
2817 def __init__(self, line, linenum, colstart, colend):
2817 def __init__(self, line, linenum, colstart, colend):
2818 self.line = line
2818 self.line = line
2819 self.linenum = linenum
2819 self.linenum = linenum
2820 self.colstart = colstart
2820 self.colstart = colstart
2821 self.colend = colend
2821 self.colend = colend
2822
2822
2823 def __hash__(self):
2823 def __hash__(self):
2824 return hash((self.linenum, self.line))
2824 return hash((self.linenum, self.line))
2825
2825
2826 def __eq__(self, other):
2826 def __eq__(self, other):
2827 return self.line == other.line
2827 return self.line == other.line
2828
2828
2829 def findpos(self):
2829 def findpos(self):
2830 """Iterate all (start, end) indices of matches"""
2830 """Iterate all (start, end) indices of matches"""
2831 yield self.colstart, self.colend
2831 yield self.colstart, self.colend
2832 p = self.colend
2832 p = self.colend
2833 while p < len(self.line):
2833 while p < len(self.line):
2834 m = regexp.search(self.line, p)
2834 m = regexp.search(self.line, p)
2835 if not m:
2835 if not m:
2836 break
2836 break
2837 yield m.span()
2837 yield m.span()
2838 p = m.end()
2838 p = m.end()
2839
2839
2840 matches = {}
2840 matches = {}
2841 copies = {}
2841 copies = {}
2842 def grepbody(fn, rev, body):
2842 def grepbody(fn, rev, body):
2843 matches[rev].setdefault(fn, [])
2843 matches[rev].setdefault(fn, [])
2844 m = matches[rev][fn]
2844 m = matches[rev][fn]
2845 for lnum, cstart, cend, line in matchlines(body):
2845 for lnum, cstart, cend, line in matchlines(body):
2846 s = linestate(line, lnum, cstart, cend)
2846 s = linestate(line, lnum, cstart, cend)
2847 m.append(s)
2847 m.append(s)
2848
2848
2849 def difflinestates(a, b):
2849 def difflinestates(a, b):
2850 sm = difflib.SequenceMatcher(None, a, b)
2850 sm = difflib.SequenceMatcher(None, a, b)
2851 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2851 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2852 if tag == r'insert':
2852 if tag == r'insert':
2853 for i in pycompat.xrange(blo, bhi):
2853 for i in pycompat.xrange(blo, bhi):
2854 yield ('+', b[i])
2854 yield ('+', b[i])
2855 elif tag == r'delete':
2855 elif tag == r'delete':
2856 for i in pycompat.xrange(alo, ahi):
2856 for i in pycompat.xrange(alo, ahi):
2857 yield ('-', a[i])
2857 yield ('-', a[i])
2858 elif tag == r'replace':
2858 elif tag == r'replace':
2859 for i in pycompat.xrange(alo, ahi):
2859 for i in pycompat.xrange(alo, ahi):
2860 yield ('-', a[i])
2860 yield ('-', a[i])
2861 for i in pycompat.xrange(blo, bhi):
2861 for i in pycompat.xrange(blo, bhi):
2862 yield ('+', b[i])
2862 yield ('+', b[i])
2863
2863
2864 uipathfn = scmutil.getuipathfn(repo)
2864 uipathfn = scmutil.getuipathfn(repo)
2865 def display(fm, fn, ctx, pstates, states):
2865 def display(fm, fn, ctx, pstates, states):
2866 rev = scmutil.intrev(ctx)
2866 rev = scmutil.intrev(ctx)
2867 if fm.isplain():
2867 if fm.isplain():
2868 formatuser = ui.shortuser
2868 formatuser = ui.shortuser
2869 else:
2869 else:
2870 formatuser = pycompat.bytestr
2870 formatuser = pycompat.bytestr
2871 if ui.quiet:
2871 if ui.quiet:
2872 datefmt = '%Y-%m-%d'
2872 datefmt = '%Y-%m-%d'
2873 else:
2873 else:
2874 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2874 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2875 found = False
2875 found = False
2876 @util.cachefunc
2876 @util.cachefunc
2877 def binary():
2877 def binary():
2878 flog = getfile(fn)
2878 flog = getfile(fn)
2879 try:
2879 try:
2880 return stringutil.binary(flog.read(ctx.filenode(fn)))
2880 return stringutil.binary(flog.read(ctx.filenode(fn)))
2881 except error.WdirUnsupported:
2881 except error.WdirUnsupported:
2882 return ctx[fn].isbinary()
2882 return ctx[fn].isbinary()
2883
2883
2884 fieldnamemap = {'linenumber': 'lineno'}
2884 fieldnamemap = {'linenumber': 'lineno'}
2885 if diff:
2885 if diff:
2886 iter = difflinestates(pstates, states)
2886 iter = difflinestates(pstates, states)
2887 else:
2887 else:
2888 iter = [('', l) for l in states]
2888 iter = [('', l) for l in states]
2889 for change, l in iter:
2889 for change, l in iter:
2890 fm.startitem()
2890 fm.startitem()
2891 fm.context(ctx=ctx)
2891 fm.context(ctx=ctx)
2892 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
2892 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
2893 fm.plain(uipathfn(fn), label='grep.filename')
2893 fm.plain(uipathfn(fn), label='grep.filename')
2894
2894
2895 cols = [
2895 cols = [
2896 ('rev', '%d', rev, not plaingrep, ''),
2896 ('rev', '%d', rev, not plaingrep, ''),
2897 ('linenumber', '%d', l.linenum, opts.get('line_number'), ''),
2897 ('linenumber', '%d', l.linenum, opts.get('line_number'), ''),
2898 ]
2898 ]
2899 if diff:
2899 if diff:
2900 cols.append(
2900 cols.append(
2901 ('change', '%s', change, True,
2901 ('change', '%s', change, True,
2902 'grep.inserted ' if change == '+' else 'grep.deleted ')
2902 'grep.inserted ' if change == '+' else 'grep.deleted ')
2903 )
2903 )
2904 cols.extend([
2904 cols.extend([
2905 ('user', '%s', formatuser(ctx.user()), opts.get('user'), ''),
2905 ('user', '%s', formatuser(ctx.user()), opts.get('user'), ''),
2906 ('date', '%s', fm.formatdate(ctx.date(), datefmt),
2906 ('date', '%s', fm.formatdate(ctx.date(), datefmt),
2907 opts.get('date'), ''),
2907 opts.get('date'), ''),
2908 ])
2908 ])
2909 for name, fmt, data, cond, extra_label in cols:
2909 for name, fmt, data, cond, extra_label in cols:
2910 if cond:
2910 if cond:
2911 fm.plain(sep, label='grep.sep')
2911 fm.plain(sep, label='grep.sep')
2912 field = fieldnamemap.get(name, name)
2912 field = fieldnamemap.get(name, name)
2913 label = extra_label + ('grep.%s' % name)
2913 label = extra_label + ('grep.%s' % name)
2914 fm.condwrite(cond, field, fmt, data, label=label)
2914 fm.condwrite(cond, field, fmt, data, label=label)
2915 if not opts.get('files_with_matches'):
2915 if not opts.get('files_with_matches'):
2916 fm.plain(sep, label='grep.sep')
2916 fm.plain(sep, label='grep.sep')
2917 if not opts.get('text') and binary():
2917 if not opts.get('text') and binary():
2918 fm.plain(_(" Binary file matches"))
2918 fm.plain(_(" Binary file matches"))
2919 else:
2919 else:
2920 displaymatches(fm.nested('texts', tmpl='{text}'), l)
2920 displaymatches(fm.nested('texts', tmpl='{text}'), l)
2921 fm.plain(eol)
2921 fm.plain(eol)
2922 found = True
2922 found = True
2923 if opts.get('files_with_matches'):
2923 if opts.get('files_with_matches'):
2924 break
2924 break
2925 return found
2925 return found
2926
2926
2927 def displaymatches(fm, l):
2927 def displaymatches(fm, l):
2928 p = 0
2928 p = 0
2929 for s, e in l.findpos():
2929 for s, e in l.findpos():
2930 if p < s:
2930 if p < s:
2931 fm.startitem()
2931 fm.startitem()
2932 fm.write('text', '%s', l.line[p:s])
2932 fm.write('text', '%s', l.line[p:s])
2933 fm.data(matched=False)
2933 fm.data(matched=False)
2934 fm.startitem()
2934 fm.startitem()
2935 fm.write('text', '%s', l.line[s:e], label='grep.match')
2935 fm.write('text', '%s', l.line[s:e], label='grep.match')
2936 fm.data(matched=True)
2936 fm.data(matched=True)
2937 p = e
2937 p = e
2938 if p < len(l.line):
2938 if p < len(l.line):
2939 fm.startitem()
2939 fm.startitem()
2940 fm.write('text', '%s', l.line[p:])
2940 fm.write('text', '%s', l.line[p:])
2941 fm.data(matched=False)
2941 fm.data(matched=False)
2942 fm.end()
2942 fm.end()
2943
2943
2944 skip = set()
2944 skip = set()
2945 revfiles = {}
2945 revfiles = {}
2946 match = scmutil.match(repo[None], pats, opts)
2946 match = scmutil.match(repo[None], pats, opts)
2947 found = False
2947 found = False
2948 follow = opts.get('follow')
2948 follow = opts.get('follow')
2949
2949
2950 getrenamed = scmutil.getrenamedfn(repo)
2950 getrenamed = scmutil.getrenamedfn(repo)
2951 def prep(ctx, fns):
2951 def prep(ctx, fns):
2952 rev = ctx.rev()
2952 rev = ctx.rev()
2953 pctx = ctx.p1()
2953 pctx = ctx.p1()
2954 parent = pctx.rev()
2954 parent = pctx.rev()
2955 matches.setdefault(rev, {})
2955 matches.setdefault(rev, {})
2956 matches.setdefault(parent, {})
2956 matches.setdefault(parent, {})
2957 files = revfiles.setdefault(rev, [])
2957 files = revfiles.setdefault(rev, [])
2958 for fn in fns:
2958 for fn in fns:
2959 flog = getfile(fn)
2959 flog = getfile(fn)
2960 try:
2960 try:
2961 fnode = ctx.filenode(fn)
2961 fnode = ctx.filenode(fn)
2962 except error.LookupError:
2962 except error.LookupError:
2963 continue
2963 continue
2964
2964
2965 copy = None
2965 copy = None
2966 if follow:
2966 if follow:
2967 copy = getrenamed(fn, rev)
2967 copy = getrenamed(fn, rev)
2968 if copy:
2968 if copy:
2969 copies.setdefault(rev, {})[fn] = copy
2969 copies.setdefault(rev, {})[fn] = copy
2970 if fn in skip:
2970 if fn in skip:
2971 skip.add(copy)
2971 skip.add(copy)
2972 if fn in skip:
2972 if fn in skip:
2973 continue
2973 continue
2974 files.append(fn)
2974 files.append(fn)
2975
2975
2976 if fn not in matches[rev]:
2976 if fn not in matches[rev]:
2977 try:
2977 try:
2978 content = flog.read(fnode)
2978 content = flog.read(fnode)
2979 except error.WdirUnsupported:
2979 except error.WdirUnsupported:
2980 content = ctx[fn].data()
2980 content = ctx[fn].data()
2981 grepbody(fn, rev, content)
2981 grepbody(fn, rev, content)
2982
2982
2983 pfn = copy or fn
2983 pfn = copy or fn
2984 if pfn not in matches[parent]:
2984 if pfn not in matches[parent]:
2985 try:
2985 try:
2986 fnode = pctx.filenode(pfn)
2986 fnode = pctx.filenode(pfn)
2987 grepbody(pfn, parent, flog.read(fnode))
2987 grepbody(pfn, parent, flog.read(fnode))
2988 except error.LookupError:
2988 except error.LookupError:
2989 pass
2989 pass
2990
2990
2991 ui.pager('grep')
2991 ui.pager('grep')
2992 fm = ui.formatter('grep', opts)
2992 fm = ui.formatter('grep', opts)
2993 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2993 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2994 rev = ctx.rev()
2994 rev = ctx.rev()
2995 parent = ctx.p1().rev()
2995 parent = ctx.p1().rev()
2996 for fn in sorted(revfiles.get(rev, [])):
2996 for fn in sorted(revfiles.get(rev, [])):
2997 states = matches[rev][fn]
2997 states = matches[rev][fn]
2998 copy = copies.get(rev, {}).get(fn)
2998 copy = copies.get(rev, {}).get(fn)
2999 if fn in skip:
2999 if fn in skip:
3000 if copy:
3000 if copy:
3001 skip.add(copy)
3001 skip.add(copy)
3002 continue
3002 continue
3003 pstates = matches.get(parent, {}).get(copy or fn, [])
3003 pstates = matches.get(parent, {}).get(copy or fn, [])
3004 if pstates or states:
3004 if pstates or states:
3005 r = display(fm, fn, ctx, pstates, states)
3005 r = display(fm, fn, ctx, pstates, states)
3006 found = found or r
3006 found = found or r
3007 if r and not diff and not all_files:
3007 if r and not diff and not all_files:
3008 skip.add(fn)
3008 skip.add(fn)
3009 if copy:
3009 if copy:
3010 skip.add(copy)
3010 skip.add(copy)
3011 del revfiles[rev]
3011 del revfiles[rev]
3012 # We will keep the matches dict for the duration of the window
3012 # We will keep the matches dict for the duration of the window
3013 # clear the matches dict once the window is over
3013 # clear the matches dict once the window is over
3014 if not revfiles:
3014 if not revfiles:
3015 matches.clear()
3015 matches.clear()
3016 fm.end()
3016 fm.end()
3017
3017
3018 return not found
3018 return not found
3019
3019
3020 @command('heads',
3020 @command('heads',
3021 [('r', 'rev', '',
3021 [('r', 'rev', '',
3022 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3022 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3023 ('t', 'topo', False, _('show topological heads only')),
3023 ('t', 'topo', False, _('show topological heads only')),
3024 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3024 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3025 ('c', 'closed', False, _('show normal and closed branch heads')),
3025 ('c', 'closed', False, _('show normal and closed branch heads')),
3026 ] + templateopts,
3026 ] + templateopts,
3027 _('[-ct] [-r STARTREV] [REV]...'),
3027 _('[-ct] [-r STARTREV] [REV]...'),
3028 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3028 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3029 intents={INTENT_READONLY})
3029 intents={INTENT_READONLY})
3030 def heads(ui, repo, *branchrevs, **opts):
3030 def heads(ui, repo, *branchrevs, **opts):
3031 """show branch heads
3031 """show branch heads
3032
3032
3033 With no arguments, show all open branch heads in the repository.
3033 With no arguments, show all open branch heads in the repository.
3034 Branch heads are changesets that have no descendants on the
3034 Branch heads are changesets that have no descendants on the
3035 same branch. They are where development generally takes place and
3035 same branch. They are where development generally takes place and
3036 are the usual targets for update and merge operations.
3036 are the usual targets for update and merge operations.
3037
3037
3038 If one or more REVs are given, only open branch heads on the
3038 If one or more REVs are given, only open branch heads on the
3039 branches associated with the specified changesets are shown. This
3039 branches associated with the specified changesets are shown. This
3040 means that you can use :hg:`heads .` to see the heads on the
3040 means that you can use :hg:`heads .` to see the heads on the
3041 currently checked-out branch.
3041 currently checked-out branch.
3042
3042
3043 If -c/--closed is specified, also show branch heads marked closed
3043 If -c/--closed is specified, also show branch heads marked closed
3044 (see :hg:`commit --close-branch`).
3044 (see :hg:`commit --close-branch`).
3045
3045
3046 If STARTREV is specified, only those heads that are descendants of
3046 If STARTREV is specified, only those heads that are descendants of
3047 STARTREV will be displayed.
3047 STARTREV will be displayed.
3048
3048
3049 If -t/--topo is specified, named branch mechanics will be ignored and only
3049 If -t/--topo is specified, named branch mechanics will be ignored and only
3050 topological heads (changesets with no children) will be shown.
3050 topological heads (changesets with no children) will be shown.
3051
3051
3052 Returns 0 if matching heads are found, 1 if not.
3052 Returns 0 if matching heads are found, 1 if not.
3053 """
3053 """
3054
3054
3055 opts = pycompat.byteskwargs(opts)
3055 opts = pycompat.byteskwargs(opts)
3056 start = None
3056 start = None
3057 rev = opts.get('rev')
3057 rev = opts.get('rev')
3058 if rev:
3058 if rev:
3059 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3059 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3060 start = scmutil.revsingle(repo, rev, None).node()
3060 start = scmutil.revsingle(repo, rev, None).node()
3061
3061
3062 if opts.get('topo'):
3062 if opts.get('topo'):
3063 heads = [repo[h] for h in repo.heads(start)]
3063 heads = [repo[h] for h in repo.heads(start)]
3064 else:
3064 else:
3065 heads = []
3065 heads = []
3066 for branch in repo.branchmap():
3066 for branch in repo.branchmap():
3067 heads += repo.branchheads(branch, start, opts.get('closed'))
3067 heads += repo.branchheads(branch, start, opts.get('closed'))
3068 heads = [repo[h] for h in heads]
3068 heads = [repo[h] for h in heads]
3069
3069
3070 if branchrevs:
3070 if branchrevs:
3071 branches = set(repo[r].branch()
3071 branches = set(repo[r].branch()
3072 for r in scmutil.revrange(repo, branchrevs))
3072 for r in scmutil.revrange(repo, branchrevs))
3073 heads = [h for h in heads if h.branch() in branches]
3073 heads = [h for h in heads if h.branch() in branches]
3074
3074
3075 if opts.get('active') and branchrevs:
3075 if opts.get('active') and branchrevs:
3076 dagheads = repo.heads(start)
3076 dagheads = repo.heads(start)
3077 heads = [h for h in heads if h.node() in dagheads]
3077 heads = [h for h in heads if h.node() in dagheads]
3078
3078
3079 if branchrevs:
3079 if branchrevs:
3080 haveheads = set(h.branch() for h in heads)
3080 haveheads = set(h.branch() for h in heads)
3081 if branches - haveheads:
3081 if branches - haveheads:
3082 headless = ', '.join(b for b in branches - haveheads)
3082 headless = ', '.join(b for b in branches - haveheads)
3083 msg = _('no open branch heads found on branches %s')
3083 msg = _('no open branch heads found on branches %s')
3084 if opts.get('rev'):
3084 if opts.get('rev'):
3085 msg += _(' (started at %s)') % opts['rev']
3085 msg += _(' (started at %s)') % opts['rev']
3086 ui.warn((msg + '\n') % headless)
3086 ui.warn((msg + '\n') % headless)
3087
3087
3088 if not heads:
3088 if not heads:
3089 return 1
3089 return 1
3090
3090
3091 ui.pager('heads')
3091 ui.pager('heads')
3092 heads = sorted(heads, key=lambda x: -x.rev())
3092 heads = sorted(heads, key=lambda x: -x.rev())
3093 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3093 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3094 for ctx in heads:
3094 for ctx in heads:
3095 displayer.show(ctx)
3095 displayer.show(ctx)
3096 displayer.close()
3096 displayer.close()
3097
3097
3098 @command('help',
3098 @command('help',
3099 [('e', 'extension', None, _('show only help for extensions')),
3099 [('e', 'extension', None, _('show only help for extensions')),
3100 ('c', 'command', None, _('show only help for commands')),
3100 ('c', 'command', None, _('show only help for commands')),
3101 ('k', 'keyword', None, _('show topics matching keyword')),
3101 ('k', 'keyword', None, _('show topics matching keyword')),
3102 ('s', 'system', [],
3102 ('s', 'system', [],
3103 _('show help for specific platform(s)'), _('PLATFORM')),
3103 _('show help for specific platform(s)'), _('PLATFORM')),
3104 ],
3104 ],
3105 _('[-eck] [-s PLATFORM] [TOPIC]'),
3105 _('[-eck] [-s PLATFORM] [TOPIC]'),
3106 helpcategory=command.CATEGORY_HELP,
3106 helpcategory=command.CATEGORY_HELP,
3107 norepo=True,
3107 norepo=True,
3108 intents={INTENT_READONLY})
3108 intents={INTENT_READONLY})
3109 def help_(ui, name=None, **opts):
3109 def help_(ui, name=None, **opts):
3110 """show help for a given topic or a help overview
3110 """show help for a given topic or a help overview
3111
3111
3112 With no arguments, print a list of commands with short help messages.
3112 With no arguments, print a list of commands with short help messages.
3113
3113
3114 Given a topic, extension, or command name, print help for that
3114 Given a topic, extension, or command name, print help for that
3115 topic.
3115 topic.
3116
3116
3117 Returns 0 if successful.
3117 Returns 0 if successful.
3118 """
3118 """
3119
3119
3120 keep = opts.get(r'system') or []
3120 keep = opts.get(r'system') or []
3121 if len(keep) == 0:
3121 if len(keep) == 0:
3122 if pycompat.sysplatform.startswith('win'):
3122 if pycompat.sysplatform.startswith('win'):
3123 keep.append('windows')
3123 keep.append('windows')
3124 elif pycompat.sysplatform == 'OpenVMS':
3124 elif pycompat.sysplatform == 'OpenVMS':
3125 keep.append('vms')
3125 keep.append('vms')
3126 elif pycompat.sysplatform == 'plan9':
3126 elif pycompat.sysplatform == 'plan9':
3127 keep.append('plan9')
3127 keep.append('plan9')
3128 else:
3128 else:
3129 keep.append('unix')
3129 keep.append('unix')
3130 keep.append(pycompat.sysplatform.lower())
3130 keep.append(pycompat.sysplatform.lower())
3131 if ui.verbose:
3131 if ui.verbose:
3132 keep.append('verbose')
3132 keep.append('verbose')
3133
3133
3134 commands = sys.modules[__name__]
3134 commands = sys.modules[__name__]
3135 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3135 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3136 ui.pager('help')
3136 ui.pager('help')
3137 ui.write(formatted)
3137 ui.write(formatted)
3138
3138
3139
3139
3140 @command('identify|id',
3140 @command('identify|id',
3141 [('r', 'rev', '',
3141 [('r', 'rev', '',
3142 _('identify the specified revision'), _('REV')),
3142 _('identify the specified revision'), _('REV')),
3143 ('n', 'num', None, _('show local revision number')),
3143 ('n', 'num', None, _('show local revision number')),
3144 ('i', 'id', None, _('show global revision id')),
3144 ('i', 'id', None, _('show global revision id')),
3145 ('b', 'branch', None, _('show branch')),
3145 ('b', 'branch', None, _('show branch')),
3146 ('t', 'tags', None, _('show tags')),
3146 ('t', 'tags', None, _('show tags')),
3147 ('B', 'bookmarks', None, _('show bookmarks')),
3147 ('B', 'bookmarks', None, _('show bookmarks')),
3148 ] + remoteopts + formatteropts,
3148 ] + remoteopts + formatteropts,
3149 _('[-nibtB] [-r REV] [SOURCE]'),
3149 _('[-nibtB] [-r REV] [SOURCE]'),
3150 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3150 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3151 optionalrepo=True,
3151 optionalrepo=True,
3152 intents={INTENT_READONLY})
3152 intents={INTENT_READONLY})
3153 def identify(ui, repo, source=None, rev=None,
3153 def identify(ui, repo, source=None, rev=None,
3154 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3154 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3155 """identify the working directory or specified revision
3155 """identify the working directory or specified revision
3156
3156
3157 Print a summary identifying the repository state at REV using one or
3157 Print a summary identifying the repository state at REV using one or
3158 two parent hash identifiers, followed by a "+" if the working
3158 two parent hash identifiers, followed by a "+" if the working
3159 directory has uncommitted changes, the branch name (if not default),
3159 directory has uncommitted changes, the branch name (if not default),
3160 a list of tags, and a list of bookmarks.
3160 a list of tags, and a list of bookmarks.
3161
3161
3162 When REV is not given, print a summary of the current state of the
3162 When REV is not given, print a summary of the current state of the
3163 repository including the working directory. Specify -r. to get information
3163 repository including the working directory. Specify -r. to get information
3164 of the working directory parent without scanning uncommitted changes.
3164 of the working directory parent without scanning uncommitted changes.
3165
3165
3166 Specifying a path to a repository root or Mercurial bundle will
3166 Specifying a path to a repository root or Mercurial bundle will
3167 cause lookup to operate on that repository/bundle.
3167 cause lookup to operate on that repository/bundle.
3168
3168
3169 .. container:: verbose
3169 .. container:: verbose
3170
3170
3171 Template:
3171 Template:
3172
3172
3173 The following keywords are supported in addition to the common template
3173 The following keywords are supported in addition to the common template
3174 keywords and functions. See also :hg:`help templates`.
3174 keywords and functions. See also :hg:`help templates`.
3175
3175
3176 :dirty: String. Character ``+`` denoting if the working directory has
3176 :dirty: String. Character ``+`` denoting if the working directory has
3177 uncommitted changes.
3177 uncommitted changes.
3178 :id: String. One or two nodes, optionally followed by ``+``.
3178 :id: String. One or two nodes, optionally followed by ``+``.
3179 :parents: List of strings. Parent nodes of the changeset.
3179 :parents: List of strings. Parent nodes of the changeset.
3180
3180
3181 Examples:
3181 Examples:
3182
3182
3183 - generate a build identifier for the working directory::
3183 - generate a build identifier for the working directory::
3184
3184
3185 hg id --id > build-id.dat
3185 hg id --id > build-id.dat
3186
3186
3187 - find the revision corresponding to a tag::
3187 - find the revision corresponding to a tag::
3188
3188
3189 hg id -n -r 1.3
3189 hg id -n -r 1.3
3190
3190
3191 - check the most recent revision of a remote repository::
3191 - check the most recent revision of a remote repository::
3192
3192
3193 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3193 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3194
3194
3195 See :hg:`log` for generating more information about specific revisions,
3195 See :hg:`log` for generating more information about specific revisions,
3196 including full hash identifiers.
3196 including full hash identifiers.
3197
3197
3198 Returns 0 if successful.
3198 Returns 0 if successful.
3199 """
3199 """
3200
3200
3201 opts = pycompat.byteskwargs(opts)
3201 opts = pycompat.byteskwargs(opts)
3202 if not repo and not source:
3202 if not repo and not source:
3203 raise error.Abort(_("there is no Mercurial repository here "
3203 raise error.Abort(_("there is no Mercurial repository here "
3204 "(.hg not found)"))
3204 "(.hg not found)"))
3205
3205
3206 default = not (num or id or branch or tags or bookmarks)
3206 default = not (num or id or branch or tags or bookmarks)
3207 output = []
3207 output = []
3208 revs = []
3208 revs = []
3209
3209
3210 if source:
3210 if source:
3211 source, branches = hg.parseurl(ui.expandpath(source))
3211 source, branches = hg.parseurl(ui.expandpath(source))
3212 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3212 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3213 repo = peer.local()
3213 repo = peer.local()
3214 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3214 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3215
3215
3216 fm = ui.formatter('identify', opts)
3216 fm = ui.formatter('identify', opts)
3217 fm.startitem()
3217 fm.startitem()
3218
3218
3219 if not repo:
3219 if not repo:
3220 if num or branch or tags:
3220 if num or branch or tags:
3221 raise error.Abort(
3221 raise error.Abort(
3222 _("can't query remote revision number, branch, or tags"))
3222 _("can't query remote revision number, branch, or tags"))
3223 if not rev and revs:
3223 if not rev and revs:
3224 rev = revs[0]
3224 rev = revs[0]
3225 if not rev:
3225 if not rev:
3226 rev = "tip"
3226 rev = "tip"
3227
3227
3228 remoterev = peer.lookup(rev)
3228 remoterev = peer.lookup(rev)
3229 hexrev = fm.hexfunc(remoterev)
3229 hexrev = fm.hexfunc(remoterev)
3230 if default or id:
3230 if default or id:
3231 output = [hexrev]
3231 output = [hexrev]
3232 fm.data(id=hexrev)
3232 fm.data(id=hexrev)
3233
3233
3234 @util.cachefunc
3234 @util.cachefunc
3235 def getbms():
3235 def getbms():
3236 bms = []
3236 bms = []
3237
3237
3238 if 'bookmarks' in peer.listkeys('namespaces'):
3238 if 'bookmarks' in peer.listkeys('namespaces'):
3239 hexremoterev = hex(remoterev)
3239 hexremoterev = hex(remoterev)
3240 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3240 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3241 if bmr == hexremoterev]
3241 if bmr == hexremoterev]
3242
3242
3243 return sorted(bms)
3243 return sorted(bms)
3244
3244
3245 if fm.isplain():
3245 if fm.isplain():
3246 if bookmarks:
3246 if bookmarks:
3247 output.extend(getbms())
3247 output.extend(getbms())
3248 elif default and not ui.quiet:
3248 elif default and not ui.quiet:
3249 # multiple bookmarks for a single parent separated by '/'
3249 # multiple bookmarks for a single parent separated by '/'
3250 bm = '/'.join(getbms())
3250 bm = '/'.join(getbms())
3251 if bm:
3251 if bm:
3252 output.append(bm)
3252 output.append(bm)
3253 else:
3253 else:
3254 fm.data(node=hex(remoterev))
3254 fm.data(node=hex(remoterev))
3255 if bookmarks or 'bookmarks' in fm.datahint():
3255 if bookmarks or 'bookmarks' in fm.datahint():
3256 fm.data(bookmarks=fm.formatlist(getbms(), name='bookmark'))
3256 fm.data(bookmarks=fm.formatlist(getbms(), name='bookmark'))
3257 else:
3257 else:
3258 if rev:
3258 if rev:
3259 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3259 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3260 ctx = scmutil.revsingle(repo, rev, None)
3260 ctx = scmutil.revsingle(repo, rev, None)
3261
3261
3262 if ctx.rev() is None:
3262 if ctx.rev() is None:
3263 ctx = repo[None]
3263 ctx = repo[None]
3264 parents = ctx.parents()
3264 parents = ctx.parents()
3265 taglist = []
3265 taglist = []
3266 for p in parents:
3266 for p in parents:
3267 taglist.extend(p.tags())
3267 taglist.extend(p.tags())
3268
3268
3269 dirty = ""
3269 dirty = ""
3270 if ctx.dirty(missing=True, merge=False, branch=False):
3270 if ctx.dirty(missing=True, merge=False, branch=False):
3271 dirty = '+'
3271 dirty = '+'
3272 fm.data(dirty=dirty)
3272 fm.data(dirty=dirty)
3273
3273
3274 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3274 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3275 if default or id:
3275 if default or id:
3276 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
3276 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
3277 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
3277 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
3278
3278
3279 if num:
3279 if num:
3280 numoutput = ["%d" % p.rev() for p in parents]
3280 numoutput = ["%d" % p.rev() for p in parents]
3281 output.append("%s%s" % ('+'.join(numoutput), dirty))
3281 output.append("%s%s" % ('+'.join(numoutput), dirty))
3282
3282
3283 fm.data(parents=fm.formatlist([fm.hexfunc(p.node())
3283 fm.data(parents=fm.formatlist([fm.hexfunc(p.node())
3284 for p in parents], name='node'))
3284 for p in parents], name='node'))
3285 else:
3285 else:
3286 hexoutput = fm.hexfunc(ctx.node())
3286 hexoutput = fm.hexfunc(ctx.node())
3287 if default or id:
3287 if default or id:
3288 output = [hexoutput]
3288 output = [hexoutput]
3289 fm.data(id=hexoutput)
3289 fm.data(id=hexoutput)
3290
3290
3291 if num:
3291 if num:
3292 output.append(pycompat.bytestr(ctx.rev()))
3292 output.append(pycompat.bytestr(ctx.rev()))
3293 taglist = ctx.tags()
3293 taglist = ctx.tags()
3294
3294
3295 if default and not ui.quiet:
3295 if default and not ui.quiet:
3296 b = ctx.branch()
3296 b = ctx.branch()
3297 if b != 'default':
3297 if b != 'default':
3298 output.append("(%s)" % b)
3298 output.append("(%s)" % b)
3299
3299
3300 # multiple tags for a single parent separated by '/'
3300 # multiple tags for a single parent separated by '/'
3301 t = '/'.join(taglist)
3301 t = '/'.join(taglist)
3302 if t:
3302 if t:
3303 output.append(t)
3303 output.append(t)
3304
3304
3305 # multiple bookmarks for a single parent separated by '/'
3305 # multiple bookmarks for a single parent separated by '/'
3306 bm = '/'.join(ctx.bookmarks())
3306 bm = '/'.join(ctx.bookmarks())
3307 if bm:
3307 if bm:
3308 output.append(bm)
3308 output.append(bm)
3309 else:
3309 else:
3310 if branch:
3310 if branch:
3311 output.append(ctx.branch())
3311 output.append(ctx.branch())
3312
3312
3313 if tags:
3313 if tags:
3314 output.extend(taglist)
3314 output.extend(taglist)
3315
3315
3316 if bookmarks:
3316 if bookmarks:
3317 output.extend(ctx.bookmarks())
3317 output.extend(ctx.bookmarks())
3318
3318
3319 fm.data(node=ctx.hex())
3319 fm.data(node=ctx.hex())
3320 fm.data(branch=ctx.branch())
3320 fm.data(branch=ctx.branch())
3321 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
3321 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
3322 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
3322 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
3323 fm.context(ctx=ctx)
3323 fm.context(ctx=ctx)
3324
3324
3325 fm.plain("%s\n" % ' '.join(output))
3325 fm.plain("%s\n" % ' '.join(output))
3326 fm.end()
3326 fm.end()
3327
3327
3328 @command('import|patch',
3328 @command('import|patch',
3329 [('p', 'strip', 1,
3329 [('p', 'strip', 1,
3330 _('directory strip option for patch. This has the same '
3330 _('directory strip option for patch. This has the same '
3331 'meaning as the corresponding patch option'), _('NUM')),
3331 'meaning as the corresponding patch option'), _('NUM')),
3332 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3332 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3333 ('e', 'edit', False, _('invoke editor on commit messages')),
3333 ('e', 'edit', False, _('invoke editor on commit messages')),
3334 ('f', 'force', None,
3334 ('f', 'force', None,
3335 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
3335 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
3336 ('', 'no-commit', None,
3336 ('', 'no-commit', None,
3337 _("don't commit, just update the working directory")),
3337 _("don't commit, just update the working directory")),
3338 ('', 'bypass', None,
3338 ('', 'bypass', None,
3339 _("apply patch without touching the working directory")),
3339 _("apply patch without touching the working directory")),
3340 ('', 'partial', None,
3340 ('', 'partial', None,
3341 _('commit even if some hunks fail')),
3341 _('commit even if some hunks fail')),
3342 ('', 'exact', None,
3342 ('', 'exact', None,
3343 _('abort if patch would apply lossily')),
3343 _('abort if patch would apply lossily')),
3344 ('', 'prefix', '',
3344 ('', 'prefix', '',
3345 _('apply patch to subdirectory'), _('DIR')),
3345 _('apply patch to subdirectory'), _('DIR')),
3346 ('', 'import-branch', None,
3346 ('', 'import-branch', None,
3347 _('use any branch information in patch (implied by --exact)'))] +
3347 _('use any branch information in patch (implied by --exact)'))] +
3348 commitopts + commitopts2 + similarityopts,
3348 commitopts + commitopts2 + similarityopts,
3349 _('[OPTION]... PATCH...'),
3349 _('[OPTION]... PATCH...'),
3350 helpcategory=command.CATEGORY_IMPORT_EXPORT)
3350 helpcategory=command.CATEGORY_IMPORT_EXPORT)
3351 def import_(ui, repo, patch1=None, *patches, **opts):
3351 def import_(ui, repo, patch1=None, *patches, **opts):
3352 """import an ordered set of patches
3352 """import an ordered set of patches
3353
3353
3354 Import a list of patches and commit them individually (unless
3354 Import a list of patches and commit them individually (unless
3355 --no-commit is specified).
3355 --no-commit is specified).
3356
3356
3357 To read a patch from standard input (stdin), use "-" as the patch
3357 To read a patch from standard input (stdin), use "-" as the patch
3358 name. If a URL is specified, the patch will be downloaded from
3358 name. If a URL is specified, the patch will be downloaded from
3359 there.
3359 there.
3360
3360
3361 Import first applies changes to the working directory (unless
3361 Import first applies changes to the working directory (unless
3362 --bypass is specified), import will abort if there are outstanding
3362 --bypass is specified), import will abort if there are outstanding
3363 changes.
3363 changes.
3364
3364
3365 Use --bypass to apply and commit patches directly to the
3365 Use --bypass to apply and commit patches directly to the
3366 repository, without affecting the working directory. Without
3366 repository, without affecting the working directory. Without
3367 --exact, patches will be applied on top of the working directory
3367 --exact, patches will be applied on top of the working directory
3368 parent revision.
3368 parent revision.
3369
3369
3370 You can import a patch straight from a mail message. Even patches
3370 You can import a patch straight from a mail message. Even patches
3371 as attachments work (to use the body part, it must have type
3371 as attachments work (to use the body part, it must have type
3372 text/plain or text/x-patch). From and Subject headers of email
3372 text/plain or text/x-patch). From and Subject headers of email
3373 message are used as default committer and commit message. All
3373 message are used as default committer and commit message. All
3374 text/plain body parts before first diff are added to the commit
3374 text/plain body parts before first diff are added to the commit
3375 message.
3375 message.
3376
3376
3377 If the imported patch was generated by :hg:`export`, user and
3377 If the imported patch was generated by :hg:`export`, user and
3378 description from patch override values from message headers and
3378 description from patch override values from message headers and
3379 body. Values given on command line with -m/--message and -u/--user
3379 body. Values given on command line with -m/--message and -u/--user
3380 override these.
3380 override these.
3381
3381
3382 If --exact is specified, import will set the working directory to
3382 If --exact is specified, import will set the working directory to
3383 the parent of each patch before applying it, and will abort if the
3383 the parent of each patch before applying it, and will abort if the
3384 resulting changeset has a different ID than the one recorded in
3384 resulting changeset has a different ID than the one recorded in
3385 the patch. This will guard against various ways that portable
3385 the patch. This will guard against various ways that portable
3386 patch formats and mail systems might fail to transfer Mercurial
3386 patch formats and mail systems might fail to transfer Mercurial
3387 data or metadata. See :hg:`bundle` for lossless transmission.
3387 data or metadata. See :hg:`bundle` for lossless transmission.
3388
3388
3389 Use --partial to ensure a changeset will be created from the patch
3389 Use --partial to ensure a changeset will be created from the patch
3390 even if some hunks fail to apply. Hunks that fail to apply will be
3390 even if some hunks fail to apply. Hunks that fail to apply will be
3391 written to a <target-file>.rej file. Conflicts can then be resolved
3391 written to a <target-file>.rej file. Conflicts can then be resolved
3392 by hand before :hg:`commit --amend` is run to update the created
3392 by hand before :hg:`commit --amend` is run to update the created
3393 changeset. This flag exists to let people import patches that
3393 changeset. This flag exists to let people import patches that
3394 partially apply without losing the associated metadata (author,
3394 partially apply without losing the associated metadata (author,
3395 date, description, ...).
3395 date, description, ...).
3396
3396
3397 .. note::
3397 .. note::
3398
3398
3399 When no hunks apply cleanly, :hg:`import --partial` will create
3399 When no hunks apply cleanly, :hg:`import --partial` will create
3400 an empty changeset, importing only the patch metadata.
3400 an empty changeset, importing only the patch metadata.
3401
3401
3402 With -s/--similarity, hg will attempt to discover renames and
3402 With -s/--similarity, hg will attempt to discover renames and
3403 copies in the patch in the same way as :hg:`addremove`.
3403 copies in the patch in the same way as :hg:`addremove`.
3404
3404
3405 It is possible to use external patch programs to perform the patch
3405 It is possible to use external patch programs to perform the patch
3406 by setting the ``ui.patch`` configuration option. For the default
3406 by setting the ``ui.patch`` configuration option. For the default
3407 internal tool, the fuzz can also be configured via ``patch.fuzz``.
3407 internal tool, the fuzz can also be configured via ``patch.fuzz``.
3408 See :hg:`help config` for more information about configuration
3408 See :hg:`help config` for more information about configuration
3409 files and how to use these options.
3409 files and how to use these options.
3410
3410
3411 See :hg:`help dates` for a list of formats valid for -d/--date.
3411 See :hg:`help dates` for a list of formats valid for -d/--date.
3412
3412
3413 .. container:: verbose
3413 .. container:: verbose
3414
3414
3415 Examples:
3415 Examples:
3416
3416
3417 - import a traditional patch from a website and detect renames::
3417 - import a traditional patch from a website and detect renames::
3418
3418
3419 hg import -s 80 http://example.com/bugfix.patch
3419 hg import -s 80 http://example.com/bugfix.patch
3420
3420
3421 - import a changeset from an hgweb server::
3421 - import a changeset from an hgweb server::
3422
3422
3423 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3423 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3424
3424
3425 - import all the patches in an Unix-style mbox::
3425 - import all the patches in an Unix-style mbox::
3426
3426
3427 hg import incoming-patches.mbox
3427 hg import incoming-patches.mbox
3428
3428
3429 - import patches from stdin::
3429 - import patches from stdin::
3430
3430
3431 hg import -
3431 hg import -
3432
3432
3433 - attempt to exactly restore an exported changeset (not always
3433 - attempt to exactly restore an exported changeset (not always
3434 possible)::
3434 possible)::
3435
3435
3436 hg import --exact proposed-fix.patch
3436 hg import --exact proposed-fix.patch
3437
3437
3438 - use an external tool to apply a patch which is too fuzzy for
3438 - use an external tool to apply a patch which is too fuzzy for
3439 the default internal tool.
3439 the default internal tool.
3440
3440
3441 hg import --config ui.patch="patch --merge" fuzzy.patch
3441 hg import --config ui.patch="patch --merge" fuzzy.patch
3442
3442
3443 - change the default fuzzing from 2 to a less strict 7
3443 - change the default fuzzing from 2 to a less strict 7
3444
3444
3445 hg import --config ui.fuzz=7 fuzz.patch
3445 hg import --config ui.fuzz=7 fuzz.patch
3446
3446
3447 Returns 0 on success, 1 on partial success (see --partial).
3447 Returns 0 on success, 1 on partial success (see --partial).
3448 """
3448 """
3449
3449
3450 opts = pycompat.byteskwargs(opts)
3450 opts = pycompat.byteskwargs(opts)
3451 if not patch1:
3451 if not patch1:
3452 raise error.Abort(_('need at least one patch to import'))
3452 raise error.Abort(_('need at least one patch to import'))
3453
3453
3454 patches = (patch1,) + patches
3454 patches = (patch1,) + patches
3455
3455
3456 date = opts.get('date')
3456 date = opts.get('date')
3457 if date:
3457 if date:
3458 opts['date'] = dateutil.parsedate(date)
3458 opts['date'] = dateutil.parsedate(date)
3459
3459
3460 exact = opts.get('exact')
3460 exact = opts.get('exact')
3461 update = not opts.get('bypass')
3461 update = not opts.get('bypass')
3462 if not update and opts.get('no_commit'):
3462 if not update and opts.get('no_commit'):
3463 raise error.Abort(_('cannot use --no-commit with --bypass'))
3463 raise error.Abort(_('cannot use --no-commit with --bypass'))
3464 try:
3464 try:
3465 sim = float(opts.get('similarity') or 0)
3465 sim = float(opts.get('similarity') or 0)
3466 except ValueError:
3466 except ValueError:
3467 raise error.Abort(_('similarity must be a number'))
3467 raise error.Abort(_('similarity must be a number'))
3468 if sim < 0 or sim > 100:
3468 if sim < 0 or sim > 100:
3469 raise error.Abort(_('similarity must be between 0 and 100'))
3469 raise error.Abort(_('similarity must be between 0 and 100'))
3470 if sim and not update:
3470 if sim and not update:
3471 raise error.Abort(_('cannot use --similarity with --bypass'))
3471 raise error.Abort(_('cannot use --similarity with --bypass'))
3472 if exact:
3472 if exact:
3473 if opts.get('edit'):
3473 if opts.get('edit'):
3474 raise error.Abort(_('cannot use --exact with --edit'))
3474 raise error.Abort(_('cannot use --exact with --edit'))
3475 if opts.get('prefix'):
3475 if opts.get('prefix'):
3476 raise error.Abort(_('cannot use --exact with --prefix'))
3476 raise error.Abort(_('cannot use --exact with --prefix'))
3477
3477
3478 base = opts["base"]
3478 base = opts["base"]
3479 msgs = []
3479 msgs = []
3480 ret = 0
3480 ret = 0
3481
3481
3482 with repo.wlock():
3482 with repo.wlock():
3483 if update:
3483 if update:
3484 cmdutil.checkunfinished(repo)
3484 cmdutil.checkunfinished(repo)
3485 if (exact or not opts.get('force')):
3485 if (exact or not opts.get('force')):
3486 cmdutil.bailifchanged(repo)
3486 cmdutil.bailifchanged(repo)
3487
3487
3488 if not opts.get('no_commit'):
3488 if not opts.get('no_commit'):
3489 lock = repo.lock
3489 lock = repo.lock
3490 tr = lambda: repo.transaction('import')
3490 tr = lambda: repo.transaction('import')
3491 dsguard = util.nullcontextmanager
3491 dsguard = util.nullcontextmanager
3492 else:
3492 else:
3493 lock = util.nullcontextmanager
3493 lock = util.nullcontextmanager
3494 tr = util.nullcontextmanager
3494 tr = util.nullcontextmanager
3495 dsguard = lambda: dirstateguard.dirstateguard(repo, 'import')
3495 dsguard = lambda: dirstateguard.dirstateguard(repo, 'import')
3496 with lock(), tr(), dsguard():
3496 with lock(), tr(), dsguard():
3497 parents = repo[None].parents()
3497 parents = repo[None].parents()
3498 for patchurl in patches:
3498 for patchurl in patches:
3499 if patchurl == '-':
3499 if patchurl == '-':
3500 ui.status(_('applying patch from stdin\n'))
3500 ui.status(_('applying patch from stdin\n'))
3501 patchfile = ui.fin
3501 patchfile = ui.fin
3502 patchurl = 'stdin' # for error message
3502 patchurl = 'stdin' # for error message
3503 else:
3503 else:
3504 patchurl = os.path.join(base, patchurl)
3504 patchurl = os.path.join(base, patchurl)
3505 ui.status(_('applying %s\n') % patchurl)
3505 ui.status(_('applying %s\n') % patchurl)
3506 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
3506 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
3507
3507
3508 haspatch = False
3508 haspatch = False
3509 for hunk in patch.split(patchfile):
3509 for hunk in patch.split(patchfile):
3510 with patch.extract(ui, hunk) as patchdata:
3510 with patch.extract(ui, hunk) as patchdata:
3511 msg, node, rej = cmdutil.tryimportone(ui, repo,
3511 msg, node, rej = cmdutil.tryimportone(ui, repo,
3512 patchdata,
3512 patchdata,
3513 parents, opts,
3513 parents, opts,
3514 msgs, hg.clean)
3514 msgs, hg.clean)
3515 if msg:
3515 if msg:
3516 haspatch = True
3516 haspatch = True
3517 ui.note(msg + '\n')
3517 ui.note(msg + '\n')
3518 if update or exact:
3518 if update or exact:
3519 parents = repo[None].parents()
3519 parents = repo[None].parents()
3520 else:
3520 else:
3521 parents = [repo[node]]
3521 parents = [repo[node]]
3522 if rej:
3522 if rej:
3523 ui.write_err(_("patch applied partially\n"))
3523 ui.write_err(_("patch applied partially\n"))
3524 ui.write_err(_("(fix the .rej files and run "
3524 ui.write_err(_("(fix the .rej files and run "
3525 "`hg commit --amend`)\n"))
3525 "`hg commit --amend`)\n"))
3526 ret = 1
3526 ret = 1
3527 break
3527 break
3528
3528
3529 if not haspatch:
3529 if not haspatch:
3530 raise error.Abort(_('%s: no diffs found') % patchurl)
3530 raise error.Abort(_('%s: no diffs found') % patchurl)
3531
3531
3532 if msgs:
3532 if msgs:
3533 repo.savecommitmessage('\n* * *\n'.join(msgs))
3533 repo.savecommitmessage('\n* * *\n'.join(msgs))
3534 return ret
3534 return ret
3535
3535
3536 @command('incoming|in',
3536 @command('incoming|in',
3537 [('f', 'force', None,
3537 [('f', 'force', None,
3538 _('run even if remote repository is unrelated')),
3538 _('run even if remote repository is unrelated')),
3539 ('n', 'newest-first', None, _('show newest record first')),
3539 ('n', 'newest-first', None, _('show newest record first')),
3540 ('', 'bundle', '',
3540 ('', 'bundle', '',
3541 _('file to store the bundles into'), _('FILE')),
3541 _('file to store the bundles into'), _('FILE')),
3542 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3542 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3543 ('B', 'bookmarks', False, _("compare bookmarks")),
3543 ('B', 'bookmarks', False, _("compare bookmarks")),
3544 ('b', 'branch', [],
3544 ('b', 'branch', [],
3545 _('a specific branch you would like to pull'), _('BRANCH')),
3545 _('a specific branch you would like to pull'), _('BRANCH')),
3546 ] + logopts + remoteopts + subrepoopts,
3546 ] + logopts + remoteopts + subrepoopts,
3547 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
3547 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
3548 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
3548 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
3549 def incoming(ui, repo, source="default", **opts):
3549 def incoming(ui, repo, source="default", **opts):
3550 """show new changesets found in source
3550 """show new changesets found in source
3551
3551
3552 Show new changesets found in the specified path/URL or the default
3552 Show new changesets found in the specified path/URL or the default
3553 pull location. These are the changesets that would have been pulled
3553 pull location. These are the changesets that would have been pulled
3554 by :hg:`pull` at the time you issued this command.
3554 by :hg:`pull` at the time you issued this command.
3555
3555
3556 See pull for valid source format details.
3556 See pull for valid source format details.
3557
3557
3558 .. container:: verbose
3558 .. container:: verbose
3559
3559
3560 With -B/--bookmarks, the result of bookmark comparison between
3560 With -B/--bookmarks, the result of bookmark comparison between
3561 local and remote repositories is displayed. With -v/--verbose,
3561 local and remote repositories is displayed. With -v/--verbose,
3562 status is also displayed for each bookmark like below::
3562 status is also displayed for each bookmark like below::
3563
3563
3564 BM1 01234567890a added
3564 BM1 01234567890a added
3565 BM2 1234567890ab advanced
3565 BM2 1234567890ab advanced
3566 BM3 234567890abc diverged
3566 BM3 234567890abc diverged
3567 BM4 34567890abcd changed
3567 BM4 34567890abcd changed
3568
3568
3569 The action taken locally when pulling depends on the
3569 The action taken locally when pulling depends on the
3570 status of each bookmark:
3570 status of each bookmark:
3571
3571
3572 :``added``: pull will create it
3572 :``added``: pull will create it
3573 :``advanced``: pull will update it
3573 :``advanced``: pull will update it
3574 :``diverged``: pull will create a divergent bookmark
3574 :``diverged``: pull will create a divergent bookmark
3575 :``changed``: result depends on remote changesets
3575 :``changed``: result depends on remote changesets
3576
3576
3577 From the point of view of pulling behavior, bookmark
3577 From the point of view of pulling behavior, bookmark
3578 existing only in the remote repository are treated as ``added``,
3578 existing only in the remote repository are treated as ``added``,
3579 even if it is in fact locally deleted.
3579 even if it is in fact locally deleted.
3580
3580
3581 .. container:: verbose
3581 .. container:: verbose
3582
3582
3583 For remote repository, using --bundle avoids downloading the
3583 For remote repository, using --bundle avoids downloading the
3584 changesets twice if the incoming is followed by a pull.
3584 changesets twice if the incoming is followed by a pull.
3585
3585
3586 Examples:
3586 Examples:
3587
3587
3588 - show incoming changes with patches and full description::
3588 - show incoming changes with patches and full description::
3589
3589
3590 hg incoming -vp
3590 hg incoming -vp
3591
3591
3592 - show incoming changes excluding merges, store a bundle::
3592 - show incoming changes excluding merges, store a bundle::
3593
3593
3594 hg in -vpM --bundle incoming.hg
3594 hg in -vpM --bundle incoming.hg
3595 hg pull incoming.hg
3595 hg pull incoming.hg
3596
3596
3597 - briefly list changes inside a bundle::
3597 - briefly list changes inside a bundle::
3598
3598
3599 hg in changes.hg -T "{desc|firstline}\\n"
3599 hg in changes.hg -T "{desc|firstline}\\n"
3600
3600
3601 Returns 0 if there are incoming changes, 1 otherwise.
3601 Returns 0 if there are incoming changes, 1 otherwise.
3602 """
3602 """
3603 opts = pycompat.byteskwargs(opts)
3603 opts = pycompat.byteskwargs(opts)
3604 if opts.get('graph'):
3604 if opts.get('graph'):
3605 logcmdutil.checkunsupportedgraphflags([], opts)
3605 logcmdutil.checkunsupportedgraphflags([], opts)
3606 def display(other, chlist, displayer):
3606 def display(other, chlist, displayer):
3607 revdag = logcmdutil.graphrevs(other, chlist, opts)
3607 revdag = logcmdutil.graphrevs(other, chlist, opts)
3608 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3608 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3609 graphmod.asciiedges)
3609 graphmod.asciiedges)
3610
3610
3611 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3611 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3612 return 0
3612 return 0
3613
3613
3614 if opts.get('bundle') and opts.get('subrepos'):
3614 if opts.get('bundle') and opts.get('subrepos'):
3615 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3615 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3616
3616
3617 if opts.get('bookmarks'):
3617 if opts.get('bookmarks'):
3618 source, branches = hg.parseurl(ui.expandpath(source),
3618 source, branches = hg.parseurl(ui.expandpath(source),
3619 opts.get('branch'))
3619 opts.get('branch'))
3620 other = hg.peer(repo, opts, source)
3620 other = hg.peer(repo, opts, source)
3621 if 'bookmarks' not in other.listkeys('namespaces'):
3621 if 'bookmarks' not in other.listkeys('namespaces'):
3622 ui.warn(_("remote doesn't support bookmarks\n"))
3622 ui.warn(_("remote doesn't support bookmarks\n"))
3623 return 0
3623 return 0
3624 ui.pager('incoming')
3624 ui.pager('incoming')
3625 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3625 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3626 return bookmarks.incoming(ui, repo, other)
3626 return bookmarks.incoming(ui, repo, other)
3627
3627
3628 repo._subtoppath = ui.expandpath(source)
3628 repo._subtoppath = ui.expandpath(source)
3629 try:
3629 try:
3630 return hg.incoming(ui, repo, source, opts)
3630 return hg.incoming(ui, repo, source, opts)
3631 finally:
3631 finally:
3632 del repo._subtoppath
3632 del repo._subtoppath
3633
3633
3634
3634
3635 @command('init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3635 @command('init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3636 helpcategory=command.CATEGORY_REPO_CREATION,
3636 helpcategory=command.CATEGORY_REPO_CREATION,
3637 helpbasic=True, norepo=True)
3637 helpbasic=True, norepo=True)
3638 def init(ui, dest=".", **opts):
3638 def init(ui, dest=".", **opts):
3639 """create a new repository in the given directory
3639 """create a new repository in the given directory
3640
3640
3641 Initialize a new repository in the given directory. If the given
3641 Initialize a new repository in the given directory. If the given
3642 directory does not exist, it will be created.
3642 directory does not exist, it will be created.
3643
3643
3644 If no directory is given, the current directory is used.
3644 If no directory is given, the current directory is used.
3645
3645
3646 It is possible to specify an ``ssh://`` URL as the destination.
3646 It is possible to specify an ``ssh://`` URL as the destination.
3647 See :hg:`help urls` for more information.
3647 See :hg:`help urls` for more information.
3648
3648
3649 Returns 0 on success.
3649 Returns 0 on success.
3650 """
3650 """
3651 opts = pycompat.byteskwargs(opts)
3651 opts = pycompat.byteskwargs(opts)
3652 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3652 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3653
3653
3654 @command('locate',
3654 @command('locate',
3655 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3655 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3656 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3656 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3657 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3657 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3658 ] + walkopts,
3658 ] + walkopts,
3659 _('[OPTION]... [PATTERN]...'),
3659 _('[OPTION]... [PATTERN]...'),
3660 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
3660 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
3661 def locate(ui, repo, *pats, **opts):
3661 def locate(ui, repo, *pats, **opts):
3662 """locate files matching specific patterns (DEPRECATED)
3662 """locate files matching specific patterns (DEPRECATED)
3663
3663
3664 Print files under Mercurial control in the working directory whose
3664 Print files under Mercurial control in the working directory whose
3665 names match the given patterns.
3665 names match the given patterns.
3666
3666
3667 By default, this command searches all directories in the working
3667 By default, this command searches all directories in the working
3668 directory. To search just the current directory and its
3668 directory. To search just the current directory and its
3669 subdirectories, use "--include .".
3669 subdirectories, use "--include .".
3670
3670
3671 If no patterns are given to match, this command prints the names
3671 If no patterns are given to match, this command prints the names
3672 of all files under Mercurial control in the working directory.
3672 of all files under Mercurial control in the working directory.
3673
3673
3674 If you want to feed the output of this command into the "xargs"
3674 If you want to feed the output of this command into the "xargs"
3675 command, use the -0 option to both this command and "xargs". This
3675 command, use the -0 option to both this command and "xargs". This
3676 will avoid the problem of "xargs" treating single filenames that
3676 will avoid the problem of "xargs" treating single filenames that
3677 contain whitespace as multiple filenames.
3677 contain whitespace as multiple filenames.
3678
3678
3679 See :hg:`help files` for a more versatile command.
3679 See :hg:`help files` for a more versatile command.
3680
3680
3681 Returns 0 if a match is found, 1 otherwise.
3681 Returns 0 if a match is found, 1 otherwise.
3682 """
3682 """
3683 opts = pycompat.byteskwargs(opts)
3683 opts = pycompat.byteskwargs(opts)
3684 if opts.get('print0'):
3684 if opts.get('print0'):
3685 end = '\0'
3685 end = '\0'
3686 else:
3686 else:
3687 end = '\n'
3687 end = '\n'
3688 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3688 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3689
3689
3690 ret = 1
3690 ret = 1
3691 m = scmutil.match(ctx, pats, opts, default='relglob',
3691 m = scmutil.match(ctx, pats, opts, default='relglob',
3692 badfn=lambda x, y: False)
3692 badfn=lambda x, y: False)
3693
3693
3694 ui.pager('locate')
3694 ui.pager('locate')
3695 if ctx.rev() is None:
3695 if ctx.rev() is None:
3696 # When run on the working copy, "locate" includes removed files, so
3696 # When run on the working copy, "locate" includes removed files, so
3697 # we get the list of files from the dirstate.
3697 # we get the list of files from the dirstate.
3698 filesgen = sorted(repo.dirstate.matches(m))
3698 filesgen = sorted(repo.dirstate.matches(m))
3699 else:
3699 else:
3700 filesgen = ctx.matches(m)
3700 filesgen = ctx.matches(m)
3701 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
3701 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
3702 for abs in filesgen:
3702 for abs in filesgen:
3703 if opts.get('fullpath'):
3703 if opts.get('fullpath'):
3704 ui.write(repo.wjoin(abs), end)
3704 ui.write(repo.wjoin(abs), end)
3705 else:
3705 else:
3706 ui.write(uipathfn(abs), end)
3706 ui.write(uipathfn(abs), end)
3707 ret = 0
3707 ret = 0
3708
3708
3709 return ret
3709 return ret
3710
3710
3711 @command('log|history',
3711 @command('log|history',
3712 [('f', 'follow', None,
3712 [('f', 'follow', None,
3713 _('follow changeset history, or file history across copies and renames')),
3713 _('follow changeset history, or file history across copies and renames')),
3714 ('', 'follow-first', None,
3714 ('', 'follow-first', None,
3715 _('only follow the first parent of merge changesets (DEPRECATED)')),
3715 _('only follow the first parent of merge changesets (DEPRECATED)')),
3716 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3716 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3717 ('C', 'copies', None, _('show copied files')),
3717 ('C', 'copies', None, _('show copied files')),
3718 ('k', 'keyword', [],
3718 ('k', 'keyword', [],
3719 _('do case-insensitive search for a given text'), _('TEXT')),
3719 _('do case-insensitive search for a given text'), _('TEXT')),
3720 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3720 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3721 ('L', 'line-range', [],
3721 ('L', 'line-range', [],
3722 _('follow line range of specified file (EXPERIMENTAL)'),
3722 _('follow line range of specified file (EXPERIMENTAL)'),
3723 _('FILE,RANGE')),
3723 _('FILE,RANGE')),
3724 ('', 'removed', None, _('include revisions where files were removed')),
3724 ('', 'removed', None, _('include revisions where files were removed')),
3725 ('m', 'only-merges', None,
3725 ('m', 'only-merges', None,
3726 _('show only merges (DEPRECATED) (use -r "merge()" instead)')),
3726 _('show only merges (DEPRECATED) (use -r "merge()" instead)')),
3727 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3727 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3728 ('', 'only-branch', [],
3728 ('', 'only-branch', [],
3729 _('show only changesets within the given named branch (DEPRECATED)'),
3729 _('show only changesets within the given named branch (DEPRECATED)'),
3730 _('BRANCH')),
3730 _('BRANCH')),
3731 ('b', 'branch', [],
3731 ('b', 'branch', [],
3732 _('show changesets within the given named branch'), _('BRANCH')),
3732 _('show changesets within the given named branch'), _('BRANCH')),
3733 ('P', 'prune', [],
3733 ('P', 'prune', [],
3734 _('do not display revision or any of its ancestors'), _('REV')),
3734 _('do not display revision or any of its ancestors'), _('REV')),
3735 ] + logopts + walkopts,
3735 ] + logopts + walkopts,
3736 _('[OPTION]... [FILE]'),
3736 _('[OPTION]... [FILE]'),
3737 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3737 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3738 helpbasic=True, inferrepo=True,
3738 helpbasic=True, inferrepo=True,
3739 intents={INTENT_READONLY})
3739 intents={INTENT_READONLY})
3740 def log(ui, repo, *pats, **opts):
3740 def log(ui, repo, *pats, **opts):
3741 """show revision history of entire repository or files
3741 """show revision history of entire repository or files
3742
3742
3743 Print the revision history of the specified files or the entire
3743 Print the revision history of the specified files or the entire
3744 project.
3744 project.
3745
3745
3746 If no revision range is specified, the default is ``tip:0`` unless
3746 If no revision range is specified, the default is ``tip:0`` unless
3747 --follow is set, in which case the working directory parent is
3747 --follow is set, in which case the working directory parent is
3748 used as the starting revision.
3748 used as the starting revision.
3749
3749
3750 File history is shown without following rename or copy history of
3750 File history is shown without following rename or copy history of
3751 files. Use -f/--follow with a filename to follow history across
3751 files. Use -f/--follow with a filename to follow history across
3752 renames and copies. --follow without a filename will only show
3752 renames and copies. --follow without a filename will only show
3753 ancestors of the starting revision.
3753 ancestors of the starting revision.
3754
3754
3755 By default this command prints revision number and changeset id,
3755 By default this command prints revision number and changeset id,
3756 tags, non-trivial parents, user, date and time, and a summary for
3756 tags, non-trivial parents, user, date and time, and a summary for
3757 each commit. When the -v/--verbose switch is used, the list of
3757 each commit. When the -v/--verbose switch is used, the list of
3758 changed files and full commit message are shown.
3758 changed files and full commit message are shown.
3759
3759
3760 With --graph the revisions are shown as an ASCII art DAG with the most
3760 With --graph the revisions are shown as an ASCII art DAG with the most
3761 recent changeset at the top.
3761 recent changeset at the top.
3762 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
3762 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
3763 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
3763 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
3764 changeset from the lines below is a parent of the 'o' merge on the same
3764 changeset from the lines below is a parent of the 'o' merge on the same
3765 line.
3765 line.
3766 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3766 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3767 of a '|' indicates one or more revisions in a path are omitted.
3767 of a '|' indicates one or more revisions in a path are omitted.
3768
3768
3769 .. container:: verbose
3769 .. container:: verbose
3770
3770
3771 Use -L/--line-range FILE,M:N options to follow the history of lines
3771 Use -L/--line-range FILE,M:N options to follow the history of lines
3772 from M to N in FILE. With -p/--patch only diff hunks affecting
3772 from M to N in FILE. With -p/--patch only diff hunks affecting
3773 specified line range will be shown. This option requires --follow;
3773 specified line range will be shown. This option requires --follow;
3774 it can be specified multiple times. Currently, this option is not
3774 it can be specified multiple times. Currently, this option is not
3775 compatible with --graph. This option is experimental.
3775 compatible with --graph. This option is experimental.
3776
3776
3777 .. note::
3777 .. note::
3778
3778
3779 :hg:`log --patch` may generate unexpected diff output for merge
3779 :hg:`log --patch` may generate unexpected diff output for merge
3780 changesets, as it will only compare the merge changeset against
3780 changesets, as it will only compare the merge changeset against
3781 its first parent. Also, only files different from BOTH parents
3781 its first parent. Also, only files different from BOTH parents
3782 will appear in files:.
3782 will appear in files:.
3783
3783
3784 .. note::
3784 .. note::
3785
3785
3786 For performance reasons, :hg:`log FILE` may omit duplicate changes
3786 For performance reasons, :hg:`log FILE` may omit duplicate changes
3787 made on branches and will not show removals or mode changes. To
3787 made on branches and will not show removals or mode changes. To
3788 see all such changes, use the --removed switch.
3788 see all such changes, use the --removed switch.
3789
3789
3790 .. container:: verbose
3790 .. container:: verbose
3791
3791
3792 .. note::
3792 .. note::
3793
3793
3794 The history resulting from -L/--line-range options depends on diff
3794 The history resulting from -L/--line-range options depends on diff
3795 options; for instance if white-spaces are ignored, respective changes
3795 options; for instance if white-spaces are ignored, respective changes
3796 with only white-spaces in specified line range will not be listed.
3796 with only white-spaces in specified line range will not be listed.
3797
3797
3798 .. container:: verbose
3798 .. container:: verbose
3799
3799
3800 Some examples:
3800 Some examples:
3801
3801
3802 - changesets with full descriptions and file lists::
3802 - changesets with full descriptions and file lists::
3803
3803
3804 hg log -v
3804 hg log -v
3805
3805
3806 - changesets ancestral to the working directory::
3806 - changesets ancestral to the working directory::
3807
3807
3808 hg log -f
3808 hg log -f
3809
3809
3810 - last 10 commits on the current branch::
3810 - last 10 commits on the current branch::
3811
3811
3812 hg log -l 10 -b .
3812 hg log -l 10 -b .
3813
3813
3814 - changesets showing all modifications of a file, including removals::
3814 - changesets showing all modifications of a file, including removals::
3815
3815
3816 hg log --removed file.c
3816 hg log --removed file.c
3817
3817
3818 - all changesets that touch a directory, with diffs, excluding merges::
3818 - all changesets that touch a directory, with diffs, excluding merges::
3819
3819
3820 hg log -Mp lib/
3820 hg log -Mp lib/
3821
3821
3822 - all revision numbers that match a keyword::
3822 - all revision numbers that match a keyword::
3823
3823
3824 hg log -k bug --template "{rev}\\n"
3824 hg log -k bug --template "{rev}\\n"
3825
3825
3826 - the full hash identifier of the working directory parent::
3826 - the full hash identifier of the working directory parent::
3827
3827
3828 hg log -r . --template "{node}\\n"
3828 hg log -r . --template "{node}\\n"
3829
3829
3830 - list available log templates::
3830 - list available log templates::
3831
3831
3832 hg log -T list
3832 hg log -T list
3833
3833
3834 - check if a given changeset is included in a tagged release::
3834 - check if a given changeset is included in a tagged release::
3835
3835
3836 hg log -r "a21ccf and ancestor(1.9)"
3836 hg log -r "a21ccf and ancestor(1.9)"
3837
3837
3838 - find all changesets by some user in a date range::
3838 - find all changesets by some user in a date range::
3839
3839
3840 hg log -k alice -d "may 2008 to jul 2008"
3840 hg log -k alice -d "may 2008 to jul 2008"
3841
3841
3842 - summary of all changesets after the last tag::
3842 - summary of all changesets after the last tag::
3843
3843
3844 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3844 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3845
3845
3846 - changesets touching lines 13 to 23 for file.c::
3846 - changesets touching lines 13 to 23 for file.c::
3847
3847
3848 hg log -L file.c,13:23
3848 hg log -L file.c,13:23
3849
3849
3850 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
3850 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
3851 main.c with patch::
3851 main.c with patch::
3852
3852
3853 hg log -L file.c,13:23 -L main.c,2:6 -p
3853 hg log -L file.c,13:23 -L main.c,2:6 -p
3854
3854
3855 See :hg:`help dates` for a list of formats valid for -d/--date.
3855 See :hg:`help dates` for a list of formats valid for -d/--date.
3856
3856
3857 See :hg:`help revisions` for more about specifying and ordering
3857 See :hg:`help revisions` for more about specifying and ordering
3858 revisions.
3858 revisions.
3859
3859
3860 See :hg:`help templates` for more about pre-packaged styles and
3860 See :hg:`help templates` for more about pre-packaged styles and
3861 specifying custom templates. The default template used by the log
3861 specifying custom templates. The default template used by the log
3862 command can be customized via the ``ui.logtemplate`` configuration
3862 command can be customized via the ``ui.logtemplate`` configuration
3863 setting.
3863 setting.
3864
3864
3865 Returns 0 on success.
3865 Returns 0 on success.
3866
3866
3867 """
3867 """
3868 opts = pycompat.byteskwargs(opts)
3868 opts = pycompat.byteskwargs(opts)
3869 linerange = opts.get('line_range')
3869 linerange = opts.get('line_range')
3870
3870
3871 if linerange and not opts.get('follow'):
3871 if linerange and not opts.get('follow'):
3872 raise error.Abort(_('--line-range requires --follow'))
3872 raise error.Abort(_('--line-range requires --follow'))
3873
3873
3874 if linerange and pats:
3874 if linerange and pats:
3875 # TODO: take pats as patterns with no line-range filter
3875 # TODO: take pats as patterns with no line-range filter
3876 raise error.Abort(
3876 raise error.Abort(
3877 _('FILE arguments are not compatible with --line-range option')
3877 _('FILE arguments are not compatible with --line-range option')
3878 )
3878 )
3879
3879
3880 repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
3880 repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
3881 revs, differ = logcmdutil.getrevs(repo, pats, opts)
3881 revs, differ = logcmdutil.getrevs(repo, pats, opts)
3882 if linerange:
3882 if linerange:
3883 # TODO: should follow file history from logcmdutil._initialrevs(),
3883 # TODO: should follow file history from logcmdutil._initialrevs(),
3884 # then filter the result by logcmdutil._makerevset() and --limit
3884 # then filter the result by logcmdutil._makerevset() and --limit
3885 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
3885 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
3886
3886
3887 getrenamed = None
3887 getrenamed = None
3888 if opts.get('copies'):
3888 if opts.get('copies'):
3889 endrev = None
3889 endrev = None
3890 if revs:
3890 if revs:
3891 endrev = revs.max() + 1
3891 endrev = revs.max() + 1
3892 getrenamed = scmutil.getrenamedfn(repo, endrev=endrev)
3892 getrenamed = scmutil.getrenamedfn(repo, endrev=endrev)
3893
3893
3894 ui.pager('log')
3894 ui.pager('log')
3895 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, differ,
3895 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, differ,
3896 buffered=True)
3896 buffered=True)
3897 if opts.get('graph'):
3897 if opts.get('graph'):
3898 displayfn = logcmdutil.displaygraphrevs
3898 displayfn = logcmdutil.displaygraphrevs
3899 else:
3899 else:
3900 displayfn = logcmdutil.displayrevs
3900 displayfn = logcmdutil.displayrevs
3901 displayfn(ui, repo, revs, displayer, getrenamed)
3901 displayfn(ui, repo, revs, displayer, getrenamed)
3902
3902
3903 @command('manifest',
3903 @command('manifest',
3904 [('r', 'rev', '', _('revision to display'), _('REV')),
3904 [('r', 'rev', '', _('revision to display'), _('REV')),
3905 ('', 'all', False, _("list files from all revisions"))]
3905 ('', 'all', False, _("list files from all revisions"))]
3906 + formatteropts,
3906 + formatteropts,
3907 _('[-r REV]'),
3907 _('[-r REV]'),
3908 helpcategory=command.CATEGORY_MAINTENANCE,
3908 helpcategory=command.CATEGORY_MAINTENANCE,
3909 intents={INTENT_READONLY})
3909 intents={INTENT_READONLY})
3910 def manifest(ui, repo, node=None, rev=None, **opts):
3910 def manifest(ui, repo, node=None, rev=None, **opts):
3911 """output the current or given revision of the project manifest
3911 """output the current or given revision of the project manifest
3912
3912
3913 Print a list of version controlled files for the given revision.
3913 Print a list of version controlled files for the given revision.
3914 If no revision is given, the first parent of the working directory
3914 If no revision is given, the first parent of the working directory
3915 is used, or the null revision if no revision is checked out.
3915 is used, or the null revision if no revision is checked out.
3916
3916
3917 With -v, print file permissions, symlink and executable bits.
3917 With -v, print file permissions, symlink and executable bits.
3918 With --debug, print file revision hashes.
3918 With --debug, print file revision hashes.
3919
3919
3920 If option --all is specified, the list of all files from all revisions
3920 If option --all is specified, the list of all files from all revisions
3921 is printed. This includes deleted and renamed files.
3921 is printed. This includes deleted and renamed files.
3922
3922
3923 Returns 0 on success.
3923 Returns 0 on success.
3924 """
3924 """
3925 opts = pycompat.byteskwargs(opts)
3925 opts = pycompat.byteskwargs(opts)
3926 fm = ui.formatter('manifest', opts)
3926 fm = ui.formatter('manifest', opts)
3927
3927
3928 if opts.get('all'):
3928 if opts.get('all'):
3929 if rev or node:
3929 if rev or node:
3930 raise error.Abort(_("can't specify a revision with --all"))
3930 raise error.Abort(_("can't specify a revision with --all"))
3931
3931
3932 res = set()
3932 res = set()
3933 for rev in repo:
3933 for rev in repo:
3934 ctx = repo[rev]
3934 ctx = repo[rev]
3935 res |= set(ctx.files())
3935 res |= set(ctx.files())
3936
3936
3937 ui.pager('manifest')
3937 ui.pager('manifest')
3938 for f in sorted(res):
3938 for f in sorted(res):
3939 fm.startitem()
3939 fm.startitem()
3940 fm.write("path", '%s\n', f)
3940 fm.write("path", '%s\n', f)
3941 fm.end()
3941 fm.end()
3942 return
3942 return
3943
3943
3944 if rev and node:
3944 if rev and node:
3945 raise error.Abort(_("please specify just one revision"))
3945 raise error.Abort(_("please specify just one revision"))
3946
3946
3947 if not node:
3947 if not node:
3948 node = rev
3948 node = rev
3949
3949
3950 char = {'l': '@', 'x': '*', '': '', 't': 'd'}
3950 char = {'l': '@', 'x': '*', '': '', 't': 'd'}
3951 mode = {'l': '644', 'x': '755', '': '644', 't': '755'}
3951 mode = {'l': '644', 'x': '755', '': '644', 't': '755'}
3952 if node:
3952 if node:
3953 repo = scmutil.unhidehashlikerevs(repo, [node], 'nowarn')
3953 repo = scmutil.unhidehashlikerevs(repo, [node], 'nowarn')
3954 ctx = scmutil.revsingle(repo, node)
3954 ctx = scmutil.revsingle(repo, node)
3955 mf = ctx.manifest()
3955 mf = ctx.manifest()
3956 ui.pager('manifest')
3956 ui.pager('manifest')
3957 for f in ctx:
3957 for f in ctx:
3958 fm.startitem()
3958 fm.startitem()
3959 fm.context(ctx=ctx)
3959 fm.context(ctx=ctx)
3960 fl = ctx[f].flags()
3960 fl = ctx[f].flags()
3961 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3961 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3962 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3962 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3963 fm.write('path', '%s\n', f)
3963 fm.write('path', '%s\n', f)
3964 fm.end()
3964 fm.end()
3965
3965
3966 @command('merge',
3966 @command('merge',
3967 [('f', 'force', None,
3967 [('f', 'force', None,
3968 _('force a merge including outstanding changes (DEPRECATED)')),
3968 _('force a merge including outstanding changes (DEPRECATED)')),
3969 ('r', 'rev', '', _('revision to merge'), _('REV')),
3969 ('r', 'rev', '', _('revision to merge'), _('REV')),
3970 ('P', 'preview', None,
3970 ('P', 'preview', None,
3971 _('review revisions to merge (no merge is performed)')),
3971 _('review revisions to merge (no merge is performed)')),
3972 ('', 'abort', None, _('abort the ongoing merge')),
3972 ('', 'abort', None, _('abort the ongoing merge')),
3973 ] + mergetoolopts,
3973 ] + mergetoolopts,
3974 _('[-P] [[-r] REV]'),
3974 _('[-P] [[-r] REV]'),
3975 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT, helpbasic=True)
3975 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT, helpbasic=True)
3976 def merge(ui, repo, node=None, **opts):
3976 def merge(ui, repo, node=None, **opts):
3977 """merge another revision into working directory
3977 """merge another revision into working directory
3978
3978
3979 The current working directory is updated with all changes made in
3979 The current working directory is updated with all changes made in
3980 the requested revision since the last common predecessor revision.
3980 the requested revision since the last common predecessor revision.
3981
3981
3982 Files that changed between either parent are marked as changed for
3982 Files that changed between either parent are marked as changed for
3983 the next commit and a commit must be performed before any further
3983 the next commit and a commit must be performed before any further
3984 updates to the repository are allowed. The next commit will have
3984 updates to the repository are allowed. The next commit will have
3985 two parents.
3985 two parents.
3986
3986
3987 ``--tool`` can be used to specify the merge tool used for file
3987 ``--tool`` can be used to specify the merge tool used for file
3988 merges. It overrides the HGMERGE environment variable and your
3988 merges. It overrides the HGMERGE environment variable and your
3989 configuration files. See :hg:`help merge-tools` for options.
3989 configuration files. See :hg:`help merge-tools` for options.
3990
3990
3991 If no revision is specified, the working directory's parent is a
3991 If no revision is specified, the working directory's parent is a
3992 head revision, and the current branch contains exactly one other
3992 head revision, and the current branch contains exactly one other
3993 head, the other head is merged with by default. Otherwise, an
3993 head, the other head is merged with by default. Otherwise, an
3994 explicit revision with which to merge with must be provided.
3994 explicit revision with which to merge with must be provided.
3995
3995
3996 See :hg:`help resolve` for information on handling file conflicts.
3996 See :hg:`help resolve` for information on handling file conflicts.
3997
3997
3998 To undo an uncommitted merge, use :hg:`merge --abort` which
3998 To undo an uncommitted merge, use :hg:`merge --abort` which
3999 will check out a clean copy of the original merge parent, losing
3999 will check out a clean copy of the original merge parent, losing
4000 all changes.
4000 all changes.
4001
4001
4002 Returns 0 on success, 1 if there are unresolved files.
4002 Returns 0 on success, 1 if there are unresolved files.
4003 """
4003 """
4004
4004
4005 opts = pycompat.byteskwargs(opts)
4005 opts = pycompat.byteskwargs(opts)
4006 abort = opts.get('abort')
4006 abort = opts.get('abort')
4007 if abort and repo.dirstate.p2() == nullid:
4007 if abort and repo.dirstate.p2() == nullid:
4008 cmdutil.wrongtooltocontinue(repo, _('merge'))
4008 cmdutil.wrongtooltocontinue(repo, _('merge'))
4009 if abort:
4009 if abort:
4010 if node:
4010 if node:
4011 raise error.Abort(_("cannot specify a node with --abort"))
4011 raise error.Abort(_("cannot specify a node with --abort"))
4012 if opts.get('rev'):
4012 if opts.get('rev'):
4013 raise error.Abort(_("cannot specify both --rev and --abort"))
4013 raise error.Abort(_("cannot specify both --rev and --abort"))
4014 if opts.get('preview'):
4014 if opts.get('preview'):
4015 raise error.Abort(_("cannot specify --preview with --abort"))
4015 raise error.Abort(_("cannot specify --preview with --abort"))
4016 if opts.get('rev') and node:
4016 if opts.get('rev') and node:
4017 raise error.Abort(_("please specify just one revision"))
4017 raise error.Abort(_("please specify just one revision"))
4018 if not node:
4018 if not node:
4019 node = opts.get('rev')
4019 node = opts.get('rev')
4020
4020
4021 if node:
4021 if node:
4022 node = scmutil.revsingle(repo, node).node()
4022 node = scmutil.revsingle(repo, node).node()
4023
4023
4024 if not node and not abort:
4024 if not node and not abort:
4025 node = repo[destutil.destmerge(repo)].node()
4025 node = repo[destutil.destmerge(repo)].node()
4026
4026
4027 if opts.get('preview'):
4027 if opts.get('preview'):
4028 # find nodes that are ancestors of p2 but not of p1
4028 # find nodes that are ancestors of p2 but not of p1
4029 p1 = repo.lookup('.')
4029 p1 = repo.lookup('.')
4030 p2 = node
4030 p2 = node
4031 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4031 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4032
4032
4033 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4033 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4034 for node in nodes:
4034 for node in nodes:
4035 displayer.show(repo[node])
4035 displayer.show(repo[node])
4036 displayer.close()
4036 displayer.close()
4037 return 0
4037 return 0
4038
4038
4039 # ui.forcemerge is an internal variable, do not document
4039 # ui.forcemerge is an internal variable, do not document
4040 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4040 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4041 with ui.configoverride(overrides, 'merge'):
4041 with ui.configoverride(overrides, 'merge'):
4042 force = opts.get('force')
4042 force = opts.get('force')
4043 labels = ['working copy', 'merge rev']
4043 labels = ['working copy', 'merge rev']
4044 return hg.merge(repo, node, force=force, mergeforce=force,
4044 return hg.merge(repo, node, force=force, mergeforce=force,
4045 labels=labels, abort=abort)
4045 labels=labels, abort=abort)
4046
4046
4047 @command('outgoing|out',
4047 @command('outgoing|out',
4048 [('f', 'force', None, _('run even when the destination is unrelated')),
4048 [('f', 'force', None, _('run even when the destination is unrelated')),
4049 ('r', 'rev', [],
4049 ('r', 'rev', [],
4050 _('a changeset intended to be included in the destination'), _('REV')),
4050 _('a changeset intended to be included in the destination'), _('REV')),
4051 ('n', 'newest-first', None, _('show newest record first')),
4051 ('n', 'newest-first', None, _('show newest record first')),
4052 ('B', 'bookmarks', False, _('compare bookmarks')),
4052 ('B', 'bookmarks', False, _('compare bookmarks')),
4053 ('b', 'branch', [], _('a specific branch you would like to push'),
4053 ('b', 'branch', [], _('a specific branch you would like to push'),
4054 _('BRANCH')),
4054 _('BRANCH')),
4055 ] + logopts + remoteopts + subrepoopts,
4055 ] + logopts + remoteopts + subrepoopts,
4056 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4056 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4057 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
4057 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
4058 def outgoing(ui, repo, dest=None, **opts):
4058 def outgoing(ui, repo, dest=None, **opts):
4059 """show changesets not found in the destination
4059 """show changesets not found in the destination
4060
4060
4061 Show changesets not found in the specified destination repository
4061 Show changesets not found in the specified destination repository
4062 or the default push location. These are the changesets that would
4062 or the default push location. These are the changesets that would
4063 be pushed if a push was requested.
4063 be pushed if a push was requested.
4064
4064
4065 See pull for details of valid destination formats.
4065 See pull for details of valid destination formats.
4066
4066
4067 .. container:: verbose
4067 .. container:: verbose
4068
4068
4069 With -B/--bookmarks, the result of bookmark comparison between
4069 With -B/--bookmarks, the result of bookmark comparison between
4070 local and remote repositories is displayed. With -v/--verbose,
4070 local and remote repositories is displayed. With -v/--verbose,
4071 status is also displayed for each bookmark like below::
4071 status is also displayed for each bookmark like below::
4072
4072
4073 BM1 01234567890a added
4073 BM1 01234567890a added
4074 BM2 deleted
4074 BM2 deleted
4075 BM3 234567890abc advanced
4075 BM3 234567890abc advanced
4076 BM4 34567890abcd diverged
4076 BM4 34567890abcd diverged
4077 BM5 4567890abcde changed
4077 BM5 4567890abcde changed
4078
4078
4079 The action taken when pushing depends on the
4079 The action taken when pushing depends on the
4080 status of each bookmark:
4080 status of each bookmark:
4081
4081
4082 :``added``: push with ``-B`` will create it
4082 :``added``: push with ``-B`` will create it
4083 :``deleted``: push with ``-B`` will delete it
4083 :``deleted``: push with ``-B`` will delete it
4084 :``advanced``: push will update it
4084 :``advanced``: push will update it
4085 :``diverged``: push with ``-B`` will update it
4085 :``diverged``: push with ``-B`` will update it
4086 :``changed``: push with ``-B`` will update it
4086 :``changed``: push with ``-B`` will update it
4087
4087
4088 From the point of view of pushing behavior, bookmarks
4088 From the point of view of pushing behavior, bookmarks
4089 existing only in the remote repository are treated as
4089 existing only in the remote repository are treated as
4090 ``deleted``, even if it is in fact added remotely.
4090 ``deleted``, even if it is in fact added remotely.
4091
4091
4092 Returns 0 if there are outgoing changes, 1 otherwise.
4092 Returns 0 if there are outgoing changes, 1 otherwise.
4093 """
4093 """
4094 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4094 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4095 # style URLs, so don't overwrite dest.
4095 # style URLs, so don't overwrite dest.
4096 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4096 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4097 if not path:
4097 if not path:
4098 raise error.Abort(_('default repository not configured!'),
4098 raise error.Abort(_('default repository not configured!'),
4099 hint=_("see 'hg help config.paths'"))
4099 hint=_("see 'hg help config.paths'"))
4100
4100
4101 opts = pycompat.byteskwargs(opts)
4101 opts = pycompat.byteskwargs(opts)
4102 if opts.get('graph'):
4102 if opts.get('graph'):
4103 logcmdutil.checkunsupportedgraphflags([], opts)
4103 logcmdutil.checkunsupportedgraphflags([], opts)
4104 o, other = hg._outgoing(ui, repo, dest, opts)
4104 o, other = hg._outgoing(ui, repo, dest, opts)
4105 if not o:
4105 if not o:
4106 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4106 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4107 return
4107 return
4108
4108
4109 revdag = logcmdutil.graphrevs(repo, o, opts)
4109 revdag = logcmdutil.graphrevs(repo, o, opts)
4110 ui.pager('outgoing')
4110 ui.pager('outgoing')
4111 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
4111 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
4112 logcmdutil.displaygraph(ui, repo, revdag, displayer,
4112 logcmdutil.displaygraph(ui, repo, revdag, displayer,
4113 graphmod.asciiedges)
4113 graphmod.asciiedges)
4114 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4114 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4115 return 0
4115 return 0
4116
4116
4117 if opts.get('bookmarks'):
4117 if opts.get('bookmarks'):
4118 dest = path.pushloc or path.loc
4118 dest = path.pushloc or path.loc
4119 other = hg.peer(repo, opts, dest)
4119 other = hg.peer(repo, opts, dest)
4120 if 'bookmarks' not in other.listkeys('namespaces'):
4120 if 'bookmarks' not in other.listkeys('namespaces'):
4121 ui.warn(_("remote doesn't support bookmarks\n"))
4121 ui.warn(_("remote doesn't support bookmarks\n"))
4122 return 0
4122 return 0
4123 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4123 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4124 ui.pager('outgoing')
4124 ui.pager('outgoing')
4125 return bookmarks.outgoing(ui, repo, other)
4125 return bookmarks.outgoing(ui, repo, other)
4126
4126
4127 repo._subtoppath = path.pushloc or path.loc
4127 repo._subtoppath = path.pushloc or path.loc
4128 try:
4128 try:
4129 return hg.outgoing(ui, repo, dest, opts)
4129 return hg.outgoing(ui, repo, dest, opts)
4130 finally:
4130 finally:
4131 del repo._subtoppath
4131 del repo._subtoppath
4132
4132
4133 @command('parents',
4133 @command('parents',
4134 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4134 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4135 ] + templateopts,
4135 ] + templateopts,
4136 _('[-r REV] [FILE]'),
4136 _('[-r REV] [FILE]'),
4137 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4137 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4138 inferrepo=True)
4138 inferrepo=True)
4139 def parents(ui, repo, file_=None, **opts):
4139 def parents(ui, repo, file_=None, **opts):
4140 """show the parents of the working directory or revision (DEPRECATED)
4140 """show the parents of the working directory or revision (DEPRECATED)
4141
4141
4142 Print the working directory's parent revisions. If a revision is
4142 Print the working directory's parent revisions. If a revision is
4143 given via -r/--rev, the parent of that revision will be printed.
4143 given via -r/--rev, the parent of that revision will be printed.
4144 If a file argument is given, the revision in which the file was
4144 If a file argument is given, the revision in which the file was
4145 last changed (before the working directory revision or the
4145 last changed (before the working directory revision or the
4146 argument to --rev if given) is printed.
4146 argument to --rev if given) is printed.
4147
4147
4148 This command is equivalent to::
4148 This command is equivalent to::
4149
4149
4150 hg log -r "p1()+p2()" or
4150 hg log -r "p1()+p2()" or
4151 hg log -r "p1(REV)+p2(REV)" or
4151 hg log -r "p1(REV)+p2(REV)" or
4152 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
4152 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
4153 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
4153 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
4154
4154
4155 See :hg:`summary` and :hg:`help revsets` for related information.
4155 See :hg:`summary` and :hg:`help revsets` for related information.
4156
4156
4157 Returns 0 on success.
4157 Returns 0 on success.
4158 """
4158 """
4159
4159
4160 opts = pycompat.byteskwargs(opts)
4160 opts = pycompat.byteskwargs(opts)
4161 rev = opts.get('rev')
4161 rev = opts.get('rev')
4162 if rev:
4162 if rev:
4163 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
4163 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
4164 ctx = scmutil.revsingle(repo, rev, None)
4164 ctx = scmutil.revsingle(repo, rev, None)
4165
4165
4166 if file_:
4166 if file_:
4167 m = scmutil.match(ctx, (file_,), opts)
4167 m = scmutil.match(ctx, (file_,), opts)
4168 if m.anypats() or len(m.files()) != 1:
4168 if m.anypats() or len(m.files()) != 1:
4169 raise error.Abort(_('can only specify an explicit filename'))
4169 raise error.Abort(_('can only specify an explicit filename'))
4170 file_ = m.files()[0]
4170 file_ = m.files()[0]
4171 filenodes = []
4171 filenodes = []
4172 for cp in ctx.parents():
4172 for cp in ctx.parents():
4173 if not cp:
4173 if not cp:
4174 continue
4174 continue
4175 try:
4175 try:
4176 filenodes.append(cp.filenode(file_))
4176 filenodes.append(cp.filenode(file_))
4177 except error.LookupError:
4177 except error.LookupError:
4178 pass
4178 pass
4179 if not filenodes:
4179 if not filenodes:
4180 raise error.Abort(_("'%s' not found in manifest!") % file_)
4180 raise error.Abort(_("'%s' not found in manifest!") % file_)
4181 p = []
4181 p = []
4182 for fn in filenodes:
4182 for fn in filenodes:
4183 fctx = repo.filectx(file_, fileid=fn)
4183 fctx = repo.filectx(file_, fileid=fn)
4184 p.append(fctx.node())
4184 p.append(fctx.node())
4185 else:
4185 else:
4186 p = [cp.node() for cp in ctx.parents()]
4186 p = [cp.node() for cp in ctx.parents()]
4187
4187
4188 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4188 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4189 for n in p:
4189 for n in p:
4190 if n != nullid:
4190 if n != nullid:
4191 displayer.show(repo[n])
4191 displayer.show(repo[n])
4192 displayer.close()
4192 displayer.close()
4193
4193
4194 @command('paths', formatteropts, _('[NAME]'),
4194 @command('paths', formatteropts, _('[NAME]'),
4195 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4195 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4196 optionalrepo=True, intents={INTENT_READONLY})
4196 optionalrepo=True, intents={INTENT_READONLY})
4197 def paths(ui, repo, search=None, **opts):
4197 def paths(ui, repo, search=None, **opts):
4198 """show aliases for remote repositories
4198 """show aliases for remote repositories
4199
4199
4200 Show definition of symbolic path name NAME. If no name is given,
4200 Show definition of symbolic path name NAME. If no name is given,
4201 show definition of all available names.
4201 show definition of all available names.
4202
4202
4203 Option -q/--quiet suppresses all output when searching for NAME
4203 Option -q/--quiet suppresses all output when searching for NAME
4204 and shows only the path names when listing all definitions.
4204 and shows only the path names when listing all definitions.
4205
4205
4206 Path names are defined in the [paths] section of your
4206 Path names are defined in the [paths] section of your
4207 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4207 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4208 repository, ``.hg/hgrc`` is used, too.
4208 repository, ``.hg/hgrc`` is used, too.
4209
4209
4210 The path names ``default`` and ``default-push`` have a special
4210 The path names ``default`` and ``default-push`` have a special
4211 meaning. When performing a push or pull operation, they are used
4211 meaning. When performing a push or pull operation, they are used
4212 as fallbacks if no location is specified on the command-line.
4212 as fallbacks if no location is specified on the command-line.
4213 When ``default-push`` is set, it will be used for push and
4213 When ``default-push`` is set, it will be used for push and
4214 ``default`` will be used for pull; otherwise ``default`` is used
4214 ``default`` will be used for pull; otherwise ``default`` is used
4215 as the fallback for both. When cloning a repository, the clone
4215 as the fallback for both. When cloning a repository, the clone
4216 source is written as ``default`` in ``.hg/hgrc``.
4216 source is written as ``default`` in ``.hg/hgrc``.
4217
4217
4218 .. note::
4218 .. note::
4219
4219
4220 ``default`` and ``default-push`` apply to all inbound (e.g.
4220 ``default`` and ``default-push`` apply to all inbound (e.g.
4221 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
4221 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
4222 and :hg:`bundle`) operations.
4222 and :hg:`bundle`) operations.
4223
4223
4224 See :hg:`help urls` for more information.
4224 See :hg:`help urls` for more information.
4225
4225
4226 .. container:: verbose
4226 .. container:: verbose
4227
4227
4228 Template:
4228 Template:
4229
4229
4230 The following keywords are supported. See also :hg:`help templates`.
4230 The following keywords are supported. See also :hg:`help templates`.
4231
4231
4232 :name: String. Symbolic name of the path alias.
4232 :name: String. Symbolic name of the path alias.
4233 :pushurl: String. URL for push operations.
4233 :pushurl: String. URL for push operations.
4234 :url: String. URL or directory path for the other operations.
4234 :url: String. URL or directory path for the other operations.
4235
4235
4236 Returns 0 on success.
4236 Returns 0 on success.
4237 """
4237 """
4238
4238
4239 opts = pycompat.byteskwargs(opts)
4239 opts = pycompat.byteskwargs(opts)
4240 ui.pager('paths')
4240 ui.pager('paths')
4241 if search:
4241 if search:
4242 pathitems = [(name, path) for name, path in ui.paths.iteritems()
4242 pathitems = [(name, path) for name, path in ui.paths.iteritems()
4243 if name == search]
4243 if name == search]
4244 else:
4244 else:
4245 pathitems = sorted(ui.paths.iteritems())
4245 pathitems = sorted(ui.paths.iteritems())
4246
4246
4247 fm = ui.formatter('paths', opts)
4247 fm = ui.formatter('paths', opts)
4248 if fm.isplain():
4248 if fm.isplain():
4249 hidepassword = util.hidepassword
4249 hidepassword = util.hidepassword
4250 else:
4250 else:
4251 hidepassword = bytes
4251 hidepassword = bytes
4252 if ui.quiet:
4252 if ui.quiet:
4253 namefmt = '%s\n'
4253 namefmt = '%s\n'
4254 else:
4254 else:
4255 namefmt = '%s = '
4255 namefmt = '%s = '
4256 showsubopts = not search and not ui.quiet
4256 showsubopts = not search and not ui.quiet
4257
4257
4258 for name, path in pathitems:
4258 for name, path in pathitems:
4259 fm.startitem()
4259 fm.startitem()
4260 fm.condwrite(not search, 'name', namefmt, name)
4260 fm.condwrite(not search, 'name', namefmt, name)
4261 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
4261 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
4262 for subopt, value in sorted(path.suboptions.items()):
4262 for subopt, value in sorted(path.suboptions.items()):
4263 assert subopt not in ('name', 'url')
4263 assert subopt not in ('name', 'url')
4264 if showsubopts:
4264 if showsubopts:
4265 fm.plain('%s:%s = ' % (name, subopt))
4265 fm.plain('%s:%s = ' % (name, subopt))
4266 fm.condwrite(showsubopts, subopt, '%s\n', value)
4266 fm.condwrite(showsubopts, subopt, '%s\n', value)
4267
4267
4268 fm.end()
4268 fm.end()
4269
4269
4270 if search and not pathitems:
4270 if search and not pathitems:
4271 if not ui.quiet:
4271 if not ui.quiet:
4272 ui.warn(_("not found!\n"))
4272 ui.warn(_("not found!\n"))
4273 return 1
4273 return 1
4274 else:
4274 else:
4275 return 0
4275 return 0
4276
4276
4277 @command('phase',
4277 @command('phase',
4278 [('p', 'public', False, _('set changeset phase to public')),
4278 [('p', 'public', False, _('set changeset phase to public')),
4279 ('d', 'draft', False, _('set changeset phase to draft')),
4279 ('d', 'draft', False, _('set changeset phase to draft')),
4280 ('s', 'secret', False, _('set changeset phase to secret')),
4280 ('s', 'secret', False, _('set changeset phase to secret')),
4281 ('f', 'force', False, _('allow to move boundary backward')),
4281 ('f', 'force', False, _('allow to move boundary backward')),
4282 ('r', 'rev', [], _('target revision'), _('REV')),
4282 ('r', 'rev', [], _('target revision'), _('REV')),
4283 ],
4283 ],
4284 _('[-p|-d|-s] [-f] [-r] [REV...]'),
4284 _('[-p|-d|-s] [-f] [-r] [REV...]'),
4285 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
4285 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
4286 def phase(ui, repo, *revs, **opts):
4286 def phase(ui, repo, *revs, **opts):
4287 """set or show the current phase name
4287 """set or show the current phase name
4288
4288
4289 With no argument, show the phase name of the current revision(s).
4289 With no argument, show the phase name of the current revision(s).
4290
4290
4291 With one of -p/--public, -d/--draft or -s/--secret, change the
4291 With one of -p/--public, -d/--draft or -s/--secret, change the
4292 phase value of the specified revisions.
4292 phase value of the specified revisions.
4293
4293
4294 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
4294 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
4295 lower phase to a higher phase. Phases are ordered as follows::
4295 lower phase to a higher phase. Phases are ordered as follows::
4296
4296
4297 public < draft < secret
4297 public < draft < secret
4298
4298
4299 Returns 0 on success, 1 if some phases could not be changed.
4299 Returns 0 on success, 1 if some phases could not be changed.
4300
4300
4301 (For more information about the phases concept, see :hg:`help phases`.)
4301 (For more information about the phases concept, see :hg:`help phases`.)
4302 """
4302 """
4303 opts = pycompat.byteskwargs(opts)
4303 opts = pycompat.byteskwargs(opts)
4304 # search for a unique phase argument
4304 # search for a unique phase argument
4305 targetphase = None
4305 targetphase = None
4306 for idx, name in enumerate(phases.cmdphasenames):
4306 for idx, name in enumerate(phases.cmdphasenames):
4307 if opts[name]:
4307 if opts[name]:
4308 if targetphase is not None:
4308 if targetphase is not None:
4309 raise error.Abort(_('only one phase can be specified'))
4309 raise error.Abort(_('only one phase can be specified'))
4310 targetphase = idx
4310 targetphase = idx
4311
4311
4312 # look for specified revision
4312 # look for specified revision
4313 revs = list(revs)
4313 revs = list(revs)
4314 revs.extend(opts['rev'])
4314 revs.extend(opts['rev'])
4315 if not revs:
4315 if not revs:
4316 # display both parents as the second parent phase can influence
4316 # display both parents as the second parent phase can influence
4317 # the phase of a merge commit
4317 # the phase of a merge commit
4318 revs = [c.rev() for c in repo[None].parents()]
4318 revs = [c.rev() for c in repo[None].parents()]
4319
4319
4320 revs = scmutil.revrange(repo, revs)
4320 revs = scmutil.revrange(repo, revs)
4321
4321
4322 ret = 0
4322 ret = 0
4323 if targetphase is None:
4323 if targetphase is None:
4324 # display
4324 # display
4325 for r in revs:
4325 for r in revs:
4326 ctx = repo[r]
4326 ctx = repo[r]
4327 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4327 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4328 else:
4328 else:
4329 with repo.lock(), repo.transaction("phase") as tr:
4329 with repo.lock(), repo.transaction("phase") as tr:
4330 # set phase
4330 # set phase
4331 if not revs:
4331 if not revs:
4332 raise error.Abort(_('empty revision set'))
4332 raise error.Abort(_('empty revision set'))
4333 nodes = [repo[r].node() for r in revs]
4333 nodes = [repo[r].node() for r in revs]
4334 # moving revision from public to draft may hide them
4334 # moving revision from public to draft may hide them
4335 # We have to check result on an unfiltered repository
4335 # We have to check result on an unfiltered repository
4336 unfi = repo.unfiltered()
4336 unfi = repo.unfiltered()
4337 getphase = unfi._phasecache.phase
4337 getphase = unfi._phasecache.phase
4338 olddata = [getphase(unfi, r) for r in unfi]
4338 olddata = [getphase(unfi, r) for r in unfi]
4339 phases.advanceboundary(repo, tr, targetphase, nodes)
4339 phases.advanceboundary(repo, tr, targetphase, nodes)
4340 if opts['force']:
4340 if opts['force']:
4341 phases.retractboundary(repo, tr, targetphase, nodes)
4341 phases.retractboundary(repo, tr, targetphase, nodes)
4342 getphase = unfi._phasecache.phase
4342 getphase = unfi._phasecache.phase
4343 newdata = [getphase(unfi, r) for r in unfi]
4343 newdata = [getphase(unfi, r) for r in unfi]
4344 changes = sum(newdata[r] != olddata[r] for r in unfi)
4344 changes = sum(newdata[r] != olddata[r] for r in unfi)
4345 cl = unfi.changelog
4345 cl = unfi.changelog
4346 rejected = [n for n in nodes
4346 rejected = [n for n in nodes
4347 if newdata[cl.rev(n)] < targetphase]
4347 if newdata[cl.rev(n)] < targetphase]
4348 if rejected:
4348 if rejected:
4349 ui.warn(_('cannot move %i changesets to a higher '
4349 ui.warn(_('cannot move %i changesets to a higher '
4350 'phase, use --force\n') % len(rejected))
4350 'phase, use --force\n') % len(rejected))
4351 ret = 1
4351 ret = 1
4352 if changes:
4352 if changes:
4353 msg = _('phase changed for %i changesets\n') % changes
4353 msg = _('phase changed for %i changesets\n') % changes
4354 if ret:
4354 if ret:
4355 ui.status(msg)
4355 ui.status(msg)
4356 else:
4356 else:
4357 ui.note(msg)
4357 ui.note(msg)
4358 else:
4358 else:
4359 ui.warn(_('no phases changed\n'))
4359 ui.warn(_('no phases changed\n'))
4360 return ret
4360 return ret
4361
4361
4362 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
4362 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
4363 """Run after a changegroup has been added via pull/unbundle
4363 """Run after a changegroup has been added via pull/unbundle
4364
4364
4365 This takes arguments below:
4365 This takes arguments below:
4366
4366
4367 :modheads: change of heads by pull/unbundle
4367 :modheads: change of heads by pull/unbundle
4368 :optupdate: updating working directory is needed or not
4368 :optupdate: updating working directory is needed or not
4369 :checkout: update destination revision (or None to default destination)
4369 :checkout: update destination revision (or None to default destination)
4370 :brev: a name, which might be a bookmark to be activated after updating
4370 :brev: a name, which might be a bookmark to be activated after updating
4371 """
4371 """
4372 if modheads == 0:
4372 if modheads == 0:
4373 return
4373 return
4374 if optupdate:
4374 if optupdate:
4375 try:
4375 try:
4376 return hg.updatetotally(ui, repo, checkout, brev)
4376 return hg.updatetotally(ui, repo, checkout, brev)
4377 except error.UpdateAbort as inst:
4377 except error.UpdateAbort as inst:
4378 msg = _("not updating: %s") % stringutil.forcebytestr(inst)
4378 msg = _("not updating: %s") % stringutil.forcebytestr(inst)
4379 hint = inst.hint
4379 hint = inst.hint
4380 raise error.UpdateAbort(msg, hint=hint)
4380 raise error.UpdateAbort(msg, hint=hint)
4381 if modheads is not None and modheads > 1:
4381 if modheads is not None and modheads > 1:
4382 currentbranchheads = len(repo.branchheads())
4382 currentbranchheads = len(repo.branchheads())
4383 if currentbranchheads == modheads:
4383 if currentbranchheads == modheads:
4384 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4384 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4385 elif currentbranchheads > 1:
4385 elif currentbranchheads > 1:
4386 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4386 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4387 "merge)\n"))
4387 "merge)\n"))
4388 else:
4388 else:
4389 ui.status(_("(run 'hg heads' to see heads)\n"))
4389 ui.status(_("(run 'hg heads' to see heads)\n"))
4390 elif not ui.configbool('commands', 'update.requiredest'):
4390 elif not ui.configbool('commands', 'update.requiredest'):
4391 ui.status(_("(run 'hg update' to get a working copy)\n"))
4391 ui.status(_("(run 'hg update' to get a working copy)\n"))
4392
4392
4393 @command('pull',
4393 @command('pull',
4394 [('u', 'update', None,
4394 [('u', 'update', None,
4395 _('update to new branch head if new descendants were pulled')),
4395 _('update to new branch head if new descendants were pulled')),
4396 ('f', 'force', None, _('run even when remote repository is unrelated')),
4396 ('f', 'force', None, _('run even when remote repository is unrelated')),
4397 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4397 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4398 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4398 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4399 ('b', 'branch', [], _('a specific branch you would like to pull'),
4399 ('b', 'branch', [], _('a specific branch you would like to pull'),
4400 _('BRANCH')),
4400 _('BRANCH')),
4401 ] + remoteopts,
4401 ] + remoteopts,
4402 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
4402 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
4403 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4403 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4404 helpbasic=True)
4404 helpbasic=True)
4405 def pull(ui, repo, source="default", **opts):
4405 def pull(ui, repo, source="default", **opts):
4406 """pull changes from the specified source
4406 """pull changes from the specified source
4407
4407
4408 Pull changes from a remote repository to a local one.
4408 Pull changes from a remote repository to a local one.
4409
4409
4410 This finds all changes from the repository at the specified path
4410 This finds all changes from the repository at the specified path
4411 or URL and adds them to a local repository (the current one unless
4411 or URL and adds them to a local repository (the current one unless
4412 -R is specified). By default, this does not update the copy of the
4412 -R is specified). By default, this does not update the copy of the
4413 project in the working directory.
4413 project in the working directory.
4414
4414
4415 When cloning from servers that support it, Mercurial may fetch
4415 When cloning from servers that support it, Mercurial may fetch
4416 pre-generated data. When this is done, hooks operating on incoming
4416 pre-generated data. When this is done, hooks operating on incoming
4417 changesets and changegroups may fire more than once, once for each
4417 changesets and changegroups may fire more than once, once for each
4418 pre-generated bundle and as well as for any additional remaining
4418 pre-generated bundle and as well as for any additional remaining
4419 data. See :hg:`help -e clonebundles` for more.
4419 data. See :hg:`help -e clonebundles` for more.
4420
4420
4421 Use :hg:`incoming` if you want to see what would have been added
4421 Use :hg:`incoming` if you want to see what would have been added
4422 by a pull at the time you issued this command. If you then decide
4422 by a pull at the time you issued this command. If you then decide
4423 to add those changes to the repository, you should use :hg:`pull
4423 to add those changes to the repository, you should use :hg:`pull
4424 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4424 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4425
4425
4426 If SOURCE is omitted, the 'default' path will be used.
4426 If SOURCE is omitted, the 'default' path will be used.
4427 See :hg:`help urls` for more information.
4427 See :hg:`help urls` for more information.
4428
4428
4429 Specifying bookmark as ``.`` is equivalent to specifying the active
4429 Specifying bookmark as ``.`` is equivalent to specifying the active
4430 bookmark's name.
4430 bookmark's name.
4431
4431
4432 Returns 0 on success, 1 if an update had unresolved files.
4432 Returns 0 on success, 1 if an update had unresolved files.
4433 """
4433 """
4434
4434
4435 opts = pycompat.byteskwargs(opts)
4435 opts = pycompat.byteskwargs(opts)
4436 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
4436 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
4437 msg = _('update destination required by configuration')
4437 msg = _('update destination required by configuration')
4438 hint = _('use hg pull followed by hg update DEST')
4438 hint = _('use hg pull followed by hg update DEST')
4439 raise error.Abort(msg, hint=hint)
4439 raise error.Abort(msg, hint=hint)
4440
4440
4441 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4441 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4442 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4442 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4443 other = hg.peer(repo, opts, source)
4443 other = hg.peer(repo, opts, source)
4444 try:
4444 try:
4445 revs, checkout = hg.addbranchrevs(repo, other, branches,
4445 revs, checkout = hg.addbranchrevs(repo, other, branches,
4446 opts.get('rev'))
4446 opts.get('rev'))
4447
4447
4448 pullopargs = {}
4448 pullopargs = {}
4449
4449
4450 nodes = None
4450 nodes = None
4451 if opts.get('bookmark') or revs:
4451 if opts.get('bookmark') or revs:
4452 # The list of bookmark used here is the same used to actually update
4452 # The list of bookmark used here is the same used to actually update
4453 # the bookmark names, to avoid the race from issue 4689 and we do
4453 # the bookmark names, to avoid the race from issue 4689 and we do
4454 # all lookup and bookmark queries in one go so they see the same
4454 # all lookup and bookmark queries in one go so they see the same
4455 # version of the server state (issue 4700).
4455 # version of the server state (issue 4700).
4456 nodes = []
4456 nodes = []
4457 fnodes = []
4457 fnodes = []
4458 revs = revs or []
4458 revs = revs or []
4459 if revs and not other.capable('lookup'):
4459 if revs and not other.capable('lookup'):
4460 err = _("other repository doesn't support revision lookup, "
4460 err = _("other repository doesn't support revision lookup, "
4461 "so a rev cannot be specified.")
4461 "so a rev cannot be specified.")
4462 raise error.Abort(err)
4462 raise error.Abort(err)
4463 with other.commandexecutor() as e:
4463 with other.commandexecutor() as e:
4464 fremotebookmarks = e.callcommand('listkeys', {
4464 fremotebookmarks = e.callcommand('listkeys', {
4465 'namespace': 'bookmarks'
4465 'namespace': 'bookmarks'
4466 })
4466 })
4467 for r in revs:
4467 for r in revs:
4468 fnodes.append(e.callcommand('lookup', {'key': r}))
4468 fnodes.append(e.callcommand('lookup', {'key': r}))
4469 remotebookmarks = fremotebookmarks.result()
4469 remotebookmarks = fremotebookmarks.result()
4470 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
4470 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
4471 pullopargs['remotebookmarks'] = remotebookmarks
4471 pullopargs['remotebookmarks'] = remotebookmarks
4472 for b in opts.get('bookmark', []):
4472 for b in opts.get('bookmark', []):
4473 b = repo._bookmarks.expandname(b)
4473 b = repo._bookmarks.expandname(b)
4474 if b not in remotebookmarks:
4474 if b not in remotebookmarks:
4475 raise error.Abort(_('remote bookmark %s not found!') % b)
4475 raise error.Abort(_('remote bookmark %s not found!') % b)
4476 nodes.append(remotebookmarks[b])
4476 nodes.append(remotebookmarks[b])
4477 for i, rev in enumerate(revs):
4477 for i, rev in enumerate(revs):
4478 node = fnodes[i].result()
4478 node = fnodes[i].result()
4479 nodes.append(node)
4479 nodes.append(node)
4480 if rev == checkout:
4480 if rev == checkout:
4481 checkout = node
4481 checkout = node
4482
4482
4483 wlock = util.nullcontextmanager()
4483 wlock = util.nullcontextmanager()
4484 if opts.get('update'):
4484 if opts.get('update'):
4485 wlock = repo.wlock()
4485 wlock = repo.wlock()
4486 with wlock:
4486 with wlock:
4487 pullopargs.update(opts.get('opargs', {}))
4487 pullopargs.update(opts.get('opargs', {}))
4488 modheads = exchange.pull(repo, other, heads=nodes,
4488 modheads = exchange.pull(repo, other, heads=nodes,
4489 force=opts.get('force'),
4489 force=opts.get('force'),
4490 bookmarks=opts.get('bookmark', ()),
4490 bookmarks=opts.get('bookmark', ()),
4491 opargs=pullopargs).cgresult
4491 opargs=pullopargs).cgresult
4492
4492
4493 # brev is a name, which might be a bookmark to be activated at
4493 # brev is a name, which might be a bookmark to be activated at
4494 # the end of the update. In other words, it is an explicit
4494 # the end of the update. In other words, it is an explicit
4495 # destination of the update
4495 # destination of the update
4496 brev = None
4496 brev = None
4497
4497
4498 if checkout:
4498 if checkout:
4499 checkout = repo.unfiltered().changelog.rev(checkout)
4499 checkout = repo.unfiltered().changelog.rev(checkout)
4500
4500
4501 # order below depends on implementation of
4501 # order below depends on implementation of
4502 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4502 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4503 # because 'checkout' is determined without it.
4503 # because 'checkout' is determined without it.
4504 if opts.get('rev'):
4504 if opts.get('rev'):
4505 brev = opts['rev'][0]
4505 brev = opts['rev'][0]
4506 elif opts.get('branch'):
4506 elif opts.get('branch'):
4507 brev = opts['branch'][0]
4507 brev = opts['branch'][0]
4508 else:
4508 else:
4509 brev = branches[0]
4509 brev = branches[0]
4510 repo._subtoppath = source
4510 repo._subtoppath = source
4511 try:
4511 try:
4512 ret = postincoming(ui, repo, modheads, opts.get('update'),
4512 ret = postincoming(ui, repo, modheads, opts.get('update'),
4513 checkout, brev)
4513 checkout, brev)
4514 except error.FilteredRepoLookupError as exc:
4514 except error.FilteredRepoLookupError as exc:
4515 msg = _('cannot update to target: %s') % exc.args[0]
4515 msg = _('cannot update to target: %s') % exc.args[0]
4516 exc.args = (msg,) + exc.args[1:]
4516 exc.args = (msg,) + exc.args[1:]
4517 raise
4517 raise
4518 finally:
4518 finally:
4519 del repo._subtoppath
4519 del repo._subtoppath
4520
4520
4521 finally:
4521 finally:
4522 other.close()
4522 other.close()
4523 return ret
4523 return ret
4524
4524
4525 @command('push',
4525 @command('push',
4526 [('f', 'force', None, _('force push')),
4526 [('f', 'force', None, _('force push')),
4527 ('r', 'rev', [],
4527 ('r', 'rev', [],
4528 _('a changeset intended to be included in the destination'),
4528 _('a changeset intended to be included in the destination'),
4529 _('REV')),
4529 _('REV')),
4530 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4530 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4531 ('b', 'branch', [],
4531 ('b', 'branch', [],
4532 _('a specific branch you would like to push'), _('BRANCH')),
4532 _('a specific branch you would like to push'), _('BRANCH')),
4533 ('', 'new-branch', False, _('allow pushing a new branch')),
4533 ('', 'new-branch', False, _('allow pushing a new branch')),
4534 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4534 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4535 ('', 'publish', False, _('push the changeset as public (EXPERIMENTAL)')),
4535 ('', 'publish', False, _('push the changeset as public (EXPERIMENTAL)')),
4536 ] + remoteopts,
4536 ] + remoteopts,
4537 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
4537 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
4538 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4538 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4539 helpbasic=True)
4539 helpbasic=True)
4540 def push(ui, repo, dest=None, **opts):
4540 def push(ui, repo, dest=None, **opts):
4541 """push changes to the specified destination
4541 """push changes to the specified destination
4542
4542
4543 Push changesets from the local repository to the specified
4543 Push changesets from the local repository to the specified
4544 destination.
4544 destination.
4545
4545
4546 This operation is symmetrical to pull: it is identical to a pull
4546 This operation is symmetrical to pull: it is identical to a pull
4547 in the destination repository from the current one.
4547 in the destination repository from the current one.
4548
4548
4549 By default, push will not allow creation of new heads at the
4549 By default, push will not allow creation of new heads at the
4550 destination, since multiple heads would make it unclear which head
4550 destination, since multiple heads would make it unclear which head
4551 to use. In this situation, it is recommended to pull and merge
4551 to use. In this situation, it is recommended to pull and merge
4552 before pushing.
4552 before pushing.
4553
4553
4554 Use --new-branch if you want to allow push to create a new named
4554 Use --new-branch if you want to allow push to create a new named
4555 branch that is not present at the destination. This allows you to
4555 branch that is not present at the destination. This allows you to
4556 only create a new branch without forcing other changes.
4556 only create a new branch without forcing other changes.
4557
4557
4558 .. note::
4558 .. note::
4559
4559
4560 Extra care should be taken with the -f/--force option,
4560 Extra care should be taken with the -f/--force option,
4561 which will push all new heads on all branches, an action which will
4561 which will push all new heads on all branches, an action which will
4562 almost always cause confusion for collaborators.
4562 almost always cause confusion for collaborators.
4563
4563
4564 If -r/--rev is used, the specified revision and all its ancestors
4564 If -r/--rev is used, the specified revision and all its ancestors
4565 will be pushed to the remote repository.
4565 will be pushed to the remote repository.
4566
4566
4567 If -B/--bookmark is used, the specified bookmarked revision, its
4567 If -B/--bookmark is used, the specified bookmarked revision, its
4568 ancestors, and the bookmark will be pushed to the remote
4568 ancestors, and the bookmark will be pushed to the remote
4569 repository. Specifying ``.`` is equivalent to specifying the active
4569 repository. Specifying ``.`` is equivalent to specifying the active
4570 bookmark's name.
4570 bookmark's name.
4571
4571
4572 Please see :hg:`help urls` for important details about ``ssh://``
4572 Please see :hg:`help urls` for important details about ``ssh://``
4573 URLs. If DESTINATION is omitted, a default path will be used.
4573 URLs. If DESTINATION is omitted, a default path will be used.
4574
4574
4575 .. container:: verbose
4575 .. container:: verbose
4576
4576
4577 The --pushvars option sends strings to the server that become
4577 The --pushvars option sends strings to the server that become
4578 environment variables prepended with ``HG_USERVAR_``. For example,
4578 environment variables prepended with ``HG_USERVAR_``. For example,
4579 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4579 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4580 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4580 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4581
4581
4582 pushvars can provide for user-overridable hooks as well as set debug
4582 pushvars can provide for user-overridable hooks as well as set debug
4583 levels. One example is having a hook that blocks commits containing
4583 levels. One example is having a hook that blocks commits containing
4584 conflict markers, but enables the user to override the hook if the file
4584 conflict markers, but enables the user to override the hook if the file
4585 is using conflict markers for testing purposes or the file format has
4585 is using conflict markers for testing purposes or the file format has
4586 strings that look like conflict markers.
4586 strings that look like conflict markers.
4587
4587
4588 By default, servers will ignore `--pushvars`. To enable it add the
4588 By default, servers will ignore `--pushvars`. To enable it add the
4589 following to your configuration file::
4589 following to your configuration file::
4590
4590
4591 [push]
4591 [push]
4592 pushvars.server = true
4592 pushvars.server = true
4593
4593
4594 Returns 0 if push was successful, 1 if nothing to push.
4594 Returns 0 if push was successful, 1 if nothing to push.
4595 """
4595 """
4596
4596
4597 opts = pycompat.byteskwargs(opts)
4597 opts = pycompat.byteskwargs(opts)
4598 if opts.get('bookmark'):
4598 if opts.get('bookmark'):
4599 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4599 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4600 for b in opts['bookmark']:
4600 for b in opts['bookmark']:
4601 # translate -B options to -r so changesets get pushed
4601 # translate -B options to -r so changesets get pushed
4602 b = repo._bookmarks.expandname(b)
4602 b = repo._bookmarks.expandname(b)
4603 if b in repo._bookmarks:
4603 if b in repo._bookmarks:
4604 opts.setdefault('rev', []).append(b)
4604 opts.setdefault('rev', []).append(b)
4605 else:
4605 else:
4606 # if we try to push a deleted bookmark, translate it to null
4606 # if we try to push a deleted bookmark, translate it to null
4607 # this lets simultaneous -r, -b options continue working
4607 # this lets simultaneous -r, -b options continue working
4608 opts.setdefault('rev', []).append("null")
4608 opts.setdefault('rev', []).append("null")
4609
4609
4610 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4610 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4611 if not path:
4611 if not path:
4612 raise error.Abort(_('default repository not configured!'),
4612 raise error.Abort(_('default repository not configured!'),
4613 hint=_("see 'hg help config.paths'"))
4613 hint=_("see 'hg help config.paths'"))
4614 dest = path.pushloc or path.loc
4614 dest = path.pushloc or path.loc
4615 branches = (path.branch, opts.get('branch') or [])
4615 branches = (path.branch, opts.get('branch') or [])
4616 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4616 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4617 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4617 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4618 other = hg.peer(repo, opts, dest)
4618 other = hg.peer(repo, opts, dest)
4619
4619
4620 if revs:
4620 if revs:
4621 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
4621 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
4622 if not revs:
4622 if not revs:
4623 raise error.Abort(_("specified revisions evaluate to an empty set"),
4623 raise error.Abort(_("specified revisions evaluate to an empty set"),
4624 hint=_("use different revision arguments"))
4624 hint=_("use different revision arguments"))
4625 elif path.pushrev:
4625 elif path.pushrev:
4626 # It doesn't make any sense to specify ancestor revisions. So limit
4626 # It doesn't make any sense to specify ancestor revisions. So limit
4627 # to DAG heads to make discovery simpler.
4627 # to DAG heads to make discovery simpler.
4628 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4628 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4629 revs = scmutil.revrange(repo, [expr])
4629 revs = scmutil.revrange(repo, [expr])
4630 revs = [repo[rev].node() for rev in revs]
4630 revs = [repo[rev].node() for rev in revs]
4631 if not revs:
4631 if not revs:
4632 raise error.Abort(_('default push revset for path evaluates to an '
4632 raise error.Abort(_('default push revset for path evaluates to an '
4633 'empty set'))
4633 'empty set'))
4634
4634
4635 repo._subtoppath = dest
4635 repo._subtoppath = dest
4636 try:
4636 try:
4637 # push subrepos depth-first for coherent ordering
4637 # push subrepos depth-first for coherent ordering
4638 c = repo['.']
4638 c = repo['.']
4639 subs = c.substate # only repos that are committed
4639 subs = c.substate # only repos that are committed
4640 for s in sorted(subs):
4640 for s in sorted(subs):
4641 result = c.sub(s).push(opts)
4641 result = c.sub(s).push(opts)
4642 if result == 0:
4642 if result == 0:
4643 return not result
4643 return not result
4644 finally:
4644 finally:
4645 del repo._subtoppath
4645 del repo._subtoppath
4646
4646
4647 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4647 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4648 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4648 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4649
4649
4650 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4650 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4651 newbranch=opts.get('new_branch'),
4651 newbranch=opts.get('new_branch'),
4652 bookmarks=opts.get('bookmark', ()),
4652 bookmarks=opts.get('bookmark', ()),
4653 publish=opts.get('publish'),
4653 publish=opts.get('publish'),
4654 opargs=opargs)
4654 opargs=opargs)
4655
4655
4656 result = not pushop.cgresult
4656 result = not pushop.cgresult
4657
4657
4658 if pushop.bkresult is not None:
4658 if pushop.bkresult is not None:
4659 if pushop.bkresult == 2:
4659 if pushop.bkresult == 2:
4660 result = 2
4660 result = 2
4661 elif not result and pushop.bkresult:
4661 elif not result and pushop.bkresult:
4662 result = 2
4662 result = 2
4663
4663
4664 return result
4664 return result
4665
4665
4666 @command('recover',
4666 @command('recover',
4667 [('','verify', True, "run `hg verify` after succesful recover"),
4667 [('','verify', True, "run `hg verify` after succesful recover"),
4668 ],
4668 ],
4669 helpcategory=command.CATEGORY_MAINTENANCE)
4669 helpcategory=command.CATEGORY_MAINTENANCE)
4670 def recover(ui, repo, **opts):
4670 def recover(ui, repo, **opts):
4671 """roll back an interrupted transaction
4671 """roll back an interrupted transaction
4672
4672
4673 Recover from an interrupted commit or pull.
4673 Recover from an interrupted commit or pull.
4674
4674
4675 This command tries to fix the repository status after an
4675 This command tries to fix the repository status after an
4676 interrupted operation. It should only be necessary when Mercurial
4676 interrupted operation. It should only be necessary when Mercurial
4677 suggests it.
4677 suggests it.
4678
4678
4679 Returns 0 if successful, 1 if nothing to recover or verify fails.
4679 Returns 0 if successful, 1 if nothing to recover or verify fails.
4680 """
4680 """
4681 ret = repo.recover()
4681 ret = repo.recover()
4682 if ret:
4682 if ret:
4683 if opts[r'verify']:
4683 if opts[r'verify']:
4684 return hg.verify(repo)
4684 return hg.verify(repo)
4685 else:
4685 else:
4686 msg = _("(verify step skipped, run `hg verify` to check your "
4686 msg = _("(verify step skipped, run `hg verify` to check your "
4687 "repository content)\n")
4687 "repository content)\n")
4688 ui.warn(msg)
4688 ui.warn(msg)
4689 return 0
4689 return 0
4690 return 1
4690 return 1
4691
4691
4692 @command('remove|rm',
4692 @command('remove|rm',
4693 [('A', 'after', None, _('record delete for missing files')),
4693 [('A', 'after', None, _('record delete for missing files')),
4694 ('f', 'force', None,
4694 ('f', 'force', None,
4695 _('forget added files, delete modified files')),
4695 _('forget added files, delete modified files')),
4696 ] + subrepoopts + walkopts + dryrunopts,
4696 ] + subrepoopts + walkopts + dryrunopts,
4697 _('[OPTION]... FILE...'),
4697 _('[OPTION]... FILE...'),
4698 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4698 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4699 helpbasic=True, inferrepo=True)
4699 helpbasic=True, inferrepo=True)
4700 def remove(ui, repo, *pats, **opts):
4700 def remove(ui, repo, *pats, **opts):
4701 """remove the specified files on the next commit
4701 """remove the specified files on the next commit
4702
4702
4703 Schedule the indicated files for removal from the current branch.
4703 Schedule the indicated files for removal from the current branch.
4704
4704
4705 This command schedules the files to be removed at the next commit.
4705 This command schedules the files to be removed at the next commit.
4706 To undo a remove before that, see :hg:`revert`. To undo added
4706 To undo a remove before that, see :hg:`revert`. To undo added
4707 files, see :hg:`forget`.
4707 files, see :hg:`forget`.
4708
4708
4709 .. container:: verbose
4709 .. container:: verbose
4710
4710
4711 -A/--after can be used to remove only files that have already
4711 -A/--after can be used to remove only files that have already
4712 been deleted, -f/--force can be used to force deletion, and -Af
4712 been deleted, -f/--force can be used to force deletion, and -Af
4713 can be used to remove files from the next revision without
4713 can be used to remove files from the next revision without
4714 deleting them from the working directory.
4714 deleting them from the working directory.
4715
4715
4716 The following table details the behavior of remove for different
4716 The following table details the behavior of remove for different
4717 file states (columns) and option combinations (rows). The file
4717 file states (columns) and option combinations (rows). The file
4718 states are Added [A], Clean [C], Modified [M] and Missing [!]
4718 states are Added [A], Clean [C], Modified [M] and Missing [!]
4719 (as reported by :hg:`status`). The actions are Warn, Remove
4719 (as reported by :hg:`status`). The actions are Warn, Remove
4720 (from branch) and Delete (from disk):
4720 (from branch) and Delete (from disk):
4721
4721
4722 ========= == == == ==
4722 ========= == == == ==
4723 opt/state A C M !
4723 opt/state A C M !
4724 ========= == == == ==
4724 ========= == == == ==
4725 none W RD W R
4725 none W RD W R
4726 -f R RD RD R
4726 -f R RD RD R
4727 -A W W W R
4727 -A W W W R
4728 -Af R R R R
4728 -Af R R R R
4729 ========= == == == ==
4729 ========= == == == ==
4730
4730
4731 .. note::
4731 .. note::
4732
4732
4733 :hg:`remove` never deletes files in Added [A] state from the
4733 :hg:`remove` never deletes files in Added [A] state from the
4734 working directory, not even if ``--force`` is specified.
4734 working directory, not even if ``--force`` is specified.
4735
4735
4736 Returns 0 on success, 1 if any warnings encountered.
4736 Returns 0 on success, 1 if any warnings encountered.
4737 """
4737 """
4738
4738
4739 opts = pycompat.byteskwargs(opts)
4739 opts = pycompat.byteskwargs(opts)
4740 after, force = opts.get('after'), opts.get('force')
4740 after, force = opts.get('after'), opts.get('force')
4741 dryrun = opts.get('dry_run')
4741 dryrun = opts.get('dry_run')
4742 if not pats and not after:
4742 if not pats and not after:
4743 raise error.Abort(_('no files specified'))
4743 raise error.Abort(_('no files specified'))
4744
4744
4745 m = scmutil.match(repo[None], pats, opts)
4745 m = scmutil.match(repo[None], pats, opts)
4746 subrepos = opts.get('subrepos')
4746 subrepos = opts.get('subrepos')
4747 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
4747 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
4748 return cmdutil.remove(ui, repo, m, "", uipathfn, after, force, subrepos,
4748 return cmdutil.remove(ui, repo, m, "", uipathfn, after, force, subrepos,
4749 dryrun=dryrun)
4749 dryrun=dryrun)
4750
4750
4751 @command('rename|move|mv',
4751 @command('rename|move|mv',
4752 [('A', 'after', None, _('record a rename that has already occurred')),
4752 [('A', 'after', None, _('record a rename that has already occurred')),
4753 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4753 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4754 ] + walkopts + dryrunopts,
4754 ] + walkopts + dryrunopts,
4755 _('[OPTION]... SOURCE... DEST'),
4755 _('[OPTION]... SOURCE... DEST'),
4756 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
4756 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
4757 def rename(ui, repo, *pats, **opts):
4757 def rename(ui, repo, *pats, **opts):
4758 """rename files; equivalent of copy + remove
4758 """rename files; equivalent of copy + remove
4759
4759
4760 Mark dest as copies of sources; mark sources for deletion. If dest
4760 Mark dest as copies of sources; mark sources for deletion. If dest
4761 is a directory, copies are put in that directory. If dest is a
4761 is a directory, copies are put in that directory. If dest is a
4762 file, there can only be one source.
4762 file, there can only be one source.
4763
4763
4764 By default, this command copies the contents of files as they
4764 By default, this command copies the contents of files as they
4765 exist in the working directory. If invoked with -A/--after, the
4765 exist in the working directory. If invoked with -A/--after, the
4766 operation is recorded, but no copying is performed.
4766 operation is recorded, but no copying is performed.
4767
4767
4768 This command takes effect at the next commit. To undo a rename
4768 This command takes effect at the next commit. To undo a rename
4769 before that, see :hg:`revert`.
4769 before that, see :hg:`revert`.
4770
4770
4771 Returns 0 on success, 1 if errors are encountered.
4771 Returns 0 on success, 1 if errors are encountered.
4772 """
4772 """
4773 opts = pycompat.byteskwargs(opts)
4773 opts = pycompat.byteskwargs(opts)
4774 with repo.wlock(False):
4774 with repo.wlock(False):
4775 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4775 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4776
4776
4777 @command('resolve',
4777 @command('resolve',
4778 [('a', 'all', None, _('select all unresolved files')),
4778 [('a', 'all', None, _('select all unresolved files')),
4779 ('l', 'list', None, _('list state of files needing merge')),
4779 ('l', 'list', None, _('list state of files needing merge')),
4780 ('m', 'mark', None, _('mark files as resolved')),
4780 ('m', 'mark', None, _('mark files as resolved')),
4781 ('u', 'unmark', None, _('mark files as unresolved')),
4781 ('u', 'unmark', None, _('mark files as unresolved')),
4782 ('n', 'no-status', None, _('hide status prefix')),
4782 ('n', 'no-status', None, _('hide status prefix')),
4783 ('', 're-merge', None, _('re-merge files'))]
4783 ('', 're-merge', None, _('re-merge files'))]
4784 + mergetoolopts + walkopts + formatteropts,
4784 + mergetoolopts + walkopts + formatteropts,
4785 _('[OPTION]... [FILE]...'),
4785 _('[OPTION]... [FILE]...'),
4786 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4786 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4787 inferrepo=True)
4787 inferrepo=True)
4788 def resolve(ui, repo, *pats, **opts):
4788 def resolve(ui, repo, *pats, **opts):
4789 """redo merges or set/view the merge status of files
4789 """redo merges or set/view the merge status of files
4790
4790
4791 Merges with unresolved conflicts are often the result of
4791 Merges with unresolved conflicts are often the result of
4792 non-interactive merging using the ``internal:merge`` configuration
4792 non-interactive merging using the ``internal:merge`` configuration
4793 setting, or a command-line merge tool like ``diff3``. The resolve
4793 setting, or a command-line merge tool like ``diff3``. The resolve
4794 command is used to manage the files involved in a merge, after
4794 command is used to manage the files involved in a merge, after
4795 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4795 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4796 working directory must have two parents). See :hg:`help
4796 working directory must have two parents). See :hg:`help
4797 merge-tools` for information on configuring merge tools.
4797 merge-tools` for information on configuring merge tools.
4798
4798
4799 The resolve command can be used in the following ways:
4799 The resolve command can be used in the following ways:
4800
4800
4801 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
4801 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
4802 the specified files, discarding any previous merge attempts. Re-merging
4802 the specified files, discarding any previous merge attempts. Re-merging
4803 is not performed for files already marked as resolved. Use ``--all/-a``
4803 is not performed for files already marked as resolved. Use ``--all/-a``
4804 to select all unresolved files. ``--tool`` can be used to specify
4804 to select all unresolved files. ``--tool`` can be used to specify
4805 the merge tool used for the given files. It overrides the HGMERGE
4805 the merge tool used for the given files. It overrides the HGMERGE
4806 environment variable and your configuration files. Previous file
4806 environment variable and your configuration files. Previous file
4807 contents are saved with a ``.orig`` suffix.
4807 contents are saved with a ``.orig`` suffix.
4808
4808
4809 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4809 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4810 (e.g. after having manually fixed-up the files). The default is
4810 (e.g. after having manually fixed-up the files). The default is
4811 to mark all unresolved files.
4811 to mark all unresolved files.
4812
4812
4813 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4813 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4814 default is to mark all resolved files.
4814 default is to mark all resolved files.
4815
4815
4816 - :hg:`resolve -l`: list files which had or still have conflicts.
4816 - :hg:`resolve -l`: list files which had or still have conflicts.
4817 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4817 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4818 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4818 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4819 the list. See :hg:`help filesets` for details.
4819 the list. See :hg:`help filesets` for details.
4820
4820
4821 .. note::
4821 .. note::
4822
4822
4823 Mercurial will not let you commit files with unresolved merge
4823 Mercurial will not let you commit files with unresolved merge
4824 conflicts. You must use :hg:`resolve -m ...` before you can
4824 conflicts. You must use :hg:`resolve -m ...` before you can
4825 commit after a conflicting merge.
4825 commit after a conflicting merge.
4826
4826
4827 .. container:: verbose
4827 .. container:: verbose
4828
4828
4829 Template:
4829 Template:
4830
4830
4831 The following keywords are supported in addition to the common template
4831 The following keywords are supported in addition to the common template
4832 keywords and functions. See also :hg:`help templates`.
4832 keywords and functions. See also :hg:`help templates`.
4833
4833
4834 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
4834 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
4835 :path: String. Repository-absolute path of the file.
4835 :path: String. Repository-absolute path of the file.
4836
4836
4837 Returns 0 on success, 1 if any files fail a resolve attempt.
4837 Returns 0 on success, 1 if any files fail a resolve attempt.
4838 """
4838 """
4839
4839
4840 opts = pycompat.byteskwargs(opts)
4840 opts = pycompat.byteskwargs(opts)
4841 confirm = ui.configbool('commands', 'resolve.confirm')
4841 confirm = ui.configbool('commands', 'resolve.confirm')
4842 flaglist = 'all mark unmark list no_status re_merge'.split()
4842 flaglist = 'all mark unmark list no_status re_merge'.split()
4843 all, mark, unmark, show, nostatus, remerge = [
4843 all, mark, unmark, show, nostatus, remerge = [
4844 opts.get(o) for o in flaglist]
4844 opts.get(o) for o in flaglist]
4845
4845
4846 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
4846 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
4847 if actioncount > 1:
4847 if actioncount > 1:
4848 raise error.Abort(_("too many actions specified"))
4848 raise error.Abort(_("too many actions specified"))
4849 elif (actioncount == 0
4849 elif (actioncount == 0
4850 and ui.configbool('commands', 'resolve.explicit-re-merge')):
4850 and ui.configbool('commands', 'resolve.explicit-re-merge')):
4851 hint = _('use --mark, --unmark, --list or --re-merge')
4851 hint = _('use --mark, --unmark, --list or --re-merge')
4852 raise error.Abort(_('no action specified'), hint=hint)
4852 raise error.Abort(_('no action specified'), hint=hint)
4853 if pats and all:
4853 if pats and all:
4854 raise error.Abort(_("can't specify --all and patterns"))
4854 raise error.Abort(_("can't specify --all and patterns"))
4855 if not (all or pats or show or mark or unmark):
4855 if not (all or pats or show or mark or unmark):
4856 raise error.Abort(_('no files or directories specified'),
4856 raise error.Abort(_('no files or directories specified'),
4857 hint=('use --all to re-merge all unresolved files'))
4857 hint=('use --all to re-merge all unresolved files'))
4858
4858
4859 if confirm:
4859 if confirm:
4860 if all:
4860 if all:
4861 if ui.promptchoice(_(b're-merge all unresolved files (yn)?'
4861 if ui.promptchoice(_(b're-merge all unresolved files (yn)?'
4862 b'$$ &Yes $$ &No')):
4862 b'$$ &Yes $$ &No')):
4863 raise error.Abort(_('user quit'))
4863 raise error.Abort(_('user quit'))
4864 if mark and not pats:
4864 if mark and not pats:
4865 if ui.promptchoice(_(b'mark all unresolved files as resolved (yn)?'
4865 if ui.promptchoice(_(b'mark all unresolved files as resolved (yn)?'
4866 b'$$ &Yes $$ &No')):
4866 b'$$ &Yes $$ &No')):
4867 raise error.Abort(_('user quit'))
4867 raise error.Abort(_('user quit'))
4868 if unmark and not pats:
4868 if unmark and not pats:
4869 if ui.promptchoice(_(b'mark all resolved files as unresolved (yn)?'
4869 if ui.promptchoice(_(b'mark all resolved files as unresolved (yn)?'
4870 b'$$ &Yes $$ &No')):
4870 b'$$ &Yes $$ &No')):
4871 raise error.Abort(_('user quit'))
4871 raise error.Abort(_('user quit'))
4872
4872
4873 uipathfn = scmutil.getuipathfn(repo)
4873 uipathfn = scmutil.getuipathfn(repo)
4874
4874
4875 if show:
4875 if show:
4876 ui.pager('resolve')
4876 ui.pager('resolve')
4877 fm = ui.formatter('resolve', opts)
4877 fm = ui.formatter('resolve', opts)
4878 ms = mergemod.mergestate.read(repo)
4878 ms = mergemod.mergestate.read(repo)
4879 wctx = repo[None]
4879 wctx = repo[None]
4880 m = scmutil.match(wctx, pats, opts)
4880 m = scmutil.match(wctx, pats, opts)
4881
4881
4882 # Labels and keys based on merge state. Unresolved path conflicts show
4882 # Labels and keys based on merge state. Unresolved path conflicts show
4883 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4883 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4884 # resolved conflicts.
4884 # resolved conflicts.
4885 mergestateinfo = {
4885 mergestateinfo = {
4886 mergemod.MERGE_RECORD_UNRESOLVED: ('resolve.unresolved', 'U'),
4886 mergemod.MERGE_RECORD_UNRESOLVED: ('resolve.unresolved', 'U'),
4887 mergemod.MERGE_RECORD_RESOLVED: ('resolve.resolved', 'R'),
4887 mergemod.MERGE_RECORD_RESOLVED: ('resolve.resolved', 'R'),
4888 mergemod.MERGE_RECORD_UNRESOLVED_PATH: ('resolve.unresolved', 'P'),
4888 mergemod.MERGE_RECORD_UNRESOLVED_PATH: ('resolve.unresolved', 'P'),
4889 mergemod.MERGE_RECORD_RESOLVED_PATH: ('resolve.resolved', 'R'),
4889 mergemod.MERGE_RECORD_RESOLVED_PATH: ('resolve.resolved', 'R'),
4890 mergemod.MERGE_RECORD_DRIVER_RESOLVED: ('resolve.driverresolved',
4890 mergemod.MERGE_RECORD_DRIVER_RESOLVED: ('resolve.driverresolved',
4891 'D'),
4891 'D'),
4892 }
4892 }
4893
4893
4894 for f in ms:
4894 for f in ms:
4895 if not m(f):
4895 if not m(f):
4896 continue
4896 continue
4897
4897
4898 label, key = mergestateinfo[ms[f]]
4898 label, key = mergestateinfo[ms[f]]
4899 fm.startitem()
4899 fm.startitem()
4900 fm.context(ctx=wctx)
4900 fm.context(ctx=wctx)
4901 fm.condwrite(not nostatus, 'mergestatus', '%s ', key, label=label)
4901 fm.condwrite(not nostatus, 'mergestatus', '%s ', key, label=label)
4902 fm.data(path=f)
4902 fm.data(path=f)
4903 fm.plain('%s\n' % uipathfn(f), label=label)
4903 fm.plain('%s\n' % uipathfn(f), label=label)
4904 fm.end()
4904 fm.end()
4905 return 0
4905 return 0
4906
4906
4907 with repo.wlock():
4907 with repo.wlock():
4908 ms = mergemod.mergestate.read(repo)
4908 ms = mergemod.mergestate.read(repo)
4909
4909
4910 if not (ms.active() or repo.dirstate.p2() != nullid):
4910 if not (ms.active() or repo.dirstate.p2() != nullid):
4911 raise error.Abort(
4911 raise error.Abort(
4912 _('resolve command not applicable when not merging'))
4912 _('resolve command not applicable when not merging'))
4913
4913
4914 wctx = repo[None]
4914 wctx = repo[None]
4915
4915
4916 if (ms.mergedriver
4916 if (ms.mergedriver
4917 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED):
4917 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED):
4918 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4918 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4919 ms.commit()
4919 ms.commit()
4920 # allow mark and unmark to go through
4920 # allow mark and unmark to go through
4921 if not mark and not unmark and not proceed:
4921 if not mark and not unmark and not proceed:
4922 return 1
4922 return 1
4923
4923
4924 m = scmutil.match(wctx, pats, opts)
4924 m = scmutil.match(wctx, pats, opts)
4925 ret = 0
4925 ret = 0
4926 didwork = False
4926 didwork = False
4927 runconclude = False
4927 runconclude = False
4928
4928
4929 tocomplete = []
4929 tocomplete = []
4930 hasconflictmarkers = []
4930 hasconflictmarkers = []
4931 if mark:
4931 if mark:
4932 markcheck = ui.config('commands', 'resolve.mark-check')
4932 markcheck = ui.config('commands', 'resolve.mark-check')
4933 if markcheck not in ['warn', 'abort']:
4933 if markcheck not in ['warn', 'abort']:
4934 # Treat all invalid / unrecognized values as 'none'.
4934 # Treat all invalid / unrecognized values as 'none'.
4935 markcheck = False
4935 markcheck = False
4936 for f in ms:
4936 for f in ms:
4937 if not m(f):
4937 if not m(f):
4938 continue
4938 continue
4939
4939
4940 didwork = True
4940 didwork = True
4941
4941
4942 # don't let driver-resolved files be marked, and run the conclude
4942 # don't let driver-resolved files be marked, and run the conclude
4943 # step if asked to resolve
4943 # step if asked to resolve
4944 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
4944 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
4945 exact = m.exact(f)
4945 exact = m.exact(f)
4946 if mark:
4946 if mark:
4947 if exact:
4947 if exact:
4948 ui.warn(_('not marking %s as it is driver-resolved\n')
4948 ui.warn(_('not marking %s as it is driver-resolved\n')
4949 % uipathfn(f))
4949 % uipathfn(f))
4950 elif unmark:
4950 elif unmark:
4951 if exact:
4951 if exact:
4952 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4952 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4953 % uipathfn(f))
4953 % uipathfn(f))
4954 else:
4954 else:
4955 runconclude = True
4955 runconclude = True
4956 continue
4956 continue
4957
4957
4958 # path conflicts must be resolved manually
4958 # path conflicts must be resolved manually
4959 if ms[f] in (mergemod.MERGE_RECORD_UNRESOLVED_PATH,
4959 if ms[f] in (mergemod.MERGE_RECORD_UNRESOLVED_PATH,
4960 mergemod.MERGE_RECORD_RESOLVED_PATH):
4960 mergemod.MERGE_RECORD_RESOLVED_PATH):
4961 if mark:
4961 if mark:
4962 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
4962 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
4963 elif unmark:
4963 elif unmark:
4964 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
4964 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
4965 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
4965 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
4966 ui.warn(_('%s: path conflict must be resolved manually\n')
4966 ui.warn(_('%s: path conflict must be resolved manually\n')
4967 % uipathfn(f))
4967 % uipathfn(f))
4968 continue
4968 continue
4969
4969
4970 if mark:
4970 if mark:
4971 if markcheck:
4971 if markcheck:
4972 fdata = repo.wvfs.tryread(f)
4972 fdata = repo.wvfs.tryread(f)
4973 if (filemerge.hasconflictmarkers(fdata) and
4973 if (filemerge.hasconflictmarkers(fdata) and
4974 ms[f] != mergemod.MERGE_RECORD_RESOLVED):
4974 ms[f] != mergemod.MERGE_RECORD_RESOLVED):
4975 hasconflictmarkers.append(f)
4975 hasconflictmarkers.append(f)
4976 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
4976 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
4977 elif unmark:
4977 elif unmark:
4978 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
4978 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
4979 else:
4979 else:
4980 # backup pre-resolve (merge uses .orig for its own purposes)
4980 # backup pre-resolve (merge uses .orig for its own purposes)
4981 a = repo.wjoin(f)
4981 a = repo.wjoin(f)
4982 try:
4982 try:
4983 util.copyfile(a, a + ".resolve")
4983 util.copyfile(a, a + ".resolve")
4984 except (IOError, OSError) as inst:
4984 except (IOError, OSError) as inst:
4985 if inst.errno != errno.ENOENT:
4985 if inst.errno != errno.ENOENT:
4986 raise
4986 raise
4987
4987
4988 try:
4988 try:
4989 # preresolve file
4989 # preresolve file
4990 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4990 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4991 with ui.configoverride(overrides, 'resolve'):
4991 with ui.configoverride(overrides, 'resolve'):
4992 complete, r = ms.preresolve(f, wctx)
4992 complete, r = ms.preresolve(f, wctx)
4993 if not complete:
4993 if not complete:
4994 tocomplete.append(f)
4994 tocomplete.append(f)
4995 elif r:
4995 elif r:
4996 ret = 1
4996 ret = 1
4997 finally:
4997 finally:
4998 ms.commit()
4998 ms.commit()
4999
4999
5000 # replace filemerge's .orig file with our resolve file, but only
5000 # replace filemerge's .orig file with our resolve file, but only
5001 # for merges that are complete
5001 # for merges that are complete
5002 if complete:
5002 if complete:
5003 try:
5003 try:
5004 util.rename(a + ".resolve",
5004 util.rename(a + ".resolve",
5005 scmutil.backuppath(ui, repo, f))
5005 scmutil.backuppath(ui, repo, f))
5006 except OSError as inst:
5006 except OSError as inst:
5007 if inst.errno != errno.ENOENT:
5007 if inst.errno != errno.ENOENT:
5008 raise
5008 raise
5009
5009
5010 if hasconflictmarkers:
5010 if hasconflictmarkers:
5011 ui.warn(_('warning: the following files still have conflict '
5011 ui.warn(_('warning: the following files still have conflict '
5012 'markers:\n') + ''.join(' ' + uipathfn(f) + '\n'
5012 'markers:\n') + ''.join(' ' + uipathfn(f) + '\n'
5013 for f in hasconflictmarkers))
5013 for f in hasconflictmarkers))
5014 if markcheck == 'abort' and not all and not pats:
5014 if markcheck == 'abort' and not all and not pats:
5015 raise error.Abort(_('conflict markers detected'),
5015 raise error.Abort(_('conflict markers detected'),
5016 hint=_('use --all to mark anyway'))
5016 hint=_('use --all to mark anyway'))
5017
5017
5018 for f in tocomplete:
5018 for f in tocomplete:
5019 try:
5019 try:
5020 # resolve file
5020 # resolve file
5021 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
5021 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
5022 with ui.configoverride(overrides, 'resolve'):
5022 with ui.configoverride(overrides, 'resolve'):
5023 r = ms.resolve(f, wctx)
5023 r = ms.resolve(f, wctx)
5024 if r:
5024 if r:
5025 ret = 1
5025 ret = 1
5026 finally:
5026 finally:
5027 ms.commit()
5027 ms.commit()
5028
5028
5029 # replace filemerge's .orig file with our resolve file
5029 # replace filemerge's .orig file with our resolve file
5030 a = repo.wjoin(f)
5030 a = repo.wjoin(f)
5031 try:
5031 try:
5032 util.rename(a + ".resolve", scmutil.backuppath(ui, repo, f))
5032 util.rename(a + ".resolve", scmutil.backuppath(ui, repo, f))
5033 except OSError as inst:
5033 except OSError as inst:
5034 if inst.errno != errno.ENOENT:
5034 if inst.errno != errno.ENOENT:
5035 raise
5035 raise
5036
5036
5037 ms.commit()
5037 ms.commit()
5038 ms.recordactions()
5038 ms.recordactions()
5039
5039
5040 if not didwork and pats:
5040 if not didwork and pats:
5041 hint = None
5041 hint = None
5042 if not any([p for p in pats if p.find(':') >= 0]):
5042 if not any([p for p in pats if p.find(':') >= 0]):
5043 pats = ['path:%s' % p for p in pats]
5043 pats = ['path:%s' % p for p in pats]
5044 m = scmutil.match(wctx, pats, opts)
5044 m = scmutil.match(wctx, pats, opts)
5045 for f in ms:
5045 for f in ms:
5046 if not m(f):
5046 if not m(f):
5047 continue
5047 continue
5048 def flag(o):
5048 def flag(o):
5049 if o == 're_merge':
5049 if o == 're_merge':
5050 return '--re-merge '
5050 return '--re-merge '
5051 return '-%s ' % o[0:1]
5051 return '-%s ' % o[0:1]
5052 flags = ''.join([flag(o) for o in flaglist if opts.get(o)])
5052 flags = ''.join([flag(o) for o in flaglist if opts.get(o)])
5053 hint = _("(try: hg resolve %s%s)\n") % (
5053 hint = _("(try: hg resolve %s%s)\n") % (
5054 flags,
5054 flags,
5055 ' '.join(pats))
5055 ' '.join(pats))
5056 break
5056 break
5057 ui.warn(_("arguments do not match paths that need resolving\n"))
5057 ui.warn(_("arguments do not match paths that need resolving\n"))
5058 if hint:
5058 if hint:
5059 ui.warn(hint)
5059 ui.warn(hint)
5060 elif ms.mergedriver and ms.mdstate() != 's':
5060 elif ms.mergedriver and ms.mdstate() != 's':
5061 # run conclude step when either a driver-resolved file is requested
5061 # run conclude step when either a driver-resolved file is requested
5062 # or there are no driver-resolved files
5062 # or there are no driver-resolved files
5063 # we can't use 'ret' to determine whether any files are unresolved
5063 # we can't use 'ret' to determine whether any files are unresolved
5064 # because we might not have tried to resolve some
5064 # because we might not have tried to resolve some
5065 if ((runconclude or not list(ms.driverresolved()))
5065 if ((runconclude or not list(ms.driverresolved()))
5066 and not list(ms.unresolved())):
5066 and not list(ms.unresolved())):
5067 proceed = mergemod.driverconclude(repo, ms, wctx)
5067 proceed = mergemod.driverconclude(repo, ms, wctx)
5068 ms.commit()
5068 ms.commit()
5069 if not proceed:
5069 if not proceed:
5070 return 1
5070 return 1
5071
5071
5072 # Nudge users into finishing an unfinished operation
5072 # Nudge users into finishing an unfinished operation
5073 unresolvedf = list(ms.unresolved())
5073 unresolvedf = list(ms.unresolved())
5074 driverresolvedf = list(ms.driverresolved())
5074 driverresolvedf = list(ms.driverresolved())
5075 if not unresolvedf and not driverresolvedf:
5075 if not unresolvedf and not driverresolvedf:
5076 ui.status(_('(no more unresolved files)\n'))
5076 ui.status(_('(no more unresolved files)\n'))
5077 cmdutil.checkafterresolved(repo)
5077 cmdutil.checkafterresolved(repo)
5078 elif not unresolvedf:
5078 elif not unresolvedf:
5079 ui.status(_('(no more unresolved files -- '
5079 ui.status(_('(no more unresolved files -- '
5080 'run "hg resolve --all" to conclude)\n'))
5080 'run "hg resolve --all" to conclude)\n'))
5081
5081
5082 return ret
5082 return ret
5083
5083
5084 @command('revert',
5084 @command('revert',
5085 [('a', 'all', None, _('revert all changes when no arguments given')),
5085 [('a', 'all', None, _('revert all changes when no arguments given')),
5086 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5086 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5087 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5087 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5088 ('C', 'no-backup', None, _('do not save backup copies of files')),
5088 ('C', 'no-backup', None, _('do not save backup copies of files')),
5089 ('i', 'interactive', None, _('interactively select the changes')),
5089 ('i', 'interactive', None, _('interactively select the changes')),
5090 ] + walkopts + dryrunopts,
5090 ] + walkopts + dryrunopts,
5091 _('[OPTION]... [-r REV] [NAME]...'),
5091 _('[OPTION]... [-r REV] [NAME]...'),
5092 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5092 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5093 def revert(ui, repo, *pats, **opts):
5093 def revert(ui, repo, *pats, **opts):
5094 """restore files to their checkout state
5094 """restore files to their checkout state
5095
5095
5096 .. note::
5096 .. note::
5097
5097
5098 To check out earlier revisions, you should use :hg:`update REV`.
5098 To check out earlier revisions, you should use :hg:`update REV`.
5099 To cancel an uncommitted merge (and lose your changes),
5099 To cancel an uncommitted merge (and lose your changes),
5100 use :hg:`merge --abort`.
5100 use :hg:`merge --abort`.
5101
5101
5102 With no revision specified, revert the specified files or directories
5102 With no revision specified, revert the specified files or directories
5103 to the contents they had in the parent of the working directory.
5103 to the contents they had in the parent of the working directory.
5104 This restores the contents of files to an unmodified
5104 This restores the contents of files to an unmodified
5105 state and unschedules adds, removes, copies, and renames. If the
5105 state and unschedules adds, removes, copies, and renames. If the
5106 working directory has two parents, you must explicitly specify a
5106 working directory has two parents, you must explicitly specify a
5107 revision.
5107 revision.
5108
5108
5109 Using the -r/--rev or -d/--date options, revert the given files or
5109 Using the -r/--rev or -d/--date options, revert the given files or
5110 directories to their states as of a specific revision. Because
5110 directories to their states as of a specific revision. Because
5111 revert does not change the working directory parents, this will
5111 revert does not change the working directory parents, this will
5112 cause these files to appear modified. This can be helpful to "back
5112 cause these files to appear modified. This can be helpful to "back
5113 out" some or all of an earlier change. See :hg:`backout` for a
5113 out" some or all of an earlier change. See :hg:`backout` for a
5114 related method.
5114 related method.
5115
5115
5116 Modified files are saved with a .orig suffix before reverting.
5116 Modified files are saved with a .orig suffix before reverting.
5117 To disable these backups, use --no-backup. It is possible to store
5117 To disable these backups, use --no-backup. It is possible to store
5118 the backup files in a custom directory relative to the root of the
5118 the backup files in a custom directory relative to the root of the
5119 repository by setting the ``ui.origbackuppath`` configuration
5119 repository by setting the ``ui.origbackuppath`` configuration
5120 option.
5120 option.
5121
5121
5122 See :hg:`help dates` for a list of formats valid for -d/--date.
5122 See :hg:`help dates` for a list of formats valid for -d/--date.
5123
5123
5124 See :hg:`help backout` for a way to reverse the effect of an
5124 See :hg:`help backout` for a way to reverse the effect of an
5125 earlier changeset.
5125 earlier changeset.
5126
5126
5127 Returns 0 on success.
5127 Returns 0 on success.
5128 """
5128 """
5129
5129
5130 opts = pycompat.byteskwargs(opts)
5130 opts = pycompat.byteskwargs(opts)
5131 if opts.get("date"):
5131 if opts.get("date"):
5132 if opts.get("rev"):
5132 if opts.get("rev"):
5133 raise error.Abort(_("you can't specify a revision and a date"))
5133 raise error.Abort(_("you can't specify a revision and a date"))
5134 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5134 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5135
5135
5136 parent, p2 = repo.dirstate.parents()
5136 parent, p2 = repo.dirstate.parents()
5137 if not opts.get('rev') and p2 != nullid:
5137 if not opts.get('rev') and p2 != nullid:
5138 # revert after merge is a trap for new users (issue2915)
5138 # revert after merge is a trap for new users (issue2915)
5139 raise error.Abort(_('uncommitted merge with no revision specified'),
5139 raise error.Abort(_('uncommitted merge with no revision specified'),
5140 hint=_("use 'hg update' or see 'hg help revert'"))
5140 hint=_("use 'hg update' or see 'hg help revert'"))
5141
5141
5142 rev = opts.get('rev')
5142 rev = opts.get('rev')
5143 if rev:
5143 if rev:
5144 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
5144 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
5145 ctx = scmutil.revsingle(repo, rev)
5145 ctx = scmutil.revsingle(repo, rev)
5146
5146
5147 if (not (pats or opts.get('include') or opts.get('exclude') or
5147 if (not (pats or opts.get('include') or opts.get('exclude') or
5148 opts.get('all') or opts.get('interactive'))):
5148 opts.get('all') or opts.get('interactive'))):
5149 msg = _("no files or directories specified")
5149 msg = _("no files or directories specified")
5150 if p2 != nullid:
5150 if p2 != nullid:
5151 hint = _("uncommitted merge, use --all to discard all changes,"
5151 hint = _("uncommitted merge, use --all to discard all changes,"
5152 " or 'hg update -C .' to abort the merge")
5152 " or 'hg update -C .' to abort the merge")
5153 raise error.Abort(msg, hint=hint)
5153 raise error.Abort(msg, hint=hint)
5154 dirty = any(repo.status())
5154 dirty = any(repo.status())
5155 node = ctx.node()
5155 node = ctx.node()
5156 if node != parent:
5156 if node != parent:
5157 if dirty:
5157 if dirty:
5158 hint = _("uncommitted changes, use --all to discard all"
5158 hint = _("uncommitted changes, use --all to discard all"
5159 " changes, or 'hg update %d' to update") % ctx.rev()
5159 " changes, or 'hg update %d' to update") % ctx.rev()
5160 else:
5160 else:
5161 hint = _("use --all to revert all files,"
5161 hint = _("use --all to revert all files,"
5162 " or 'hg update %d' to update") % ctx.rev()
5162 " or 'hg update %d' to update") % ctx.rev()
5163 elif dirty:
5163 elif dirty:
5164 hint = _("uncommitted changes, use --all to discard all changes")
5164 hint = _("uncommitted changes, use --all to discard all changes")
5165 else:
5165 else:
5166 hint = _("use --all to revert all files")
5166 hint = _("use --all to revert all files")
5167 raise error.Abort(msg, hint=hint)
5167 raise error.Abort(msg, hint=hint)
5168
5168
5169 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats,
5169 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats,
5170 **pycompat.strkwargs(opts))
5170 **pycompat.strkwargs(opts))
5171
5171
5172 @command(
5172 @command(
5173 'rollback',
5173 'rollback',
5174 dryrunopts + [('f', 'force', False, _('ignore safety measures'))],
5174 dryrunopts + [('f', 'force', False, _('ignore safety measures'))],
5175 helpcategory=command.CATEGORY_MAINTENANCE)
5175 helpcategory=command.CATEGORY_MAINTENANCE)
5176 def rollback(ui, repo, **opts):
5176 def rollback(ui, repo, **opts):
5177 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5177 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5178
5178
5179 Please use :hg:`commit --amend` instead of rollback to correct
5179 Please use :hg:`commit --amend` instead of rollback to correct
5180 mistakes in the last commit.
5180 mistakes in the last commit.
5181
5181
5182 This command should be used with care. There is only one level of
5182 This command should be used with care. There is only one level of
5183 rollback, and there is no way to undo a rollback. It will also
5183 rollback, and there is no way to undo a rollback. It will also
5184 restore the dirstate at the time of the last transaction, losing
5184 restore the dirstate at the time of the last transaction, losing
5185 any dirstate changes since that time. This command does not alter
5185 any dirstate changes since that time. This command does not alter
5186 the working directory.
5186 the working directory.
5187
5187
5188 Transactions are used to encapsulate the effects of all commands
5188 Transactions are used to encapsulate the effects of all commands
5189 that create new changesets or propagate existing changesets into a
5189 that create new changesets or propagate existing changesets into a
5190 repository.
5190 repository.
5191
5191
5192 .. container:: verbose
5192 .. container:: verbose
5193
5193
5194 For example, the following commands are transactional, and their
5194 For example, the following commands are transactional, and their
5195 effects can be rolled back:
5195 effects can be rolled back:
5196
5196
5197 - commit
5197 - commit
5198 - import
5198 - import
5199 - pull
5199 - pull
5200 - push (with this repository as the destination)
5200 - push (with this repository as the destination)
5201 - unbundle
5201 - unbundle
5202
5202
5203 To avoid permanent data loss, rollback will refuse to rollback a
5203 To avoid permanent data loss, rollback will refuse to rollback a
5204 commit transaction if it isn't checked out. Use --force to
5204 commit transaction if it isn't checked out. Use --force to
5205 override this protection.
5205 override this protection.
5206
5206
5207 The rollback command can be entirely disabled by setting the
5207 The rollback command can be entirely disabled by setting the
5208 ``ui.rollback`` configuration setting to false. If you're here
5208 ``ui.rollback`` configuration setting to false. If you're here
5209 because you want to use rollback and it's disabled, you can
5209 because you want to use rollback and it's disabled, you can
5210 re-enable the command by setting ``ui.rollback`` to true.
5210 re-enable the command by setting ``ui.rollback`` to true.
5211
5211
5212 This command is not intended for use on public repositories. Once
5212 This command is not intended for use on public repositories. Once
5213 changes are visible for pull by other users, rolling a transaction
5213 changes are visible for pull by other users, rolling a transaction
5214 back locally is ineffective (someone else may already have pulled
5214 back locally is ineffective (someone else may already have pulled
5215 the changes). Furthermore, a race is possible with readers of the
5215 the changes). Furthermore, a race is possible with readers of the
5216 repository; for example an in-progress pull from the repository
5216 repository; for example an in-progress pull from the repository
5217 may fail if a rollback is performed.
5217 may fail if a rollback is performed.
5218
5218
5219 Returns 0 on success, 1 if no rollback data is available.
5219 Returns 0 on success, 1 if no rollback data is available.
5220 """
5220 """
5221 if not ui.configbool('ui', 'rollback'):
5221 if not ui.configbool('ui', 'rollback'):
5222 raise error.Abort(_('rollback is disabled because it is unsafe'),
5222 raise error.Abort(_('rollback is disabled because it is unsafe'),
5223 hint=('see `hg help -v rollback` for information'))
5223 hint=('see `hg help -v rollback` for information'))
5224 return repo.rollback(dryrun=opts.get(r'dry_run'),
5224 return repo.rollback(dryrun=opts.get(r'dry_run'),
5225 force=opts.get(r'force'))
5225 force=opts.get(r'force'))
5226
5226
5227 @command(
5227 @command(
5228 'root', [], intents={INTENT_READONLY},
5228 'root', [], intents={INTENT_READONLY},
5229 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5229 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5230 def root(ui, repo):
5230 def root(ui, repo):
5231 """print the root (top) of the current working directory
5231 """print the root (top) of the current working directory
5232
5232
5233 Print the root directory of the current repository.
5233 Print the root directory of the current repository.
5234
5234
5235 Returns 0 on success.
5235 Returns 0 on success.
5236 """
5236 """
5237 ui.write(repo.root + "\n")
5237 ui.write(repo.root + "\n")
5238
5238
5239 @command('serve',
5239 @command('serve',
5240 [('A', 'accesslog', '', _('name of access log file to write to'),
5240 [('A', 'accesslog', '', _('name of access log file to write to'),
5241 _('FILE')),
5241 _('FILE')),
5242 ('d', 'daemon', None, _('run server in background')),
5242 ('d', 'daemon', None, _('run server in background')),
5243 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
5243 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
5244 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5244 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5245 # use string type, then we can check if something was passed
5245 # use string type, then we can check if something was passed
5246 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5246 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5247 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5247 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5248 _('ADDR')),
5248 _('ADDR')),
5249 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5249 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5250 _('PREFIX')),
5250 _('PREFIX')),
5251 ('n', 'name', '',
5251 ('n', 'name', '',
5252 _('name to show in web pages (default: working directory)'), _('NAME')),
5252 _('name to show in web pages (default: working directory)'), _('NAME')),
5253 ('', 'web-conf', '',
5253 ('', 'web-conf', '',
5254 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
5254 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
5255 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5255 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5256 _('FILE')),
5256 _('FILE')),
5257 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5257 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5258 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
5258 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
5259 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
5259 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
5260 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5260 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5261 ('', 'style', '', _('template style to use'), _('STYLE')),
5261 ('', 'style', '', _('template style to use'), _('STYLE')),
5262 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5262 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5263 ('', 'certificate', '', _('SSL certificate file'), _('FILE')),
5263 ('', 'certificate', '', _('SSL certificate file'), _('FILE')),
5264 ('', 'print-url', None, _('start and print only the URL'))]
5264 ('', 'print-url', None, _('start and print only the URL'))]
5265 + subrepoopts,
5265 + subrepoopts,
5266 _('[OPTION]...'),
5266 _('[OPTION]...'),
5267 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5267 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5268 helpbasic=True, optionalrepo=True)
5268 helpbasic=True, optionalrepo=True)
5269 def serve(ui, repo, **opts):
5269 def serve(ui, repo, **opts):
5270 """start stand-alone webserver
5270 """start stand-alone webserver
5271
5271
5272 Start a local HTTP repository browser and pull server. You can use
5272 Start a local HTTP repository browser and pull server. You can use
5273 this for ad-hoc sharing and browsing of repositories. It is
5273 this for ad-hoc sharing and browsing of repositories. It is
5274 recommended to use a real web server to serve a repository for
5274 recommended to use a real web server to serve a repository for
5275 longer periods of time.
5275 longer periods of time.
5276
5276
5277 Please note that the server does not implement access control.
5277 Please note that the server does not implement access control.
5278 This means that, by default, anybody can read from the server and
5278 This means that, by default, anybody can read from the server and
5279 nobody can write to it by default. Set the ``web.allow-push``
5279 nobody can write to it by default. Set the ``web.allow-push``
5280 option to ``*`` to allow everybody to push to the server. You
5280 option to ``*`` to allow everybody to push to the server. You
5281 should use a real web server if you need to authenticate users.
5281 should use a real web server if you need to authenticate users.
5282
5282
5283 By default, the server logs accesses to stdout and errors to
5283 By default, the server logs accesses to stdout and errors to
5284 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5284 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5285 files.
5285 files.
5286
5286
5287 To have the server choose a free port number to listen on, specify
5287 To have the server choose a free port number to listen on, specify
5288 a port number of 0; in this case, the server will print the port
5288 a port number of 0; in this case, the server will print the port
5289 number it uses.
5289 number it uses.
5290
5290
5291 Returns 0 on success.
5291 Returns 0 on success.
5292 """
5292 """
5293
5293
5294 opts = pycompat.byteskwargs(opts)
5294 opts = pycompat.byteskwargs(opts)
5295 if opts["stdio"] and opts["cmdserver"]:
5295 if opts["stdio"] and opts["cmdserver"]:
5296 raise error.Abort(_("cannot use --stdio with --cmdserver"))
5296 raise error.Abort(_("cannot use --stdio with --cmdserver"))
5297 if opts["print_url"] and ui.verbose:
5297 if opts["print_url"] and ui.verbose:
5298 raise error.Abort(_("cannot use --print-url with --verbose"))
5298 raise error.Abort(_("cannot use --print-url with --verbose"))
5299
5299
5300 if opts["stdio"]:
5300 if opts["stdio"]:
5301 if repo is None:
5301 if repo is None:
5302 raise error.RepoError(_("there is no Mercurial repository here"
5302 raise error.RepoError(_("there is no Mercurial repository here"
5303 " (.hg not found)"))
5303 " (.hg not found)"))
5304 s = wireprotoserver.sshserver(ui, repo)
5304 s = wireprotoserver.sshserver(ui, repo)
5305 s.serve_forever()
5305 s.serve_forever()
5306
5306
5307 service = server.createservice(ui, repo, opts)
5307 service = server.createservice(ui, repo, opts)
5308 return server.runservice(opts, initfn=service.init, runfn=service.run)
5308 return server.runservice(opts, initfn=service.init, runfn=service.run)
5309
5309
5310 _NOTTERSE = 'nothing'
5310 _NOTTERSE = 'nothing'
5311
5311
5312 @command('status|st',
5312 @command('status|st',
5313 [('A', 'all', None, _('show status of all files')),
5313 [('A', 'all', None, _('show status of all files')),
5314 ('m', 'modified', None, _('show only modified files')),
5314 ('m', 'modified', None, _('show only modified files')),
5315 ('a', 'added', None, _('show only added files')),
5315 ('a', 'added', None, _('show only added files')),
5316 ('r', 'removed', None, _('show only removed files')),
5316 ('r', 'removed', None, _('show only removed files')),
5317 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5317 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5318 ('c', 'clean', None, _('show only files without changes')),
5318 ('c', 'clean', None, _('show only files without changes')),
5319 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5319 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5320 ('i', 'ignored', None, _('show only ignored files')),
5320 ('i', 'ignored', None, _('show only ignored files')),
5321 ('n', 'no-status', None, _('hide status prefix')),
5321 ('n', 'no-status', None, _('hide status prefix')),
5322 ('t', 'terse', _NOTTERSE, _('show the terse output (EXPERIMENTAL)')),
5322 ('t', 'terse', _NOTTERSE, _('show the terse output (EXPERIMENTAL)')),
5323 ('C', 'copies', None, _('show source of copied files')),
5323 ('C', 'copies', None, _('show source of copied files')),
5324 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5324 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5325 ('', 'rev', [], _('show difference from revision'), _('REV')),
5325 ('', 'rev', [], _('show difference from revision'), _('REV')),
5326 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5326 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5327 ] + walkopts + subrepoopts + formatteropts,
5327 ] + walkopts + subrepoopts + formatteropts,
5328 _('[OPTION]... [FILE]...'),
5328 _('[OPTION]... [FILE]...'),
5329 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5329 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5330 helpbasic=True, inferrepo=True,
5330 helpbasic=True, inferrepo=True,
5331 intents={INTENT_READONLY})
5331 intents={INTENT_READONLY})
5332 def status(ui, repo, *pats, **opts):
5332 def status(ui, repo, *pats, **opts):
5333 """show changed files in the working directory
5333 """show changed files in the working directory
5334
5334
5335 Show status of files in the repository. If names are given, only
5335 Show status of files in the repository. If names are given, only
5336 files that match are shown. Files that are clean or ignored or
5336 files that match are shown. Files that are clean or ignored or
5337 the source of a copy/move operation, are not listed unless
5337 the source of a copy/move operation, are not listed unless
5338 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5338 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5339 Unless options described with "show only ..." are given, the
5339 Unless options described with "show only ..." are given, the
5340 options -mardu are used.
5340 options -mardu are used.
5341
5341
5342 Option -q/--quiet hides untracked (unknown and ignored) files
5342 Option -q/--quiet hides untracked (unknown and ignored) files
5343 unless explicitly requested with -u/--unknown or -i/--ignored.
5343 unless explicitly requested with -u/--unknown or -i/--ignored.
5344
5344
5345 .. note::
5345 .. note::
5346
5346
5347 :hg:`status` may appear to disagree with diff if permissions have
5347 :hg:`status` may appear to disagree with diff if permissions have
5348 changed or a merge has occurred. The standard diff format does
5348 changed or a merge has occurred. The standard diff format does
5349 not report permission changes and diff only reports changes
5349 not report permission changes and diff only reports changes
5350 relative to one merge parent.
5350 relative to one merge parent.
5351
5351
5352 If one revision is given, it is used as the base revision.
5352 If one revision is given, it is used as the base revision.
5353 If two revisions are given, the differences between them are
5353 If two revisions are given, the differences between them are
5354 shown. The --change option can also be used as a shortcut to list
5354 shown. The --change option can also be used as a shortcut to list
5355 the changed files of a revision from its first parent.
5355 the changed files of a revision from its first parent.
5356
5356
5357 The codes used to show the status of files are::
5357 The codes used to show the status of files are::
5358
5358
5359 M = modified
5359 M = modified
5360 A = added
5360 A = added
5361 R = removed
5361 R = removed
5362 C = clean
5362 C = clean
5363 ! = missing (deleted by non-hg command, but still tracked)
5363 ! = missing (deleted by non-hg command, but still tracked)
5364 ? = not tracked
5364 ? = not tracked
5365 I = ignored
5365 I = ignored
5366 = origin of the previous file (with --copies)
5366 = origin of the previous file (with --copies)
5367
5367
5368 .. container:: verbose
5368 .. container:: verbose
5369
5369
5370 The -t/--terse option abbreviates the output by showing only the directory
5370 The -t/--terse option abbreviates the output by showing only the directory
5371 name if all the files in it share the same status. The option takes an
5371 name if all the files in it share the same status. The option takes an
5372 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
5372 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
5373 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
5373 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
5374 for 'ignored' and 'c' for clean.
5374 for 'ignored' and 'c' for clean.
5375
5375
5376 It abbreviates only those statuses which are passed. Note that clean and
5376 It abbreviates only those statuses which are passed. Note that clean and
5377 ignored files are not displayed with '--terse ic' unless the -c/--clean
5377 ignored files are not displayed with '--terse ic' unless the -c/--clean
5378 and -i/--ignored options are also used.
5378 and -i/--ignored options are also used.
5379
5379
5380 The -v/--verbose option shows information when the repository is in an
5380 The -v/--verbose option shows information when the repository is in an
5381 unfinished merge, shelve, rebase state etc. You can have this behavior
5381 unfinished merge, shelve, rebase state etc. You can have this behavior
5382 turned on by default by enabling the ``commands.status.verbose`` option.
5382 turned on by default by enabling the ``commands.status.verbose`` option.
5383
5383
5384 You can skip displaying some of these states by setting
5384 You can skip displaying some of these states by setting
5385 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
5385 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
5386 'histedit', 'merge', 'rebase', or 'unshelve'.
5386 'histedit', 'merge', 'rebase', or 'unshelve'.
5387
5387
5388 Template:
5388 Template:
5389
5389
5390 The following keywords are supported in addition to the common template
5390 The following keywords are supported in addition to the common template
5391 keywords and functions. See also :hg:`help templates`.
5391 keywords and functions. See also :hg:`help templates`.
5392
5392
5393 :path: String. Repository-absolute path of the file.
5393 :path: String. Repository-absolute path of the file.
5394 :source: String. Repository-absolute path of the file originated from.
5394 :source: String. Repository-absolute path of the file originated from.
5395 Available if ``--copies`` is specified.
5395 Available if ``--copies`` is specified.
5396 :status: String. Character denoting file's status.
5396 :status: String. Character denoting file's status.
5397
5397
5398 Examples:
5398 Examples:
5399
5399
5400 - show changes in the working directory relative to a
5400 - show changes in the working directory relative to a
5401 changeset::
5401 changeset::
5402
5402
5403 hg status --rev 9353
5403 hg status --rev 9353
5404
5404
5405 - show changes in the working directory relative to the
5405 - show changes in the working directory relative to the
5406 current directory (see :hg:`help patterns` for more information)::
5406 current directory (see :hg:`help patterns` for more information)::
5407
5407
5408 hg status re:
5408 hg status re:
5409
5409
5410 - show all changes including copies in an existing changeset::
5410 - show all changes including copies in an existing changeset::
5411
5411
5412 hg status --copies --change 9353
5412 hg status --copies --change 9353
5413
5413
5414 - get a NUL separated list of added files, suitable for xargs::
5414 - get a NUL separated list of added files, suitable for xargs::
5415
5415
5416 hg status -an0
5416 hg status -an0
5417
5417
5418 - show more information about the repository status, abbreviating
5418 - show more information about the repository status, abbreviating
5419 added, removed, modified, deleted, and untracked paths::
5419 added, removed, modified, deleted, and untracked paths::
5420
5420
5421 hg status -v -t mardu
5421 hg status -v -t mardu
5422
5422
5423 Returns 0 on success.
5423 Returns 0 on success.
5424
5424
5425 """
5425 """
5426
5426
5427 opts = pycompat.byteskwargs(opts)
5427 opts = pycompat.byteskwargs(opts)
5428 revs = opts.get('rev')
5428 revs = opts.get('rev')
5429 change = opts.get('change')
5429 change = opts.get('change')
5430 terse = opts.get('terse')
5430 terse = opts.get('terse')
5431 if terse is _NOTTERSE:
5431 if terse is _NOTTERSE:
5432 if revs:
5432 if revs:
5433 terse = ''
5433 terse = ''
5434 else:
5434 else:
5435 terse = ui.config('commands', 'status.terse')
5435 terse = ui.config('commands', 'status.terse')
5436
5436
5437 if revs and change:
5437 if revs and change:
5438 msg = _('cannot specify --rev and --change at the same time')
5438 msg = _('cannot specify --rev and --change at the same time')
5439 raise error.Abort(msg)
5439 raise error.Abort(msg)
5440 elif revs and terse:
5440 elif revs and terse:
5441 msg = _('cannot use --terse with --rev')
5441 msg = _('cannot use --terse with --rev')
5442 raise error.Abort(msg)
5442 raise error.Abort(msg)
5443 elif change:
5443 elif change:
5444 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
5444 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
5445 ctx2 = scmutil.revsingle(repo, change, None)
5445 ctx2 = scmutil.revsingle(repo, change, None)
5446 ctx1 = ctx2.p1()
5446 ctx1 = ctx2.p1()
5447 else:
5447 else:
5448 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
5448 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
5449 ctx1, ctx2 = scmutil.revpair(repo, revs)
5449 ctx1, ctx2 = scmutil.revpair(repo, revs)
5450
5450
5451 forcerelativevalue = None
5451 forcerelativevalue = None
5452 if ui.hasconfig('commands', 'status.relative'):
5452 if ui.hasconfig('commands', 'status.relative'):
5453 forcerelativevalue = ui.configbool('commands', 'status.relative')
5453 forcerelativevalue = ui.configbool('commands', 'status.relative')
5454 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats),
5454 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats),
5455 forcerelativevalue=forcerelativevalue)
5455 forcerelativevalue=forcerelativevalue)
5456
5456
5457 if opts.get('print0'):
5457 if opts.get('print0'):
5458 end = '\0'
5458 end = '\0'
5459 else:
5459 else:
5460 end = '\n'
5460 end = '\n'
5461 copy = {}
5461 copy = {}
5462 states = 'modified added removed deleted unknown ignored clean'.split()
5462 states = 'modified added removed deleted unknown ignored clean'.split()
5463 show = [k for k in states if opts.get(k)]
5463 show = [k for k in states if opts.get(k)]
5464 if opts.get('all'):
5464 if opts.get('all'):
5465 show += ui.quiet and (states[:4] + ['clean']) or states
5465 show += ui.quiet and (states[:4] + ['clean']) or states
5466
5466
5467 if not show:
5467 if not show:
5468 if ui.quiet:
5468 if ui.quiet:
5469 show = states[:4]
5469 show = states[:4]
5470 else:
5470 else:
5471 show = states[:5]
5471 show = states[:5]
5472
5472
5473 m = scmutil.match(ctx2, pats, opts)
5473 m = scmutil.match(ctx2, pats, opts)
5474 if terse:
5474 if terse:
5475 # we need to compute clean and unknown to terse
5475 # we need to compute clean and unknown to terse
5476 stat = repo.status(ctx1.node(), ctx2.node(), m,
5476 stat = repo.status(ctx1.node(), ctx2.node(), m,
5477 'ignored' in show or 'i' in terse,
5477 'ignored' in show or 'i' in terse,
5478 clean=True, unknown=True,
5478 clean=True, unknown=True,
5479 listsubrepos=opts.get('subrepos'))
5479 listsubrepos=opts.get('subrepos'))
5480
5480
5481 stat = cmdutil.tersedir(stat, terse)
5481 stat = cmdutil.tersedir(stat, terse)
5482 else:
5482 else:
5483 stat = repo.status(ctx1.node(), ctx2.node(), m,
5483 stat = repo.status(ctx1.node(), ctx2.node(), m,
5484 'ignored' in show, 'clean' in show,
5484 'ignored' in show, 'clean' in show,
5485 'unknown' in show, opts.get('subrepos'))
5485 'unknown' in show, opts.get('subrepos'))
5486
5486
5487 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
5487 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
5488
5488
5489 if (opts.get('all') or opts.get('copies')
5489 if (opts.get('all') or opts.get('copies')
5490 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5490 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5491 copy = copies.pathcopies(ctx1, ctx2, m)
5491 copy = copies.pathcopies(ctx1, ctx2, m)
5492
5492
5493 ui.pager('status')
5493 ui.pager('status')
5494 fm = ui.formatter('status', opts)
5494 fm = ui.formatter('status', opts)
5495 fmt = '%s' + end
5495 fmt = '%s' + end
5496 showchar = not opts.get('no_status')
5496 showchar = not opts.get('no_status')
5497
5497
5498 for state, char, files in changestates:
5498 for state, char, files in changestates:
5499 if state in show:
5499 if state in show:
5500 label = 'status.' + state
5500 label = 'status.' + state
5501 for f in files:
5501 for f in files:
5502 fm.startitem()
5502 fm.startitem()
5503 fm.context(ctx=ctx2)
5503 fm.context(ctx=ctx2)
5504 fm.data(path=f)
5504 fm.data(path=f)
5505 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5505 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5506 fm.plain(fmt % uipathfn(f), label=label)
5506 fm.plain(fmt % uipathfn(f), label=label)
5507 if f in copy:
5507 if f in copy:
5508 fm.data(source=copy[f])
5508 fm.data(source=copy[f])
5509 fm.plain((' %s' + end) % uipathfn(copy[f]),
5509 fm.plain((' %s' + end) % uipathfn(copy[f]),
5510 label='status.copied')
5510 label='status.copied')
5511
5511
5512 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
5512 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
5513 and not ui.plain()):
5513 and not ui.plain()):
5514 cmdutil.morestatus(repo, fm)
5514 cmdutil.morestatus(repo, fm)
5515 fm.end()
5515 fm.end()
5516
5516
5517 @command('summary|sum',
5517 @command('summary|sum',
5518 [('', 'remote', None, _('check for push and pull'))],
5518 [('', 'remote', None, _('check for push and pull'))],
5519 '[--remote]',
5519 '[--remote]',
5520 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5520 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5521 helpbasic=True,
5521 helpbasic=True,
5522 intents={INTENT_READONLY})
5522 intents={INTENT_READONLY})
5523 def summary(ui, repo, **opts):
5523 def summary(ui, repo, **opts):
5524 """summarize working directory state
5524 """summarize working directory state
5525
5525
5526 This generates a brief summary of the working directory state,
5526 This generates a brief summary of the working directory state,
5527 including parents, branch, commit status, phase and available updates.
5527 including parents, branch, commit status, phase and available updates.
5528
5528
5529 With the --remote option, this will check the default paths for
5529 With the --remote option, this will check the default paths for
5530 incoming and outgoing changes. This can be time-consuming.
5530 incoming and outgoing changes. This can be time-consuming.
5531
5531
5532 Returns 0 on success.
5532 Returns 0 on success.
5533 """
5533 """
5534
5534
5535 opts = pycompat.byteskwargs(opts)
5535 opts = pycompat.byteskwargs(opts)
5536 ui.pager('summary')
5536 ui.pager('summary')
5537 ctx = repo[None]
5537 ctx = repo[None]
5538 parents = ctx.parents()
5538 parents = ctx.parents()
5539 pnode = parents[0].node()
5539 pnode = parents[0].node()
5540 marks = []
5540 marks = []
5541
5541
5542 try:
5542 try:
5543 ms = mergemod.mergestate.read(repo)
5543 ms = mergemod.mergestate.read(repo)
5544 except error.UnsupportedMergeRecords as e:
5544 except error.UnsupportedMergeRecords as e:
5545 s = ' '.join(e.recordtypes)
5545 s = ' '.join(e.recordtypes)
5546 ui.warn(
5546 ui.warn(
5547 _('warning: merge state has unsupported record types: %s\n') % s)
5547 _('warning: merge state has unsupported record types: %s\n') % s)
5548 unresolved = []
5548 unresolved = []
5549 else:
5549 else:
5550 unresolved = list(ms.unresolved())
5550 unresolved = list(ms.unresolved())
5551
5551
5552 for p in parents:
5552 for p in parents:
5553 # label with log.changeset (instead of log.parent) since this
5553 # label with log.changeset (instead of log.parent) since this
5554 # shows a working directory parent *changeset*:
5554 # shows a working directory parent *changeset*:
5555 # i18n: column positioning for "hg summary"
5555 # i18n: column positioning for "hg summary"
5556 ui.write(_('parent: %d:%s ') % (p.rev(), p),
5556 ui.write(_('parent: %d:%s ') % (p.rev(), p),
5557 label=logcmdutil.changesetlabels(p))
5557 label=logcmdutil.changesetlabels(p))
5558 ui.write(' '.join(p.tags()), label='log.tag')
5558 ui.write(' '.join(p.tags()), label='log.tag')
5559 if p.bookmarks():
5559 if p.bookmarks():
5560 marks.extend(p.bookmarks())
5560 marks.extend(p.bookmarks())
5561 if p.rev() == -1:
5561 if p.rev() == -1:
5562 if not len(repo):
5562 if not len(repo):
5563 ui.write(_(' (empty repository)'))
5563 ui.write(_(' (empty repository)'))
5564 else:
5564 else:
5565 ui.write(_(' (no revision checked out)'))
5565 ui.write(_(' (no revision checked out)'))
5566 if p.obsolete():
5566 if p.obsolete():
5567 ui.write(_(' (obsolete)'))
5567 ui.write(_(' (obsolete)'))
5568 if p.isunstable():
5568 if p.isunstable():
5569 instabilities = (ui.label(instability, 'trouble.%s' % instability)
5569 instabilities = (ui.label(instability, 'trouble.%s' % instability)
5570 for instability in p.instabilities())
5570 for instability in p.instabilities())
5571 ui.write(' ('
5571 ui.write(' ('
5572 + ', '.join(instabilities)
5572 + ', '.join(instabilities)
5573 + ')')
5573 + ')')
5574 ui.write('\n')
5574 ui.write('\n')
5575 if p.description():
5575 if p.description():
5576 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5576 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5577 label='log.summary')
5577 label='log.summary')
5578
5578
5579 branch = ctx.branch()
5579 branch = ctx.branch()
5580 bheads = repo.branchheads(branch)
5580 bheads = repo.branchheads(branch)
5581 # i18n: column positioning for "hg summary"
5581 # i18n: column positioning for "hg summary"
5582 m = _('branch: %s\n') % branch
5582 m = _('branch: %s\n') % branch
5583 if branch != 'default':
5583 if branch != 'default':
5584 ui.write(m, label='log.branch')
5584 ui.write(m, label='log.branch')
5585 else:
5585 else:
5586 ui.status(m, label='log.branch')
5586 ui.status(m, label='log.branch')
5587
5587
5588 if marks:
5588 if marks:
5589 active = repo._activebookmark
5589 active = repo._activebookmark
5590 # i18n: column positioning for "hg summary"
5590 # i18n: column positioning for "hg summary"
5591 ui.write(_('bookmarks:'), label='log.bookmark')
5591 ui.write(_('bookmarks:'), label='log.bookmark')
5592 if active is not None:
5592 if active is not None:
5593 if active in marks:
5593 if active in marks:
5594 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
5594 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
5595 marks.remove(active)
5595 marks.remove(active)
5596 else:
5596 else:
5597 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
5597 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
5598 for m in marks:
5598 for m in marks:
5599 ui.write(' ' + m, label='log.bookmark')
5599 ui.write(' ' + m, label='log.bookmark')
5600 ui.write('\n', label='log.bookmark')
5600 ui.write('\n', label='log.bookmark')
5601
5601
5602 status = repo.status(unknown=True)
5602 status = repo.status(unknown=True)
5603
5603
5604 c = repo.dirstate.copies()
5604 c = repo.dirstate.copies()
5605 copied, renamed = [], []
5605 copied, renamed = [], []
5606 for d, s in c.iteritems():
5606 for d, s in c.iteritems():
5607 if s in status.removed:
5607 if s in status.removed:
5608 status.removed.remove(s)
5608 status.removed.remove(s)
5609 renamed.append(d)
5609 renamed.append(d)
5610 else:
5610 else:
5611 copied.append(d)
5611 copied.append(d)
5612 if d in status.added:
5612 if d in status.added:
5613 status.added.remove(d)
5613 status.added.remove(d)
5614
5614
5615 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5615 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5616
5616
5617 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5617 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5618 (ui.label(_('%d added'), 'status.added'), status.added),
5618 (ui.label(_('%d added'), 'status.added'), status.added),
5619 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5619 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5620 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5620 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5621 (ui.label(_('%d copied'), 'status.copied'), copied),
5621 (ui.label(_('%d copied'), 'status.copied'), copied),
5622 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5622 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5623 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5623 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5624 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5624 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5625 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5625 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5626 t = []
5626 t = []
5627 for l, s in labels:
5627 for l, s in labels:
5628 if s:
5628 if s:
5629 t.append(l % len(s))
5629 t.append(l % len(s))
5630
5630
5631 t = ', '.join(t)
5631 t = ', '.join(t)
5632 cleanworkdir = False
5632 cleanworkdir = False
5633
5633
5634 if repo.vfs.exists('graftstate'):
5634 if repo.vfs.exists('graftstate'):
5635 t += _(' (graft in progress)')
5635 t += _(' (graft in progress)')
5636 if repo.vfs.exists('updatestate'):
5636 if repo.vfs.exists('updatestate'):
5637 t += _(' (interrupted update)')
5637 t += _(' (interrupted update)')
5638 elif len(parents) > 1:
5638 elif len(parents) > 1:
5639 t += _(' (merge)')
5639 t += _(' (merge)')
5640 elif branch != parents[0].branch():
5640 elif branch != parents[0].branch():
5641 t += _(' (new branch)')
5641 t += _(' (new branch)')
5642 elif (parents[0].closesbranch() and
5642 elif (parents[0].closesbranch() and
5643 pnode in repo.branchheads(branch, closed=True)):
5643 pnode in repo.branchheads(branch, closed=True)):
5644 t += _(' (head closed)')
5644 t += _(' (head closed)')
5645 elif not (status.modified or status.added or status.removed or renamed or
5645 elif not (status.modified or status.added or status.removed or renamed or
5646 copied or subs):
5646 copied or subs):
5647 t += _(' (clean)')
5647 t += _(' (clean)')
5648 cleanworkdir = True
5648 cleanworkdir = True
5649 elif pnode not in bheads:
5649 elif pnode not in bheads:
5650 t += _(' (new branch head)')
5650 t += _(' (new branch head)')
5651
5651
5652 if parents:
5652 if parents:
5653 pendingphase = max(p.phase() for p in parents)
5653 pendingphase = max(p.phase() for p in parents)
5654 else:
5654 else:
5655 pendingphase = phases.public
5655 pendingphase = phases.public
5656
5656
5657 if pendingphase > phases.newcommitphase(ui):
5657 if pendingphase > phases.newcommitphase(ui):
5658 t += ' (%s)' % phases.phasenames[pendingphase]
5658 t += ' (%s)' % phases.phasenames[pendingphase]
5659
5659
5660 if cleanworkdir:
5660 if cleanworkdir:
5661 # i18n: column positioning for "hg summary"
5661 # i18n: column positioning for "hg summary"
5662 ui.status(_('commit: %s\n') % t.strip())
5662 ui.status(_('commit: %s\n') % t.strip())
5663 else:
5663 else:
5664 # i18n: column positioning for "hg summary"
5664 # i18n: column positioning for "hg summary"
5665 ui.write(_('commit: %s\n') % t.strip())
5665 ui.write(_('commit: %s\n') % t.strip())
5666
5666
5667 # all ancestors of branch heads - all ancestors of parent = new csets
5667 # all ancestors of branch heads - all ancestors of parent = new csets
5668 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5668 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5669 bheads))
5669 bheads))
5670
5670
5671 if new == 0:
5671 if new == 0:
5672 # i18n: column positioning for "hg summary"
5672 # i18n: column positioning for "hg summary"
5673 ui.status(_('update: (current)\n'))
5673 ui.status(_('update: (current)\n'))
5674 elif pnode not in bheads:
5674 elif pnode not in bheads:
5675 # i18n: column positioning for "hg summary"
5675 # i18n: column positioning for "hg summary"
5676 ui.write(_('update: %d new changesets (update)\n') % new)
5676 ui.write(_('update: %d new changesets (update)\n') % new)
5677 else:
5677 else:
5678 # i18n: column positioning for "hg summary"
5678 # i18n: column positioning for "hg summary"
5679 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5679 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5680 (new, len(bheads)))
5680 (new, len(bheads)))
5681
5681
5682 t = []
5682 t = []
5683 draft = len(repo.revs('draft()'))
5683 draft = len(repo.revs('draft()'))
5684 if draft:
5684 if draft:
5685 t.append(_('%d draft') % draft)
5685 t.append(_('%d draft') % draft)
5686 secret = len(repo.revs('secret()'))
5686 secret = len(repo.revs('secret()'))
5687 if secret:
5687 if secret:
5688 t.append(_('%d secret') % secret)
5688 t.append(_('%d secret') % secret)
5689
5689
5690 if draft or secret:
5690 if draft or secret:
5691 ui.status(_('phases: %s\n') % ', '.join(t))
5691 ui.status(_('phases: %s\n') % ', '.join(t))
5692
5692
5693 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5693 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5694 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5694 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5695 numtrouble = len(repo.revs(trouble + "()"))
5695 numtrouble = len(repo.revs(trouble + "()"))
5696 # We write all the possibilities to ease translation
5696 # We write all the possibilities to ease translation
5697 troublemsg = {
5697 troublemsg = {
5698 "orphan": _("orphan: %d changesets"),
5698 "orphan": _("orphan: %d changesets"),
5699 "contentdivergent": _("content-divergent: %d changesets"),
5699 "contentdivergent": _("content-divergent: %d changesets"),
5700 "phasedivergent": _("phase-divergent: %d changesets"),
5700 "phasedivergent": _("phase-divergent: %d changesets"),
5701 }
5701 }
5702 if numtrouble > 0:
5702 if numtrouble > 0:
5703 ui.status(troublemsg[trouble] % numtrouble + "\n")
5703 ui.status(troublemsg[trouble] % numtrouble + "\n")
5704
5704
5705 cmdutil.summaryhooks(ui, repo)
5705 cmdutil.summaryhooks(ui, repo)
5706
5706
5707 if opts.get('remote'):
5707 if opts.get('remote'):
5708 needsincoming, needsoutgoing = True, True
5708 needsincoming, needsoutgoing = True, True
5709 else:
5709 else:
5710 needsincoming, needsoutgoing = False, False
5710 needsincoming, needsoutgoing = False, False
5711 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5711 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5712 if i:
5712 if i:
5713 needsincoming = True
5713 needsincoming = True
5714 if o:
5714 if o:
5715 needsoutgoing = True
5715 needsoutgoing = True
5716 if not needsincoming and not needsoutgoing:
5716 if not needsincoming and not needsoutgoing:
5717 return
5717 return
5718
5718
5719 def getincoming():
5719 def getincoming():
5720 source, branches = hg.parseurl(ui.expandpath('default'))
5720 source, branches = hg.parseurl(ui.expandpath('default'))
5721 sbranch = branches[0]
5721 sbranch = branches[0]
5722 try:
5722 try:
5723 other = hg.peer(repo, {}, source)
5723 other = hg.peer(repo, {}, source)
5724 except error.RepoError:
5724 except error.RepoError:
5725 if opts.get('remote'):
5725 if opts.get('remote'):
5726 raise
5726 raise
5727 return source, sbranch, None, None, None
5727 return source, sbranch, None, None, None
5728 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5728 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5729 if revs:
5729 if revs:
5730 revs = [other.lookup(rev) for rev in revs]
5730 revs = [other.lookup(rev) for rev in revs]
5731 ui.debug('comparing with %s\n' % util.hidepassword(source))
5731 ui.debug('comparing with %s\n' % util.hidepassword(source))
5732 repo.ui.pushbuffer()
5732 repo.ui.pushbuffer()
5733 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5733 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5734 repo.ui.popbuffer()
5734 repo.ui.popbuffer()
5735 return source, sbranch, other, commoninc, commoninc[1]
5735 return source, sbranch, other, commoninc, commoninc[1]
5736
5736
5737 if needsincoming:
5737 if needsincoming:
5738 source, sbranch, sother, commoninc, incoming = getincoming()
5738 source, sbranch, sother, commoninc, incoming = getincoming()
5739 else:
5739 else:
5740 source = sbranch = sother = commoninc = incoming = None
5740 source = sbranch = sother = commoninc = incoming = None
5741
5741
5742 def getoutgoing():
5742 def getoutgoing():
5743 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5743 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5744 dbranch = branches[0]
5744 dbranch = branches[0]
5745 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5745 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5746 if source != dest:
5746 if source != dest:
5747 try:
5747 try:
5748 dother = hg.peer(repo, {}, dest)
5748 dother = hg.peer(repo, {}, dest)
5749 except error.RepoError:
5749 except error.RepoError:
5750 if opts.get('remote'):
5750 if opts.get('remote'):
5751 raise
5751 raise
5752 return dest, dbranch, None, None
5752 return dest, dbranch, None, None
5753 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5753 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5754 elif sother is None:
5754 elif sother is None:
5755 # there is no explicit destination peer, but source one is invalid
5755 # there is no explicit destination peer, but source one is invalid
5756 return dest, dbranch, None, None
5756 return dest, dbranch, None, None
5757 else:
5757 else:
5758 dother = sother
5758 dother = sother
5759 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5759 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5760 common = None
5760 common = None
5761 else:
5761 else:
5762 common = commoninc
5762 common = commoninc
5763 if revs:
5763 if revs:
5764 revs = [repo.lookup(rev) for rev in revs]
5764 revs = [repo.lookup(rev) for rev in revs]
5765 repo.ui.pushbuffer()
5765 repo.ui.pushbuffer()
5766 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5766 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5767 commoninc=common)
5767 commoninc=common)
5768 repo.ui.popbuffer()
5768 repo.ui.popbuffer()
5769 return dest, dbranch, dother, outgoing
5769 return dest, dbranch, dother, outgoing
5770
5770
5771 if needsoutgoing:
5771 if needsoutgoing:
5772 dest, dbranch, dother, outgoing = getoutgoing()
5772 dest, dbranch, dother, outgoing = getoutgoing()
5773 else:
5773 else:
5774 dest = dbranch = dother = outgoing = None
5774 dest = dbranch = dother = outgoing = None
5775
5775
5776 if opts.get('remote'):
5776 if opts.get('remote'):
5777 t = []
5777 t = []
5778 if incoming:
5778 if incoming:
5779 t.append(_('1 or more incoming'))
5779 t.append(_('1 or more incoming'))
5780 o = outgoing.missing
5780 o = outgoing.missing
5781 if o:
5781 if o:
5782 t.append(_('%d outgoing') % len(o))
5782 t.append(_('%d outgoing') % len(o))
5783 other = dother or sother
5783 other = dother or sother
5784 if 'bookmarks' in other.listkeys('namespaces'):
5784 if 'bookmarks' in other.listkeys('namespaces'):
5785 counts = bookmarks.summary(repo, other)
5785 counts = bookmarks.summary(repo, other)
5786 if counts[0] > 0:
5786 if counts[0] > 0:
5787 t.append(_('%d incoming bookmarks') % counts[0])
5787 t.append(_('%d incoming bookmarks') % counts[0])
5788 if counts[1] > 0:
5788 if counts[1] > 0:
5789 t.append(_('%d outgoing bookmarks') % counts[1])
5789 t.append(_('%d outgoing bookmarks') % counts[1])
5790
5790
5791 if t:
5791 if t:
5792 # i18n: column positioning for "hg summary"
5792 # i18n: column positioning for "hg summary"
5793 ui.write(_('remote: %s\n') % (', '.join(t)))
5793 ui.write(_('remote: %s\n') % (', '.join(t)))
5794 else:
5794 else:
5795 # i18n: column positioning for "hg summary"
5795 # i18n: column positioning for "hg summary"
5796 ui.status(_('remote: (synced)\n'))
5796 ui.status(_('remote: (synced)\n'))
5797
5797
5798 cmdutil.summaryremotehooks(ui, repo, opts,
5798 cmdutil.summaryremotehooks(ui, repo, opts,
5799 ((source, sbranch, sother, commoninc),
5799 ((source, sbranch, sother, commoninc),
5800 (dest, dbranch, dother, outgoing)))
5800 (dest, dbranch, dother, outgoing)))
5801
5801
5802 @command('tag',
5802 @command('tag',
5803 [('f', 'force', None, _('force tag')),
5803 [('f', 'force', None, _('force tag')),
5804 ('l', 'local', None, _('make the tag local')),
5804 ('l', 'local', None, _('make the tag local')),
5805 ('r', 'rev', '', _('revision to tag'), _('REV')),
5805 ('r', 'rev', '', _('revision to tag'), _('REV')),
5806 ('', 'remove', None, _('remove a tag')),
5806 ('', 'remove', None, _('remove a tag')),
5807 # -l/--local is already there, commitopts cannot be used
5807 # -l/--local is already there, commitopts cannot be used
5808 ('e', 'edit', None, _('invoke editor on commit messages')),
5808 ('e', 'edit', None, _('invoke editor on commit messages')),
5809 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5809 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5810 ] + commitopts2,
5810 ] + commitopts2,
5811 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
5811 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
5812 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
5812 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
5813 def tag(ui, repo, name1, *names, **opts):
5813 def tag(ui, repo, name1, *names, **opts):
5814 """add one or more tags for the current or given revision
5814 """add one or more tags for the current or given revision
5815
5815
5816 Name a particular revision using <name>.
5816 Name a particular revision using <name>.
5817
5817
5818 Tags are used to name particular revisions of the repository and are
5818 Tags are used to name particular revisions of the repository and are
5819 very useful to compare different revisions, to go back to significant
5819 very useful to compare different revisions, to go back to significant
5820 earlier versions or to mark branch points as releases, etc. Changing
5820 earlier versions or to mark branch points as releases, etc. Changing
5821 an existing tag is normally disallowed; use -f/--force to override.
5821 an existing tag is normally disallowed; use -f/--force to override.
5822
5822
5823 If no revision is given, the parent of the working directory is
5823 If no revision is given, the parent of the working directory is
5824 used.
5824 used.
5825
5825
5826 To facilitate version control, distribution, and merging of tags,
5826 To facilitate version control, distribution, and merging of tags,
5827 they are stored as a file named ".hgtags" which is managed similarly
5827 they are stored as a file named ".hgtags" which is managed similarly
5828 to other project files and can be hand-edited if necessary. This
5828 to other project files and can be hand-edited if necessary. This
5829 also means that tagging creates a new commit. The file
5829 also means that tagging creates a new commit. The file
5830 ".hg/localtags" is used for local tags (not shared among
5830 ".hg/localtags" is used for local tags (not shared among
5831 repositories).
5831 repositories).
5832
5832
5833 Tag commits are usually made at the head of a branch. If the parent
5833 Tag commits are usually made at the head of a branch. If the parent
5834 of the working directory is not a branch head, :hg:`tag` aborts; use
5834 of the working directory is not a branch head, :hg:`tag` aborts; use
5835 -f/--force to force the tag commit to be based on a non-head
5835 -f/--force to force the tag commit to be based on a non-head
5836 changeset.
5836 changeset.
5837
5837
5838 See :hg:`help dates` for a list of formats valid for -d/--date.
5838 See :hg:`help dates` for a list of formats valid for -d/--date.
5839
5839
5840 Since tag names have priority over branch names during revision
5840 Since tag names have priority over branch names during revision
5841 lookup, using an existing branch name as a tag name is discouraged.
5841 lookup, using an existing branch name as a tag name is discouraged.
5842
5842
5843 Returns 0 on success.
5843 Returns 0 on success.
5844 """
5844 """
5845 opts = pycompat.byteskwargs(opts)
5845 opts = pycompat.byteskwargs(opts)
5846 with repo.wlock(), repo.lock():
5846 with repo.wlock(), repo.lock():
5847 rev_ = "."
5847 rev_ = "."
5848 names = [t.strip() for t in (name1,) + names]
5848 names = [t.strip() for t in (name1,) + names]
5849 if len(names) != len(set(names)):
5849 if len(names) != len(set(names)):
5850 raise error.Abort(_('tag names must be unique'))
5850 raise error.Abort(_('tag names must be unique'))
5851 for n in names:
5851 for n in names:
5852 scmutil.checknewlabel(repo, n, 'tag')
5852 scmutil.checknewlabel(repo, n, 'tag')
5853 if not n:
5853 if not n:
5854 raise error.Abort(_('tag names cannot consist entirely of '
5854 raise error.Abort(_('tag names cannot consist entirely of '
5855 'whitespace'))
5855 'whitespace'))
5856 if opts.get('rev') and opts.get('remove'):
5856 if opts.get('rev') and opts.get('remove'):
5857 raise error.Abort(_("--rev and --remove are incompatible"))
5857 raise error.Abort(_("--rev and --remove are incompatible"))
5858 if opts.get('rev'):
5858 if opts.get('rev'):
5859 rev_ = opts['rev']
5859 rev_ = opts['rev']
5860 message = opts.get('message')
5860 message = opts.get('message')
5861 if opts.get('remove'):
5861 if opts.get('remove'):
5862 if opts.get('local'):
5862 if opts.get('local'):
5863 expectedtype = 'local'
5863 expectedtype = 'local'
5864 else:
5864 else:
5865 expectedtype = 'global'
5865 expectedtype = 'global'
5866
5866
5867 for n in names:
5867 for n in names:
5868 if repo.tagtype(n) == 'global':
5868 if repo.tagtype(n) == 'global':
5869 alltags = tagsmod.findglobaltags(ui, repo)
5869 alltags = tagsmod.findglobaltags(ui, repo)
5870 if alltags[n][0] == nullid:
5870 if alltags[n][0] == nullid:
5871 raise error.Abort(_("tag '%s' is already removed") % n)
5871 raise error.Abort(_("tag '%s' is already removed") % n)
5872 if not repo.tagtype(n):
5872 if not repo.tagtype(n):
5873 raise error.Abort(_("tag '%s' does not exist") % n)
5873 raise error.Abort(_("tag '%s' does not exist") % n)
5874 if repo.tagtype(n) != expectedtype:
5874 if repo.tagtype(n) != expectedtype:
5875 if expectedtype == 'global':
5875 if expectedtype == 'global':
5876 raise error.Abort(_("tag '%s' is not a global tag") % n)
5876 raise error.Abort(_("tag '%s' is not a global tag") % n)
5877 else:
5877 else:
5878 raise error.Abort(_("tag '%s' is not a local tag") % n)
5878 raise error.Abort(_("tag '%s' is not a local tag") % n)
5879 rev_ = 'null'
5879 rev_ = 'null'
5880 if not message:
5880 if not message:
5881 # we don't translate commit messages
5881 # we don't translate commit messages
5882 message = 'Removed tag %s' % ', '.join(names)
5882 message = 'Removed tag %s' % ', '.join(names)
5883 elif not opts.get('force'):
5883 elif not opts.get('force'):
5884 for n in names:
5884 for n in names:
5885 if n in repo.tags():
5885 if n in repo.tags():
5886 raise error.Abort(_("tag '%s' already exists "
5886 raise error.Abort(_("tag '%s' already exists "
5887 "(use -f to force)") % n)
5887 "(use -f to force)") % n)
5888 if not opts.get('local'):
5888 if not opts.get('local'):
5889 p1, p2 = repo.dirstate.parents()
5889 p1, p2 = repo.dirstate.parents()
5890 if p2 != nullid:
5890 if p2 != nullid:
5891 raise error.Abort(_('uncommitted merge'))
5891 raise error.Abort(_('uncommitted merge'))
5892 bheads = repo.branchheads()
5892 bheads = repo.branchheads()
5893 if not opts.get('force') and bheads and p1 not in bheads:
5893 if not opts.get('force') and bheads and p1 not in bheads:
5894 raise error.Abort(_('working directory is not at a branch head '
5894 raise error.Abort(_('working directory is not at a branch head '
5895 '(use -f to force)'))
5895 '(use -f to force)'))
5896 node = scmutil.revsingle(repo, rev_).node()
5896 node = scmutil.revsingle(repo, rev_).node()
5897
5897
5898 if not message:
5898 if not message:
5899 # we don't translate commit messages
5899 # we don't translate commit messages
5900 message = ('Added tag %s for changeset %s' %
5900 message = ('Added tag %s for changeset %s' %
5901 (', '.join(names), short(node)))
5901 (', '.join(names), short(node)))
5902
5902
5903 date = opts.get('date')
5903 date = opts.get('date')
5904 if date:
5904 if date:
5905 date = dateutil.parsedate(date)
5905 date = dateutil.parsedate(date)
5906
5906
5907 if opts.get('remove'):
5907 if opts.get('remove'):
5908 editform = 'tag.remove'
5908 editform = 'tag.remove'
5909 else:
5909 else:
5910 editform = 'tag.add'
5910 editform = 'tag.add'
5911 editor = cmdutil.getcommiteditor(editform=editform,
5911 editor = cmdutil.getcommiteditor(editform=editform,
5912 **pycompat.strkwargs(opts))
5912 **pycompat.strkwargs(opts))
5913
5913
5914 # don't allow tagging the null rev
5914 # don't allow tagging the null rev
5915 if (not opts.get('remove') and
5915 if (not opts.get('remove') and
5916 scmutil.revsingle(repo, rev_).rev() == nullrev):
5916 scmutil.revsingle(repo, rev_).rev() == nullrev):
5917 raise error.Abort(_("cannot tag null revision"))
5917 raise error.Abort(_("cannot tag null revision"))
5918
5918
5919 tagsmod.tag(repo, names, node, message, opts.get('local'),
5919 tagsmod.tag(repo, names, node, message, opts.get('local'),
5920 opts.get('user'), date, editor=editor)
5920 opts.get('user'), date, editor=editor)
5921
5921
5922 @command(
5922 @command(
5923 'tags', formatteropts, '',
5923 'tags', formatteropts, '',
5924 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5924 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5925 intents={INTENT_READONLY})
5925 intents={INTENT_READONLY})
5926 def tags(ui, repo, **opts):
5926 def tags(ui, repo, **opts):
5927 """list repository tags
5927 """list repository tags
5928
5928
5929 This lists both regular and local tags. When the -v/--verbose
5929 This lists both regular and local tags. When the -v/--verbose
5930 switch is used, a third column "local" is printed for local tags.
5930 switch is used, a third column "local" is printed for local tags.
5931 When the -q/--quiet switch is used, only the tag name is printed.
5931 When the -q/--quiet switch is used, only the tag name is printed.
5932
5932
5933 .. container:: verbose
5933 .. container:: verbose
5934
5934
5935 Template:
5935 Template:
5936
5936
5937 The following keywords are supported in addition to the common template
5937 The following keywords are supported in addition to the common template
5938 keywords and functions such as ``{tag}``. See also
5938 keywords and functions such as ``{tag}``. See also
5939 :hg:`help templates`.
5939 :hg:`help templates`.
5940
5940
5941 :type: String. ``local`` for local tags.
5941 :type: String. ``local`` for local tags.
5942
5942
5943 Returns 0 on success.
5943 Returns 0 on success.
5944 """
5944 """
5945
5945
5946 opts = pycompat.byteskwargs(opts)
5946 opts = pycompat.byteskwargs(opts)
5947 ui.pager('tags')
5947 ui.pager('tags')
5948 fm = ui.formatter('tags', opts)
5948 fm = ui.formatter('tags', opts)
5949 hexfunc = fm.hexfunc
5949 hexfunc = fm.hexfunc
5950
5950
5951 for t, n in reversed(repo.tagslist()):
5951 for t, n in reversed(repo.tagslist()):
5952 hn = hexfunc(n)
5952 hn = hexfunc(n)
5953 label = 'tags.normal'
5953 label = 'tags.normal'
5954 tagtype = ''
5954 tagtype = ''
5955 if repo.tagtype(t) == 'local':
5955 if repo.tagtype(t) == 'local':
5956 label = 'tags.local'
5956 label = 'tags.local'
5957 tagtype = 'local'
5957 tagtype = 'local'
5958
5958
5959 fm.startitem()
5959 fm.startitem()
5960 fm.context(repo=repo)
5960 fm.context(repo=repo)
5961 fm.write('tag', '%s', t, label=label)
5961 fm.write('tag', '%s', t, label=label)
5962 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5962 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5963 fm.condwrite(not ui.quiet, 'rev node', fmt,
5963 fm.condwrite(not ui.quiet, 'rev node', fmt,
5964 repo.changelog.rev(n), hn, label=label)
5964 repo.changelog.rev(n), hn, label=label)
5965 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5965 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5966 tagtype, label=label)
5966 tagtype, label=label)
5967 fm.plain('\n')
5967 fm.plain('\n')
5968 fm.end()
5968 fm.end()
5969
5969
5970 @command('tip',
5970 @command('tip',
5971 [('p', 'patch', None, _('show patch')),
5971 [('p', 'patch', None, _('show patch')),
5972 ('g', 'git', None, _('use git extended diff format')),
5972 ('g', 'git', None, _('use git extended diff format')),
5973 ] + templateopts,
5973 ] + templateopts,
5974 _('[-p] [-g]'),
5974 _('[-p] [-g]'),
5975 helpcategory=command.CATEGORY_CHANGE_NAVIGATION)
5975 helpcategory=command.CATEGORY_CHANGE_NAVIGATION)
5976 def tip(ui, repo, **opts):
5976 def tip(ui, repo, **opts):
5977 """show the tip revision (DEPRECATED)
5977 """show the tip revision (DEPRECATED)
5978
5978
5979 The tip revision (usually just called the tip) is the changeset
5979 The tip revision (usually just called the tip) is the changeset
5980 most recently added to the repository (and therefore the most
5980 most recently added to the repository (and therefore the most
5981 recently changed head).
5981 recently changed head).
5982
5982
5983 If you have just made a commit, that commit will be the tip. If
5983 If you have just made a commit, that commit will be the tip. If
5984 you have just pulled changes from another repository, the tip of
5984 you have just pulled changes from another repository, the tip of
5985 that repository becomes the current tip. The "tip" tag is special
5985 that repository becomes the current tip. The "tip" tag is special
5986 and cannot be renamed or assigned to a different changeset.
5986 and cannot be renamed or assigned to a different changeset.
5987
5987
5988 This command is deprecated, please use :hg:`heads` instead.
5988 This command is deprecated, please use :hg:`heads` instead.
5989
5989
5990 Returns 0 on success.
5990 Returns 0 on success.
5991 """
5991 """
5992 opts = pycompat.byteskwargs(opts)
5992 opts = pycompat.byteskwargs(opts)
5993 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5993 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5994 displayer.show(repo['tip'])
5994 displayer.show(repo['tip'])
5995 displayer.close()
5995 displayer.close()
5996
5996
5997 @command('unbundle',
5997 @command('unbundle',
5998 [('u', 'update', None,
5998 [('u', 'update', None,
5999 _('update to new branch head if changesets were unbundled'))],
5999 _('update to new branch head if changesets were unbundled'))],
6000 _('[-u] FILE...'),
6000 _('[-u] FILE...'),
6001 helpcategory=command.CATEGORY_IMPORT_EXPORT)
6001 helpcategory=command.CATEGORY_IMPORT_EXPORT)
6002 def unbundle(ui, repo, fname1, *fnames, **opts):
6002 def unbundle(ui, repo, fname1, *fnames, **opts):
6003 """apply one or more bundle files
6003 """apply one or more bundle files
6004
6004
6005 Apply one or more bundle files generated by :hg:`bundle`.
6005 Apply one or more bundle files generated by :hg:`bundle`.
6006
6006
6007 Returns 0 on success, 1 if an update has unresolved files.
6007 Returns 0 on success, 1 if an update has unresolved files.
6008 """
6008 """
6009 fnames = (fname1,) + fnames
6009 fnames = (fname1,) + fnames
6010
6010
6011 with repo.lock():
6011 with repo.lock():
6012 for fname in fnames:
6012 for fname in fnames:
6013 f = hg.openpath(ui, fname)
6013 f = hg.openpath(ui, fname)
6014 gen = exchange.readbundle(ui, f, fname)
6014 gen = exchange.readbundle(ui, f, fname)
6015 if isinstance(gen, streamclone.streamcloneapplier):
6015 if isinstance(gen, streamclone.streamcloneapplier):
6016 raise error.Abort(
6016 raise error.Abort(
6017 _('packed bundles cannot be applied with '
6017 _('packed bundles cannot be applied with '
6018 '"hg unbundle"'),
6018 '"hg unbundle"'),
6019 hint=_('use "hg debugapplystreamclonebundle"'))
6019 hint=_('use "hg debugapplystreamclonebundle"'))
6020 url = 'bundle:' + fname
6020 url = 'bundle:' + fname
6021 try:
6021 try:
6022 txnname = 'unbundle'
6022 txnname = 'unbundle'
6023 if not isinstance(gen, bundle2.unbundle20):
6023 if not isinstance(gen, bundle2.unbundle20):
6024 txnname = 'unbundle\n%s' % util.hidepassword(url)
6024 txnname = 'unbundle\n%s' % util.hidepassword(url)
6025 with repo.transaction(txnname) as tr:
6025 with repo.transaction(txnname) as tr:
6026 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
6026 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
6027 url=url)
6027 url=url)
6028 except error.BundleUnknownFeatureError as exc:
6028 except error.BundleUnknownFeatureError as exc:
6029 raise error.Abort(
6029 raise error.Abort(
6030 _('%s: unknown bundle feature, %s') % (fname, exc),
6030 _('%s: unknown bundle feature, %s') % (fname, exc),
6031 hint=_("see https://mercurial-scm.org/"
6031 hint=_("see https://mercurial-scm.org/"
6032 "wiki/BundleFeature for more "
6032 "wiki/BundleFeature for more "
6033 "information"))
6033 "information"))
6034 modheads = bundle2.combinechangegroupresults(op)
6034 modheads = bundle2.combinechangegroupresults(op)
6035
6035
6036 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
6036 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
6037
6037
6038 @command('update|up|checkout|co',
6038 @command('update|up|checkout|co',
6039 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6039 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6040 ('c', 'check', None, _('require clean working directory')),
6040 ('c', 'check', None, _('require clean working directory')),
6041 ('m', 'merge', None, _('merge uncommitted changes')),
6041 ('m', 'merge', None, _('merge uncommitted changes')),
6042 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6042 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6043 ('r', 'rev', '', _('revision'), _('REV'))
6043 ('r', 'rev', '', _('revision'), _('REV'))
6044 ] + mergetoolopts,
6044 ] + mergetoolopts,
6045 _('[-C|-c|-m] [-d DATE] [[-r] REV]'),
6045 _('[-C|-c|-m] [-d DATE] [[-r] REV]'),
6046 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6046 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6047 helpbasic=True)
6047 helpbasic=True)
6048 def update(ui, repo, node=None, **opts):
6048 def update(ui, repo, node=None, **opts):
6049 """update working directory (or switch revisions)
6049 """update working directory (or switch revisions)
6050
6050
6051 Update the repository's working directory to the specified
6051 Update the repository's working directory to the specified
6052 changeset. If no changeset is specified, update to the tip of the
6052 changeset. If no changeset is specified, update to the tip of the
6053 current named branch and move the active bookmark (see :hg:`help
6053 current named branch and move the active bookmark (see :hg:`help
6054 bookmarks`).
6054 bookmarks`).
6055
6055
6056 Update sets the working directory's parent revision to the specified
6056 Update sets the working directory's parent revision to the specified
6057 changeset (see :hg:`help parents`).
6057 changeset (see :hg:`help parents`).
6058
6058
6059 If the changeset is not a descendant or ancestor of the working
6059 If the changeset is not a descendant or ancestor of the working
6060 directory's parent and there are uncommitted changes, the update is
6060 directory's parent and there are uncommitted changes, the update is
6061 aborted. With the -c/--check option, the working directory is checked
6061 aborted. With the -c/--check option, the working directory is checked
6062 for uncommitted changes; if none are found, the working directory is
6062 for uncommitted changes; if none are found, the working directory is
6063 updated to the specified changeset.
6063 updated to the specified changeset.
6064
6064
6065 .. container:: verbose
6065 .. container:: verbose
6066
6066
6067 The -C/--clean, -c/--check, and -m/--merge options control what
6067 The -C/--clean, -c/--check, and -m/--merge options control what
6068 happens if the working directory contains uncommitted changes.
6068 happens if the working directory contains uncommitted changes.
6069 At most of one of them can be specified.
6069 At most of one of them can be specified.
6070
6070
6071 1. If no option is specified, and if
6071 1. If no option is specified, and if
6072 the requested changeset is an ancestor or descendant of
6072 the requested changeset is an ancestor or descendant of
6073 the working directory's parent, the uncommitted changes
6073 the working directory's parent, the uncommitted changes
6074 are merged into the requested changeset and the merged
6074 are merged into the requested changeset and the merged
6075 result is left uncommitted. If the requested changeset is
6075 result is left uncommitted. If the requested changeset is
6076 not an ancestor or descendant (that is, it is on another
6076 not an ancestor or descendant (that is, it is on another
6077 branch), the update is aborted and the uncommitted changes
6077 branch), the update is aborted and the uncommitted changes
6078 are preserved.
6078 are preserved.
6079
6079
6080 2. With the -m/--merge option, the update is allowed even if the
6080 2. With the -m/--merge option, the update is allowed even if the
6081 requested changeset is not an ancestor or descendant of
6081 requested changeset is not an ancestor or descendant of
6082 the working directory's parent.
6082 the working directory's parent.
6083
6083
6084 3. With the -c/--check option, the update is aborted and the
6084 3. With the -c/--check option, the update is aborted and the
6085 uncommitted changes are preserved.
6085 uncommitted changes are preserved.
6086
6086
6087 4. With the -C/--clean option, uncommitted changes are discarded and
6087 4. With the -C/--clean option, uncommitted changes are discarded and
6088 the working directory is updated to the requested changeset.
6088 the working directory is updated to the requested changeset.
6089
6089
6090 To cancel an uncommitted merge (and lose your changes), use
6090 To cancel an uncommitted merge (and lose your changes), use
6091 :hg:`merge --abort`.
6091 :hg:`merge --abort`.
6092
6092
6093 Use null as the changeset to remove the working directory (like
6093 Use null as the changeset to remove the working directory (like
6094 :hg:`clone -U`).
6094 :hg:`clone -U`).
6095
6095
6096 If you want to revert just one file to an older revision, use
6096 If you want to revert just one file to an older revision, use
6097 :hg:`revert [-r REV] NAME`.
6097 :hg:`revert [-r REV] NAME`.
6098
6098
6099 See :hg:`help dates` for a list of formats valid for -d/--date.
6099 See :hg:`help dates` for a list of formats valid for -d/--date.
6100
6100
6101 Returns 0 on success, 1 if there are unresolved files.
6101 Returns 0 on success, 1 if there are unresolved files.
6102 """
6102 """
6103 rev = opts.get(r'rev')
6103 rev = opts.get(r'rev')
6104 date = opts.get(r'date')
6104 date = opts.get(r'date')
6105 clean = opts.get(r'clean')
6105 clean = opts.get(r'clean')
6106 check = opts.get(r'check')
6106 check = opts.get(r'check')
6107 merge = opts.get(r'merge')
6107 merge = opts.get(r'merge')
6108 if rev and node:
6108 if rev and node:
6109 raise error.Abort(_("please specify just one revision"))
6109 raise error.Abort(_("please specify just one revision"))
6110
6110
6111 if ui.configbool('commands', 'update.requiredest'):
6111 if ui.configbool('commands', 'update.requiredest'):
6112 if not node and not rev and not date:
6112 if not node and not rev and not date:
6113 raise error.Abort(_('you must specify a destination'),
6113 raise error.Abort(_('you must specify a destination'),
6114 hint=_('for example: hg update ".::"'))
6114 hint=_('for example: hg update ".::"'))
6115
6115
6116 if rev is None or rev == '':
6116 if rev is None or rev == '':
6117 rev = node
6117 rev = node
6118
6118
6119 if date and rev is not None:
6119 if date and rev is not None:
6120 raise error.Abort(_("you can't specify a revision and a date"))
6120 raise error.Abort(_("you can't specify a revision and a date"))
6121
6121
6122 if len([x for x in (clean, check, merge) if x]) > 1:
6122 if len([x for x in (clean, check, merge) if x]) > 1:
6123 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
6123 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
6124 "or -m/--merge"))
6124 "or -m/--merge"))
6125
6125
6126 updatecheck = None
6126 updatecheck = None
6127 if check:
6127 if check:
6128 updatecheck = 'abort'
6128 updatecheck = 'abort'
6129 elif merge:
6129 elif merge:
6130 updatecheck = 'none'
6130 updatecheck = 'none'
6131
6131
6132 with repo.wlock():
6132 with repo.wlock():
6133 cmdutil.clearunfinished(repo)
6133 cmdutil.clearunfinished(repo)
6134
6134
6135 if date:
6135 if date:
6136 rev = cmdutil.finddate(ui, repo, date)
6136 rev = cmdutil.finddate(ui, repo, date)
6137
6137
6138 # if we defined a bookmark, we have to remember the original name
6138 # if we defined a bookmark, we have to remember the original name
6139 brev = rev
6139 brev = rev
6140 if rev:
6140 if rev:
6141 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
6141 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
6142 ctx = scmutil.revsingle(repo, rev, default=None)
6142 ctx = scmutil.revsingle(repo, rev, default=None)
6143 rev = ctx.rev()
6143 rev = ctx.rev()
6144 hidden = ctx.hidden()
6144 hidden = ctx.hidden()
6145 overrides = {('ui', 'forcemerge'): opts.get(r'tool', '')}
6145 overrides = {('ui', 'forcemerge'): opts.get(r'tool', '')}
6146 with ui.configoverride(overrides, 'update'):
6146 with ui.configoverride(overrides, 'update'):
6147 ret = hg.updatetotally(ui, repo, rev, brev, clean=clean,
6147 ret = hg.updatetotally(ui, repo, rev, brev, clean=clean,
6148 updatecheck=updatecheck)
6148 updatecheck=updatecheck)
6149 if hidden:
6149 if hidden:
6150 ctxstr = ctx.hex()[:12]
6150 ctxstr = ctx.hex()[:12]
6151 ui.warn(_("updated to hidden changeset %s\n") % ctxstr)
6151 ui.warn(_("updated to hidden changeset %s\n") % ctxstr)
6152
6152
6153 if ctx.obsolete():
6153 if ctx.obsolete():
6154 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
6154 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
6155 ui.warn("(%s)\n" % obsfatemsg)
6155 ui.warn("(%s)\n" % obsfatemsg)
6156 return ret
6156 return ret
6157
6157
6158 @command('verify',
6158 @command('verify',
6159 [('', 'full', False, 'perform more checks (EXPERIMENTAL)')],
6159 [('', 'full', False, 'perform more checks (EXPERIMENTAL)')],
6160 helpcategory=command.CATEGORY_MAINTENANCE)
6160 helpcategory=command.CATEGORY_MAINTENANCE)
6161 def verify(ui, repo, **opts):
6161 def verify(ui, repo, **opts):
6162 """verify the integrity of the repository
6162 """verify the integrity of the repository
6163
6163
6164 Verify the integrity of the current repository.
6164 Verify the integrity of the current repository.
6165
6165
6166 This will perform an extensive check of the repository's
6166 This will perform an extensive check of the repository's
6167 integrity, validating the hashes and checksums of each entry in
6167 integrity, validating the hashes and checksums of each entry in
6168 the changelog, manifest, and tracked files, as well as the
6168 the changelog, manifest, and tracked files, as well as the
6169 integrity of their crosslinks and indices.
6169 integrity of their crosslinks and indices.
6170
6170
6171 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
6171 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
6172 for more information about recovery from corruption of the
6172 for more information about recovery from corruption of the
6173 repository.
6173 repository.
6174
6174
6175 Returns 0 on success, 1 if errors are encountered.
6175 Returns 0 on success, 1 if errors are encountered.
6176 """
6176 """
6177 opts = pycompat.byteskwargs(opts)
6177 opts = pycompat.byteskwargs(opts)
6178
6178
6179 level = None
6179 level = None
6180 if opts['full']:
6180 if opts['full']:
6181 level = verifymod.VERIFY_FULL
6181 level = verifymod.VERIFY_FULL
6182 return hg.verify(repo, level)
6182 return hg.verify(repo, level)
6183
6183
6184 @command(
6184 @command(
6185 'version', [] + formatteropts, helpcategory=command.CATEGORY_HELP,
6185 'version', [] + formatteropts, helpcategory=command.CATEGORY_HELP,
6186 norepo=True, intents={INTENT_READONLY})
6186 norepo=True, intents={INTENT_READONLY})
6187 def version_(ui, **opts):
6187 def version_(ui, **opts):
6188 """output version and copyright information
6188 """output version and copyright information
6189
6189
6190 .. container:: verbose
6190 .. container:: verbose
6191
6191
6192 Template:
6192 Template:
6193
6193
6194 The following keywords are supported. See also :hg:`help templates`.
6194 The following keywords are supported. See also :hg:`help templates`.
6195
6195
6196 :extensions: List of extensions.
6196 :extensions: List of extensions.
6197 :ver: String. Version number.
6197 :ver: String. Version number.
6198
6198
6199 And each entry of ``{extensions}`` provides the following sub-keywords
6199 And each entry of ``{extensions}`` provides the following sub-keywords
6200 in addition to ``{ver}``.
6200 in addition to ``{ver}``.
6201
6201
6202 :bundled: Boolean. True if included in the release.
6202 :bundled: Boolean. True if included in the release.
6203 :name: String. Extension name.
6203 :name: String. Extension name.
6204 """
6204 """
6205 opts = pycompat.byteskwargs(opts)
6205 opts = pycompat.byteskwargs(opts)
6206 if ui.verbose:
6206 if ui.verbose:
6207 ui.pager('version')
6207 ui.pager('version')
6208 fm = ui.formatter("version", opts)
6208 fm = ui.formatter("version", opts)
6209 fm.startitem()
6209 fm.startitem()
6210 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
6210 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
6211 util.version())
6211 util.version())
6212 license = _(
6212 license = _(
6213 "(see https://mercurial-scm.org for more information)\n"
6213 "(see https://mercurial-scm.org for more information)\n"
6214 "\nCopyright (C) 2005-2019 Matt Mackall and others\n"
6214 "\nCopyright (C) 2005-2019 Matt Mackall and others\n"
6215 "This is free software; see the source for copying conditions. "
6215 "This is free software; see the source for copying conditions. "
6216 "There is NO\nwarranty; "
6216 "There is NO\nwarranty; "
6217 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6217 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6218 )
6218 )
6219 if not ui.quiet:
6219 if not ui.quiet:
6220 fm.plain(license)
6220 fm.plain(license)
6221
6221
6222 if ui.verbose:
6222 if ui.verbose:
6223 fm.plain(_("\nEnabled extensions:\n\n"))
6223 fm.plain(_("\nEnabled extensions:\n\n"))
6224 # format names and versions into columns
6224 # format names and versions into columns
6225 names = []
6225 names = []
6226 vers = []
6226 vers = []
6227 isinternals = []
6227 isinternals = []
6228 for name, module in extensions.extensions():
6228 for name, module in extensions.extensions():
6229 names.append(name)
6229 names.append(name)
6230 vers.append(extensions.moduleversion(module) or None)
6230 vers.append(extensions.moduleversion(module) or None)
6231 isinternals.append(extensions.ismoduleinternal(module))
6231 isinternals.append(extensions.ismoduleinternal(module))
6232 fn = fm.nested("extensions", tmpl='{name}\n')
6232 fn = fm.nested("extensions", tmpl='{name}\n')
6233 if names:
6233 if names:
6234 namefmt = " %%-%ds " % max(len(n) for n in names)
6234 namefmt = " %%-%ds " % max(len(n) for n in names)
6235 places = [_("external"), _("internal")]
6235 places = [_("external"), _("internal")]
6236 for n, v, p in zip(names, vers, isinternals):
6236 for n, v, p in zip(names, vers, isinternals):
6237 fn.startitem()
6237 fn.startitem()
6238 fn.condwrite(ui.verbose, "name", namefmt, n)
6238 fn.condwrite(ui.verbose, "name", namefmt, n)
6239 if ui.verbose:
6239 if ui.verbose:
6240 fn.plain("%s " % places[p])
6240 fn.plain("%s " % places[p])
6241 fn.data(bundled=p)
6241 fn.data(bundled=p)
6242 fn.condwrite(ui.verbose and v, "ver", "%s", v)
6242 fn.condwrite(ui.verbose and v, "ver", "%s", v)
6243 if ui.verbose:
6243 if ui.verbose:
6244 fn.plain("\n")
6244 fn.plain("\n")
6245 fn.end()
6245 fn.end()
6246 fm.end()
6246 fm.end()
6247
6247
6248 def loadcmdtable(ui, name, cmdtable):
6248 def loadcmdtable(ui, name, cmdtable):
6249 """Load command functions from specified cmdtable
6249 """Load command functions from specified cmdtable
6250 """
6250 """
6251 cmdtable = cmdtable.copy()
6252 for cmd in list(cmdtable):
6253 if not cmd.startswith('^'):
6254 continue
6255 ui.deprecwarn("old-style command registration '%s' in extension '%s'"
6256 % (cmd, name), '4.8')
6257 entry = cmdtable.pop(cmd)
6258 entry[0].helpbasic = True
6259 cmdtable[cmd[1:]] = entry
6260
6261 overrides = [cmd for cmd in cmdtable if cmd in table]
6251 overrides = [cmd for cmd in cmdtable if cmd in table]
6262 if overrides:
6252 if overrides:
6263 ui.warn(_("extension '%s' overrides commands: %s\n")
6253 ui.warn(_("extension '%s' overrides commands: %s\n")
6264 % (name, " ".join(overrides)))
6254 % (name, " ".join(overrides)))
6265 table.update(cmdtable)
6255 table.update(cmdtable)
General Comments 0
You need to be logged in to leave comments. Login now