##// END OF EJS Templates
config: fix fm.data() handling of defaultvalue...
Navaneeth Suresh -
r42934:60789444 default
parent child Browse files
Show More
@@ -1,6449 +1,6450 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 revsetlang,
56 revsetlang,
57 rewriteutil,
57 rewriteutil,
58 scmutil,
58 scmutil,
59 server,
59 server,
60 shelve as shelvemod,
60 shelve as shelvemod,
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('abort',
134 @command('abort',
135 dryrunopts, helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
135 dryrunopts, helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
136 helpbasic=True)
136 helpbasic=True)
137 def abort(ui, repo, **opts):
137 def abort(ui, repo, **opts):
138 """abort an unfinished operation (EXPERIMENTAL)
138 """abort an unfinished operation (EXPERIMENTAL)
139
139
140 Aborts a multistep operation like graft, histedit, rebase, merge,
140 Aborts a multistep operation like graft, histedit, rebase, merge,
141 and unshelve if they are in an unfinished state.
141 and unshelve if they are in an unfinished state.
142
142
143 use --dry-run/-n to dry run the command.
143 use --dry-run/-n to dry run the command.
144 """
144 """
145 dryrun = opts.get(r'dry_run')
145 dryrun = opts.get(r'dry_run')
146 abortstate = cmdutil.getunfinishedstate(repo)
146 abortstate = cmdutil.getunfinishedstate(repo)
147 if not abortstate:
147 if not abortstate:
148 raise error.Abort(_('no operation in progress'))
148 raise error.Abort(_('no operation in progress'))
149 if not abortstate.abortfunc:
149 if not abortstate.abortfunc:
150 raise error.Abort((_("%s in progress but does not support 'hg abort'") %
150 raise error.Abort((_("%s in progress but does not support 'hg abort'") %
151 (abortstate._opname)), hint=abortstate.hint())
151 (abortstate._opname)), hint=abortstate.hint())
152 if dryrun:
152 if dryrun:
153 ui.status(_('%s in progress, will be aborted\n') % (abortstate._opname))
153 ui.status(_('%s in progress, will be aborted\n') % (abortstate._opname))
154 return
154 return
155 return abortstate.abortfunc(ui, repo)
155 return abortstate.abortfunc(ui, repo)
156
156
157 @command('add',
157 @command('add',
158 walkopts + subrepoopts + dryrunopts,
158 walkopts + subrepoopts + dryrunopts,
159 _('[OPTION]... [FILE]...'),
159 _('[OPTION]... [FILE]...'),
160 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
160 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
161 helpbasic=True, inferrepo=True)
161 helpbasic=True, inferrepo=True)
162 def add(ui, repo, *pats, **opts):
162 def add(ui, repo, *pats, **opts):
163 """add the specified files on the next commit
163 """add the specified files on the next commit
164
164
165 Schedule files to be version controlled and added to the
165 Schedule files to be version controlled and added to the
166 repository.
166 repository.
167
167
168 The files will be added to the repository at the next commit. To
168 The files will be added to the repository at the next commit. To
169 undo an add before that, see :hg:`forget`.
169 undo an add before that, see :hg:`forget`.
170
170
171 If no names are given, add all files to the repository (except
171 If no names are given, add all files to the repository (except
172 files matching ``.hgignore``).
172 files matching ``.hgignore``).
173
173
174 .. container:: verbose
174 .. container:: verbose
175
175
176 Examples:
176 Examples:
177
177
178 - New (unknown) files are added
178 - New (unknown) files are added
179 automatically by :hg:`add`::
179 automatically by :hg:`add`::
180
180
181 $ ls
181 $ ls
182 foo.c
182 foo.c
183 $ hg status
183 $ hg status
184 ? foo.c
184 ? foo.c
185 $ hg add
185 $ hg add
186 adding foo.c
186 adding foo.c
187 $ hg status
187 $ hg status
188 A foo.c
188 A foo.c
189
189
190 - Specific files to be added can be specified::
190 - Specific files to be added can be specified::
191
191
192 $ ls
192 $ ls
193 bar.c foo.c
193 bar.c foo.c
194 $ hg status
194 $ hg status
195 ? bar.c
195 ? bar.c
196 ? foo.c
196 ? foo.c
197 $ hg add bar.c
197 $ hg add bar.c
198 $ hg status
198 $ hg status
199 A bar.c
199 A bar.c
200 ? foo.c
200 ? foo.c
201
201
202 Returns 0 if all files are successfully added.
202 Returns 0 if all files are successfully added.
203 """
203 """
204
204
205 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
205 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
206 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
206 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
207 rejected = cmdutil.add(ui, repo, m, "", uipathfn, False, **opts)
207 rejected = cmdutil.add(ui, repo, m, "", uipathfn, False, **opts)
208 return rejected and 1 or 0
208 return rejected and 1 or 0
209
209
210 @command('addremove',
210 @command('addremove',
211 similarityopts + subrepoopts + walkopts + dryrunopts,
211 similarityopts + subrepoopts + walkopts + dryrunopts,
212 _('[OPTION]... [FILE]...'),
212 _('[OPTION]... [FILE]...'),
213 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
213 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
214 inferrepo=True)
214 inferrepo=True)
215 def addremove(ui, repo, *pats, **opts):
215 def addremove(ui, repo, *pats, **opts):
216 """add all new files, delete all missing files
216 """add all new files, delete all missing files
217
217
218 Add all new files and remove all missing files from the
218 Add all new files and remove all missing files from the
219 repository.
219 repository.
220
220
221 Unless names are given, new files are ignored if they match any of
221 Unless names are given, new files are ignored if they match any of
222 the patterns in ``.hgignore``. As with add, these changes take
222 the patterns in ``.hgignore``. As with add, these changes take
223 effect at the next commit.
223 effect at the next commit.
224
224
225 Use the -s/--similarity option to detect renamed files. This
225 Use the -s/--similarity option to detect renamed files. This
226 option takes a percentage between 0 (disabled) and 100 (files must
226 option takes a percentage between 0 (disabled) and 100 (files must
227 be identical) as its parameter. With a parameter greater than 0,
227 be identical) as its parameter. With a parameter greater than 0,
228 this compares every removed file with every added file and records
228 this compares every removed file with every added file and records
229 those similar enough as renames. Detecting renamed files this way
229 those similar enough as renames. Detecting renamed files this way
230 can be expensive. After using this option, :hg:`status -C` can be
230 can be expensive. After using this option, :hg:`status -C` can be
231 used to check which files were identified as moved or renamed. If
231 used to check which files were identified as moved or renamed. If
232 not specified, -s/--similarity defaults to 100 and only renames of
232 not specified, -s/--similarity defaults to 100 and only renames of
233 identical files are detected.
233 identical files are detected.
234
234
235 .. container:: verbose
235 .. container:: verbose
236
236
237 Examples:
237 Examples:
238
238
239 - A number of files (bar.c and foo.c) are new,
239 - A number of files (bar.c and foo.c) are new,
240 while foobar.c has been removed (without using :hg:`remove`)
240 while foobar.c has been removed (without using :hg:`remove`)
241 from the repository::
241 from the repository::
242
242
243 $ ls
243 $ ls
244 bar.c foo.c
244 bar.c foo.c
245 $ hg status
245 $ hg status
246 ! foobar.c
246 ! foobar.c
247 ? bar.c
247 ? bar.c
248 ? foo.c
248 ? foo.c
249 $ hg addremove
249 $ hg addremove
250 adding bar.c
250 adding bar.c
251 adding foo.c
251 adding foo.c
252 removing foobar.c
252 removing foobar.c
253 $ hg status
253 $ hg status
254 A bar.c
254 A bar.c
255 A foo.c
255 A foo.c
256 R foobar.c
256 R foobar.c
257
257
258 - A file foobar.c was moved to foo.c without using :hg:`rename`.
258 - A file foobar.c was moved to foo.c without using :hg:`rename`.
259 Afterwards, it was edited slightly::
259 Afterwards, it was edited slightly::
260
260
261 $ ls
261 $ ls
262 foo.c
262 foo.c
263 $ hg status
263 $ hg status
264 ! foobar.c
264 ! foobar.c
265 ? foo.c
265 ? foo.c
266 $ hg addremove --similarity 90
266 $ hg addremove --similarity 90
267 removing foobar.c
267 removing foobar.c
268 adding foo.c
268 adding foo.c
269 recording removal of foobar.c as rename to foo.c (94% similar)
269 recording removal of foobar.c as rename to foo.c (94% similar)
270 $ hg status -C
270 $ hg status -C
271 A foo.c
271 A foo.c
272 foobar.c
272 foobar.c
273 R foobar.c
273 R foobar.c
274
274
275 Returns 0 if all files are successfully added.
275 Returns 0 if all files are successfully added.
276 """
276 """
277 opts = pycompat.byteskwargs(opts)
277 opts = pycompat.byteskwargs(opts)
278 if not opts.get('similarity'):
278 if not opts.get('similarity'):
279 opts['similarity'] = '100'
279 opts['similarity'] = '100'
280 matcher = scmutil.match(repo[None], pats, opts)
280 matcher = scmutil.match(repo[None], pats, opts)
281 relative = scmutil.anypats(pats, opts)
281 relative = scmutil.anypats(pats, opts)
282 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
282 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
283 return scmutil.addremove(repo, matcher, "", uipathfn, opts)
283 return scmutil.addremove(repo, matcher, "", uipathfn, opts)
284
284
285 @command('annotate|blame',
285 @command('annotate|blame',
286 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
286 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
287 ('', 'follow', None,
287 ('', 'follow', None,
288 _('follow copies/renames and list the filename (DEPRECATED)')),
288 _('follow copies/renames and list the filename (DEPRECATED)')),
289 ('', 'no-follow', None, _("don't follow copies and renames")),
289 ('', 'no-follow', None, _("don't follow copies and renames")),
290 ('a', 'text', None, _('treat all files as text')),
290 ('a', 'text', None, _('treat all files as text')),
291 ('u', 'user', None, _('list the author (long with -v)')),
291 ('u', 'user', None, _('list the author (long with -v)')),
292 ('f', 'file', None, _('list the filename')),
292 ('f', 'file', None, _('list the filename')),
293 ('d', 'date', None, _('list the date (short with -q)')),
293 ('d', 'date', None, _('list the date (short with -q)')),
294 ('n', 'number', None, _('list the revision number (default)')),
294 ('n', 'number', None, _('list the revision number (default)')),
295 ('c', 'changeset', None, _('list the changeset')),
295 ('c', 'changeset', None, _('list the changeset')),
296 ('l', 'line-number', None, _('show line number at the first appearance')),
296 ('l', 'line-number', None, _('show line number at the first appearance')),
297 ('', 'skip', [], _('revision to not display (EXPERIMENTAL)'), _('REV')),
297 ('', 'skip', [], _('revision to not display (EXPERIMENTAL)'), _('REV')),
298 ] + diffwsopts + walkopts + formatteropts,
298 ] + diffwsopts + walkopts + formatteropts,
299 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
299 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
300 helpcategory=command.CATEGORY_FILE_CONTENTS,
300 helpcategory=command.CATEGORY_FILE_CONTENTS,
301 helpbasic=True, inferrepo=True)
301 helpbasic=True, inferrepo=True)
302 def annotate(ui, repo, *pats, **opts):
302 def annotate(ui, repo, *pats, **opts):
303 """show changeset information by line for each file
303 """show changeset information by line for each file
304
304
305 List changes in files, showing the revision id responsible for
305 List changes in files, showing the revision id responsible for
306 each line.
306 each line.
307
307
308 This command is useful for discovering when a change was made and
308 This command is useful for discovering when a change was made and
309 by whom.
309 by whom.
310
310
311 If you include --file, --user, or --date, the revision number is
311 If you include --file, --user, or --date, the revision number is
312 suppressed unless you also include --number.
312 suppressed unless you also include --number.
313
313
314 Without the -a/--text option, annotate will avoid processing files
314 Without the -a/--text option, annotate will avoid processing files
315 it detects as binary. With -a, annotate will annotate the file
315 it detects as binary. With -a, annotate will annotate the file
316 anyway, although the results will probably be neither useful
316 anyway, although the results will probably be neither useful
317 nor desirable.
317 nor desirable.
318
318
319 .. container:: verbose
319 .. container:: verbose
320
320
321 Template:
321 Template:
322
322
323 The following keywords are supported in addition to the common template
323 The following keywords are supported in addition to the common template
324 keywords and functions. See also :hg:`help templates`.
324 keywords and functions. See also :hg:`help templates`.
325
325
326 :lines: List of lines with annotation data.
326 :lines: List of lines with annotation data.
327 :path: String. Repository-absolute path of the specified file.
327 :path: String. Repository-absolute path of the specified file.
328
328
329 And each entry of ``{lines}`` provides the following sub-keywords in
329 And each entry of ``{lines}`` provides the following sub-keywords in
330 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
330 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
331
331
332 :line: String. Line content.
332 :line: String. Line content.
333 :lineno: Integer. Line number at that revision.
333 :lineno: Integer. Line number at that revision.
334 :path: String. Repository-absolute path of the file at that revision.
334 :path: String. Repository-absolute path of the file at that revision.
335
335
336 See :hg:`help templates.operators` for the list expansion syntax.
336 See :hg:`help templates.operators` for the list expansion syntax.
337
337
338 Returns 0 on success.
338 Returns 0 on success.
339 """
339 """
340 opts = pycompat.byteskwargs(opts)
340 opts = pycompat.byteskwargs(opts)
341 if not pats:
341 if not pats:
342 raise error.Abort(_('at least one filename or pattern is required'))
342 raise error.Abort(_('at least one filename or pattern is required'))
343
343
344 if opts.get('follow'):
344 if opts.get('follow'):
345 # --follow is deprecated and now just an alias for -f/--file
345 # --follow is deprecated and now just an alias for -f/--file
346 # to mimic the behavior of Mercurial before version 1.5
346 # to mimic the behavior of Mercurial before version 1.5
347 opts['file'] = True
347 opts['file'] = True
348
348
349 if (not opts.get('user') and not opts.get('changeset')
349 if (not opts.get('user') and not opts.get('changeset')
350 and not opts.get('date') and not opts.get('file')):
350 and not opts.get('date') and not opts.get('file')):
351 opts['number'] = True
351 opts['number'] = True
352
352
353 linenumber = opts.get('line_number') is not None
353 linenumber = opts.get('line_number') is not None
354 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
354 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
355 raise error.Abort(_('at least one of -n/-c is required for -l'))
355 raise error.Abort(_('at least one of -n/-c is required for -l'))
356
356
357 rev = opts.get('rev')
357 rev = opts.get('rev')
358 if rev:
358 if rev:
359 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
359 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
360 ctx = scmutil.revsingle(repo, rev)
360 ctx = scmutil.revsingle(repo, rev)
361
361
362 ui.pager('annotate')
362 ui.pager('annotate')
363 rootfm = ui.formatter('annotate', opts)
363 rootfm = ui.formatter('annotate', opts)
364 if ui.debugflag:
364 if ui.debugflag:
365 shorthex = pycompat.identity
365 shorthex = pycompat.identity
366 else:
366 else:
367 def shorthex(h):
367 def shorthex(h):
368 return h[:12]
368 return h[:12]
369 if ui.quiet:
369 if ui.quiet:
370 datefunc = dateutil.shortdate
370 datefunc = dateutil.shortdate
371 else:
371 else:
372 datefunc = dateutil.datestr
372 datefunc = dateutil.datestr
373 if ctx.rev() is None:
373 if ctx.rev() is None:
374 if opts.get('changeset'):
374 if opts.get('changeset'):
375 # omit "+" suffix which is appended to node hex
375 # omit "+" suffix which is appended to node hex
376 def formatrev(rev):
376 def formatrev(rev):
377 if rev == wdirrev:
377 if rev == wdirrev:
378 return '%d' % ctx.p1().rev()
378 return '%d' % ctx.p1().rev()
379 else:
379 else:
380 return '%d' % rev
380 return '%d' % rev
381 else:
381 else:
382 def formatrev(rev):
382 def formatrev(rev):
383 if rev == wdirrev:
383 if rev == wdirrev:
384 return '%d+' % ctx.p1().rev()
384 return '%d+' % ctx.p1().rev()
385 else:
385 else:
386 return '%d ' % rev
386 return '%d ' % rev
387 def formathex(h):
387 def formathex(h):
388 if h == wdirhex:
388 if h == wdirhex:
389 return '%s+' % shorthex(hex(ctx.p1().node()))
389 return '%s+' % shorthex(hex(ctx.p1().node()))
390 else:
390 else:
391 return '%s ' % shorthex(h)
391 return '%s ' % shorthex(h)
392 else:
392 else:
393 formatrev = b'%d'.__mod__
393 formatrev = b'%d'.__mod__
394 formathex = shorthex
394 formathex = shorthex
395
395
396 opmap = [
396 opmap = [
397 ('user', ' ', lambda x: x.fctx.user(), ui.shortuser),
397 ('user', ' ', lambda x: x.fctx.user(), ui.shortuser),
398 ('rev', ' ', lambda x: scmutil.intrev(x.fctx), formatrev),
398 ('rev', ' ', lambda x: scmutil.intrev(x.fctx), formatrev),
399 ('node', ' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
399 ('node', ' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
400 ('date', ' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
400 ('date', ' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
401 ('path', ' ', lambda x: x.fctx.path(), pycompat.bytestr),
401 ('path', ' ', lambda x: x.fctx.path(), pycompat.bytestr),
402 ('lineno', ':', lambda x: x.lineno, pycompat.bytestr),
402 ('lineno', ':', lambda x: x.lineno, pycompat.bytestr),
403 ]
403 ]
404 opnamemap = {
404 opnamemap = {
405 'rev': 'number',
405 'rev': 'number',
406 'node': 'changeset',
406 'node': 'changeset',
407 'path': 'file',
407 'path': 'file',
408 'lineno': 'line_number',
408 'lineno': 'line_number',
409 }
409 }
410
410
411 if rootfm.isplain():
411 if rootfm.isplain():
412 def makefunc(get, fmt):
412 def makefunc(get, fmt):
413 return lambda x: fmt(get(x))
413 return lambda x: fmt(get(x))
414 else:
414 else:
415 def makefunc(get, fmt):
415 def makefunc(get, fmt):
416 return get
416 return get
417 datahint = rootfm.datahint()
417 datahint = rootfm.datahint()
418 funcmap = [(makefunc(get, fmt), sep) for fn, sep, get, fmt in opmap
418 funcmap = [(makefunc(get, fmt), sep) for fn, sep, get, fmt in opmap
419 if opts.get(opnamemap.get(fn, fn)) or fn in datahint]
419 if opts.get(opnamemap.get(fn, fn)) or fn in datahint]
420 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
420 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
421 fields = ' '.join(fn for fn, sep, get, fmt in opmap
421 fields = ' '.join(fn for fn, sep, get, fmt in opmap
422 if opts.get(opnamemap.get(fn, fn)) or fn in datahint)
422 if opts.get(opnamemap.get(fn, fn)) or fn in datahint)
423
423
424 def bad(x, y):
424 def bad(x, y):
425 raise error.Abort("%s: %s" % (x, y))
425 raise error.Abort("%s: %s" % (x, y))
426
426
427 m = scmutil.match(ctx, pats, opts, badfn=bad)
427 m = scmutil.match(ctx, pats, opts, badfn=bad)
428
428
429 follow = not opts.get('no_follow')
429 follow = not opts.get('no_follow')
430 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
430 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
431 whitespace=True)
431 whitespace=True)
432 skiprevs = opts.get('skip')
432 skiprevs = opts.get('skip')
433 if skiprevs:
433 if skiprevs:
434 skiprevs = scmutil.revrange(repo, skiprevs)
434 skiprevs = scmutil.revrange(repo, skiprevs)
435
435
436 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
436 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
437 for abs in ctx.walk(m):
437 for abs in ctx.walk(m):
438 fctx = ctx[abs]
438 fctx = ctx[abs]
439 rootfm.startitem()
439 rootfm.startitem()
440 rootfm.data(path=abs)
440 rootfm.data(path=abs)
441 if not opts.get('text') and fctx.isbinary():
441 if not opts.get('text') and fctx.isbinary():
442 rootfm.plain(_("%s: binary file\n") % uipathfn(abs))
442 rootfm.plain(_("%s: binary file\n") % uipathfn(abs))
443 continue
443 continue
444
444
445 fm = rootfm.nested('lines', tmpl='{rev}: {line}')
445 fm = rootfm.nested('lines', tmpl='{rev}: {line}')
446 lines = fctx.annotate(follow=follow, skiprevs=skiprevs,
446 lines = fctx.annotate(follow=follow, skiprevs=skiprevs,
447 diffopts=diffopts)
447 diffopts=diffopts)
448 if not lines:
448 if not lines:
449 fm.end()
449 fm.end()
450 continue
450 continue
451 formats = []
451 formats = []
452 pieces = []
452 pieces = []
453
453
454 for f, sep in funcmap:
454 for f, sep in funcmap:
455 l = [f(n) for n in lines]
455 l = [f(n) for n in lines]
456 if fm.isplain():
456 if fm.isplain():
457 sizes = [encoding.colwidth(x) for x in l]
457 sizes = [encoding.colwidth(x) for x in l]
458 ml = max(sizes)
458 ml = max(sizes)
459 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
459 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
460 else:
460 else:
461 formats.append(['%s' for x in l])
461 formats.append(['%s' for x in l])
462 pieces.append(l)
462 pieces.append(l)
463
463
464 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
464 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
465 fm.startitem()
465 fm.startitem()
466 fm.context(fctx=n.fctx)
466 fm.context(fctx=n.fctx)
467 fm.write(fields, "".join(f), *p)
467 fm.write(fields, "".join(f), *p)
468 if n.skip:
468 if n.skip:
469 fmt = "* %s"
469 fmt = "* %s"
470 else:
470 else:
471 fmt = ": %s"
471 fmt = ": %s"
472 fm.write('line', fmt, n.text)
472 fm.write('line', fmt, n.text)
473
473
474 if not lines[-1].text.endswith('\n'):
474 if not lines[-1].text.endswith('\n'):
475 fm.plain('\n')
475 fm.plain('\n')
476 fm.end()
476 fm.end()
477
477
478 rootfm.end()
478 rootfm.end()
479
479
480 @command('archive',
480 @command('archive',
481 [('', 'no-decode', None, _('do not pass files through decoders')),
481 [('', 'no-decode', None, _('do not pass files through decoders')),
482 ('p', 'prefix', '', _('directory prefix for files in archive'),
482 ('p', 'prefix', '', _('directory prefix for files in archive'),
483 _('PREFIX')),
483 _('PREFIX')),
484 ('r', 'rev', '', _('revision to distribute'), _('REV')),
484 ('r', 'rev', '', _('revision to distribute'), _('REV')),
485 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
485 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
486 ] + subrepoopts + walkopts,
486 ] + subrepoopts + walkopts,
487 _('[OPTION]... DEST'),
487 _('[OPTION]... DEST'),
488 helpcategory=command.CATEGORY_IMPORT_EXPORT)
488 helpcategory=command.CATEGORY_IMPORT_EXPORT)
489 def archive(ui, repo, dest, **opts):
489 def archive(ui, repo, dest, **opts):
490 '''create an unversioned archive of a repository revision
490 '''create an unversioned archive of a repository revision
491
491
492 By default, the revision used is the parent of the working
492 By default, the revision used is the parent of the working
493 directory; use -r/--rev to specify a different revision.
493 directory; use -r/--rev to specify a different revision.
494
494
495 The archive type is automatically detected based on file
495 The archive type is automatically detected based on file
496 extension (to override, use -t/--type).
496 extension (to override, use -t/--type).
497
497
498 .. container:: verbose
498 .. container:: verbose
499
499
500 Examples:
500 Examples:
501
501
502 - create a zip file containing the 1.0 release::
502 - create a zip file containing the 1.0 release::
503
503
504 hg archive -r 1.0 project-1.0.zip
504 hg archive -r 1.0 project-1.0.zip
505
505
506 - create a tarball excluding .hg files::
506 - create a tarball excluding .hg files::
507
507
508 hg archive project.tar.gz -X ".hg*"
508 hg archive project.tar.gz -X ".hg*"
509
509
510 Valid types are:
510 Valid types are:
511
511
512 :``files``: a directory full of files (default)
512 :``files``: a directory full of files (default)
513 :``tar``: tar archive, uncompressed
513 :``tar``: tar archive, uncompressed
514 :``tbz2``: tar archive, compressed using bzip2
514 :``tbz2``: tar archive, compressed using bzip2
515 :``tgz``: tar archive, compressed using gzip
515 :``tgz``: tar archive, compressed using gzip
516 :``uzip``: zip archive, uncompressed
516 :``uzip``: zip archive, uncompressed
517 :``zip``: zip archive, compressed using deflate
517 :``zip``: zip archive, compressed using deflate
518
518
519 The exact name of the destination archive or directory is given
519 The exact name of the destination archive or directory is given
520 using a format string; see :hg:`help export` for details.
520 using a format string; see :hg:`help export` for details.
521
521
522 Each member added to an archive file has a directory prefix
522 Each member added to an archive file has a directory prefix
523 prepended. Use -p/--prefix to specify a format string for the
523 prepended. Use -p/--prefix to specify a format string for the
524 prefix. The default is the basename of the archive, with suffixes
524 prefix. The default is the basename of the archive, with suffixes
525 removed.
525 removed.
526
526
527 Returns 0 on success.
527 Returns 0 on success.
528 '''
528 '''
529
529
530 opts = pycompat.byteskwargs(opts)
530 opts = pycompat.byteskwargs(opts)
531 rev = opts.get('rev')
531 rev = opts.get('rev')
532 if rev:
532 if rev:
533 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
533 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
534 ctx = scmutil.revsingle(repo, rev)
534 ctx = scmutil.revsingle(repo, rev)
535 if not ctx:
535 if not ctx:
536 raise error.Abort(_('no working directory: please specify a revision'))
536 raise error.Abort(_('no working directory: please specify a revision'))
537 node = ctx.node()
537 node = ctx.node()
538 dest = cmdutil.makefilename(ctx, dest)
538 dest = cmdutil.makefilename(ctx, dest)
539 if os.path.realpath(dest) == repo.root:
539 if os.path.realpath(dest) == repo.root:
540 raise error.Abort(_('repository root cannot be destination'))
540 raise error.Abort(_('repository root cannot be destination'))
541
541
542 kind = opts.get('type') or archival.guesskind(dest) or 'files'
542 kind = opts.get('type') or archival.guesskind(dest) or 'files'
543 prefix = opts.get('prefix')
543 prefix = opts.get('prefix')
544
544
545 if dest == '-':
545 if dest == '-':
546 if kind == 'files':
546 if kind == 'files':
547 raise error.Abort(_('cannot archive plain files to stdout'))
547 raise error.Abort(_('cannot archive plain files to stdout'))
548 dest = cmdutil.makefileobj(ctx, dest)
548 dest = cmdutil.makefileobj(ctx, dest)
549 if not prefix:
549 if not prefix:
550 prefix = os.path.basename(repo.root) + '-%h'
550 prefix = os.path.basename(repo.root) + '-%h'
551
551
552 prefix = cmdutil.makefilename(ctx, prefix)
552 prefix = cmdutil.makefilename(ctx, prefix)
553 match = scmutil.match(ctx, [], opts)
553 match = scmutil.match(ctx, [], opts)
554 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
554 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
555 match, prefix, subrepos=opts.get('subrepos'))
555 match, prefix, subrepos=opts.get('subrepos'))
556
556
557 @command('backout',
557 @command('backout',
558 [('', 'merge', None, _('merge with old dirstate parent after backout')),
558 [('', 'merge', None, _('merge with old dirstate parent after backout')),
559 ('', 'commit', None,
559 ('', 'commit', None,
560 _('commit if no conflicts were encountered (DEPRECATED)')),
560 _('commit if no conflicts were encountered (DEPRECATED)')),
561 ('', 'no-commit', None, _('do not commit')),
561 ('', 'no-commit', None, _('do not commit')),
562 ('', 'parent', '',
562 ('', 'parent', '',
563 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
563 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
564 ('r', 'rev', '', _('revision to backout'), _('REV')),
564 ('r', 'rev', '', _('revision to backout'), _('REV')),
565 ('e', 'edit', False, _('invoke editor on commit messages')),
565 ('e', 'edit', False, _('invoke editor on commit messages')),
566 ] + mergetoolopts + walkopts + commitopts + commitopts2,
566 ] + mergetoolopts + walkopts + commitopts + commitopts2,
567 _('[OPTION]... [-r] REV'),
567 _('[OPTION]... [-r] REV'),
568 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
568 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
569 def backout(ui, repo, node=None, rev=None, **opts):
569 def backout(ui, repo, node=None, rev=None, **opts):
570 '''reverse effect of earlier changeset
570 '''reverse effect of earlier changeset
571
571
572 Prepare a new changeset with the effect of REV undone in the
572 Prepare a new changeset with the effect of REV undone in the
573 current working directory. If no conflicts were encountered,
573 current working directory. If no conflicts were encountered,
574 it will be committed immediately.
574 it will be committed immediately.
575
575
576 If REV is the parent of the working directory, then this new changeset
576 If REV is the parent of the working directory, then this new changeset
577 is committed automatically (unless --no-commit is specified).
577 is committed automatically (unless --no-commit is specified).
578
578
579 .. note::
579 .. note::
580
580
581 :hg:`backout` cannot be used to fix either an unwanted or
581 :hg:`backout` cannot be used to fix either an unwanted or
582 incorrect merge.
582 incorrect merge.
583
583
584 .. container:: verbose
584 .. container:: verbose
585
585
586 Examples:
586 Examples:
587
587
588 - Reverse the effect of the parent of the working directory.
588 - Reverse the effect of the parent of the working directory.
589 This backout will be committed immediately::
589 This backout will be committed immediately::
590
590
591 hg backout -r .
591 hg backout -r .
592
592
593 - Reverse the effect of previous bad revision 23::
593 - Reverse the effect of previous bad revision 23::
594
594
595 hg backout -r 23
595 hg backout -r 23
596
596
597 - Reverse the effect of previous bad revision 23 and
597 - Reverse the effect of previous bad revision 23 and
598 leave changes uncommitted::
598 leave changes uncommitted::
599
599
600 hg backout -r 23 --no-commit
600 hg backout -r 23 --no-commit
601 hg commit -m "Backout revision 23"
601 hg commit -m "Backout revision 23"
602
602
603 By default, the pending changeset will have one parent,
603 By default, the pending changeset will have one parent,
604 maintaining a linear history. With --merge, the pending
604 maintaining a linear history. With --merge, the pending
605 changeset will instead have two parents: the old parent of the
605 changeset will instead have two parents: the old parent of the
606 working directory and a new child of REV that simply undoes REV.
606 working directory and a new child of REV that simply undoes REV.
607
607
608 Before version 1.7, the behavior without --merge was equivalent
608 Before version 1.7, the behavior without --merge was equivalent
609 to specifying --merge followed by :hg:`update --clean .` to
609 to specifying --merge followed by :hg:`update --clean .` to
610 cancel the merge and leave the child of REV as a head to be
610 cancel the merge and leave the child of REV as a head to be
611 merged separately.
611 merged separately.
612
612
613 See :hg:`help dates` for a list of formats valid for -d/--date.
613 See :hg:`help dates` for a list of formats valid for -d/--date.
614
614
615 See :hg:`help revert` for a way to restore files to the state
615 See :hg:`help revert` for a way to restore files to the state
616 of another revision.
616 of another revision.
617
617
618 Returns 0 on success, 1 if nothing to backout or there are unresolved
618 Returns 0 on success, 1 if nothing to backout or there are unresolved
619 files.
619 files.
620 '''
620 '''
621 with repo.wlock(), repo.lock():
621 with repo.wlock(), repo.lock():
622 return _dobackout(ui, repo, node, rev, **opts)
622 return _dobackout(ui, repo, node, rev, **opts)
623
623
624 def _dobackout(ui, repo, node=None, rev=None, **opts):
624 def _dobackout(ui, repo, node=None, rev=None, **opts):
625 opts = pycompat.byteskwargs(opts)
625 opts = pycompat.byteskwargs(opts)
626 if opts.get('commit') and opts.get('no_commit'):
626 if opts.get('commit') and opts.get('no_commit'):
627 raise error.Abort(_("cannot use --commit with --no-commit"))
627 raise error.Abort(_("cannot use --commit with --no-commit"))
628 if opts.get('merge') and opts.get('no_commit'):
628 if opts.get('merge') and opts.get('no_commit'):
629 raise error.Abort(_("cannot use --merge with --no-commit"))
629 raise error.Abort(_("cannot use --merge with --no-commit"))
630
630
631 if rev and node:
631 if rev and node:
632 raise error.Abort(_("please specify just one revision"))
632 raise error.Abort(_("please specify just one revision"))
633
633
634 if not rev:
634 if not rev:
635 rev = node
635 rev = node
636
636
637 if not rev:
637 if not rev:
638 raise error.Abort(_("please specify a revision to backout"))
638 raise error.Abort(_("please specify a revision to backout"))
639
639
640 date = opts.get('date')
640 date = opts.get('date')
641 if date:
641 if date:
642 opts['date'] = dateutil.parsedate(date)
642 opts['date'] = dateutil.parsedate(date)
643
643
644 cmdutil.checkunfinished(repo)
644 cmdutil.checkunfinished(repo)
645 cmdutil.bailifchanged(repo)
645 cmdutil.bailifchanged(repo)
646 node = scmutil.revsingle(repo, rev).node()
646 node = scmutil.revsingle(repo, rev).node()
647
647
648 op1, op2 = repo.dirstate.parents()
648 op1, op2 = repo.dirstate.parents()
649 if not repo.changelog.isancestor(node, op1):
649 if not repo.changelog.isancestor(node, op1):
650 raise error.Abort(_('cannot backout change that is not an ancestor'))
650 raise error.Abort(_('cannot backout change that is not an ancestor'))
651
651
652 p1, p2 = repo.changelog.parents(node)
652 p1, p2 = repo.changelog.parents(node)
653 if p1 == nullid:
653 if p1 == nullid:
654 raise error.Abort(_('cannot backout a change with no parents'))
654 raise error.Abort(_('cannot backout a change with no parents'))
655 if p2 != nullid:
655 if p2 != nullid:
656 if not opts.get('parent'):
656 if not opts.get('parent'):
657 raise error.Abort(_('cannot backout a merge changeset'))
657 raise error.Abort(_('cannot backout a merge changeset'))
658 p = repo.lookup(opts['parent'])
658 p = repo.lookup(opts['parent'])
659 if p not in (p1, p2):
659 if p not in (p1, p2):
660 raise error.Abort(_('%s is not a parent of %s') %
660 raise error.Abort(_('%s is not a parent of %s') %
661 (short(p), short(node)))
661 (short(p), short(node)))
662 parent = p
662 parent = p
663 else:
663 else:
664 if opts.get('parent'):
664 if opts.get('parent'):
665 raise error.Abort(_('cannot use --parent on non-merge changeset'))
665 raise error.Abort(_('cannot use --parent on non-merge changeset'))
666 parent = p1
666 parent = p1
667
667
668 # the backout should appear on the same branch
668 # the backout should appear on the same branch
669 branch = repo.dirstate.branch()
669 branch = repo.dirstate.branch()
670 bheads = repo.branchheads(branch)
670 bheads = repo.branchheads(branch)
671 rctx = scmutil.revsingle(repo, hex(parent))
671 rctx = scmutil.revsingle(repo, hex(parent))
672 if not opts.get('merge') and op1 != node:
672 if not opts.get('merge') and op1 != node:
673 with dirstateguard.dirstateguard(repo, 'backout'):
673 with dirstateguard.dirstateguard(repo, 'backout'):
674 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
674 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
675 with ui.configoverride(overrides, 'backout'):
675 with ui.configoverride(overrides, 'backout'):
676 stats = mergemod.update(repo, parent, branchmerge=True,
676 stats = mergemod.update(repo, parent, branchmerge=True,
677 force=True, ancestor=node,
677 force=True, ancestor=node,
678 mergeancestor=False)
678 mergeancestor=False)
679 repo.setparents(op1, op2)
679 repo.setparents(op1, op2)
680 hg._showstats(repo, stats)
680 hg._showstats(repo, stats)
681 if stats.unresolvedcount:
681 if stats.unresolvedcount:
682 repo.ui.status(_("use 'hg resolve' to retry unresolved "
682 repo.ui.status(_("use 'hg resolve' to retry unresolved "
683 "file merges\n"))
683 "file merges\n"))
684 return 1
684 return 1
685 else:
685 else:
686 hg.clean(repo, node, show_stats=False)
686 hg.clean(repo, node, show_stats=False)
687 repo.dirstate.setbranch(branch)
687 repo.dirstate.setbranch(branch)
688 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
688 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
689
689
690 if opts.get('no_commit'):
690 if opts.get('no_commit'):
691 msg = _("changeset %s backed out, "
691 msg = _("changeset %s backed out, "
692 "don't forget to commit.\n")
692 "don't forget to commit.\n")
693 ui.status(msg % short(node))
693 ui.status(msg % short(node))
694 return 0
694 return 0
695
695
696 def commitfunc(ui, repo, message, match, opts):
696 def commitfunc(ui, repo, message, match, opts):
697 editform = 'backout'
697 editform = 'backout'
698 e = cmdutil.getcommiteditor(editform=editform,
698 e = cmdutil.getcommiteditor(editform=editform,
699 **pycompat.strkwargs(opts))
699 **pycompat.strkwargs(opts))
700 if not message:
700 if not message:
701 # we don't translate commit messages
701 # we don't translate commit messages
702 message = "Backed out changeset %s" % short(node)
702 message = "Backed out changeset %s" % short(node)
703 e = cmdutil.getcommiteditor(edit=True, editform=editform)
703 e = cmdutil.getcommiteditor(edit=True, editform=editform)
704 return repo.commit(message, opts.get('user'), opts.get('date'),
704 return repo.commit(message, opts.get('user'), opts.get('date'),
705 match, editor=e)
705 match, editor=e)
706 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
706 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
707 if not newnode:
707 if not newnode:
708 ui.status(_("nothing changed\n"))
708 ui.status(_("nothing changed\n"))
709 return 1
709 return 1
710 cmdutil.commitstatus(repo, newnode, branch, bheads)
710 cmdutil.commitstatus(repo, newnode, branch, bheads)
711
711
712 def nice(node):
712 def nice(node):
713 return '%d:%s' % (repo.changelog.rev(node), short(node))
713 return '%d:%s' % (repo.changelog.rev(node), short(node))
714 ui.status(_('changeset %s backs out changeset %s\n') %
714 ui.status(_('changeset %s backs out changeset %s\n') %
715 (nice(repo.changelog.tip()), nice(node)))
715 (nice(repo.changelog.tip()), nice(node)))
716 if opts.get('merge') and op1 != node:
716 if opts.get('merge') and op1 != node:
717 hg.clean(repo, op1, show_stats=False)
717 hg.clean(repo, op1, show_stats=False)
718 ui.status(_('merging with changeset %s\n')
718 ui.status(_('merging with changeset %s\n')
719 % nice(repo.changelog.tip()))
719 % nice(repo.changelog.tip()))
720 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
720 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
721 with ui.configoverride(overrides, 'backout'):
721 with ui.configoverride(overrides, 'backout'):
722 return hg.merge(repo, hex(repo.changelog.tip()))
722 return hg.merge(repo, hex(repo.changelog.tip()))
723 return 0
723 return 0
724
724
725 @command('bisect',
725 @command('bisect',
726 [('r', 'reset', False, _('reset bisect state')),
726 [('r', 'reset', False, _('reset bisect state')),
727 ('g', 'good', False, _('mark changeset good')),
727 ('g', 'good', False, _('mark changeset good')),
728 ('b', 'bad', False, _('mark changeset bad')),
728 ('b', 'bad', False, _('mark changeset bad')),
729 ('s', 'skip', False, _('skip testing changeset')),
729 ('s', 'skip', False, _('skip testing changeset')),
730 ('e', 'extend', False, _('extend the bisect range')),
730 ('e', 'extend', False, _('extend the bisect range')),
731 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
731 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
732 ('U', 'noupdate', False, _('do not update to target'))],
732 ('U', 'noupdate', False, _('do not update to target'))],
733 _("[-gbsr] [-U] [-c CMD] [REV]"),
733 _("[-gbsr] [-U] [-c CMD] [REV]"),
734 helpcategory=command.CATEGORY_CHANGE_NAVIGATION)
734 helpcategory=command.CATEGORY_CHANGE_NAVIGATION)
735 def bisect(ui, repo, rev=None, extra=None, command=None,
735 def bisect(ui, repo, rev=None, extra=None, command=None,
736 reset=None, good=None, bad=None, skip=None, extend=None,
736 reset=None, good=None, bad=None, skip=None, extend=None,
737 noupdate=None):
737 noupdate=None):
738 """subdivision search of changesets
738 """subdivision search of changesets
739
739
740 This command helps to find changesets which introduce problems. To
740 This command helps to find changesets which introduce problems. To
741 use, mark the earliest changeset you know exhibits the problem as
741 use, mark the earliest changeset you know exhibits the problem as
742 bad, then mark the latest changeset which is free from the problem
742 bad, then mark the latest changeset which is free from the problem
743 as good. Bisect will update your working directory to a revision
743 as good. Bisect will update your working directory to a revision
744 for testing (unless the -U/--noupdate option is specified). Once
744 for testing (unless the -U/--noupdate option is specified). Once
745 you have performed tests, mark the working directory as good or
745 you have performed tests, mark the working directory as good or
746 bad, and bisect will either update to another candidate changeset
746 bad, and bisect will either update to another candidate changeset
747 or announce that it has found the bad revision.
747 or announce that it has found the bad revision.
748
748
749 As a shortcut, you can also use the revision argument to mark a
749 As a shortcut, you can also use the revision argument to mark a
750 revision as good or bad without checking it out first.
750 revision as good or bad without checking it out first.
751
751
752 If you supply a command, it will be used for automatic bisection.
752 If you supply a command, it will be used for automatic bisection.
753 The environment variable HG_NODE will contain the ID of the
753 The environment variable HG_NODE will contain the ID of the
754 changeset being tested. The exit status of the command will be
754 changeset being tested. The exit status of the command will be
755 used to mark revisions as good or bad: status 0 means good, 125
755 used to mark revisions as good or bad: status 0 means good, 125
756 means to skip the revision, 127 (command not found) will abort the
756 means to skip the revision, 127 (command not found) will abort the
757 bisection, and any other non-zero exit status means the revision
757 bisection, and any other non-zero exit status means the revision
758 is bad.
758 is bad.
759
759
760 .. container:: verbose
760 .. container:: verbose
761
761
762 Some examples:
762 Some examples:
763
763
764 - start a bisection with known bad revision 34, and good revision 12::
764 - start a bisection with known bad revision 34, and good revision 12::
765
765
766 hg bisect --bad 34
766 hg bisect --bad 34
767 hg bisect --good 12
767 hg bisect --good 12
768
768
769 - advance the current bisection by marking current revision as good or
769 - advance the current bisection by marking current revision as good or
770 bad::
770 bad::
771
771
772 hg bisect --good
772 hg bisect --good
773 hg bisect --bad
773 hg bisect --bad
774
774
775 - mark the current revision, or a known revision, to be skipped (e.g. if
775 - mark the current revision, or a known revision, to be skipped (e.g. if
776 that revision is not usable because of another issue)::
776 that revision is not usable because of another issue)::
777
777
778 hg bisect --skip
778 hg bisect --skip
779 hg bisect --skip 23
779 hg bisect --skip 23
780
780
781 - skip all revisions that do not touch directories ``foo`` or ``bar``::
781 - skip all revisions that do not touch directories ``foo`` or ``bar``::
782
782
783 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
783 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
784
784
785 - forget the current bisection::
785 - forget the current bisection::
786
786
787 hg bisect --reset
787 hg bisect --reset
788
788
789 - use 'make && make tests' to automatically find the first broken
789 - use 'make && make tests' to automatically find the first broken
790 revision::
790 revision::
791
791
792 hg bisect --reset
792 hg bisect --reset
793 hg bisect --bad 34
793 hg bisect --bad 34
794 hg bisect --good 12
794 hg bisect --good 12
795 hg bisect --command "make && make tests"
795 hg bisect --command "make && make tests"
796
796
797 - see all changesets whose states are already known in the current
797 - see all changesets whose states are already known in the current
798 bisection::
798 bisection::
799
799
800 hg log -r "bisect(pruned)"
800 hg log -r "bisect(pruned)"
801
801
802 - see the changeset currently being bisected (especially useful
802 - see the changeset currently being bisected (especially useful
803 if running with -U/--noupdate)::
803 if running with -U/--noupdate)::
804
804
805 hg log -r "bisect(current)"
805 hg log -r "bisect(current)"
806
806
807 - see all changesets that took part in the current bisection::
807 - see all changesets that took part in the current bisection::
808
808
809 hg log -r "bisect(range)"
809 hg log -r "bisect(range)"
810
810
811 - you can even get a nice graph::
811 - you can even get a nice graph::
812
812
813 hg log --graph -r "bisect(range)"
813 hg log --graph -r "bisect(range)"
814
814
815 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
815 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
816
816
817 Returns 0 on success.
817 Returns 0 on success.
818 """
818 """
819 # backward compatibility
819 # backward compatibility
820 if rev in "good bad reset init".split():
820 if rev in "good bad reset init".split():
821 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
821 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
822 cmd, rev, extra = rev, extra, None
822 cmd, rev, extra = rev, extra, None
823 if cmd == "good":
823 if cmd == "good":
824 good = True
824 good = True
825 elif cmd == "bad":
825 elif cmd == "bad":
826 bad = True
826 bad = True
827 else:
827 else:
828 reset = True
828 reset = True
829 elif extra:
829 elif extra:
830 raise error.Abort(_('incompatible arguments'))
830 raise error.Abort(_('incompatible arguments'))
831
831
832 incompatibles = {
832 incompatibles = {
833 '--bad': bad,
833 '--bad': bad,
834 '--command': bool(command),
834 '--command': bool(command),
835 '--extend': extend,
835 '--extend': extend,
836 '--good': good,
836 '--good': good,
837 '--reset': reset,
837 '--reset': reset,
838 '--skip': skip,
838 '--skip': skip,
839 }
839 }
840
840
841 enabled = [x for x in incompatibles if incompatibles[x]]
841 enabled = [x for x in incompatibles if incompatibles[x]]
842
842
843 if len(enabled) > 1:
843 if len(enabled) > 1:
844 raise error.Abort(_('%s and %s are incompatible') %
844 raise error.Abort(_('%s and %s are incompatible') %
845 tuple(sorted(enabled)[0:2]))
845 tuple(sorted(enabled)[0:2]))
846
846
847 if reset:
847 if reset:
848 hbisect.resetstate(repo)
848 hbisect.resetstate(repo)
849 return
849 return
850
850
851 state = hbisect.load_state(repo)
851 state = hbisect.load_state(repo)
852
852
853 # update state
853 # update state
854 if good or bad or skip:
854 if good or bad or skip:
855 if rev:
855 if rev:
856 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
856 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
857 else:
857 else:
858 nodes = [repo.lookup('.')]
858 nodes = [repo.lookup('.')]
859 if good:
859 if good:
860 state['good'] += nodes
860 state['good'] += nodes
861 elif bad:
861 elif bad:
862 state['bad'] += nodes
862 state['bad'] += nodes
863 elif skip:
863 elif skip:
864 state['skip'] += nodes
864 state['skip'] += nodes
865 hbisect.save_state(repo, state)
865 hbisect.save_state(repo, state)
866 if not (state['good'] and state['bad']):
866 if not (state['good'] and state['bad']):
867 return
867 return
868
868
869 def mayupdate(repo, node, show_stats=True):
869 def mayupdate(repo, node, show_stats=True):
870 """common used update sequence"""
870 """common used update sequence"""
871 if noupdate:
871 if noupdate:
872 return
872 return
873 cmdutil.checkunfinished(repo)
873 cmdutil.checkunfinished(repo)
874 cmdutil.bailifchanged(repo)
874 cmdutil.bailifchanged(repo)
875 return hg.clean(repo, node, show_stats=show_stats)
875 return hg.clean(repo, node, show_stats=show_stats)
876
876
877 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
877 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
878
878
879 if command:
879 if command:
880 changesets = 1
880 changesets = 1
881 if noupdate:
881 if noupdate:
882 try:
882 try:
883 node = state['current'][0]
883 node = state['current'][0]
884 except LookupError:
884 except LookupError:
885 raise error.Abort(_('current bisect revision is unknown - '
885 raise error.Abort(_('current bisect revision is unknown - '
886 'start a new bisect to fix'))
886 'start a new bisect to fix'))
887 else:
887 else:
888 node, p2 = repo.dirstate.parents()
888 node, p2 = repo.dirstate.parents()
889 if p2 != nullid:
889 if p2 != nullid:
890 raise error.Abort(_('current bisect revision is a merge'))
890 raise error.Abort(_('current bisect revision is a merge'))
891 if rev:
891 if rev:
892 node = repo[scmutil.revsingle(repo, rev, node)].node()
892 node = repo[scmutil.revsingle(repo, rev, node)].node()
893 try:
893 try:
894 while changesets:
894 while changesets:
895 # update state
895 # update state
896 state['current'] = [node]
896 state['current'] = [node]
897 hbisect.save_state(repo, state)
897 hbisect.save_state(repo, state)
898 status = ui.system(command, environ={'HG_NODE': hex(node)},
898 status = ui.system(command, environ={'HG_NODE': hex(node)},
899 blockedtag='bisect_check')
899 blockedtag='bisect_check')
900 if status == 125:
900 if status == 125:
901 transition = "skip"
901 transition = "skip"
902 elif status == 0:
902 elif status == 0:
903 transition = "good"
903 transition = "good"
904 # status < 0 means process was killed
904 # status < 0 means process was killed
905 elif status == 127:
905 elif status == 127:
906 raise error.Abort(_("failed to execute %s") % command)
906 raise error.Abort(_("failed to execute %s") % command)
907 elif status < 0:
907 elif status < 0:
908 raise error.Abort(_("%s killed") % command)
908 raise error.Abort(_("%s killed") % command)
909 else:
909 else:
910 transition = "bad"
910 transition = "bad"
911 state[transition].append(node)
911 state[transition].append(node)
912 ctx = repo[node]
912 ctx = repo[node]
913 ui.status(_('changeset %d:%s: %s\n') % (ctx.rev(), ctx,
913 ui.status(_('changeset %d:%s: %s\n') % (ctx.rev(), ctx,
914 transition))
914 transition))
915 hbisect.checkstate(state)
915 hbisect.checkstate(state)
916 # bisect
916 # bisect
917 nodes, changesets, bgood = hbisect.bisect(repo, state)
917 nodes, changesets, bgood = hbisect.bisect(repo, state)
918 # update to next check
918 # update to next check
919 node = nodes[0]
919 node = nodes[0]
920 mayupdate(repo, node, show_stats=False)
920 mayupdate(repo, node, show_stats=False)
921 finally:
921 finally:
922 state['current'] = [node]
922 state['current'] = [node]
923 hbisect.save_state(repo, state)
923 hbisect.save_state(repo, state)
924 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
924 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
925 return
925 return
926
926
927 hbisect.checkstate(state)
927 hbisect.checkstate(state)
928
928
929 # actually bisect
929 # actually bisect
930 nodes, changesets, good = hbisect.bisect(repo, state)
930 nodes, changesets, good = hbisect.bisect(repo, state)
931 if extend:
931 if extend:
932 if not changesets:
932 if not changesets:
933 extendnode = hbisect.extendrange(repo, state, nodes, good)
933 extendnode = hbisect.extendrange(repo, state, nodes, good)
934 if extendnode is not None:
934 if extendnode is not None:
935 ui.write(_("Extending search to changeset %d:%s\n")
935 ui.write(_("Extending search to changeset %d:%s\n")
936 % (extendnode.rev(), extendnode))
936 % (extendnode.rev(), extendnode))
937 state['current'] = [extendnode.node()]
937 state['current'] = [extendnode.node()]
938 hbisect.save_state(repo, state)
938 hbisect.save_state(repo, state)
939 return mayupdate(repo, extendnode.node())
939 return mayupdate(repo, extendnode.node())
940 raise error.Abort(_("nothing to extend"))
940 raise error.Abort(_("nothing to extend"))
941
941
942 if changesets == 0:
942 if changesets == 0:
943 hbisect.printresult(ui, repo, state, displayer, nodes, good)
943 hbisect.printresult(ui, repo, state, displayer, nodes, good)
944 else:
944 else:
945 assert len(nodes) == 1 # only a single node can be tested next
945 assert len(nodes) == 1 # only a single node can be tested next
946 node = nodes[0]
946 node = nodes[0]
947 # compute the approximate number of remaining tests
947 # compute the approximate number of remaining tests
948 tests, size = 0, 2
948 tests, size = 0, 2
949 while size <= changesets:
949 while size <= changesets:
950 tests, size = tests + 1, size * 2
950 tests, size = tests + 1, size * 2
951 rev = repo.changelog.rev(node)
951 rev = repo.changelog.rev(node)
952 ui.write(_("Testing changeset %d:%s "
952 ui.write(_("Testing changeset %d:%s "
953 "(%d changesets remaining, ~%d tests)\n")
953 "(%d changesets remaining, ~%d tests)\n")
954 % (rev, short(node), changesets, tests))
954 % (rev, short(node), changesets, tests))
955 state['current'] = [node]
955 state['current'] = [node]
956 hbisect.save_state(repo, state)
956 hbisect.save_state(repo, state)
957 return mayupdate(repo, node)
957 return mayupdate(repo, node)
958
958
959 @command('bookmarks|bookmark',
959 @command('bookmarks|bookmark',
960 [('f', 'force', False, _('force')),
960 [('f', 'force', False, _('force')),
961 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
961 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
962 ('d', 'delete', False, _('delete a given bookmark')),
962 ('d', 'delete', False, _('delete a given bookmark')),
963 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
963 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
964 ('i', 'inactive', False, _('mark a bookmark inactive')),
964 ('i', 'inactive', False, _('mark a bookmark inactive')),
965 ('l', 'list', False, _('list existing bookmarks')),
965 ('l', 'list', False, _('list existing bookmarks')),
966 ] + formatteropts,
966 ] + formatteropts,
967 _('hg bookmarks [OPTIONS]... [NAME]...'),
967 _('hg bookmarks [OPTIONS]... [NAME]...'),
968 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
968 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
969 def bookmark(ui, repo, *names, **opts):
969 def bookmark(ui, repo, *names, **opts):
970 '''create a new bookmark or list existing bookmarks
970 '''create a new bookmark or list existing bookmarks
971
971
972 Bookmarks are labels on changesets to help track lines of development.
972 Bookmarks are labels on changesets to help track lines of development.
973 Bookmarks are unversioned and can be moved, renamed and deleted.
973 Bookmarks are unversioned and can be moved, renamed and deleted.
974 Deleting or moving a bookmark has no effect on the associated changesets.
974 Deleting or moving a bookmark has no effect on the associated changesets.
975
975
976 Creating or updating to a bookmark causes it to be marked as 'active'.
976 Creating or updating to a bookmark causes it to be marked as 'active'.
977 The active bookmark is indicated with a '*'.
977 The active bookmark is indicated with a '*'.
978 When a commit is made, the active bookmark will advance to the new commit.
978 When a commit is made, the active bookmark will advance to the new commit.
979 A plain :hg:`update` will also advance an active bookmark, if possible.
979 A plain :hg:`update` will also advance an active bookmark, if possible.
980 Updating away from a bookmark will cause it to be deactivated.
980 Updating away from a bookmark will cause it to be deactivated.
981
981
982 Bookmarks can be pushed and pulled between repositories (see
982 Bookmarks can be pushed and pulled between repositories (see
983 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
983 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
984 diverged, a new 'divergent bookmark' of the form 'name@path' will
984 diverged, a new 'divergent bookmark' of the form 'name@path' will
985 be created. Using :hg:`merge` will resolve the divergence.
985 be created. Using :hg:`merge` will resolve the divergence.
986
986
987 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
987 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
988 the active bookmark's name.
988 the active bookmark's name.
989
989
990 A bookmark named '@' has the special property that :hg:`clone` will
990 A bookmark named '@' has the special property that :hg:`clone` will
991 check it out by default if it exists.
991 check it out by default if it exists.
992
992
993 .. container:: verbose
993 .. container:: verbose
994
994
995 Template:
995 Template:
996
996
997 The following keywords are supported in addition to the common template
997 The following keywords are supported in addition to the common template
998 keywords and functions such as ``{bookmark}``. See also
998 keywords and functions such as ``{bookmark}``. See also
999 :hg:`help templates`.
999 :hg:`help templates`.
1000
1000
1001 :active: Boolean. True if the bookmark is active.
1001 :active: Boolean. True if the bookmark is active.
1002
1002
1003 Examples:
1003 Examples:
1004
1004
1005 - create an active bookmark for a new line of development::
1005 - create an active bookmark for a new line of development::
1006
1006
1007 hg book new-feature
1007 hg book new-feature
1008
1008
1009 - create an inactive bookmark as a place marker::
1009 - create an inactive bookmark as a place marker::
1010
1010
1011 hg book -i reviewed
1011 hg book -i reviewed
1012
1012
1013 - create an inactive bookmark on another changeset::
1013 - create an inactive bookmark on another changeset::
1014
1014
1015 hg book -r .^ tested
1015 hg book -r .^ tested
1016
1016
1017 - rename bookmark turkey to dinner::
1017 - rename bookmark turkey to dinner::
1018
1018
1019 hg book -m turkey dinner
1019 hg book -m turkey dinner
1020
1020
1021 - move the '@' bookmark from another branch::
1021 - move the '@' bookmark from another branch::
1022
1022
1023 hg book -f @
1023 hg book -f @
1024
1024
1025 - print only the active bookmark name::
1025 - print only the active bookmark name::
1026
1026
1027 hg book -ql .
1027 hg book -ql .
1028 '''
1028 '''
1029 opts = pycompat.byteskwargs(opts)
1029 opts = pycompat.byteskwargs(opts)
1030 force = opts.get('force')
1030 force = opts.get('force')
1031 rev = opts.get('rev')
1031 rev = opts.get('rev')
1032 inactive = opts.get('inactive') # meaning add/rename to inactive bookmark
1032 inactive = opts.get('inactive') # meaning add/rename to inactive bookmark
1033
1033
1034 selactions = [k for k in ['delete', 'rename', 'list'] if opts.get(k)]
1034 selactions = [k for k in ['delete', 'rename', 'list'] if opts.get(k)]
1035 if len(selactions) > 1:
1035 if len(selactions) > 1:
1036 raise error.Abort(_('--%s and --%s are incompatible')
1036 raise error.Abort(_('--%s and --%s are incompatible')
1037 % tuple(selactions[:2]))
1037 % tuple(selactions[:2]))
1038 if selactions:
1038 if selactions:
1039 action = selactions[0]
1039 action = selactions[0]
1040 elif names or rev:
1040 elif names or rev:
1041 action = 'add'
1041 action = 'add'
1042 elif inactive:
1042 elif inactive:
1043 action = 'inactive' # meaning deactivate
1043 action = 'inactive' # meaning deactivate
1044 else:
1044 else:
1045 action = 'list'
1045 action = 'list'
1046
1046
1047 if rev and action in {'delete', 'rename', 'list'}:
1047 if rev and action in {'delete', 'rename', 'list'}:
1048 raise error.Abort(_("--rev is incompatible with --%s") % action)
1048 raise error.Abort(_("--rev is incompatible with --%s") % action)
1049 if inactive and action in {'delete', 'list'}:
1049 if inactive and action in {'delete', 'list'}:
1050 raise error.Abort(_("--inactive is incompatible with --%s") % action)
1050 raise error.Abort(_("--inactive is incompatible with --%s") % action)
1051 if not names and action in {'add', 'delete'}:
1051 if not names and action in {'add', 'delete'}:
1052 raise error.Abort(_("bookmark name required"))
1052 raise error.Abort(_("bookmark name required"))
1053
1053
1054 if action in {'add', 'delete', 'rename', 'inactive'}:
1054 if action in {'add', 'delete', 'rename', 'inactive'}:
1055 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
1055 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
1056 if action == 'delete':
1056 if action == 'delete':
1057 names = pycompat.maplist(repo._bookmarks.expandname, names)
1057 names = pycompat.maplist(repo._bookmarks.expandname, names)
1058 bookmarks.delete(repo, tr, names)
1058 bookmarks.delete(repo, tr, names)
1059 elif action == 'rename':
1059 elif action == 'rename':
1060 if not names:
1060 if not names:
1061 raise error.Abort(_("new bookmark name required"))
1061 raise error.Abort(_("new bookmark name required"))
1062 elif len(names) > 1:
1062 elif len(names) > 1:
1063 raise error.Abort(_("only one new bookmark name allowed"))
1063 raise error.Abort(_("only one new bookmark name allowed"))
1064 oldname = repo._bookmarks.expandname(opts['rename'])
1064 oldname = repo._bookmarks.expandname(opts['rename'])
1065 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1065 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1066 elif action == 'add':
1066 elif action == 'add':
1067 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1067 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1068 elif action == 'inactive':
1068 elif action == 'inactive':
1069 if len(repo._bookmarks) == 0:
1069 if len(repo._bookmarks) == 0:
1070 ui.status(_("no bookmarks set\n"))
1070 ui.status(_("no bookmarks set\n"))
1071 elif not repo._activebookmark:
1071 elif not repo._activebookmark:
1072 ui.status(_("no active bookmark\n"))
1072 ui.status(_("no active bookmark\n"))
1073 else:
1073 else:
1074 bookmarks.deactivate(repo)
1074 bookmarks.deactivate(repo)
1075 elif action == 'list':
1075 elif action == 'list':
1076 names = pycompat.maplist(repo._bookmarks.expandname, names)
1076 names = pycompat.maplist(repo._bookmarks.expandname, names)
1077 with ui.formatter('bookmarks', opts) as fm:
1077 with ui.formatter('bookmarks', opts) as fm:
1078 bookmarks.printbookmarks(ui, repo, fm, names)
1078 bookmarks.printbookmarks(ui, repo, fm, names)
1079 else:
1079 else:
1080 raise error.ProgrammingError('invalid action: %s' % action)
1080 raise error.ProgrammingError('invalid action: %s' % action)
1081
1081
1082 @command('branch',
1082 @command('branch',
1083 [('f', 'force', None,
1083 [('f', 'force', None,
1084 _('set branch name even if it shadows an existing branch')),
1084 _('set branch name even if it shadows an existing branch')),
1085 ('C', 'clean', None, _('reset branch name to parent branch name')),
1085 ('C', 'clean', None, _('reset branch name to parent branch name')),
1086 ('r', 'rev', [], _('change branches of the given revs (EXPERIMENTAL)')),
1086 ('r', 'rev', [], _('change branches of the given revs (EXPERIMENTAL)')),
1087 ],
1087 ],
1088 _('[-fC] [NAME]'),
1088 _('[-fC] [NAME]'),
1089 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
1089 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
1090 def branch(ui, repo, label=None, **opts):
1090 def branch(ui, repo, label=None, **opts):
1091 """set or show the current branch name
1091 """set or show the current branch name
1092
1092
1093 .. note::
1093 .. note::
1094
1094
1095 Branch names are permanent and global. Use :hg:`bookmark` to create a
1095 Branch names are permanent and global. Use :hg:`bookmark` to create a
1096 light-weight bookmark instead. See :hg:`help glossary` for more
1096 light-weight bookmark instead. See :hg:`help glossary` for more
1097 information about named branches and bookmarks.
1097 information about named branches and bookmarks.
1098
1098
1099 With no argument, show the current branch name. With one argument,
1099 With no argument, show the current branch name. With one argument,
1100 set the working directory branch name (the branch will not exist
1100 set the working directory branch name (the branch will not exist
1101 in the repository until the next commit). Standard practice
1101 in the repository until the next commit). Standard practice
1102 recommends that primary development take place on the 'default'
1102 recommends that primary development take place on the 'default'
1103 branch.
1103 branch.
1104
1104
1105 Unless -f/--force is specified, branch will not let you set a
1105 Unless -f/--force is specified, branch will not let you set a
1106 branch name that already exists.
1106 branch name that already exists.
1107
1107
1108 Use -C/--clean to reset the working directory branch to that of
1108 Use -C/--clean to reset the working directory branch to that of
1109 the parent of the working directory, negating a previous branch
1109 the parent of the working directory, negating a previous branch
1110 change.
1110 change.
1111
1111
1112 Use the command :hg:`update` to switch to an existing branch. Use
1112 Use the command :hg:`update` to switch to an existing branch. Use
1113 :hg:`commit --close-branch` to mark this branch head as closed.
1113 :hg:`commit --close-branch` to mark this branch head as closed.
1114 When all heads of a branch are closed, the branch will be
1114 When all heads of a branch are closed, the branch will be
1115 considered closed.
1115 considered closed.
1116
1116
1117 Returns 0 on success.
1117 Returns 0 on success.
1118 """
1118 """
1119 opts = pycompat.byteskwargs(opts)
1119 opts = pycompat.byteskwargs(opts)
1120 revs = opts.get('rev')
1120 revs = opts.get('rev')
1121 if label:
1121 if label:
1122 label = label.strip()
1122 label = label.strip()
1123
1123
1124 if not opts.get('clean') and not label:
1124 if not opts.get('clean') and not label:
1125 if revs:
1125 if revs:
1126 raise error.Abort(_("no branch name specified for the revisions"))
1126 raise error.Abort(_("no branch name specified for the revisions"))
1127 ui.write("%s\n" % repo.dirstate.branch())
1127 ui.write("%s\n" % repo.dirstate.branch())
1128 return
1128 return
1129
1129
1130 with repo.wlock():
1130 with repo.wlock():
1131 if opts.get('clean'):
1131 if opts.get('clean'):
1132 label = repo['.'].branch()
1132 label = repo['.'].branch()
1133 repo.dirstate.setbranch(label)
1133 repo.dirstate.setbranch(label)
1134 ui.status(_('reset working directory to branch %s\n') % label)
1134 ui.status(_('reset working directory to branch %s\n') % label)
1135 elif label:
1135 elif label:
1136
1136
1137 scmutil.checknewlabel(repo, label, 'branch')
1137 scmutil.checknewlabel(repo, label, 'branch')
1138 if revs:
1138 if revs:
1139 return cmdutil.changebranch(ui, repo, revs, label)
1139 return cmdutil.changebranch(ui, repo, revs, label)
1140
1140
1141 if not opts.get('force') and label in repo.branchmap():
1141 if not opts.get('force') and label in repo.branchmap():
1142 if label not in [p.branch() for p in repo[None].parents()]:
1142 if label not in [p.branch() for p in repo[None].parents()]:
1143 raise error.Abort(_('a branch of the same name already'
1143 raise error.Abort(_('a branch of the same name already'
1144 ' exists'),
1144 ' exists'),
1145 # i18n: "it" refers to an existing branch
1145 # i18n: "it" refers to an existing branch
1146 hint=_("use 'hg update' to switch to it"))
1146 hint=_("use 'hg update' to switch to it"))
1147
1147
1148 repo.dirstate.setbranch(label)
1148 repo.dirstate.setbranch(label)
1149 ui.status(_('marked working directory as branch %s\n') % label)
1149 ui.status(_('marked working directory as branch %s\n') % label)
1150
1150
1151 # find any open named branches aside from default
1151 # find any open named branches aside from default
1152 for n, h, t, c in repo.branchmap().iterbranches():
1152 for n, h, t, c in repo.branchmap().iterbranches():
1153 if n != "default" and not c:
1153 if n != "default" and not c:
1154 return 0
1154 return 0
1155 ui.status(_('(branches are permanent and global, '
1155 ui.status(_('(branches are permanent and global, '
1156 'did you want a bookmark?)\n'))
1156 'did you want a bookmark?)\n'))
1157
1157
1158 @command('branches',
1158 @command('branches',
1159 [('a', 'active', False,
1159 [('a', 'active', False,
1160 _('show only branches that have unmerged heads (DEPRECATED)')),
1160 _('show only branches that have unmerged heads (DEPRECATED)')),
1161 ('c', 'closed', False, _('show normal and closed branches')),
1161 ('c', 'closed', False, _('show normal and closed branches')),
1162 ('r', 'rev', [], _('show branch name(s) of the given rev'))
1162 ('r', 'rev', [], _('show branch name(s) of the given rev'))
1163 ] + formatteropts,
1163 ] + formatteropts,
1164 _('[-c]'),
1164 _('[-c]'),
1165 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1165 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1166 intents={INTENT_READONLY})
1166 intents={INTENT_READONLY})
1167 def branches(ui, repo, active=False, closed=False, **opts):
1167 def branches(ui, repo, active=False, closed=False, **opts):
1168 """list repository named branches
1168 """list repository named branches
1169
1169
1170 List the repository's named branches, indicating which ones are
1170 List the repository's named branches, indicating which ones are
1171 inactive. If -c/--closed is specified, also list branches which have
1171 inactive. If -c/--closed is specified, also list branches which have
1172 been marked closed (see :hg:`commit --close-branch`).
1172 been marked closed (see :hg:`commit --close-branch`).
1173
1173
1174 Use the command :hg:`update` to switch to an existing branch.
1174 Use the command :hg:`update` to switch to an existing branch.
1175
1175
1176 .. container:: verbose
1176 .. container:: verbose
1177
1177
1178 Template:
1178 Template:
1179
1179
1180 The following keywords are supported in addition to the common template
1180 The following keywords are supported in addition to the common template
1181 keywords and functions such as ``{branch}``. See also
1181 keywords and functions such as ``{branch}``. See also
1182 :hg:`help templates`.
1182 :hg:`help templates`.
1183
1183
1184 :active: Boolean. True if the branch is active.
1184 :active: Boolean. True if the branch is active.
1185 :closed: Boolean. True if the branch is closed.
1185 :closed: Boolean. True if the branch is closed.
1186 :current: Boolean. True if it is the current branch.
1186 :current: Boolean. True if it is the current branch.
1187
1187
1188 Returns 0.
1188 Returns 0.
1189 """
1189 """
1190
1190
1191 opts = pycompat.byteskwargs(opts)
1191 opts = pycompat.byteskwargs(opts)
1192 revs = opts.get('rev')
1192 revs = opts.get('rev')
1193 selectedbranches = None
1193 selectedbranches = None
1194 if revs:
1194 if revs:
1195 revs = scmutil.revrange(repo, revs)
1195 revs = scmutil.revrange(repo, revs)
1196 getbi = repo.revbranchcache().branchinfo
1196 getbi = repo.revbranchcache().branchinfo
1197 selectedbranches = {getbi(r)[0] for r in revs}
1197 selectedbranches = {getbi(r)[0] for r in revs}
1198
1198
1199 ui.pager('branches')
1199 ui.pager('branches')
1200 fm = ui.formatter('branches', opts)
1200 fm = ui.formatter('branches', opts)
1201 hexfunc = fm.hexfunc
1201 hexfunc = fm.hexfunc
1202
1202
1203 allheads = set(repo.heads())
1203 allheads = set(repo.heads())
1204 branches = []
1204 branches = []
1205 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1205 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1206 if selectedbranches is not None and tag not in selectedbranches:
1206 if selectedbranches is not None and tag not in selectedbranches:
1207 continue
1207 continue
1208 isactive = False
1208 isactive = False
1209 if not isclosed:
1209 if not isclosed:
1210 openheads = set(repo.branchmap().iteropen(heads))
1210 openheads = set(repo.branchmap().iteropen(heads))
1211 isactive = bool(openheads & allheads)
1211 isactive = bool(openheads & allheads)
1212 branches.append((tag, repo[tip], isactive, not isclosed))
1212 branches.append((tag, repo[tip], isactive, not isclosed))
1213 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1213 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1214 reverse=True)
1214 reverse=True)
1215
1215
1216 for tag, ctx, isactive, isopen in branches:
1216 for tag, ctx, isactive, isopen in branches:
1217 if active and not isactive:
1217 if active and not isactive:
1218 continue
1218 continue
1219 if isactive:
1219 if isactive:
1220 label = 'branches.active'
1220 label = 'branches.active'
1221 notice = ''
1221 notice = ''
1222 elif not isopen:
1222 elif not isopen:
1223 if not closed:
1223 if not closed:
1224 continue
1224 continue
1225 label = 'branches.closed'
1225 label = 'branches.closed'
1226 notice = _(' (closed)')
1226 notice = _(' (closed)')
1227 else:
1227 else:
1228 label = 'branches.inactive'
1228 label = 'branches.inactive'
1229 notice = _(' (inactive)')
1229 notice = _(' (inactive)')
1230 current = (tag == repo.dirstate.branch())
1230 current = (tag == repo.dirstate.branch())
1231 if current:
1231 if current:
1232 label = 'branches.current'
1232 label = 'branches.current'
1233
1233
1234 fm.startitem()
1234 fm.startitem()
1235 fm.write('branch', '%s', tag, label=label)
1235 fm.write('branch', '%s', tag, label=label)
1236 rev = ctx.rev()
1236 rev = ctx.rev()
1237 padsize = max(31 - len("%d" % rev) - encoding.colwidth(tag), 0)
1237 padsize = max(31 - len("%d" % rev) - encoding.colwidth(tag), 0)
1238 fmt = ' ' * padsize + ' %d:%s'
1238 fmt = ' ' * padsize + ' %d:%s'
1239 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1239 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1240 label='log.changeset changeset.%s' % ctx.phasestr())
1240 label='log.changeset changeset.%s' % ctx.phasestr())
1241 fm.context(ctx=ctx)
1241 fm.context(ctx=ctx)
1242 fm.data(active=isactive, closed=not isopen, current=current)
1242 fm.data(active=isactive, closed=not isopen, current=current)
1243 if not ui.quiet:
1243 if not ui.quiet:
1244 fm.plain(notice)
1244 fm.plain(notice)
1245 fm.plain('\n')
1245 fm.plain('\n')
1246 fm.end()
1246 fm.end()
1247
1247
1248 @command('bundle',
1248 @command('bundle',
1249 [('f', 'force', None, _('run even when the destination is unrelated')),
1249 [('f', 'force', None, _('run even when the destination is unrelated')),
1250 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1250 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1251 _('REV')),
1251 _('REV')),
1252 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1252 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1253 _('BRANCH')),
1253 _('BRANCH')),
1254 ('', 'base', [],
1254 ('', 'base', [],
1255 _('a base changeset assumed to be available at the destination'),
1255 _('a base changeset assumed to be available at the destination'),
1256 _('REV')),
1256 _('REV')),
1257 ('a', 'all', None, _('bundle all changesets in the repository')),
1257 ('a', 'all', None, _('bundle all changesets in the repository')),
1258 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1258 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1259 ] + remoteopts,
1259 ] + remoteopts,
1260 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1260 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1261 helpcategory=command.CATEGORY_IMPORT_EXPORT)
1261 helpcategory=command.CATEGORY_IMPORT_EXPORT)
1262 def bundle(ui, repo, fname, dest=None, **opts):
1262 def bundle(ui, repo, fname, dest=None, **opts):
1263 """create a bundle file
1263 """create a bundle file
1264
1264
1265 Generate a bundle file containing data to be transferred to another
1265 Generate a bundle file containing data to be transferred to another
1266 repository.
1266 repository.
1267
1267
1268 To create a bundle containing all changesets, use -a/--all
1268 To create a bundle containing all changesets, use -a/--all
1269 (or --base null). Otherwise, hg assumes the destination will have
1269 (or --base null). Otherwise, hg assumes the destination will have
1270 all the nodes you specify with --base parameters. Otherwise, hg
1270 all the nodes you specify with --base parameters. Otherwise, hg
1271 will assume the repository has all the nodes in destination, or
1271 will assume the repository has all the nodes in destination, or
1272 default-push/default if no destination is specified, where destination
1272 default-push/default if no destination is specified, where destination
1273 is the repository you provide through DEST option.
1273 is the repository you provide through DEST option.
1274
1274
1275 You can change bundle format with the -t/--type option. See
1275 You can change bundle format with the -t/--type option. See
1276 :hg:`help bundlespec` for documentation on this format. By default,
1276 :hg:`help bundlespec` for documentation on this format. By default,
1277 the most appropriate format is used and compression defaults to
1277 the most appropriate format is used and compression defaults to
1278 bzip2.
1278 bzip2.
1279
1279
1280 The bundle file can then be transferred using conventional means
1280 The bundle file can then be transferred using conventional means
1281 and applied to another repository with the unbundle or pull
1281 and applied to another repository with the unbundle or pull
1282 command. This is useful when direct push and pull are not
1282 command. This is useful when direct push and pull are not
1283 available or when exporting an entire repository is undesirable.
1283 available or when exporting an entire repository is undesirable.
1284
1284
1285 Applying bundles preserves all changeset contents including
1285 Applying bundles preserves all changeset contents including
1286 permissions, copy/rename information, and revision history.
1286 permissions, copy/rename information, and revision history.
1287
1287
1288 Returns 0 on success, 1 if no changes found.
1288 Returns 0 on success, 1 if no changes found.
1289 """
1289 """
1290 opts = pycompat.byteskwargs(opts)
1290 opts = pycompat.byteskwargs(opts)
1291 revs = None
1291 revs = None
1292 if 'rev' in opts:
1292 if 'rev' in opts:
1293 revstrings = opts['rev']
1293 revstrings = opts['rev']
1294 revs = scmutil.revrange(repo, revstrings)
1294 revs = scmutil.revrange(repo, revstrings)
1295 if revstrings and not revs:
1295 if revstrings and not revs:
1296 raise error.Abort(_('no commits to bundle'))
1296 raise error.Abort(_('no commits to bundle'))
1297
1297
1298 bundletype = opts.get('type', 'bzip2').lower()
1298 bundletype = opts.get('type', 'bzip2').lower()
1299 try:
1299 try:
1300 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1300 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1301 except error.UnsupportedBundleSpecification as e:
1301 except error.UnsupportedBundleSpecification as e:
1302 raise error.Abort(pycompat.bytestr(e),
1302 raise error.Abort(pycompat.bytestr(e),
1303 hint=_("see 'hg help bundlespec' for supported "
1303 hint=_("see 'hg help bundlespec' for supported "
1304 "values for --type"))
1304 "values for --type"))
1305 cgversion = bundlespec.contentopts["cg.version"]
1305 cgversion = bundlespec.contentopts["cg.version"]
1306
1306
1307 # Packed bundles are a pseudo bundle format for now.
1307 # Packed bundles are a pseudo bundle format for now.
1308 if cgversion == 's1':
1308 if cgversion == 's1':
1309 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1309 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1310 hint=_("use 'hg debugcreatestreamclonebundle'"))
1310 hint=_("use 'hg debugcreatestreamclonebundle'"))
1311
1311
1312 if opts.get('all'):
1312 if opts.get('all'):
1313 if dest:
1313 if dest:
1314 raise error.Abort(_("--all is incompatible with specifying "
1314 raise error.Abort(_("--all is incompatible with specifying "
1315 "a destination"))
1315 "a destination"))
1316 if opts.get('base'):
1316 if opts.get('base'):
1317 ui.warn(_("ignoring --base because --all was specified\n"))
1317 ui.warn(_("ignoring --base because --all was specified\n"))
1318 base = [nullrev]
1318 base = [nullrev]
1319 else:
1319 else:
1320 base = scmutil.revrange(repo, opts.get('base'))
1320 base = scmutil.revrange(repo, opts.get('base'))
1321 if cgversion not in changegroup.supportedoutgoingversions(repo):
1321 if cgversion not in changegroup.supportedoutgoingversions(repo):
1322 raise error.Abort(_("repository does not support bundle version %s") %
1322 raise error.Abort(_("repository does not support bundle version %s") %
1323 cgversion)
1323 cgversion)
1324
1324
1325 if base:
1325 if base:
1326 if dest:
1326 if dest:
1327 raise error.Abort(_("--base is incompatible with specifying "
1327 raise error.Abort(_("--base is incompatible with specifying "
1328 "a destination"))
1328 "a destination"))
1329 common = [repo[rev].node() for rev in base]
1329 common = [repo[rev].node() for rev in base]
1330 heads = [repo[r].node() for r in revs] if revs else None
1330 heads = [repo[r].node() for r in revs] if revs else None
1331 outgoing = discovery.outgoing(repo, common, heads)
1331 outgoing = discovery.outgoing(repo, common, heads)
1332 else:
1332 else:
1333 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1333 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1334 dest, branches = hg.parseurl(dest, opts.get('branch'))
1334 dest, branches = hg.parseurl(dest, opts.get('branch'))
1335 other = hg.peer(repo, opts, dest)
1335 other = hg.peer(repo, opts, dest)
1336 revs = [repo[r].hex() for r in revs]
1336 revs = [repo[r].hex() for r in revs]
1337 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1337 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1338 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1338 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1339 outgoing = discovery.findcommonoutgoing(repo, other,
1339 outgoing = discovery.findcommonoutgoing(repo, other,
1340 onlyheads=heads,
1340 onlyheads=heads,
1341 force=opts.get('force'),
1341 force=opts.get('force'),
1342 portable=True)
1342 portable=True)
1343
1343
1344 if not outgoing.missing:
1344 if not outgoing.missing:
1345 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1345 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1346 return 1
1346 return 1
1347
1347
1348 if cgversion == '01': #bundle1
1348 if cgversion == '01': #bundle1
1349 bversion = 'HG10' + bundlespec.wirecompression
1349 bversion = 'HG10' + bundlespec.wirecompression
1350 bcompression = None
1350 bcompression = None
1351 elif cgversion in ('02', '03'):
1351 elif cgversion in ('02', '03'):
1352 bversion = 'HG20'
1352 bversion = 'HG20'
1353 bcompression = bundlespec.wirecompression
1353 bcompression = bundlespec.wirecompression
1354 else:
1354 else:
1355 raise error.ProgrammingError(
1355 raise error.ProgrammingError(
1356 'bundle: unexpected changegroup version %s' % cgversion)
1356 'bundle: unexpected changegroup version %s' % cgversion)
1357
1357
1358 # TODO compression options should be derived from bundlespec parsing.
1358 # TODO compression options should be derived from bundlespec parsing.
1359 # This is a temporary hack to allow adjusting bundle compression
1359 # This is a temporary hack to allow adjusting bundle compression
1360 # level without a) formalizing the bundlespec changes to declare it
1360 # level without a) formalizing the bundlespec changes to declare it
1361 # b) introducing a command flag.
1361 # b) introducing a command flag.
1362 compopts = {}
1362 compopts = {}
1363 complevel = ui.configint('experimental',
1363 complevel = ui.configint('experimental',
1364 'bundlecomplevel.' + bundlespec.compression)
1364 'bundlecomplevel.' + bundlespec.compression)
1365 if complevel is None:
1365 if complevel is None:
1366 complevel = ui.configint('experimental', 'bundlecomplevel')
1366 complevel = ui.configint('experimental', 'bundlecomplevel')
1367 if complevel is not None:
1367 if complevel is not None:
1368 compopts['level'] = complevel
1368 compopts['level'] = complevel
1369
1369
1370 # Allow overriding the bundling of obsmarker in phases through
1370 # Allow overriding the bundling of obsmarker in phases through
1371 # configuration while we don't have a bundle version that include them
1371 # configuration while we don't have a bundle version that include them
1372 if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker'):
1372 if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker'):
1373 bundlespec.contentopts['obsolescence'] = True
1373 bundlespec.contentopts['obsolescence'] = True
1374 if repo.ui.configbool('experimental', 'bundle-phases'):
1374 if repo.ui.configbool('experimental', 'bundle-phases'):
1375 bundlespec.contentopts['phases'] = True
1375 bundlespec.contentopts['phases'] = True
1376
1376
1377 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1377 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1378 bundlespec.contentopts, compression=bcompression,
1378 bundlespec.contentopts, compression=bcompression,
1379 compopts=compopts)
1379 compopts=compopts)
1380
1380
1381 @command('cat',
1381 @command('cat',
1382 [('o', 'output', '',
1382 [('o', 'output', '',
1383 _('print output to file with formatted name'), _('FORMAT')),
1383 _('print output to file with formatted name'), _('FORMAT')),
1384 ('r', 'rev', '', _('print the given revision'), _('REV')),
1384 ('r', 'rev', '', _('print the given revision'), _('REV')),
1385 ('', 'decode', None, _('apply any matching decode filter')),
1385 ('', 'decode', None, _('apply any matching decode filter')),
1386 ] + walkopts + formatteropts,
1386 ] + walkopts + formatteropts,
1387 _('[OPTION]... FILE...'),
1387 _('[OPTION]... FILE...'),
1388 helpcategory=command.CATEGORY_FILE_CONTENTS,
1388 helpcategory=command.CATEGORY_FILE_CONTENTS,
1389 inferrepo=True,
1389 inferrepo=True,
1390 intents={INTENT_READONLY})
1390 intents={INTENT_READONLY})
1391 def cat(ui, repo, file1, *pats, **opts):
1391 def cat(ui, repo, file1, *pats, **opts):
1392 """output the current or given revision of files
1392 """output the current or given revision of files
1393
1393
1394 Print the specified files as they were at the given revision. If
1394 Print the specified files as they were at the given revision. If
1395 no revision is given, the parent of the working directory is used.
1395 no revision is given, the parent of the working directory is used.
1396
1396
1397 Output may be to a file, in which case the name of the file is
1397 Output may be to a file, in which case the name of the file is
1398 given using a template string. See :hg:`help templates`. In addition
1398 given using a template string. See :hg:`help templates`. In addition
1399 to the common template keywords, the following formatting rules are
1399 to the common template keywords, the following formatting rules are
1400 supported:
1400 supported:
1401
1401
1402 :``%%``: literal "%" character
1402 :``%%``: literal "%" character
1403 :``%s``: basename of file being printed
1403 :``%s``: basename of file being printed
1404 :``%d``: dirname of file being printed, or '.' if in repository root
1404 :``%d``: dirname of file being printed, or '.' if in repository root
1405 :``%p``: root-relative path name of file being printed
1405 :``%p``: root-relative path name of file being printed
1406 :``%H``: changeset hash (40 hexadecimal digits)
1406 :``%H``: changeset hash (40 hexadecimal digits)
1407 :``%R``: changeset revision number
1407 :``%R``: changeset revision number
1408 :``%h``: short-form changeset hash (12 hexadecimal digits)
1408 :``%h``: short-form changeset hash (12 hexadecimal digits)
1409 :``%r``: zero-padded changeset revision number
1409 :``%r``: zero-padded changeset revision number
1410 :``%b``: basename of the exporting repository
1410 :``%b``: basename of the exporting repository
1411 :``\\``: literal "\\" character
1411 :``\\``: literal "\\" character
1412
1412
1413 .. container:: verbose
1413 .. container:: verbose
1414
1414
1415 Template:
1415 Template:
1416
1416
1417 The following keywords are supported in addition to the common template
1417 The following keywords are supported in addition to the common template
1418 keywords and functions. See also :hg:`help templates`.
1418 keywords and functions. See also :hg:`help templates`.
1419
1419
1420 :data: String. File content.
1420 :data: String. File content.
1421 :path: String. Repository-absolute path of the file.
1421 :path: String. Repository-absolute path of the file.
1422
1422
1423 Returns 0 on success.
1423 Returns 0 on success.
1424 """
1424 """
1425 opts = pycompat.byteskwargs(opts)
1425 opts = pycompat.byteskwargs(opts)
1426 rev = opts.get('rev')
1426 rev = opts.get('rev')
1427 if rev:
1427 if rev:
1428 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
1428 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
1429 ctx = scmutil.revsingle(repo, rev)
1429 ctx = scmutil.revsingle(repo, rev)
1430 m = scmutil.match(ctx, (file1,) + pats, opts)
1430 m = scmutil.match(ctx, (file1,) + pats, opts)
1431 fntemplate = opts.pop('output', '')
1431 fntemplate = opts.pop('output', '')
1432 if cmdutil.isstdiofilename(fntemplate):
1432 if cmdutil.isstdiofilename(fntemplate):
1433 fntemplate = ''
1433 fntemplate = ''
1434
1434
1435 if fntemplate:
1435 if fntemplate:
1436 fm = formatter.nullformatter(ui, 'cat', opts)
1436 fm = formatter.nullformatter(ui, 'cat', opts)
1437 else:
1437 else:
1438 ui.pager('cat')
1438 ui.pager('cat')
1439 fm = ui.formatter('cat', opts)
1439 fm = ui.formatter('cat', opts)
1440 with fm:
1440 with fm:
1441 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '',
1441 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '',
1442 **pycompat.strkwargs(opts))
1442 **pycompat.strkwargs(opts))
1443
1443
1444 @command('clone',
1444 @command('clone',
1445 [('U', 'noupdate', None, _('the clone will include an empty working '
1445 [('U', 'noupdate', None, _('the clone will include an empty working '
1446 'directory (only a repository)')),
1446 'directory (only a repository)')),
1447 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1447 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1448 _('REV')),
1448 _('REV')),
1449 ('r', 'rev', [], _('do not clone everything, but include this changeset'
1449 ('r', 'rev', [], _('do not clone everything, but include this changeset'
1450 ' and its ancestors'), _('REV')),
1450 ' and its ancestors'), _('REV')),
1451 ('b', 'branch', [], _('do not clone everything, but include this branch\'s'
1451 ('b', 'branch', [], _('do not clone everything, but include this branch\'s'
1452 ' changesets and their ancestors'), _('BRANCH')),
1452 ' changesets and their ancestors'), _('BRANCH')),
1453 ('', 'pull', None, _('use pull protocol to copy metadata')),
1453 ('', 'pull', None, _('use pull protocol to copy metadata')),
1454 ('', 'uncompressed', None,
1454 ('', 'uncompressed', None,
1455 _('an alias to --stream (DEPRECATED)')),
1455 _('an alias to --stream (DEPRECATED)')),
1456 ('', 'stream', None,
1456 ('', 'stream', None,
1457 _('clone with minimal data processing')),
1457 _('clone with minimal data processing')),
1458 ] + remoteopts,
1458 ] + remoteopts,
1459 _('[OPTION]... SOURCE [DEST]'),
1459 _('[OPTION]... SOURCE [DEST]'),
1460 helpcategory=command.CATEGORY_REPO_CREATION,
1460 helpcategory=command.CATEGORY_REPO_CREATION,
1461 helpbasic=True, norepo=True)
1461 helpbasic=True, norepo=True)
1462 def clone(ui, source, dest=None, **opts):
1462 def clone(ui, source, dest=None, **opts):
1463 """make a copy of an existing repository
1463 """make a copy of an existing repository
1464
1464
1465 Create a copy of an existing repository in a new directory.
1465 Create a copy of an existing repository in a new directory.
1466
1466
1467 If no destination directory name is specified, it defaults to the
1467 If no destination directory name is specified, it defaults to the
1468 basename of the source.
1468 basename of the source.
1469
1469
1470 The location of the source is added to the new repository's
1470 The location of the source is added to the new repository's
1471 ``.hg/hgrc`` file, as the default to be used for future pulls.
1471 ``.hg/hgrc`` file, as the default to be used for future pulls.
1472
1472
1473 Only local paths and ``ssh://`` URLs are supported as
1473 Only local paths and ``ssh://`` URLs are supported as
1474 destinations. For ``ssh://`` destinations, no working directory or
1474 destinations. For ``ssh://`` destinations, no working directory or
1475 ``.hg/hgrc`` will be created on the remote side.
1475 ``.hg/hgrc`` will be created on the remote side.
1476
1476
1477 If the source repository has a bookmark called '@' set, that
1477 If the source repository has a bookmark called '@' set, that
1478 revision will be checked out in the new repository by default.
1478 revision will be checked out in the new repository by default.
1479
1479
1480 To check out a particular version, use -u/--update, or
1480 To check out a particular version, use -u/--update, or
1481 -U/--noupdate to create a clone with no working directory.
1481 -U/--noupdate to create a clone with no working directory.
1482
1482
1483 To pull only a subset of changesets, specify one or more revisions
1483 To pull only a subset of changesets, specify one or more revisions
1484 identifiers with -r/--rev or branches with -b/--branch. The
1484 identifiers with -r/--rev or branches with -b/--branch. The
1485 resulting clone will contain only the specified changesets and
1485 resulting clone will contain only the specified changesets and
1486 their ancestors. These options (or 'clone src#rev dest') imply
1486 their ancestors. These options (or 'clone src#rev dest') imply
1487 --pull, even for local source repositories.
1487 --pull, even for local source repositories.
1488
1488
1489 In normal clone mode, the remote normalizes repository data into a common
1489 In normal clone mode, the remote normalizes repository data into a common
1490 exchange format and the receiving end translates this data into its local
1490 exchange format and the receiving end translates this data into its local
1491 storage format. --stream activates a different clone mode that essentially
1491 storage format. --stream activates a different clone mode that essentially
1492 copies repository files from the remote with minimal data processing. This
1492 copies repository files from the remote with minimal data processing. This
1493 significantly reduces the CPU cost of a clone both remotely and locally.
1493 significantly reduces the CPU cost of a clone both remotely and locally.
1494 However, it often increases the transferred data size by 30-40%. This can
1494 However, it often increases the transferred data size by 30-40%. This can
1495 result in substantially faster clones where I/O throughput is plentiful,
1495 result in substantially faster clones where I/O throughput is plentiful,
1496 especially for larger repositories. A side-effect of --stream clones is
1496 especially for larger repositories. A side-effect of --stream clones is
1497 that storage settings and requirements on the remote are applied locally:
1497 that storage settings and requirements on the remote are applied locally:
1498 a modern client may inherit legacy or inefficient storage used by the
1498 a modern client may inherit legacy or inefficient storage used by the
1499 remote or a legacy Mercurial client may not be able to clone from a
1499 remote or a legacy Mercurial client may not be able to clone from a
1500 modern Mercurial remote.
1500 modern Mercurial remote.
1501
1501
1502 .. note::
1502 .. note::
1503
1503
1504 Specifying a tag will include the tagged changeset but not the
1504 Specifying a tag will include the tagged changeset but not the
1505 changeset containing the tag.
1505 changeset containing the tag.
1506
1506
1507 .. container:: verbose
1507 .. container:: verbose
1508
1508
1509 For efficiency, hardlinks are used for cloning whenever the
1509 For efficiency, hardlinks are used for cloning whenever the
1510 source and destination are on the same filesystem (note this
1510 source and destination are on the same filesystem (note this
1511 applies only to the repository data, not to the working
1511 applies only to the repository data, not to the working
1512 directory). Some filesystems, such as AFS, implement hardlinking
1512 directory). Some filesystems, such as AFS, implement hardlinking
1513 incorrectly, but do not report errors. In these cases, use the
1513 incorrectly, but do not report errors. In these cases, use the
1514 --pull option to avoid hardlinking.
1514 --pull option to avoid hardlinking.
1515
1515
1516 Mercurial will update the working directory to the first applicable
1516 Mercurial will update the working directory to the first applicable
1517 revision from this list:
1517 revision from this list:
1518
1518
1519 a) null if -U or the source repository has no changesets
1519 a) null if -U or the source repository has no changesets
1520 b) if -u . and the source repository is local, the first parent of
1520 b) if -u . and the source repository is local, the first parent of
1521 the source repository's working directory
1521 the source repository's working directory
1522 c) the changeset specified with -u (if a branch name, this means the
1522 c) the changeset specified with -u (if a branch name, this means the
1523 latest head of that branch)
1523 latest head of that branch)
1524 d) the changeset specified with -r
1524 d) the changeset specified with -r
1525 e) the tipmost head specified with -b
1525 e) the tipmost head specified with -b
1526 f) the tipmost head specified with the url#branch source syntax
1526 f) the tipmost head specified with the url#branch source syntax
1527 g) the revision marked with the '@' bookmark, if present
1527 g) the revision marked with the '@' bookmark, if present
1528 h) the tipmost head of the default branch
1528 h) the tipmost head of the default branch
1529 i) tip
1529 i) tip
1530
1530
1531 When cloning from servers that support it, Mercurial may fetch
1531 When cloning from servers that support it, Mercurial may fetch
1532 pre-generated data from a server-advertised URL or inline from the
1532 pre-generated data from a server-advertised URL or inline from the
1533 same stream. When this is done, hooks operating on incoming changesets
1533 same stream. When this is done, hooks operating on incoming changesets
1534 and changegroups may fire more than once, once for each pre-generated
1534 and changegroups may fire more than once, once for each pre-generated
1535 bundle and as well as for any additional remaining data. In addition,
1535 bundle and as well as for any additional remaining data. In addition,
1536 if an error occurs, the repository may be rolled back to a partial
1536 if an error occurs, the repository may be rolled back to a partial
1537 clone. This behavior may change in future releases.
1537 clone. This behavior may change in future releases.
1538 See :hg:`help -e clonebundles` for more.
1538 See :hg:`help -e clonebundles` for more.
1539
1539
1540 Examples:
1540 Examples:
1541
1541
1542 - clone a remote repository to a new directory named hg/::
1542 - clone a remote repository to a new directory named hg/::
1543
1543
1544 hg clone https://www.mercurial-scm.org/repo/hg/
1544 hg clone https://www.mercurial-scm.org/repo/hg/
1545
1545
1546 - create a lightweight local clone::
1546 - create a lightweight local clone::
1547
1547
1548 hg clone project/ project-feature/
1548 hg clone project/ project-feature/
1549
1549
1550 - clone from an absolute path on an ssh server (note double-slash)::
1550 - clone from an absolute path on an ssh server (note double-slash)::
1551
1551
1552 hg clone ssh://user@server//home/projects/alpha/
1552 hg clone ssh://user@server//home/projects/alpha/
1553
1553
1554 - do a streaming clone while checking out a specified version::
1554 - do a streaming clone while checking out a specified version::
1555
1555
1556 hg clone --stream http://server/repo -u 1.5
1556 hg clone --stream http://server/repo -u 1.5
1557
1557
1558 - create a repository without changesets after a particular revision::
1558 - create a repository without changesets after a particular revision::
1559
1559
1560 hg clone -r 04e544 experimental/ good/
1560 hg clone -r 04e544 experimental/ good/
1561
1561
1562 - clone (and track) a particular named branch::
1562 - clone (and track) a particular named branch::
1563
1563
1564 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1564 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1565
1565
1566 See :hg:`help urls` for details on specifying URLs.
1566 See :hg:`help urls` for details on specifying URLs.
1567
1567
1568 Returns 0 on success.
1568 Returns 0 on success.
1569 """
1569 """
1570 opts = pycompat.byteskwargs(opts)
1570 opts = pycompat.byteskwargs(opts)
1571 if opts.get('noupdate') and opts.get('updaterev'):
1571 if opts.get('noupdate') and opts.get('updaterev'):
1572 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1572 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1573
1573
1574 # --include/--exclude can come from narrow or sparse.
1574 # --include/--exclude can come from narrow or sparse.
1575 includepats, excludepats = None, None
1575 includepats, excludepats = None, None
1576
1576
1577 # hg.clone() differentiates between None and an empty set. So make sure
1577 # hg.clone() differentiates between None and an empty set. So make sure
1578 # patterns are sets if narrow is requested without patterns.
1578 # patterns are sets if narrow is requested without patterns.
1579 if opts.get('narrow'):
1579 if opts.get('narrow'):
1580 includepats = set()
1580 includepats = set()
1581 excludepats = set()
1581 excludepats = set()
1582
1582
1583 if opts.get('include'):
1583 if opts.get('include'):
1584 includepats = narrowspec.parsepatterns(opts.get('include'))
1584 includepats = narrowspec.parsepatterns(opts.get('include'))
1585 if opts.get('exclude'):
1585 if opts.get('exclude'):
1586 excludepats = narrowspec.parsepatterns(opts.get('exclude'))
1586 excludepats = narrowspec.parsepatterns(opts.get('exclude'))
1587
1587
1588 r = hg.clone(ui, opts, source, dest,
1588 r = hg.clone(ui, opts, source, dest,
1589 pull=opts.get('pull'),
1589 pull=opts.get('pull'),
1590 stream=opts.get('stream') or opts.get('uncompressed'),
1590 stream=opts.get('stream') or opts.get('uncompressed'),
1591 revs=opts.get('rev'),
1591 revs=opts.get('rev'),
1592 update=opts.get('updaterev') or not opts.get('noupdate'),
1592 update=opts.get('updaterev') or not opts.get('noupdate'),
1593 branch=opts.get('branch'),
1593 branch=opts.get('branch'),
1594 shareopts=opts.get('shareopts'),
1594 shareopts=opts.get('shareopts'),
1595 storeincludepats=includepats,
1595 storeincludepats=includepats,
1596 storeexcludepats=excludepats,
1596 storeexcludepats=excludepats,
1597 depth=opts.get('depth') or None)
1597 depth=opts.get('depth') or None)
1598
1598
1599 return r is None
1599 return r is None
1600
1600
1601 @command('commit|ci',
1601 @command('commit|ci',
1602 [('A', 'addremove', None,
1602 [('A', 'addremove', None,
1603 _('mark new/missing files as added/removed before committing')),
1603 _('mark new/missing files as added/removed before committing')),
1604 ('', 'close-branch', None,
1604 ('', 'close-branch', None,
1605 _('mark a branch head as closed')),
1605 _('mark a branch head as closed')),
1606 ('', 'amend', None, _('amend the parent of the working directory')),
1606 ('', 'amend', None, _('amend the parent of the working directory')),
1607 ('s', 'secret', None, _('use the secret phase for committing')),
1607 ('s', 'secret', None, _('use the secret phase for committing')),
1608 ('e', 'edit', None, _('invoke editor on commit messages')),
1608 ('e', 'edit', None, _('invoke editor on commit messages')),
1609 ('', 'force-close-branch', None,
1609 ('', 'force-close-branch', None,
1610 _('forcibly close branch from a non-head changeset (ADVANCED)')),
1610 _('forcibly close branch from a non-head changeset (ADVANCED)')),
1611 ('i', 'interactive', None, _('use interactive mode')),
1611 ('i', 'interactive', None, _('use interactive mode')),
1612 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1612 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1613 _('[OPTION]... [FILE]...'),
1613 _('[OPTION]... [FILE]...'),
1614 helpcategory=command.CATEGORY_COMMITTING, helpbasic=True,
1614 helpcategory=command.CATEGORY_COMMITTING, helpbasic=True,
1615 inferrepo=True)
1615 inferrepo=True)
1616 def commit(ui, repo, *pats, **opts):
1616 def commit(ui, repo, *pats, **opts):
1617 """commit the specified files or all outstanding changes
1617 """commit the specified files or all outstanding changes
1618
1618
1619 Commit changes to the given files into the repository. Unlike a
1619 Commit changes to the given files into the repository. Unlike a
1620 centralized SCM, this operation is a local operation. See
1620 centralized SCM, this operation is a local operation. See
1621 :hg:`push` for a way to actively distribute your changes.
1621 :hg:`push` for a way to actively distribute your changes.
1622
1622
1623 If a list of files is omitted, all changes reported by :hg:`status`
1623 If a list of files is omitted, all changes reported by :hg:`status`
1624 will be committed.
1624 will be committed.
1625
1625
1626 If you are committing the result of a merge, do not provide any
1626 If you are committing the result of a merge, do not provide any
1627 filenames or -I/-X filters.
1627 filenames or -I/-X filters.
1628
1628
1629 If no commit message is specified, Mercurial starts your
1629 If no commit message is specified, Mercurial starts your
1630 configured editor where you can enter a message. In case your
1630 configured editor where you can enter a message. In case your
1631 commit fails, you will find a backup of your message in
1631 commit fails, you will find a backup of your message in
1632 ``.hg/last-message.txt``.
1632 ``.hg/last-message.txt``.
1633
1633
1634 The --close-branch flag can be used to mark the current branch
1634 The --close-branch flag can be used to mark the current branch
1635 head closed. When all heads of a branch are closed, the branch
1635 head closed. When all heads of a branch are closed, the branch
1636 will be considered closed and no longer listed.
1636 will be considered closed and no longer listed.
1637
1637
1638 The --amend flag can be used to amend the parent of the
1638 The --amend flag can be used to amend the parent of the
1639 working directory with a new commit that contains the changes
1639 working directory with a new commit that contains the changes
1640 in the parent in addition to those currently reported by :hg:`status`,
1640 in the parent in addition to those currently reported by :hg:`status`,
1641 if there are any. The old commit is stored in a backup bundle in
1641 if there are any. The old commit is stored in a backup bundle in
1642 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1642 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1643 on how to restore it).
1643 on how to restore it).
1644
1644
1645 Message, user and date are taken from the amended commit unless
1645 Message, user and date are taken from the amended commit unless
1646 specified. When a message isn't specified on the command line,
1646 specified. When a message isn't specified on the command line,
1647 the editor will open with the message of the amended commit.
1647 the editor will open with the message of the amended commit.
1648
1648
1649 It is not possible to amend public changesets (see :hg:`help phases`)
1649 It is not possible to amend public changesets (see :hg:`help phases`)
1650 or changesets that have children.
1650 or changesets that have children.
1651
1651
1652 See :hg:`help dates` for a list of formats valid for -d/--date.
1652 See :hg:`help dates` for a list of formats valid for -d/--date.
1653
1653
1654 Returns 0 on success, 1 if nothing changed.
1654 Returns 0 on success, 1 if nothing changed.
1655
1655
1656 .. container:: verbose
1656 .. container:: verbose
1657
1657
1658 Examples:
1658 Examples:
1659
1659
1660 - commit all files ending in .py::
1660 - commit all files ending in .py::
1661
1661
1662 hg commit --include "set:**.py"
1662 hg commit --include "set:**.py"
1663
1663
1664 - commit all non-binary files::
1664 - commit all non-binary files::
1665
1665
1666 hg commit --exclude "set:binary()"
1666 hg commit --exclude "set:binary()"
1667
1667
1668 - amend the current commit and set the date to now::
1668 - amend the current commit and set the date to now::
1669
1669
1670 hg commit --amend --date now
1670 hg commit --amend --date now
1671 """
1671 """
1672 with repo.wlock(), repo.lock():
1672 with repo.wlock(), repo.lock():
1673 return _docommit(ui, repo, *pats, **opts)
1673 return _docommit(ui, repo, *pats, **opts)
1674
1674
1675 def _docommit(ui, repo, *pats, **opts):
1675 def _docommit(ui, repo, *pats, **opts):
1676 if opts.get(r'interactive'):
1676 if opts.get(r'interactive'):
1677 opts.pop(r'interactive')
1677 opts.pop(r'interactive')
1678 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1678 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1679 cmdutil.recordfilter, *pats,
1679 cmdutil.recordfilter, *pats,
1680 **opts)
1680 **opts)
1681 # ret can be 0 (no changes to record) or the value returned by
1681 # ret can be 0 (no changes to record) or the value returned by
1682 # commit(), 1 if nothing changed or None on success.
1682 # commit(), 1 if nothing changed or None on success.
1683 return 1 if ret == 0 else ret
1683 return 1 if ret == 0 else ret
1684
1684
1685 opts = pycompat.byteskwargs(opts)
1685 opts = pycompat.byteskwargs(opts)
1686 if opts.get('subrepos'):
1686 if opts.get('subrepos'):
1687 if opts.get('amend'):
1687 if opts.get('amend'):
1688 raise error.Abort(_('cannot amend with --subrepos'))
1688 raise error.Abort(_('cannot amend with --subrepos'))
1689 # Let --subrepos on the command line override config setting.
1689 # Let --subrepos on the command line override config setting.
1690 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1690 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1691
1691
1692 cmdutil.checkunfinished(repo, commit=True)
1692 cmdutil.checkunfinished(repo, commit=True)
1693
1693
1694 branch = repo[None].branch()
1694 branch = repo[None].branch()
1695 bheads = repo.branchheads(branch)
1695 bheads = repo.branchheads(branch)
1696
1696
1697 extra = {}
1697 extra = {}
1698 if opts.get('close_branch') or opts.get('force_close_branch'):
1698 if opts.get('close_branch') or opts.get('force_close_branch'):
1699 extra['close'] = '1'
1699 extra['close'] = '1'
1700
1700
1701 if repo['.'].closesbranch():
1701 if repo['.'].closesbranch():
1702 raise error.Abort(_('current revision is already a branch closing'
1702 raise error.Abort(_('current revision is already a branch closing'
1703 ' head'))
1703 ' head'))
1704 elif not bheads:
1704 elif not bheads:
1705 raise error.Abort(_('branch "%s" has no heads to close') % branch)
1705 raise error.Abort(_('branch "%s" has no heads to close') % branch)
1706 elif (branch == repo['.'].branch() and repo['.'].node() not in bheads
1706 elif (branch == repo['.'].branch() and repo['.'].node() not in bheads
1707 and not opts.get('force_close_branch')):
1707 and not opts.get('force_close_branch')):
1708 hint = _('use --force-close-branch to close branch from a non-head'
1708 hint = _('use --force-close-branch to close branch from a non-head'
1709 ' changeset')
1709 ' changeset')
1710 raise error.Abort(_('can only close branch heads'), hint=hint)
1710 raise error.Abort(_('can only close branch heads'), hint=hint)
1711 elif opts.get('amend'):
1711 elif opts.get('amend'):
1712 if (repo['.'].p1().branch() != branch and
1712 if (repo['.'].p1().branch() != branch and
1713 repo['.'].p2().branch() != branch):
1713 repo['.'].p2().branch() != branch):
1714 raise error.Abort(_('can only close branch heads'))
1714 raise error.Abort(_('can only close branch heads'))
1715
1715
1716 if opts.get('amend'):
1716 if opts.get('amend'):
1717 if ui.configbool('ui', 'commitsubrepos'):
1717 if ui.configbool('ui', 'commitsubrepos'):
1718 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1718 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1719
1719
1720 old = repo['.']
1720 old = repo['.']
1721 rewriteutil.precheck(repo, [old.rev()], 'amend')
1721 rewriteutil.precheck(repo, [old.rev()], 'amend')
1722
1722
1723 # Currently histedit gets confused if an amend happens while histedit
1723 # Currently histedit gets confused if an amend happens while histedit
1724 # is in progress. Since we have a checkunfinished command, we are
1724 # is in progress. Since we have a checkunfinished command, we are
1725 # temporarily honoring it.
1725 # temporarily honoring it.
1726 #
1726 #
1727 # Note: eventually this guard will be removed. Please do not expect
1727 # Note: eventually this guard will be removed. Please do not expect
1728 # this behavior to remain.
1728 # this behavior to remain.
1729 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1729 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1730 cmdutil.checkunfinished(repo)
1730 cmdutil.checkunfinished(repo)
1731
1731
1732 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
1732 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
1733 if node == old.node():
1733 if node == old.node():
1734 ui.status(_("nothing changed\n"))
1734 ui.status(_("nothing changed\n"))
1735 return 1
1735 return 1
1736 else:
1736 else:
1737 def commitfunc(ui, repo, message, match, opts):
1737 def commitfunc(ui, repo, message, match, opts):
1738 overrides = {}
1738 overrides = {}
1739 if opts.get('secret'):
1739 if opts.get('secret'):
1740 overrides[('phases', 'new-commit')] = 'secret'
1740 overrides[('phases', 'new-commit')] = 'secret'
1741
1741
1742 baseui = repo.baseui
1742 baseui = repo.baseui
1743 with baseui.configoverride(overrides, 'commit'):
1743 with baseui.configoverride(overrides, 'commit'):
1744 with ui.configoverride(overrides, 'commit'):
1744 with ui.configoverride(overrides, 'commit'):
1745 editform = cmdutil.mergeeditform(repo[None],
1745 editform = cmdutil.mergeeditform(repo[None],
1746 'commit.normal')
1746 'commit.normal')
1747 editor = cmdutil.getcommiteditor(
1747 editor = cmdutil.getcommiteditor(
1748 editform=editform, **pycompat.strkwargs(opts))
1748 editform=editform, **pycompat.strkwargs(opts))
1749 return repo.commit(message,
1749 return repo.commit(message,
1750 opts.get('user'),
1750 opts.get('user'),
1751 opts.get('date'),
1751 opts.get('date'),
1752 match,
1752 match,
1753 editor=editor,
1753 editor=editor,
1754 extra=extra)
1754 extra=extra)
1755
1755
1756 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1756 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1757
1757
1758 if not node:
1758 if not node:
1759 stat = cmdutil.postcommitstatus(repo, pats, opts)
1759 stat = cmdutil.postcommitstatus(repo, pats, opts)
1760 if stat[3]:
1760 if stat[3]:
1761 ui.status(_("nothing changed (%d missing files, see "
1761 ui.status(_("nothing changed (%d missing files, see "
1762 "'hg status')\n") % len(stat[3]))
1762 "'hg status')\n") % len(stat[3]))
1763 else:
1763 else:
1764 ui.status(_("nothing changed\n"))
1764 ui.status(_("nothing changed\n"))
1765 return 1
1765 return 1
1766
1766
1767 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1767 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1768
1768
1769 if not ui.quiet and ui.configbool('commands', 'commit.post-status'):
1769 if not ui.quiet and ui.configbool('commands', 'commit.post-status'):
1770 status(ui, repo, modified=True, added=True, removed=True, deleted=True,
1770 status(ui, repo, modified=True, added=True, removed=True, deleted=True,
1771 unknown=True, subrepos=opts.get('subrepos'))
1771 unknown=True, subrepos=opts.get('subrepos'))
1772
1772
1773 @command('config|showconfig|debugconfig',
1773 @command('config|showconfig|debugconfig',
1774 [('u', 'untrusted', None, _('show untrusted configuration options')),
1774 [('u', 'untrusted', None, _('show untrusted configuration options')),
1775 ('e', 'edit', None, _('edit user config')),
1775 ('e', 'edit', None, _('edit user config')),
1776 ('l', 'local', None, _('edit repository config')),
1776 ('l', 'local', None, _('edit repository config')),
1777 ('g', 'global', None, _('edit global config'))] + formatteropts,
1777 ('g', 'global', None, _('edit global config'))] + formatteropts,
1778 _('[-u] [NAME]...'),
1778 _('[-u] [NAME]...'),
1779 helpcategory=command.CATEGORY_HELP,
1779 helpcategory=command.CATEGORY_HELP,
1780 optionalrepo=True,
1780 optionalrepo=True,
1781 intents={INTENT_READONLY})
1781 intents={INTENT_READONLY})
1782 def config(ui, repo, *values, **opts):
1782 def config(ui, repo, *values, **opts):
1783 """show combined config settings from all hgrc files
1783 """show combined config settings from all hgrc files
1784
1784
1785 With no arguments, print names and values of all config items.
1785 With no arguments, print names and values of all config items.
1786
1786
1787 With one argument of the form section.name, print just the value
1787 With one argument of the form section.name, print just the value
1788 of that config item.
1788 of that config item.
1789
1789
1790 With multiple arguments, print names and values of all config
1790 With multiple arguments, print names and values of all config
1791 items with matching section names or section.names.
1791 items with matching section names or section.names.
1792
1792
1793 With --edit, start an editor on the user-level config file. With
1793 With --edit, start an editor on the user-level config file. With
1794 --global, edit the system-wide config file. With --local, edit the
1794 --global, edit the system-wide config file. With --local, edit the
1795 repository-level config file.
1795 repository-level config file.
1796
1796
1797 With --debug, the source (filename and line number) is printed
1797 With --debug, the source (filename and line number) is printed
1798 for each config item.
1798 for each config item.
1799
1799
1800 See :hg:`help config` for more information about config files.
1800 See :hg:`help config` for more information about config files.
1801
1801
1802 .. container:: verbose
1802 .. container:: verbose
1803
1803
1804 Template:
1804 Template:
1805
1805
1806 The following keywords are supported. See also :hg:`help templates`.
1806 The following keywords are supported. See also :hg:`help templates`.
1807
1807
1808 :name: String. Config name.
1808 :name: String. Config name.
1809 :source: String. Filename and line number where the item is defined.
1809 :source: String. Filename and line number where the item is defined.
1810 :value: String. Config value.
1810 :value: String. Config value.
1811
1811
1812 Returns 0 on success, 1 if NAME does not exist.
1812 Returns 0 on success, 1 if NAME does not exist.
1813
1813
1814 """
1814 """
1815
1815
1816 opts = pycompat.byteskwargs(opts)
1816 opts = pycompat.byteskwargs(opts)
1817 if opts.get('edit') or opts.get('local') or opts.get('global'):
1817 if opts.get('edit') or opts.get('local') or opts.get('global'):
1818 if opts.get('local') and opts.get('global'):
1818 if opts.get('local') and opts.get('global'):
1819 raise error.Abort(_("can't use --local and --global together"))
1819 raise error.Abort(_("can't use --local and --global together"))
1820
1820
1821 if opts.get('local'):
1821 if opts.get('local'):
1822 if not repo:
1822 if not repo:
1823 raise error.Abort(_("can't use --local outside a repository"))
1823 raise error.Abort(_("can't use --local outside a repository"))
1824 paths = [repo.vfs.join('hgrc')]
1824 paths = [repo.vfs.join('hgrc')]
1825 elif opts.get('global'):
1825 elif opts.get('global'):
1826 paths = rcutil.systemrcpath()
1826 paths = rcutil.systemrcpath()
1827 else:
1827 else:
1828 paths = rcutil.userrcpath()
1828 paths = rcutil.userrcpath()
1829
1829
1830 for f in paths:
1830 for f in paths:
1831 if os.path.exists(f):
1831 if os.path.exists(f):
1832 break
1832 break
1833 else:
1833 else:
1834 if opts.get('global'):
1834 if opts.get('global'):
1835 samplehgrc = uimod.samplehgrcs['global']
1835 samplehgrc = uimod.samplehgrcs['global']
1836 elif opts.get('local'):
1836 elif opts.get('local'):
1837 samplehgrc = uimod.samplehgrcs['local']
1837 samplehgrc = uimod.samplehgrcs['local']
1838 else:
1838 else:
1839 samplehgrc = uimod.samplehgrcs['user']
1839 samplehgrc = uimod.samplehgrcs['user']
1840
1840
1841 f = paths[0]
1841 f = paths[0]
1842 fp = open(f, "wb")
1842 fp = open(f, "wb")
1843 fp.write(util.tonativeeol(samplehgrc))
1843 fp.write(util.tonativeeol(samplehgrc))
1844 fp.close()
1844 fp.close()
1845
1845
1846 editor = ui.geteditor()
1846 editor = ui.geteditor()
1847 ui.system("%s \"%s\"" % (editor, f),
1847 ui.system("%s \"%s\"" % (editor, f),
1848 onerr=error.Abort, errprefix=_("edit failed"),
1848 onerr=error.Abort, errprefix=_("edit failed"),
1849 blockedtag='config_edit')
1849 blockedtag='config_edit')
1850 return
1850 return
1851 ui.pager('config')
1851 ui.pager('config')
1852 fm = ui.formatter('config', opts)
1852 fm = ui.formatter('config', opts)
1853 for t, f in rcutil.rccomponents():
1853 for t, f in rcutil.rccomponents():
1854 if t == 'path':
1854 if t == 'path':
1855 ui.debug('read config from: %s\n' % f)
1855 ui.debug('read config from: %s\n' % f)
1856 elif t == 'items':
1856 elif t == 'items':
1857 for section, name, value, source in f:
1857 for section, name, value, source in f:
1858 ui.debug('set config by: %s\n' % source)
1858 ui.debug('set config by: %s\n' % source)
1859 else:
1859 else:
1860 raise error.ProgrammingError('unknown rctype: %s' % t)
1860 raise error.ProgrammingError('unknown rctype: %s' % t)
1861 untrusted = bool(opts.get('untrusted'))
1861 untrusted = bool(opts.get('untrusted'))
1862
1862
1863 selsections = selentries = []
1863 selsections = selentries = []
1864 if values:
1864 if values:
1865 selsections = [v for v in values if '.' not in v]
1865 selsections = [v for v in values if '.' not in v]
1866 selentries = [v for v in values if '.' in v]
1866 selentries = [v for v in values if '.' in v]
1867 uniquesel = (len(selentries) == 1 and not selsections)
1867 uniquesel = (len(selentries) == 1 and not selsections)
1868 selsections = set(selsections)
1868 selsections = set(selsections)
1869 selentries = set(selentries)
1869 selentries = set(selentries)
1870
1870
1871 matched = False
1871 matched = False
1872 for section, name, value in ui.walkconfig(untrusted=untrusted):
1872 for section, name, value in ui.walkconfig(untrusted=untrusted):
1873 source = ui.configsource(section, name, untrusted)
1873 source = ui.configsource(section, name, untrusted)
1874 value = pycompat.bytestr(value)
1874 value = pycompat.bytestr(value)
1875 defaultvalue = ui.configdefault(section, name)
1875 defaultvalue = ui.configdefault(section, name)
1876 if fm.isplain():
1876 if fm.isplain():
1877 source = source or 'none'
1877 source = source or 'none'
1878 value = value.replace('\n', '\\n')
1878 value = value.replace('\n', '\\n')
1879 entryname = section + '.' + name
1879 entryname = section + '.' + name
1880 if values and not (section in selsections or entryname in selentries):
1880 if values and not (section in selsections or entryname in selentries):
1881 continue
1881 continue
1882 fm.startitem()
1882 fm.startitem()
1883 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1883 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1884 if uniquesel:
1884 if uniquesel:
1885 fm.data(name=entryname, defaultvalue=defaultvalue)
1885 fm.data(name=entryname)
1886 fm.write('value', '%s\n', value)
1886 fm.write('value', '%s\n', value)
1887 else:
1887 else:
1888 fm.write('name value', '%s=%s\n', entryname, value)
1888 fm.write('name value', '%s=%s\n', entryname, value)
1889 fm.data(defaultvalue=defaultvalue)
1889 matched = True
1890 matched = True
1890 fm.end()
1891 fm.end()
1891 if matched:
1892 if matched:
1892 return 0
1893 return 0
1893 return 1
1894 return 1
1894
1895
1895 @command('continue',
1896 @command('continue',
1896 dryrunopts, helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
1897 dryrunopts, helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
1897 helpbasic=True)
1898 helpbasic=True)
1898 def continuecmd(ui, repo, **opts):
1899 def continuecmd(ui, repo, **opts):
1899 """resumes an interrupted operation (EXPERIMENTAL)
1900 """resumes an interrupted operation (EXPERIMENTAL)
1900
1901
1901 Finishes a multistep operation like graft, histedit, rebase, merge,
1902 Finishes a multistep operation like graft, histedit, rebase, merge,
1902 and unshelve if they are in an interrupted state.
1903 and unshelve if they are in an interrupted state.
1903
1904
1904 use --dry-run/-n to dry run the command.
1905 use --dry-run/-n to dry run the command.
1905 """
1906 """
1906 dryrun = opts.get(r'dry_run')
1907 dryrun = opts.get(r'dry_run')
1907 contstate = cmdutil.getunfinishedstate(repo)
1908 contstate = cmdutil.getunfinishedstate(repo)
1908 if not contstate:
1909 if not contstate:
1909 raise error.Abort(_('no operation in progress'))
1910 raise error.Abort(_('no operation in progress'))
1910 if not contstate.continuefunc:
1911 if not contstate.continuefunc:
1911 raise error.Abort((_("%s in progress but does not support "
1912 raise error.Abort((_("%s in progress but does not support "
1912 "'hg continue'") % (contstate._opname)),
1913 "'hg continue'") % (contstate._opname)),
1913 hint=contstate.continuemsg())
1914 hint=contstate.continuemsg())
1914 if dryrun:
1915 if dryrun:
1915 ui.status(_('%s in progress, will be resumed\n') % (contstate._opname))
1916 ui.status(_('%s in progress, will be resumed\n') % (contstate._opname))
1916 return
1917 return
1917 return contstate.continuefunc(ui, repo)
1918 return contstate.continuefunc(ui, repo)
1918
1919
1919 @command('copy|cp',
1920 @command('copy|cp',
1920 [('A', 'after', None, _('record a copy that has already occurred')),
1921 [('A', 'after', None, _('record a copy that has already occurred')),
1921 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1922 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1922 ] + walkopts + dryrunopts,
1923 ] + walkopts + dryrunopts,
1923 _('[OPTION]... SOURCE... DEST'),
1924 _('[OPTION]... SOURCE... DEST'),
1924 helpcategory=command.CATEGORY_FILE_CONTENTS)
1925 helpcategory=command.CATEGORY_FILE_CONTENTS)
1925 def copy(ui, repo, *pats, **opts):
1926 def copy(ui, repo, *pats, **opts):
1926 """mark files as copied for the next commit
1927 """mark files as copied for the next commit
1927
1928
1928 Mark dest as having copies of source files. If dest is a
1929 Mark dest as having copies of source files. If dest is a
1929 directory, copies are put in that directory. If dest is a file,
1930 directory, copies are put in that directory. If dest is a file,
1930 the source must be a single file.
1931 the source must be a single file.
1931
1932
1932 By default, this command copies the contents of files as they
1933 By default, this command copies the contents of files as they
1933 exist in the working directory. If invoked with -A/--after, the
1934 exist in the working directory. If invoked with -A/--after, the
1934 operation is recorded, but no copying is performed.
1935 operation is recorded, but no copying is performed.
1935
1936
1936 This command takes effect with the next commit. To undo a copy
1937 This command takes effect with the next commit. To undo a copy
1937 before that, see :hg:`revert`.
1938 before that, see :hg:`revert`.
1938
1939
1939 Returns 0 on success, 1 if errors are encountered.
1940 Returns 0 on success, 1 if errors are encountered.
1940 """
1941 """
1941 opts = pycompat.byteskwargs(opts)
1942 opts = pycompat.byteskwargs(opts)
1942 with repo.wlock(False):
1943 with repo.wlock(False):
1943 return cmdutil.copy(ui, repo, pats, opts)
1944 return cmdutil.copy(ui, repo, pats, opts)
1944
1945
1945 @command(
1946 @command(
1946 'debugcommands', [], _('[COMMAND]'),
1947 'debugcommands', [], _('[COMMAND]'),
1947 helpcategory=command.CATEGORY_HELP,
1948 helpcategory=command.CATEGORY_HELP,
1948 norepo=True)
1949 norepo=True)
1949 def debugcommands(ui, cmd='', *args):
1950 def debugcommands(ui, cmd='', *args):
1950 """list all available commands and options"""
1951 """list all available commands and options"""
1951 for cmd, vals in sorted(table.iteritems()):
1952 for cmd, vals in sorted(table.iteritems()):
1952 cmd = cmd.split('|')[0]
1953 cmd = cmd.split('|')[0]
1953 opts = ', '.join([i[1] for i in vals[1]])
1954 opts = ', '.join([i[1] for i in vals[1]])
1954 ui.write('%s: %s\n' % (cmd, opts))
1955 ui.write('%s: %s\n' % (cmd, opts))
1955
1956
1956 @command('debugcomplete',
1957 @command('debugcomplete',
1957 [('o', 'options', None, _('show the command options'))],
1958 [('o', 'options', None, _('show the command options'))],
1958 _('[-o] CMD'),
1959 _('[-o] CMD'),
1959 helpcategory=command.CATEGORY_HELP,
1960 helpcategory=command.CATEGORY_HELP,
1960 norepo=True)
1961 norepo=True)
1961 def debugcomplete(ui, cmd='', **opts):
1962 def debugcomplete(ui, cmd='', **opts):
1962 """returns the completion list associated with the given command"""
1963 """returns the completion list associated with the given command"""
1963
1964
1964 if opts.get(r'options'):
1965 if opts.get(r'options'):
1965 options = []
1966 options = []
1966 otables = [globalopts]
1967 otables = [globalopts]
1967 if cmd:
1968 if cmd:
1968 aliases, entry = cmdutil.findcmd(cmd, table, False)
1969 aliases, entry = cmdutil.findcmd(cmd, table, False)
1969 otables.append(entry[1])
1970 otables.append(entry[1])
1970 for t in otables:
1971 for t in otables:
1971 for o in t:
1972 for o in t:
1972 if "(DEPRECATED)" in o[3]:
1973 if "(DEPRECATED)" in o[3]:
1973 continue
1974 continue
1974 if o[0]:
1975 if o[0]:
1975 options.append('-%s' % o[0])
1976 options.append('-%s' % o[0])
1976 options.append('--%s' % o[1])
1977 options.append('--%s' % o[1])
1977 ui.write("%s\n" % "\n".join(options))
1978 ui.write("%s\n" % "\n".join(options))
1978 return
1979 return
1979
1980
1980 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1981 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1981 if ui.verbose:
1982 if ui.verbose:
1982 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1983 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1983 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1984 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1984
1985
1985 @command('diff',
1986 @command('diff',
1986 [('r', 'rev', [], _('revision'), _('REV')),
1987 [('r', 'rev', [], _('revision'), _('REV')),
1987 ('c', 'change', '', _('change made by revision'), _('REV'))
1988 ('c', 'change', '', _('change made by revision'), _('REV'))
1988 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1989 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1989 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1990 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1990 helpcategory=command.CATEGORY_FILE_CONTENTS,
1991 helpcategory=command.CATEGORY_FILE_CONTENTS,
1991 helpbasic=True, inferrepo=True, intents={INTENT_READONLY})
1992 helpbasic=True, inferrepo=True, intents={INTENT_READONLY})
1992 def diff(ui, repo, *pats, **opts):
1993 def diff(ui, repo, *pats, **opts):
1993 """diff repository (or selected files)
1994 """diff repository (or selected files)
1994
1995
1995 Show differences between revisions for the specified files.
1996 Show differences between revisions for the specified files.
1996
1997
1997 Differences between files are shown using the unified diff format.
1998 Differences between files are shown using the unified diff format.
1998
1999
1999 .. note::
2000 .. note::
2000
2001
2001 :hg:`diff` may generate unexpected results for merges, as it will
2002 :hg:`diff` may generate unexpected results for merges, as it will
2002 default to comparing against the working directory's first
2003 default to comparing against the working directory's first
2003 parent changeset if no revisions are specified.
2004 parent changeset if no revisions are specified.
2004
2005
2005 When two revision arguments are given, then changes are shown
2006 When two revision arguments are given, then changes are shown
2006 between those revisions. If only one revision is specified then
2007 between those revisions. If only one revision is specified then
2007 that revision is compared to the working directory, and, when no
2008 that revision is compared to the working directory, and, when no
2008 revisions are specified, the working directory files are compared
2009 revisions are specified, the working directory files are compared
2009 to its first parent.
2010 to its first parent.
2010
2011
2011 Alternatively you can specify -c/--change with a revision to see
2012 Alternatively you can specify -c/--change with a revision to see
2012 the changes in that changeset relative to its first parent.
2013 the changes in that changeset relative to its first parent.
2013
2014
2014 Without the -a/--text option, diff will avoid generating diffs of
2015 Without the -a/--text option, diff will avoid generating diffs of
2015 files it detects as binary. With -a, diff will generate a diff
2016 files it detects as binary. With -a, diff will generate a diff
2016 anyway, probably with undesirable results.
2017 anyway, probably with undesirable results.
2017
2018
2018 Use the -g/--git option to generate diffs in the git extended diff
2019 Use the -g/--git option to generate diffs in the git extended diff
2019 format. For more information, read :hg:`help diffs`.
2020 format. For more information, read :hg:`help diffs`.
2020
2021
2021 .. container:: verbose
2022 .. container:: verbose
2022
2023
2023 Examples:
2024 Examples:
2024
2025
2025 - compare a file in the current working directory to its parent::
2026 - compare a file in the current working directory to its parent::
2026
2027
2027 hg diff foo.c
2028 hg diff foo.c
2028
2029
2029 - compare two historical versions of a directory, with rename info::
2030 - compare two historical versions of a directory, with rename info::
2030
2031
2031 hg diff --git -r 1.0:1.2 lib/
2032 hg diff --git -r 1.0:1.2 lib/
2032
2033
2033 - get change stats relative to the last change on some date::
2034 - get change stats relative to the last change on some date::
2034
2035
2035 hg diff --stat -r "date('may 2')"
2036 hg diff --stat -r "date('may 2')"
2036
2037
2037 - diff all newly-added files that contain a keyword::
2038 - diff all newly-added files that contain a keyword::
2038
2039
2039 hg diff "set:added() and grep(GNU)"
2040 hg diff "set:added() and grep(GNU)"
2040
2041
2041 - compare a revision and its parents::
2042 - compare a revision and its parents::
2042
2043
2043 hg diff -c 9353 # compare against first parent
2044 hg diff -c 9353 # compare against first parent
2044 hg diff -r 9353^:9353 # same using revset syntax
2045 hg diff -r 9353^:9353 # same using revset syntax
2045 hg diff -r 9353^2:9353 # compare against the second parent
2046 hg diff -r 9353^2:9353 # compare against the second parent
2046
2047
2047 Returns 0 on success.
2048 Returns 0 on success.
2048 """
2049 """
2049
2050
2050 opts = pycompat.byteskwargs(opts)
2051 opts = pycompat.byteskwargs(opts)
2051 revs = opts.get('rev')
2052 revs = opts.get('rev')
2052 change = opts.get('change')
2053 change = opts.get('change')
2053 stat = opts.get('stat')
2054 stat = opts.get('stat')
2054 reverse = opts.get('reverse')
2055 reverse = opts.get('reverse')
2055
2056
2056 if revs and change:
2057 if revs and change:
2057 msg = _('cannot specify --rev and --change at the same time')
2058 msg = _('cannot specify --rev and --change at the same time')
2058 raise error.Abort(msg)
2059 raise error.Abort(msg)
2059 elif change:
2060 elif change:
2060 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
2061 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
2061 ctx2 = scmutil.revsingle(repo, change, None)
2062 ctx2 = scmutil.revsingle(repo, change, None)
2062 ctx1 = ctx2.p1()
2063 ctx1 = ctx2.p1()
2063 else:
2064 else:
2064 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
2065 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
2065 ctx1, ctx2 = scmutil.revpair(repo, revs)
2066 ctx1, ctx2 = scmutil.revpair(repo, revs)
2066 node1, node2 = ctx1.node(), ctx2.node()
2067 node1, node2 = ctx1.node(), ctx2.node()
2067
2068
2068 if reverse:
2069 if reverse:
2069 node1, node2 = node2, node1
2070 node1, node2 = node2, node1
2070
2071
2071 diffopts = patch.diffallopts(ui, opts)
2072 diffopts = patch.diffallopts(ui, opts)
2072 m = scmutil.match(ctx2, pats, opts)
2073 m = scmutil.match(ctx2, pats, opts)
2073 m = repo.narrowmatch(m)
2074 m = repo.narrowmatch(m)
2074 ui.pager('diff')
2075 ui.pager('diff')
2075 logcmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2076 logcmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2076 listsubrepos=opts.get('subrepos'),
2077 listsubrepos=opts.get('subrepos'),
2077 root=opts.get('root'))
2078 root=opts.get('root'))
2078
2079
2079 @command('export',
2080 @command('export',
2080 [('B', 'bookmark', '',
2081 [('B', 'bookmark', '',
2081 _('export changes only reachable by given bookmark'), _('BOOKMARK')),
2082 _('export changes only reachable by given bookmark'), _('BOOKMARK')),
2082 ('o', 'output', '',
2083 ('o', 'output', '',
2083 _('print output to file with formatted name'), _('FORMAT')),
2084 _('print output to file with formatted name'), _('FORMAT')),
2084 ('', 'switch-parent', None, _('diff against the second parent')),
2085 ('', 'switch-parent', None, _('diff against the second parent')),
2085 ('r', 'rev', [], _('revisions to export'), _('REV')),
2086 ('r', 'rev', [], _('revisions to export'), _('REV')),
2086 ] + diffopts + formatteropts,
2087 ] + diffopts + formatteropts,
2087 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2088 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2088 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2089 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2089 helpbasic=True, intents={INTENT_READONLY})
2090 helpbasic=True, intents={INTENT_READONLY})
2090 def export(ui, repo, *changesets, **opts):
2091 def export(ui, repo, *changesets, **opts):
2091 """dump the header and diffs for one or more changesets
2092 """dump the header and diffs for one or more changesets
2092
2093
2093 Print the changeset header and diffs for one or more revisions.
2094 Print the changeset header and diffs for one or more revisions.
2094 If no revision is given, the parent of the working directory is used.
2095 If no revision is given, the parent of the working directory is used.
2095
2096
2096 The information shown in the changeset header is: author, date,
2097 The information shown in the changeset header is: author, date,
2097 branch name (if non-default), changeset hash, parent(s) and commit
2098 branch name (if non-default), changeset hash, parent(s) and commit
2098 comment.
2099 comment.
2099
2100
2100 .. note::
2101 .. note::
2101
2102
2102 :hg:`export` may generate unexpected diff output for merge
2103 :hg:`export` may generate unexpected diff output for merge
2103 changesets, as it will compare the merge changeset against its
2104 changesets, as it will compare the merge changeset against its
2104 first parent only.
2105 first parent only.
2105
2106
2106 Output may be to a file, in which case the name of the file is
2107 Output may be to a file, in which case the name of the file is
2107 given using a template string. See :hg:`help templates`. In addition
2108 given using a template string. See :hg:`help templates`. In addition
2108 to the common template keywords, the following formatting rules are
2109 to the common template keywords, the following formatting rules are
2109 supported:
2110 supported:
2110
2111
2111 :``%%``: literal "%" character
2112 :``%%``: literal "%" character
2112 :``%H``: changeset hash (40 hexadecimal digits)
2113 :``%H``: changeset hash (40 hexadecimal digits)
2113 :``%N``: number of patches being generated
2114 :``%N``: number of patches being generated
2114 :``%R``: changeset revision number
2115 :``%R``: changeset revision number
2115 :``%b``: basename of the exporting repository
2116 :``%b``: basename of the exporting repository
2116 :``%h``: short-form changeset hash (12 hexadecimal digits)
2117 :``%h``: short-form changeset hash (12 hexadecimal digits)
2117 :``%m``: first line of the commit message (only alphanumeric characters)
2118 :``%m``: first line of the commit message (only alphanumeric characters)
2118 :``%n``: zero-padded sequence number, starting at 1
2119 :``%n``: zero-padded sequence number, starting at 1
2119 :``%r``: zero-padded changeset revision number
2120 :``%r``: zero-padded changeset revision number
2120 :``\\``: literal "\\" character
2121 :``\\``: literal "\\" character
2121
2122
2122 Without the -a/--text option, export will avoid generating diffs
2123 Without the -a/--text option, export will avoid generating diffs
2123 of files it detects as binary. With -a, export will generate a
2124 of files it detects as binary. With -a, export will generate a
2124 diff anyway, probably with undesirable results.
2125 diff anyway, probably with undesirable results.
2125
2126
2126 With -B/--bookmark changesets reachable by the given bookmark are
2127 With -B/--bookmark changesets reachable by the given bookmark are
2127 selected.
2128 selected.
2128
2129
2129 Use the -g/--git option to generate diffs in the git extended diff
2130 Use the -g/--git option to generate diffs in the git extended diff
2130 format. See :hg:`help diffs` for more information.
2131 format. See :hg:`help diffs` for more information.
2131
2132
2132 With the --switch-parent option, the diff will be against the
2133 With the --switch-parent option, the diff will be against the
2133 second parent. It can be useful to review a merge.
2134 second parent. It can be useful to review a merge.
2134
2135
2135 .. container:: verbose
2136 .. container:: verbose
2136
2137
2137 Template:
2138 Template:
2138
2139
2139 The following keywords are supported in addition to the common template
2140 The following keywords are supported in addition to the common template
2140 keywords and functions. See also :hg:`help templates`.
2141 keywords and functions. See also :hg:`help templates`.
2141
2142
2142 :diff: String. Diff content.
2143 :diff: String. Diff content.
2143 :parents: List of strings. Parent nodes of the changeset.
2144 :parents: List of strings. Parent nodes of the changeset.
2144
2145
2145 Examples:
2146 Examples:
2146
2147
2147 - use export and import to transplant a bugfix to the current
2148 - use export and import to transplant a bugfix to the current
2148 branch::
2149 branch::
2149
2150
2150 hg export -r 9353 | hg import -
2151 hg export -r 9353 | hg import -
2151
2152
2152 - export all the changesets between two revisions to a file with
2153 - export all the changesets between two revisions to a file with
2153 rename information::
2154 rename information::
2154
2155
2155 hg export --git -r 123:150 > changes.txt
2156 hg export --git -r 123:150 > changes.txt
2156
2157
2157 - split outgoing changes into a series of patches with
2158 - split outgoing changes into a series of patches with
2158 descriptive names::
2159 descriptive names::
2159
2160
2160 hg export -r "outgoing()" -o "%n-%m.patch"
2161 hg export -r "outgoing()" -o "%n-%m.patch"
2161
2162
2162 Returns 0 on success.
2163 Returns 0 on success.
2163 """
2164 """
2164 opts = pycompat.byteskwargs(opts)
2165 opts = pycompat.byteskwargs(opts)
2165 bookmark = opts.get('bookmark')
2166 bookmark = opts.get('bookmark')
2166 changesets += tuple(opts.get('rev', []))
2167 changesets += tuple(opts.get('rev', []))
2167
2168
2168 if bookmark and changesets:
2169 if bookmark and changesets:
2169 raise error.Abort(_("-r and -B are mutually exclusive"))
2170 raise error.Abort(_("-r and -B are mutually exclusive"))
2170
2171
2171 if bookmark:
2172 if bookmark:
2172 if bookmark not in repo._bookmarks:
2173 if bookmark not in repo._bookmarks:
2173 raise error.Abort(_("bookmark '%s' not found") % bookmark)
2174 raise error.Abort(_("bookmark '%s' not found") % bookmark)
2174
2175
2175 revs = scmutil.bookmarkrevs(repo, bookmark)
2176 revs = scmutil.bookmarkrevs(repo, bookmark)
2176 else:
2177 else:
2177 if not changesets:
2178 if not changesets:
2178 changesets = ['.']
2179 changesets = ['.']
2179
2180
2180 repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn')
2181 repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn')
2181 revs = scmutil.revrange(repo, changesets)
2182 revs = scmutil.revrange(repo, changesets)
2182
2183
2183 if not revs:
2184 if not revs:
2184 raise error.Abort(_("export requires at least one changeset"))
2185 raise error.Abort(_("export requires at least one changeset"))
2185 if len(revs) > 1:
2186 if len(revs) > 1:
2186 ui.note(_('exporting patches:\n'))
2187 ui.note(_('exporting patches:\n'))
2187 else:
2188 else:
2188 ui.note(_('exporting patch:\n'))
2189 ui.note(_('exporting patch:\n'))
2189
2190
2190 fntemplate = opts.get('output')
2191 fntemplate = opts.get('output')
2191 if cmdutil.isstdiofilename(fntemplate):
2192 if cmdutil.isstdiofilename(fntemplate):
2192 fntemplate = ''
2193 fntemplate = ''
2193
2194
2194 if fntemplate:
2195 if fntemplate:
2195 fm = formatter.nullformatter(ui, 'export', opts)
2196 fm = formatter.nullformatter(ui, 'export', opts)
2196 else:
2197 else:
2197 ui.pager('export')
2198 ui.pager('export')
2198 fm = ui.formatter('export', opts)
2199 fm = ui.formatter('export', opts)
2199 with fm:
2200 with fm:
2200 cmdutil.export(repo, revs, fm, fntemplate=fntemplate,
2201 cmdutil.export(repo, revs, fm, fntemplate=fntemplate,
2201 switch_parent=opts.get('switch_parent'),
2202 switch_parent=opts.get('switch_parent'),
2202 opts=patch.diffallopts(ui, opts))
2203 opts=patch.diffallopts(ui, opts))
2203
2204
2204 @command('files',
2205 @command('files',
2205 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
2206 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
2206 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2207 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2207 ] + walkopts + formatteropts + subrepoopts,
2208 ] + walkopts + formatteropts + subrepoopts,
2208 _('[OPTION]... [FILE]...'),
2209 _('[OPTION]... [FILE]...'),
2209 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2210 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2210 intents={INTENT_READONLY})
2211 intents={INTENT_READONLY})
2211 def files(ui, repo, *pats, **opts):
2212 def files(ui, repo, *pats, **opts):
2212 """list tracked files
2213 """list tracked files
2213
2214
2214 Print files under Mercurial control in the working directory or
2215 Print files under Mercurial control in the working directory or
2215 specified revision for given files (excluding removed files).
2216 specified revision for given files (excluding removed files).
2216 Files can be specified as filenames or filesets.
2217 Files can be specified as filenames or filesets.
2217
2218
2218 If no files are given to match, this command prints the names
2219 If no files are given to match, this command prints the names
2219 of all files under Mercurial control.
2220 of all files under Mercurial control.
2220
2221
2221 .. container:: verbose
2222 .. container:: verbose
2222
2223
2223 Template:
2224 Template:
2224
2225
2225 The following keywords are supported in addition to the common template
2226 The following keywords are supported in addition to the common template
2226 keywords and functions. See also :hg:`help templates`.
2227 keywords and functions. See also :hg:`help templates`.
2227
2228
2228 :flags: String. Character denoting file's symlink and executable bits.
2229 :flags: String. Character denoting file's symlink and executable bits.
2229 :path: String. Repository-absolute path of the file.
2230 :path: String. Repository-absolute path of the file.
2230 :size: Integer. Size of the file in bytes.
2231 :size: Integer. Size of the file in bytes.
2231
2232
2232 Examples:
2233 Examples:
2233
2234
2234 - list all files under the current directory::
2235 - list all files under the current directory::
2235
2236
2236 hg files .
2237 hg files .
2237
2238
2238 - shows sizes and flags for current revision::
2239 - shows sizes and flags for current revision::
2239
2240
2240 hg files -vr .
2241 hg files -vr .
2241
2242
2242 - list all files named README::
2243 - list all files named README::
2243
2244
2244 hg files -I "**/README"
2245 hg files -I "**/README"
2245
2246
2246 - list all binary files::
2247 - list all binary files::
2247
2248
2248 hg files "set:binary()"
2249 hg files "set:binary()"
2249
2250
2250 - find files containing a regular expression::
2251 - find files containing a regular expression::
2251
2252
2252 hg files "set:grep('bob')"
2253 hg files "set:grep('bob')"
2253
2254
2254 - search tracked file contents with xargs and grep::
2255 - search tracked file contents with xargs and grep::
2255
2256
2256 hg files -0 | xargs -0 grep foo
2257 hg files -0 | xargs -0 grep foo
2257
2258
2258 See :hg:`help patterns` and :hg:`help filesets` for more information
2259 See :hg:`help patterns` and :hg:`help filesets` for more information
2259 on specifying file patterns.
2260 on specifying file patterns.
2260
2261
2261 Returns 0 if a match is found, 1 otherwise.
2262 Returns 0 if a match is found, 1 otherwise.
2262
2263
2263 """
2264 """
2264
2265
2265 opts = pycompat.byteskwargs(opts)
2266 opts = pycompat.byteskwargs(opts)
2266 rev = opts.get('rev')
2267 rev = opts.get('rev')
2267 if rev:
2268 if rev:
2268 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2269 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2269 ctx = scmutil.revsingle(repo, rev, None)
2270 ctx = scmutil.revsingle(repo, rev, None)
2270
2271
2271 end = '\n'
2272 end = '\n'
2272 if opts.get('print0'):
2273 if opts.get('print0'):
2273 end = '\0'
2274 end = '\0'
2274 fmt = '%s' + end
2275 fmt = '%s' + end
2275
2276
2276 m = scmutil.match(ctx, pats, opts)
2277 m = scmutil.match(ctx, pats, opts)
2277 ui.pager('files')
2278 ui.pager('files')
2278 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2279 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2279 with ui.formatter('files', opts) as fm:
2280 with ui.formatter('files', opts) as fm:
2280 return cmdutil.files(ui, ctx, m, uipathfn, fm, fmt,
2281 return cmdutil.files(ui, ctx, m, uipathfn, fm, fmt,
2281 opts.get('subrepos'))
2282 opts.get('subrepos'))
2282
2283
2283 @command(
2284 @command(
2284 'forget',
2285 'forget',
2285 [('i', 'interactive', None, _('use interactive mode')),
2286 [('i', 'interactive', None, _('use interactive mode')),
2286 ] + walkopts + dryrunopts,
2287 ] + walkopts + dryrunopts,
2287 _('[OPTION]... FILE...'),
2288 _('[OPTION]... FILE...'),
2288 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2289 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2289 helpbasic=True, inferrepo=True)
2290 helpbasic=True, inferrepo=True)
2290 def forget(ui, repo, *pats, **opts):
2291 def forget(ui, repo, *pats, **opts):
2291 """forget the specified files on the next commit
2292 """forget the specified files on the next commit
2292
2293
2293 Mark the specified files so they will no longer be tracked
2294 Mark the specified files so they will no longer be tracked
2294 after the next commit.
2295 after the next commit.
2295
2296
2296 This only removes files from the current branch, not from the
2297 This only removes files from the current branch, not from the
2297 entire project history, and it does not delete them from the
2298 entire project history, and it does not delete them from the
2298 working directory.
2299 working directory.
2299
2300
2300 To delete the file from the working directory, see :hg:`remove`.
2301 To delete the file from the working directory, see :hg:`remove`.
2301
2302
2302 To undo a forget before the next commit, see :hg:`add`.
2303 To undo a forget before the next commit, see :hg:`add`.
2303
2304
2304 .. container:: verbose
2305 .. container:: verbose
2305
2306
2306 Examples:
2307 Examples:
2307
2308
2308 - forget newly-added binary files::
2309 - forget newly-added binary files::
2309
2310
2310 hg forget "set:added() and binary()"
2311 hg forget "set:added() and binary()"
2311
2312
2312 - forget files that would be excluded by .hgignore::
2313 - forget files that would be excluded by .hgignore::
2313
2314
2314 hg forget "set:hgignore()"
2315 hg forget "set:hgignore()"
2315
2316
2316 Returns 0 on success.
2317 Returns 0 on success.
2317 """
2318 """
2318
2319
2319 opts = pycompat.byteskwargs(opts)
2320 opts = pycompat.byteskwargs(opts)
2320 if not pats:
2321 if not pats:
2321 raise error.Abort(_('no files specified'))
2322 raise error.Abort(_('no files specified'))
2322
2323
2323 m = scmutil.match(repo[None], pats, opts)
2324 m = scmutil.match(repo[None], pats, opts)
2324 dryrun, interactive = opts.get('dry_run'), opts.get('interactive')
2325 dryrun, interactive = opts.get('dry_run'), opts.get('interactive')
2325 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2326 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2326 rejected = cmdutil.forget(ui, repo, m, prefix="", uipathfn=uipathfn,
2327 rejected = cmdutil.forget(ui, repo, m, prefix="", uipathfn=uipathfn,
2327 explicitonly=False, dryrun=dryrun,
2328 explicitonly=False, dryrun=dryrun,
2328 interactive=interactive)[0]
2329 interactive=interactive)[0]
2329 return rejected and 1 or 0
2330 return rejected and 1 or 0
2330
2331
2331 @command(
2332 @command(
2332 'graft',
2333 'graft',
2333 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2334 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2334 ('', 'base', '',
2335 ('', 'base', '',
2335 _('base revision when doing the graft merge (ADVANCED)'), _('REV')),
2336 _('base revision when doing the graft merge (ADVANCED)'), _('REV')),
2336 ('c', 'continue', False, _('resume interrupted graft')),
2337 ('c', 'continue', False, _('resume interrupted graft')),
2337 ('', 'stop', False, _('stop interrupted graft')),
2338 ('', 'stop', False, _('stop interrupted graft')),
2338 ('', 'abort', False, _('abort interrupted graft')),
2339 ('', 'abort', False, _('abort interrupted graft')),
2339 ('e', 'edit', False, _('invoke editor on commit messages')),
2340 ('e', 'edit', False, _('invoke editor on commit messages')),
2340 ('', 'log', None, _('append graft info to log message')),
2341 ('', 'log', None, _('append graft info to log message')),
2341 ('', 'no-commit', None,
2342 ('', 'no-commit', None,
2342 _("don't commit, just apply the changes in working directory")),
2343 _("don't commit, just apply the changes in working directory")),
2343 ('f', 'force', False, _('force graft')),
2344 ('f', 'force', False, _('force graft')),
2344 ('D', 'currentdate', False,
2345 ('D', 'currentdate', False,
2345 _('record the current date as commit date')),
2346 _('record the current date as commit date')),
2346 ('U', 'currentuser', False,
2347 ('U', 'currentuser', False,
2347 _('record the current user as committer'))]
2348 _('record the current user as committer'))]
2348 + commitopts2 + mergetoolopts + dryrunopts,
2349 + commitopts2 + mergetoolopts + dryrunopts,
2349 _('[OPTION]... [-r REV]... REV...'),
2350 _('[OPTION]... [-r REV]... REV...'),
2350 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
2351 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
2351 def graft(ui, repo, *revs, **opts):
2352 def graft(ui, repo, *revs, **opts):
2352 '''copy changes from other branches onto the current branch
2353 '''copy changes from other branches onto the current branch
2353
2354
2354 This command uses Mercurial's merge logic to copy individual
2355 This command uses Mercurial's merge logic to copy individual
2355 changes from other branches without merging branches in the
2356 changes from other branches without merging branches in the
2356 history graph. This is sometimes known as 'backporting' or
2357 history graph. This is sometimes known as 'backporting' or
2357 'cherry-picking'. By default, graft will copy user, date, and
2358 'cherry-picking'. By default, graft will copy user, date, and
2358 description from the source changesets.
2359 description from the source changesets.
2359
2360
2360 Changesets that are ancestors of the current revision, that have
2361 Changesets that are ancestors of the current revision, that have
2361 already been grafted, or that are merges will be skipped.
2362 already been grafted, or that are merges will be skipped.
2362
2363
2363 If --log is specified, log messages will have a comment appended
2364 If --log is specified, log messages will have a comment appended
2364 of the form::
2365 of the form::
2365
2366
2366 (grafted from CHANGESETHASH)
2367 (grafted from CHANGESETHASH)
2367
2368
2368 If --force is specified, revisions will be grafted even if they
2369 If --force is specified, revisions will be grafted even if they
2369 are already ancestors of, or have been grafted to, the destination.
2370 are already ancestors of, or have been grafted to, the destination.
2370 This is useful when the revisions have since been backed out.
2371 This is useful when the revisions have since been backed out.
2371
2372
2372 If a graft merge results in conflicts, the graft process is
2373 If a graft merge results in conflicts, the graft process is
2373 interrupted so that the current merge can be manually resolved.
2374 interrupted so that the current merge can be manually resolved.
2374 Once all conflicts are addressed, the graft process can be
2375 Once all conflicts are addressed, the graft process can be
2375 continued with the -c/--continue option.
2376 continued with the -c/--continue option.
2376
2377
2377 The -c/--continue option reapplies all the earlier options.
2378 The -c/--continue option reapplies all the earlier options.
2378
2379
2379 .. container:: verbose
2380 .. container:: verbose
2380
2381
2381 The --base option exposes more of how graft internally uses merge with a
2382 The --base option exposes more of how graft internally uses merge with a
2382 custom base revision. --base can be used to specify another ancestor than
2383 custom base revision. --base can be used to specify another ancestor than
2383 the first and only parent.
2384 the first and only parent.
2384
2385
2385 The command::
2386 The command::
2386
2387
2387 hg graft -r 345 --base 234
2388 hg graft -r 345 --base 234
2388
2389
2389 is thus pretty much the same as::
2390 is thus pretty much the same as::
2390
2391
2391 hg diff -r 234 -r 345 | hg import
2392 hg diff -r 234 -r 345 | hg import
2392
2393
2393 but using merge to resolve conflicts and track moved files.
2394 but using merge to resolve conflicts and track moved files.
2394
2395
2395 The result of a merge can thus be backported as a single commit by
2396 The result of a merge can thus be backported as a single commit by
2396 specifying one of the merge parents as base, and thus effectively
2397 specifying one of the merge parents as base, and thus effectively
2397 grafting the changes from the other side.
2398 grafting the changes from the other side.
2398
2399
2399 It is also possible to collapse multiple changesets and clean up history
2400 It is also possible to collapse multiple changesets and clean up history
2400 by specifying another ancestor as base, much like rebase --collapse
2401 by specifying another ancestor as base, much like rebase --collapse
2401 --keep.
2402 --keep.
2402
2403
2403 The commit message can be tweaked after the fact using commit --amend .
2404 The commit message can be tweaked after the fact using commit --amend .
2404
2405
2405 For using non-ancestors as the base to backout changes, see the backout
2406 For using non-ancestors as the base to backout changes, see the backout
2406 command and the hidden --parent option.
2407 command and the hidden --parent option.
2407
2408
2408 .. container:: verbose
2409 .. container:: verbose
2409
2410
2410 Examples:
2411 Examples:
2411
2412
2412 - copy a single change to the stable branch and edit its description::
2413 - copy a single change to the stable branch and edit its description::
2413
2414
2414 hg update stable
2415 hg update stable
2415 hg graft --edit 9393
2416 hg graft --edit 9393
2416
2417
2417 - graft a range of changesets with one exception, updating dates::
2418 - graft a range of changesets with one exception, updating dates::
2418
2419
2419 hg graft -D "2085::2093 and not 2091"
2420 hg graft -D "2085::2093 and not 2091"
2420
2421
2421 - continue a graft after resolving conflicts::
2422 - continue a graft after resolving conflicts::
2422
2423
2423 hg graft -c
2424 hg graft -c
2424
2425
2425 - show the source of a grafted changeset::
2426 - show the source of a grafted changeset::
2426
2427
2427 hg log --debug -r .
2428 hg log --debug -r .
2428
2429
2429 - show revisions sorted by date::
2430 - show revisions sorted by date::
2430
2431
2431 hg log -r "sort(all(), date)"
2432 hg log -r "sort(all(), date)"
2432
2433
2433 - backport the result of a merge as a single commit::
2434 - backport the result of a merge as a single commit::
2434
2435
2435 hg graft -r 123 --base 123^
2436 hg graft -r 123 --base 123^
2436
2437
2437 - land a feature branch as one changeset::
2438 - land a feature branch as one changeset::
2438
2439
2439 hg up -cr default
2440 hg up -cr default
2440 hg graft -r featureX --base "ancestor('featureX', 'default')"
2441 hg graft -r featureX --base "ancestor('featureX', 'default')"
2441
2442
2442 See :hg:`help revisions` for more about specifying revisions.
2443 See :hg:`help revisions` for more about specifying revisions.
2443
2444
2444 Returns 0 on successful completion.
2445 Returns 0 on successful completion.
2445 '''
2446 '''
2446 with repo.wlock():
2447 with repo.wlock():
2447 return _dograft(ui, repo, *revs, **opts)
2448 return _dograft(ui, repo, *revs, **opts)
2448
2449
2449 def _dograft(ui, repo, *revs, **opts):
2450 def _dograft(ui, repo, *revs, **opts):
2450 opts = pycompat.byteskwargs(opts)
2451 opts = pycompat.byteskwargs(opts)
2451 if revs and opts.get('rev'):
2452 if revs and opts.get('rev'):
2452 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2453 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2453 'revision ordering!\n'))
2454 'revision ordering!\n'))
2454
2455
2455 revs = list(revs)
2456 revs = list(revs)
2456 revs.extend(opts.get('rev'))
2457 revs.extend(opts.get('rev'))
2457 basectx = None
2458 basectx = None
2458 if opts.get('base'):
2459 if opts.get('base'):
2459 basectx = scmutil.revsingle(repo, opts['base'], None)
2460 basectx = scmutil.revsingle(repo, opts['base'], None)
2460 # a dict of data to be stored in state file
2461 # a dict of data to be stored in state file
2461 statedata = {}
2462 statedata = {}
2462 # list of new nodes created by ongoing graft
2463 # list of new nodes created by ongoing graft
2463 statedata['newnodes'] = []
2464 statedata['newnodes'] = []
2464
2465
2465 if opts.get('user') and opts.get('currentuser'):
2466 if opts.get('user') and opts.get('currentuser'):
2466 raise error.Abort(_('--user and --currentuser are mutually exclusive'))
2467 raise error.Abort(_('--user and --currentuser are mutually exclusive'))
2467 if opts.get('date') and opts.get('currentdate'):
2468 if opts.get('date') and opts.get('currentdate'):
2468 raise error.Abort(_('--date and --currentdate are mutually exclusive'))
2469 raise error.Abort(_('--date and --currentdate are mutually exclusive'))
2469 if not opts.get('user') and opts.get('currentuser'):
2470 if not opts.get('user') and opts.get('currentuser'):
2470 opts['user'] = ui.username()
2471 opts['user'] = ui.username()
2471 if not opts.get('date') and opts.get('currentdate'):
2472 if not opts.get('date') and opts.get('currentdate'):
2472 opts['date'] = "%d %d" % dateutil.makedate()
2473 opts['date'] = "%d %d" % dateutil.makedate()
2473
2474
2474 editor = cmdutil.getcommiteditor(editform='graft',
2475 editor = cmdutil.getcommiteditor(editform='graft',
2475 **pycompat.strkwargs(opts))
2476 **pycompat.strkwargs(opts))
2476
2477
2477 cont = False
2478 cont = False
2478 if opts.get('no_commit'):
2479 if opts.get('no_commit'):
2479 if opts.get('edit'):
2480 if opts.get('edit'):
2480 raise error.Abort(_("cannot specify --no-commit and "
2481 raise error.Abort(_("cannot specify --no-commit and "
2481 "--edit together"))
2482 "--edit together"))
2482 if opts.get('currentuser'):
2483 if opts.get('currentuser'):
2483 raise error.Abort(_("cannot specify --no-commit and "
2484 raise error.Abort(_("cannot specify --no-commit and "
2484 "--currentuser together"))
2485 "--currentuser together"))
2485 if opts.get('currentdate'):
2486 if opts.get('currentdate'):
2486 raise error.Abort(_("cannot specify --no-commit and "
2487 raise error.Abort(_("cannot specify --no-commit and "
2487 "--currentdate together"))
2488 "--currentdate together"))
2488 if opts.get('log'):
2489 if opts.get('log'):
2489 raise error.Abort(_("cannot specify --no-commit and "
2490 raise error.Abort(_("cannot specify --no-commit and "
2490 "--log together"))
2491 "--log together"))
2491
2492
2492 graftstate = statemod.cmdstate(repo, 'graftstate')
2493 graftstate = statemod.cmdstate(repo, 'graftstate')
2493
2494
2494 if opts.get('stop'):
2495 if opts.get('stop'):
2495 if opts.get('continue'):
2496 if opts.get('continue'):
2496 raise error.Abort(_("cannot use '--continue' and "
2497 raise error.Abort(_("cannot use '--continue' and "
2497 "'--stop' together"))
2498 "'--stop' together"))
2498 if opts.get('abort'):
2499 if opts.get('abort'):
2499 raise error.Abort(_("cannot use '--abort' and '--stop' together"))
2500 raise error.Abort(_("cannot use '--abort' and '--stop' together"))
2500
2501
2501 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2502 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2502 opts.get('date'), opts.get('currentdate'),
2503 opts.get('date'), opts.get('currentdate'),
2503 opts.get('currentuser'), opts.get('rev'))):
2504 opts.get('currentuser'), opts.get('rev'))):
2504 raise error.Abort(_("cannot specify any other flag with '--stop'"))
2505 raise error.Abort(_("cannot specify any other flag with '--stop'"))
2505 return _stopgraft(ui, repo, graftstate)
2506 return _stopgraft(ui, repo, graftstate)
2506 elif opts.get('abort'):
2507 elif opts.get('abort'):
2507 if opts.get('continue'):
2508 if opts.get('continue'):
2508 raise error.Abort(_("cannot use '--continue' and "
2509 raise error.Abort(_("cannot use '--continue' and "
2509 "'--abort' together"))
2510 "'--abort' together"))
2510 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2511 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2511 opts.get('date'), opts.get('currentdate'),
2512 opts.get('date'), opts.get('currentdate'),
2512 opts.get('currentuser'), opts.get('rev'))):
2513 opts.get('currentuser'), opts.get('rev'))):
2513 raise error.Abort(_("cannot specify any other flag with '--abort'"))
2514 raise error.Abort(_("cannot specify any other flag with '--abort'"))
2514
2515
2515 return cmdutil.abortgraft(ui, repo, graftstate)
2516 return cmdutil.abortgraft(ui, repo, graftstate)
2516 elif opts.get('continue'):
2517 elif opts.get('continue'):
2517 cont = True
2518 cont = True
2518 if revs:
2519 if revs:
2519 raise error.Abort(_("can't specify --continue and revisions"))
2520 raise error.Abort(_("can't specify --continue and revisions"))
2520 # read in unfinished revisions
2521 # read in unfinished revisions
2521 if graftstate.exists():
2522 if graftstate.exists():
2522 statedata = cmdutil.readgraftstate(repo, graftstate)
2523 statedata = cmdutil.readgraftstate(repo, graftstate)
2523 if statedata.get('date'):
2524 if statedata.get('date'):
2524 opts['date'] = statedata['date']
2525 opts['date'] = statedata['date']
2525 if statedata.get('user'):
2526 if statedata.get('user'):
2526 opts['user'] = statedata['user']
2527 opts['user'] = statedata['user']
2527 if statedata.get('log'):
2528 if statedata.get('log'):
2528 opts['log'] = True
2529 opts['log'] = True
2529 if statedata.get('no_commit'):
2530 if statedata.get('no_commit'):
2530 opts['no_commit'] = statedata.get('no_commit')
2531 opts['no_commit'] = statedata.get('no_commit')
2531 nodes = statedata['nodes']
2532 nodes = statedata['nodes']
2532 revs = [repo[node].rev() for node in nodes]
2533 revs = [repo[node].rev() for node in nodes]
2533 else:
2534 else:
2534 cmdutil.wrongtooltocontinue(repo, _('graft'))
2535 cmdutil.wrongtooltocontinue(repo, _('graft'))
2535 else:
2536 else:
2536 if not revs:
2537 if not revs:
2537 raise error.Abort(_('no revisions specified'))
2538 raise error.Abort(_('no revisions specified'))
2538 cmdutil.checkunfinished(repo)
2539 cmdutil.checkunfinished(repo)
2539 cmdutil.bailifchanged(repo)
2540 cmdutil.bailifchanged(repo)
2540 revs = scmutil.revrange(repo, revs)
2541 revs = scmutil.revrange(repo, revs)
2541
2542
2542 skipped = set()
2543 skipped = set()
2543 if basectx is None:
2544 if basectx is None:
2544 # check for merges
2545 # check for merges
2545 for rev in repo.revs('%ld and merge()', revs):
2546 for rev in repo.revs('%ld and merge()', revs):
2546 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2547 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2547 skipped.add(rev)
2548 skipped.add(rev)
2548 revs = [r for r in revs if r not in skipped]
2549 revs = [r for r in revs if r not in skipped]
2549 if not revs:
2550 if not revs:
2550 return -1
2551 return -1
2551 if basectx is not None and len(revs) != 1:
2552 if basectx is not None and len(revs) != 1:
2552 raise error.Abort(_('only one revision allowed with --base '))
2553 raise error.Abort(_('only one revision allowed with --base '))
2553
2554
2554 # Don't check in the --continue case, in effect retaining --force across
2555 # Don't check in the --continue case, in effect retaining --force across
2555 # --continues. That's because without --force, any revisions we decided to
2556 # --continues. That's because without --force, any revisions we decided to
2556 # skip would have been filtered out here, so they wouldn't have made their
2557 # skip would have been filtered out here, so they wouldn't have made their
2557 # way to the graftstate. With --force, any revisions we would have otherwise
2558 # way to the graftstate. With --force, any revisions we would have otherwise
2558 # skipped would not have been filtered out, and if they hadn't been applied
2559 # skipped would not have been filtered out, and if they hadn't been applied
2559 # already, they'd have been in the graftstate.
2560 # already, they'd have been in the graftstate.
2560 if not (cont or opts.get('force')) and basectx is None:
2561 if not (cont or opts.get('force')) and basectx is None:
2561 # check for ancestors of dest branch
2562 # check for ancestors of dest branch
2562 crev = repo['.'].rev()
2563 crev = repo['.'].rev()
2563 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2564 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2564 # XXX make this lazy in the future
2565 # XXX make this lazy in the future
2565 # don't mutate while iterating, create a copy
2566 # don't mutate while iterating, create a copy
2566 for rev in list(revs):
2567 for rev in list(revs):
2567 if rev in ancestors:
2568 if rev in ancestors:
2568 ui.warn(_('skipping ancestor revision %d:%s\n') %
2569 ui.warn(_('skipping ancestor revision %d:%s\n') %
2569 (rev, repo[rev]))
2570 (rev, repo[rev]))
2570 # XXX remove on list is slow
2571 # XXX remove on list is slow
2571 revs.remove(rev)
2572 revs.remove(rev)
2572 if not revs:
2573 if not revs:
2573 return -1
2574 return -1
2574
2575
2575 # analyze revs for earlier grafts
2576 # analyze revs for earlier grafts
2576 ids = {}
2577 ids = {}
2577 for ctx in repo.set("%ld", revs):
2578 for ctx in repo.set("%ld", revs):
2578 ids[ctx.hex()] = ctx.rev()
2579 ids[ctx.hex()] = ctx.rev()
2579 n = ctx.extra().get('source')
2580 n = ctx.extra().get('source')
2580 if n:
2581 if n:
2581 ids[n] = ctx.rev()
2582 ids[n] = ctx.rev()
2582
2583
2583 # check ancestors for earlier grafts
2584 # check ancestors for earlier grafts
2584 ui.debug('scanning for duplicate grafts\n')
2585 ui.debug('scanning for duplicate grafts\n')
2585
2586
2586 # The only changesets we can be sure doesn't contain grafts of any
2587 # The only changesets we can be sure doesn't contain grafts of any
2587 # revs, are the ones that are common ancestors of *all* revs:
2588 # revs, are the ones that are common ancestors of *all* revs:
2588 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2589 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2589 ctx = repo[rev]
2590 ctx = repo[rev]
2590 n = ctx.extra().get('source')
2591 n = ctx.extra().get('source')
2591 if n in ids:
2592 if n in ids:
2592 try:
2593 try:
2593 r = repo[n].rev()
2594 r = repo[n].rev()
2594 except error.RepoLookupError:
2595 except error.RepoLookupError:
2595 r = None
2596 r = None
2596 if r in revs:
2597 if r in revs:
2597 ui.warn(_('skipping revision %d:%s '
2598 ui.warn(_('skipping revision %d:%s '
2598 '(already grafted to %d:%s)\n')
2599 '(already grafted to %d:%s)\n')
2599 % (r, repo[r], rev, ctx))
2600 % (r, repo[r], rev, ctx))
2600 revs.remove(r)
2601 revs.remove(r)
2601 elif ids[n] in revs:
2602 elif ids[n] in revs:
2602 if r is None:
2603 if r is None:
2603 ui.warn(_('skipping already grafted revision %d:%s '
2604 ui.warn(_('skipping already grafted revision %d:%s '
2604 '(%d:%s also has unknown origin %s)\n')
2605 '(%d:%s also has unknown origin %s)\n')
2605 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2606 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2606 else:
2607 else:
2607 ui.warn(_('skipping already grafted revision %d:%s '
2608 ui.warn(_('skipping already grafted revision %d:%s '
2608 '(%d:%s also has origin %d:%s)\n')
2609 '(%d:%s also has origin %d:%s)\n')
2609 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2610 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2610 revs.remove(ids[n])
2611 revs.remove(ids[n])
2611 elif ctx.hex() in ids:
2612 elif ctx.hex() in ids:
2612 r = ids[ctx.hex()]
2613 r = ids[ctx.hex()]
2613 if r in revs:
2614 if r in revs:
2614 ui.warn(_('skipping already grafted revision %d:%s '
2615 ui.warn(_('skipping already grafted revision %d:%s '
2615 '(was grafted from %d:%s)\n') %
2616 '(was grafted from %d:%s)\n') %
2616 (r, repo[r], rev, ctx))
2617 (r, repo[r], rev, ctx))
2617 revs.remove(r)
2618 revs.remove(r)
2618 if not revs:
2619 if not revs:
2619 return -1
2620 return -1
2620
2621
2621 if opts.get('no_commit'):
2622 if opts.get('no_commit'):
2622 statedata['no_commit'] = True
2623 statedata['no_commit'] = True
2623 for pos, ctx in enumerate(repo.set("%ld", revs)):
2624 for pos, ctx in enumerate(repo.set("%ld", revs)):
2624 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2625 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2625 ctx.description().split('\n', 1)[0])
2626 ctx.description().split('\n', 1)[0])
2626 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2627 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2627 if names:
2628 if names:
2628 desc += ' (%s)' % ' '.join(names)
2629 desc += ' (%s)' % ' '.join(names)
2629 ui.status(_('grafting %s\n') % desc)
2630 ui.status(_('grafting %s\n') % desc)
2630 if opts.get('dry_run'):
2631 if opts.get('dry_run'):
2631 continue
2632 continue
2632
2633
2633 source = ctx.extra().get('source')
2634 source = ctx.extra().get('source')
2634 extra = {}
2635 extra = {}
2635 if source:
2636 if source:
2636 extra['source'] = source
2637 extra['source'] = source
2637 extra['intermediate-source'] = ctx.hex()
2638 extra['intermediate-source'] = ctx.hex()
2638 else:
2639 else:
2639 extra['source'] = ctx.hex()
2640 extra['source'] = ctx.hex()
2640 user = ctx.user()
2641 user = ctx.user()
2641 if opts.get('user'):
2642 if opts.get('user'):
2642 user = opts['user']
2643 user = opts['user']
2643 statedata['user'] = user
2644 statedata['user'] = user
2644 date = ctx.date()
2645 date = ctx.date()
2645 if opts.get('date'):
2646 if opts.get('date'):
2646 date = opts['date']
2647 date = opts['date']
2647 statedata['date'] = date
2648 statedata['date'] = date
2648 message = ctx.description()
2649 message = ctx.description()
2649 if opts.get('log'):
2650 if opts.get('log'):
2650 message += '\n(grafted from %s)' % ctx.hex()
2651 message += '\n(grafted from %s)' % ctx.hex()
2651 statedata['log'] = True
2652 statedata['log'] = True
2652
2653
2653 # we don't merge the first commit when continuing
2654 # we don't merge the first commit when continuing
2654 if not cont:
2655 if not cont:
2655 # perform the graft merge with p1(rev) as 'ancestor'
2656 # perform the graft merge with p1(rev) as 'ancestor'
2656 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
2657 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
2657 base = ctx.p1() if basectx is None else basectx
2658 base = ctx.p1() if basectx is None else basectx
2658 with ui.configoverride(overrides, 'graft'):
2659 with ui.configoverride(overrides, 'graft'):
2659 stats = mergemod.graft(repo, ctx, base, ['local', 'graft'])
2660 stats = mergemod.graft(repo, ctx, base, ['local', 'graft'])
2660 # report any conflicts
2661 # report any conflicts
2661 if stats.unresolvedcount > 0:
2662 if stats.unresolvedcount > 0:
2662 # write out state for --continue
2663 # write out state for --continue
2663 nodes = [repo[rev].hex() for rev in revs[pos:]]
2664 nodes = [repo[rev].hex() for rev in revs[pos:]]
2664 statedata['nodes'] = nodes
2665 statedata['nodes'] = nodes
2665 stateversion = 1
2666 stateversion = 1
2666 graftstate.save(stateversion, statedata)
2667 graftstate.save(stateversion, statedata)
2667 hint = _("use 'hg resolve' and 'hg graft --continue'")
2668 hint = _("use 'hg resolve' and 'hg graft --continue'")
2668 raise error.Abort(
2669 raise error.Abort(
2669 _("unresolved conflicts, can't continue"),
2670 _("unresolved conflicts, can't continue"),
2670 hint=hint)
2671 hint=hint)
2671 else:
2672 else:
2672 cont = False
2673 cont = False
2673
2674
2674 # commit if --no-commit is false
2675 # commit if --no-commit is false
2675 if not opts.get('no_commit'):
2676 if not opts.get('no_commit'):
2676 node = repo.commit(text=message, user=user, date=date, extra=extra,
2677 node = repo.commit(text=message, user=user, date=date, extra=extra,
2677 editor=editor)
2678 editor=editor)
2678 if node is None:
2679 if node is None:
2679 ui.warn(
2680 ui.warn(
2680 _('note: graft of %d:%s created no changes to commit\n') %
2681 _('note: graft of %d:%s created no changes to commit\n') %
2681 (ctx.rev(), ctx))
2682 (ctx.rev(), ctx))
2682 # checking that newnodes exist because old state files won't have it
2683 # checking that newnodes exist because old state files won't have it
2683 elif statedata.get('newnodes') is not None:
2684 elif statedata.get('newnodes') is not None:
2684 statedata['newnodes'].append(node)
2685 statedata['newnodes'].append(node)
2685
2686
2686 # remove state when we complete successfully
2687 # remove state when we complete successfully
2687 if not opts.get('dry_run'):
2688 if not opts.get('dry_run'):
2688 graftstate.delete()
2689 graftstate.delete()
2689
2690
2690 return 0
2691 return 0
2691
2692
2692 def _stopgraft(ui, repo, graftstate):
2693 def _stopgraft(ui, repo, graftstate):
2693 """stop the interrupted graft"""
2694 """stop the interrupted graft"""
2694 if not graftstate.exists():
2695 if not graftstate.exists():
2695 raise error.Abort(_("no interrupted graft found"))
2696 raise error.Abort(_("no interrupted graft found"))
2696 pctx = repo['.']
2697 pctx = repo['.']
2697 hg.updaterepo(repo, pctx.node(), overwrite=True)
2698 hg.updaterepo(repo, pctx.node(), overwrite=True)
2698 graftstate.delete()
2699 graftstate.delete()
2699 ui.status(_("stopped the interrupted graft\n"))
2700 ui.status(_("stopped the interrupted graft\n"))
2700 ui.status(_("working directory is now at %s\n") % pctx.hex()[:12])
2701 ui.status(_("working directory is now at %s\n") % pctx.hex()[:12])
2701 return 0
2702 return 0
2702
2703
2703 statemod.addunfinished(
2704 statemod.addunfinished(
2704 'graft', fname='graftstate', clearable=True, stopflag=True,
2705 'graft', fname='graftstate', clearable=True, stopflag=True,
2705 continueflag=True, abortfunc=cmdutil.hgabortgraft,
2706 continueflag=True, abortfunc=cmdutil.hgabortgraft,
2706 cmdhint=_("use 'hg graft --continue' or 'hg graft --stop' to stop")
2707 cmdhint=_("use 'hg graft --continue' or 'hg graft --stop' to stop")
2707 )
2708 )
2708
2709
2709 @command('grep',
2710 @command('grep',
2710 [('0', 'print0', None, _('end fields with NUL')),
2711 [('0', 'print0', None, _('end fields with NUL')),
2711 ('', 'all', None, _('print all revisions that match (DEPRECATED) ')),
2712 ('', 'all', None, _('print all revisions that match (DEPRECATED) ')),
2712 ('', 'diff', None, _('print all revisions when the term was introduced '
2713 ('', 'diff', None, _('print all revisions when the term was introduced '
2713 'or removed')),
2714 'or removed')),
2714 ('a', 'text', None, _('treat all files as text')),
2715 ('a', 'text', None, _('treat all files as text')),
2715 ('f', 'follow', None,
2716 ('f', 'follow', None,
2716 _('follow changeset history,'
2717 _('follow changeset history,'
2717 ' or file history across copies and renames')),
2718 ' or file history across copies and renames')),
2718 ('i', 'ignore-case', None, _('ignore case when matching')),
2719 ('i', 'ignore-case', None, _('ignore case when matching')),
2719 ('l', 'files-with-matches', None,
2720 ('l', 'files-with-matches', None,
2720 _('print only filenames and revisions that match')),
2721 _('print only filenames and revisions that match')),
2721 ('n', 'line-number', None, _('print matching line numbers')),
2722 ('n', 'line-number', None, _('print matching line numbers')),
2722 ('r', 'rev', [],
2723 ('r', 'rev', [],
2723 _('only search files changed within revision range'), _('REV')),
2724 _('only search files changed within revision range'), _('REV')),
2724 ('', 'all-files', None,
2725 ('', 'all-files', None,
2725 _('include all files in the changeset while grepping (EXPERIMENTAL)')),
2726 _('include all files in the changeset while grepping (EXPERIMENTAL)')),
2726 ('u', 'user', None, _('list the author (long with -v)')),
2727 ('u', 'user', None, _('list the author (long with -v)')),
2727 ('d', 'date', None, _('list the date (short with -q)')),
2728 ('d', 'date', None, _('list the date (short with -q)')),
2728 ] + formatteropts + walkopts,
2729 ] + formatteropts + walkopts,
2729 _('[OPTION]... PATTERN [FILE]...'),
2730 _('[OPTION]... PATTERN [FILE]...'),
2730 helpcategory=command.CATEGORY_FILE_CONTENTS,
2731 helpcategory=command.CATEGORY_FILE_CONTENTS,
2731 inferrepo=True,
2732 inferrepo=True,
2732 intents={INTENT_READONLY})
2733 intents={INTENT_READONLY})
2733 def grep(ui, repo, pattern, *pats, **opts):
2734 def grep(ui, repo, pattern, *pats, **opts):
2734 """search revision history for a pattern in specified files
2735 """search revision history for a pattern in specified files
2735
2736
2736 Search revision history for a regular expression in the specified
2737 Search revision history for a regular expression in the specified
2737 files or the entire project.
2738 files or the entire project.
2738
2739
2739 By default, grep prints the most recent revision number for each
2740 By default, grep prints the most recent revision number for each
2740 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
2741 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
2742 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
2743 --diff flag.
2744 --diff flag.
2744
2745
2745 PATTERN can be any Python (roughly Perl-compatible) regular
2746 PATTERN can be any Python (roughly Perl-compatible) regular
2746 expression.
2747 expression.
2747
2748
2748 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
2749 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
2750 current branch or have been deleted in a prior changeset.
2751 current branch or have been deleted in a prior changeset.
2751
2752
2752 .. container:: verbose
2753 .. container:: verbose
2753
2754
2754 Template:
2755 Template:
2755
2756
2756 The following keywords are supported in addition to the common template
2757 The following keywords are supported in addition to the common template
2757 keywords and functions. See also :hg:`help templates`.
2758 keywords and functions. See also :hg:`help templates`.
2758
2759
2759 :change: String. Character denoting insertion ``+`` or removal ``-``.
2760 :change: String. Character denoting insertion ``+`` or removal ``-``.
2760 Available if ``--diff`` is specified.
2761 Available if ``--diff`` is specified.
2761 :lineno: Integer. Line number of the match.
2762 :lineno: Integer. Line number of the match.
2762 :path: String. Repository-absolute path of the file.
2763 :path: String. Repository-absolute path of the file.
2763 :texts: List of text chunks.
2764 :texts: List of text chunks.
2764
2765
2765 And each entry of ``{texts}`` provides the following sub-keywords.
2766 And each entry of ``{texts}`` provides the following sub-keywords.
2766
2767
2767 :matched: Boolean. True if the chunk matches the specified pattern.
2768 :matched: Boolean. True if the chunk matches the specified pattern.
2768 :text: String. Chunk content.
2769 :text: String. Chunk content.
2769
2770
2770 See :hg:`help templates.operators` for the list expansion syntax.
2771 See :hg:`help templates.operators` for the list expansion syntax.
2771
2772
2772 Returns 0 if a match is found, 1 otherwise.
2773 Returns 0 if a match is found, 1 otherwise.
2773 """
2774 """
2774 opts = pycompat.byteskwargs(opts)
2775 opts = pycompat.byteskwargs(opts)
2775 diff = opts.get('all') or opts.get('diff')
2776 diff = opts.get('all') or opts.get('diff')
2776 all_files = opts.get('all_files')
2777 all_files = opts.get('all_files')
2777 if diff and opts.get('all_files'):
2778 if diff and opts.get('all_files'):
2778 raise error.Abort(_('--diff and --all-files are mutually exclusive'))
2779 raise error.Abort(_('--diff and --all-files are mutually exclusive'))
2779 # 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
2780 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:
2781 # experimental config: commands.grep.all-files
2782 # experimental config: commands.grep.all-files
2782 opts['all_files'] = ui.configbool('commands', 'grep.all-files')
2783 opts['all_files'] = ui.configbool('commands', 'grep.all-files')
2783 plaingrep = opts.get('all_files') and not opts.get('rev')
2784 plaingrep = opts.get('all_files') and not opts.get('rev')
2784 if plaingrep:
2785 if plaingrep:
2785 opts['rev'] = ['wdir()']
2786 opts['rev'] = ['wdir()']
2786
2787
2787 reflags = re.M
2788 reflags = re.M
2788 if opts.get('ignore_case'):
2789 if opts.get('ignore_case'):
2789 reflags |= re.I
2790 reflags |= re.I
2790 try:
2791 try:
2791 regexp = util.re.compile(pattern, reflags)
2792 regexp = util.re.compile(pattern, reflags)
2792 except re.error as inst:
2793 except re.error as inst:
2793 ui.warn(_("grep: invalid match pattern: %s\n") % pycompat.bytestr(inst))
2794 ui.warn(_("grep: invalid match pattern: %s\n") % pycompat.bytestr(inst))
2794 return 1
2795 return 1
2795 sep, eol = ':', '\n'
2796 sep, eol = ':', '\n'
2796 if opts.get('print0'):
2797 if opts.get('print0'):
2797 sep = eol = '\0'
2798 sep = eol = '\0'
2798
2799
2799 getfile = util.lrucachefunc(repo.file)
2800 getfile = util.lrucachefunc(repo.file)
2800
2801
2801 def matchlines(body):
2802 def matchlines(body):
2802 begin = 0
2803 begin = 0
2803 linenum = 0
2804 linenum = 0
2804 while begin < len(body):
2805 while begin < len(body):
2805 match = regexp.search(body, begin)
2806 match = regexp.search(body, begin)
2806 if not match:
2807 if not match:
2807 break
2808 break
2808 mstart, mend = match.span()
2809 mstart, mend = match.span()
2809 linenum += body.count('\n', begin, mstart) + 1
2810 linenum += body.count('\n', begin, mstart) + 1
2810 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2811 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2811 begin = body.find('\n', mend) + 1 or len(body) + 1
2812 begin = body.find('\n', mend) + 1 or len(body) + 1
2812 lend = begin - 1
2813 lend = begin - 1
2813 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2814 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2814
2815
2815 class linestate(object):
2816 class linestate(object):
2816 def __init__(self, line, linenum, colstart, colend):
2817 def __init__(self, line, linenum, colstart, colend):
2817 self.line = line
2818 self.line = line
2818 self.linenum = linenum
2819 self.linenum = linenum
2819 self.colstart = colstart
2820 self.colstart = colstart
2820 self.colend = colend
2821 self.colend = colend
2821
2822
2822 def __hash__(self):
2823 def __hash__(self):
2823 return hash((self.linenum, self.line))
2824 return hash((self.linenum, self.line))
2824
2825
2825 def __eq__(self, other):
2826 def __eq__(self, other):
2826 return self.line == other.line
2827 return self.line == other.line
2827
2828
2828 def findpos(self):
2829 def findpos(self):
2829 """Iterate all (start, end) indices of matches"""
2830 """Iterate all (start, end) indices of matches"""
2830 yield self.colstart, self.colend
2831 yield self.colstart, self.colend
2831 p = self.colend
2832 p = self.colend
2832 while p < len(self.line):
2833 while p < len(self.line):
2833 m = regexp.search(self.line, p)
2834 m = regexp.search(self.line, p)
2834 if not m:
2835 if not m:
2835 break
2836 break
2836 yield m.span()
2837 yield m.span()
2837 p = m.end()
2838 p = m.end()
2838
2839
2839 matches = {}
2840 matches = {}
2840 copies = {}
2841 copies = {}
2841 def grepbody(fn, rev, body):
2842 def grepbody(fn, rev, body):
2842 matches[rev].setdefault(fn, [])
2843 matches[rev].setdefault(fn, [])
2843 m = matches[rev][fn]
2844 m = matches[rev][fn]
2844 for lnum, cstart, cend, line in matchlines(body):
2845 for lnum, cstart, cend, line in matchlines(body):
2845 s = linestate(line, lnum, cstart, cend)
2846 s = linestate(line, lnum, cstart, cend)
2846 m.append(s)
2847 m.append(s)
2847
2848
2848 def difflinestates(a, b):
2849 def difflinestates(a, b):
2849 sm = difflib.SequenceMatcher(None, a, b)
2850 sm = difflib.SequenceMatcher(None, a, b)
2850 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2851 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2851 if tag == r'insert':
2852 if tag == r'insert':
2852 for i in pycompat.xrange(blo, bhi):
2853 for i in pycompat.xrange(blo, bhi):
2853 yield ('+', b[i])
2854 yield ('+', b[i])
2854 elif tag == r'delete':
2855 elif tag == r'delete':
2855 for i in pycompat.xrange(alo, ahi):
2856 for i in pycompat.xrange(alo, ahi):
2856 yield ('-', a[i])
2857 yield ('-', a[i])
2857 elif tag == r'replace':
2858 elif tag == r'replace':
2858 for i in pycompat.xrange(alo, ahi):
2859 for i in pycompat.xrange(alo, ahi):
2859 yield ('-', a[i])
2860 yield ('-', a[i])
2860 for i in pycompat.xrange(blo, bhi):
2861 for i in pycompat.xrange(blo, bhi):
2861 yield ('+', b[i])
2862 yield ('+', b[i])
2862
2863
2863 uipathfn = scmutil.getuipathfn(repo)
2864 uipathfn = scmutil.getuipathfn(repo)
2864 def display(fm, fn, ctx, pstates, states):
2865 def display(fm, fn, ctx, pstates, states):
2865 rev = scmutil.intrev(ctx)
2866 rev = scmutil.intrev(ctx)
2866 if fm.isplain():
2867 if fm.isplain():
2867 formatuser = ui.shortuser
2868 formatuser = ui.shortuser
2868 else:
2869 else:
2869 formatuser = pycompat.bytestr
2870 formatuser = pycompat.bytestr
2870 if ui.quiet:
2871 if ui.quiet:
2871 datefmt = '%Y-%m-%d'
2872 datefmt = '%Y-%m-%d'
2872 else:
2873 else:
2873 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2874 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2874 found = False
2875 found = False
2875 @util.cachefunc
2876 @util.cachefunc
2876 def binary():
2877 def binary():
2877 flog = getfile(fn)
2878 flog = getfile(fn)
2878 try:
2879 try:
2879 return stringutil.binary(flog.read(ctx.filenode(fn)))
2880 return stringutil.binary(flog.read(ctx.filenode(fn)))
2880 except error.WdirUnsupported:
2881 except error.WdirUnsupported:
2881 return ctx[fn].isbinary()
2882 return ctx[fn].isbinary()
2882
2883
2883 fieldnamemap = {'linenumber': 'lineno'}
2884 fieldnamemap = {'linenumber': 'lineno'}
2884 if diff:
2885 if diff:
2885 iter = difflinestates(pstates, states)
2886 iter = difflinestates(pstates, states)
2886 else:
2887 else:
2887 iter = [('', l) for l in states]
2888 iter = [('', l) for l in states]
2888 for change, l in iter:
2889 for change, l in iter:
2889 fm.startitem()
2890 fm.startitem()
2890 fm.context(ctx=ctx)
2891 fm.context(ctx=ctx)
2891 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
2892 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
2892 fm.plain(uipathfn(fn), label='grep.filename')
2893 fm.plain(uipathfn(fn), label='grep.filename')
2893
2894
2894 cols = [
2895 cols = [
2895 ('rev', '%d', rev, not plaingrep, ''),
2896 ('rev', '%d', rev, not plaingrep, ''),
2896 ('linenumber', '%d', l.linenum, opts.get('line_number'), ''),
2897 ('linenumber', '%d', l.linenum, opts.get('line_number'), ''),
2897 ]
2898 ]
2898 if diff:
2899 if diff:
2899 cols.append(
2900 cols.append(
2900 ('change', '%s', change, True,
2901 ('change', '%s', change, True,
2901 'grep.inserted ' if change == '+' else 'grep.deleted ')
2902 'grep.inserted ' if change == '+' else 'grep.deleted ')
2902 )
2903 )
2903 cols.extend([
2904 cols.extend([
2904 ('user', '%s', formatuser(ctx.user()), opts.get('user'), ''),
2905 ('user', '%s', formatuser(ctx.user()), opts.get('user'), ''),
2905 ('date', '%s', fm.formatdate(ctx.date(), datefmt),
2906 ('date', '%s', fm.formatdate(ctx.date(), datefmt),
2906 opts.get('date'), ''),
2907 opts.get('date'), ''),
2907 ])
2908 ])
2908 for name, fmt, data, cond, extra_label in cols:
2909 for name, fmt, data, cond, extra_label in cols:
2909 if cond:
2910 if cond:
2910 fm.plain(sep, label='grep.sep')
2911 fm.plain(sep, label='grep.sep')
2911 field = fieldnamemap.get(name, name)
2912 field = fieldnamemap.get(name, name)
2912 label = extra_label + ('grep.%s' % name)
2913 label = extra_label + ('grep.%s' % name)
2913 fm.condwrite(cond, field, fmt, data, label=label)
2914 fm.condwrite(cond, field, fmt, data, label=label)
2914 if not opts.get('files_with_matches'):
2915 if not opts.get('files_with_matches'):
2915 fm.plain(sep, label='grep.sep')
2916 fm.plain(sep, label='grep.sep')
2916 if not opts.get('text') and binary():
2917 if not opts.get('text') and binary():
2917 fm.plain(_(" Binary file matches"))
2918 fm.plain(_(" Binary file matches"))
2918 else:
2919 else:
2919 displaymatches(fm.nested('texts', tmpl='{text}'), l)
2920 displaymatches(fm.nested('texts', tmpl='{text}'), l)
2920 fm.plain(eol)
2921 fm.plain(eol)
2921 found = True
2922 found = True
2922 if opts.get('files_with_matches'):
2923 if opts.get('files_with_matches'):
2923 break
2924 break
2924 return found
2925 return found
2925
2926
2926 def displaymatches(fm, l):
2927 def displaymatches(fm, l):
2927 p = 0
2928 p = 0
2928 for s, e in l.findpos():
2929 for s, e in l.findpos():
2929 if p < s:
2930 if p < s:
2930 fm.startitem()
2931 fm.startitem()
2931 fm.write('text', '%s', l.line[p:s])
2932 fm.write('text', '%s', l.line[p:s])
2932 fm.data(matched=False)
2933 fm.data(matched=False)
2933 fm.startitem()
2934 fm.startitem()
2934 fm.write('text', '%s', l.line[s:e], label='grep.match')
2935 fm.write('text', '%s', l.line[s:e], label='grep.match')
2935 fm.data(matched=True)
2936 fm.data(matched=True)
2936 p = e
2937 p = e
2937 if p < len(l.line):
2938 if p < len(l.line):
2938 fm.startitem()
2939 fm.startitem()
2939 fm.write('text', '%s', l.line[p:])
2940 fm.write('text', '%s', l.line[p:])
2940 fm.data(matched=False)
2941 fm.data(matched=False)
2941 fm.end()
2942 fm.end()
2942
2943
2943 skip = set()
2944 skip = set()
2944 revfiles = {}
2945 revfiles = {}
2945 match = scmutil.match(repo[None], pats, opts)
2946 match = scmutil.match(repo[None], pats, opts)
2946 found = False
2947 found = False
2947 follow = opts.get('follow')
2948 follow = opts.get('follow')
2948
2949
2949 getrenamed = scmutil.getrenamedfn(repo)
2950 getrenamed = scmutil.getrenamedfn(repo)
2950 def prep(ctx, fns):
2951 def prep(ctx, fns):
2951 rev = ctx.rev()
2952 rev = ctx.rev()
2952 pctx = ctx.p1()
2953 pctx = ctx.p1()
2953 parent = pctx.rev()
2954 parent = pctx.rev()
2954 matches.setdefault(rev, {})
2955 matches.setdefault(rev, {})
2955 matches.setdefault(parent, {})
2956 matches.setdefault(parent, {})
2956 files = revfiles.setdefault(rev, [])
2957 files = revfiles.setdefault(rev, [])
2957 for fn in fns:
2958 for fn in fns:
2958 flog = getfile(fn)
2959 flog = getfile(fn)
2959 try:
2960 try:
2960 fnode = ctx.filenode(fn)
2961 fnode = ctx.filenode(fn)
2961 except error.LookupError:
2962 except error.LookupError:
2962 continue
2963 continue
2963
2964
2964 copy = None
2965 copy = None
2965 if follow:
2966 if follow:
2966 copy = getrenamed(fn, rev)
2967 copy = getrenamed(fn, rev)
2967 if copy:
2968 if copy:
2968 copies.setdefault(rev, {})[fn] = copy
2969 copies.setdefault(rev, {})[fn] = copy
2969 if fn in skip:
2970 if fn in skip:
2970 skip.add(copy)
2971 skip.add(copy)
2971 if fn in skip:
2972 if fn in skip:
2972 continue
2973 continue
2973 files.append(fn)
2974 files.append(fn)
2974
2975
2975 if fn not in matches[rev]:
2976 if fn not in matches[rev]:
2976 try:
2977 try:
2977 content = flog.read(fnode)
2978 content = flog.read(fnode)
2978 except error.WdirUnsupported:
2979 except error.WdirUnsupported:
2979 content = ctx[fn].data()
2980 content = ctx[fn].data()
2980 grepbody(fn, rev, content)
2981 grepbody(fn, rev, content)
2981
2982
2982 pfn = copy or fn
2983 pfn = copy or fn
2983 if pfn not in matches[parent]:
2984 if pfn not in matches[parent]:
2984 try:
2985 try:
2985 fnode = pctx.filenode(pfn)
2986 fnode = pctx.filenode(pfn)
2986 grepbody(pfn, parent, flog.read(fnode))
2987 grepbody(pfn, parent, flog.read(fnode))
2987 except error.LookupError:
2988 except error.LookupError:
2988 pass
2989 pass
2989
2990
2990 ui.pager('grep')
2991 ui.pager('grep')
2991 fm = ui.formatter('grep', opts)
2992 fm = ui.formatter('grep', opts)
2992 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2993 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2993 rev = ctx.rev()
2994 rev = ctx.rev()
2994 parent = ctx.p1().rev()
2995 parent = ctx.p1().rev()
2995 for fn in sorted(revfiles.get(rev, [])):
2996 for fn in sorted(revfiles.get(rev, [])):
2996 states = matches[rev][fn]
2997 states = matches[rev][fn]
2997 copy = copies.get(rev, {}).get(fn)
2998 copy = copies.get(rev, {}).get(fn)
2998 if fn in skip:
2999 if fn in skip:
2999 if copy:
3000 if copy:
3000 skip.add(copy)
3001 skip.add(copy)
3001 continue
3002 continue
3002 pstates = matches.get(parent, {}).get(copy or fn, [])
3003 pstates = matches.get(parent, {}).get(copy or fn, [])
3003 if pstates or states:
3004 if pstates or states:
3004 r = display(fm, fn, ctx, pstates, states)
3005 r = display(fm, fn, ctx, pstates, states)
3005 found = found or r
3006 found = found or r
3006 if r and not diff and not all_files:
3007 if r and not diff and not all_files:
3007 skip.add(fn)
3008 skip.add(fn)
3008 if copy:
3009 if copy:
3009 skip.add(copy)
3010 skip.add(copy)
3010 del revfiles[rev]
3011 del revfiles[rev]
3011 # 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
3012 # clear the matches dict once the window is over
3013 # clear the matches dict once the window is over
3013 if not revfiles:
3014 if not revfiles:
3014 matches.clear()
3015 matches.clear()
3015 fm.end()
3016 fm.end()
3016
3017
3017 return not found
3018 return not found
3018
3019
3019 @command('heads',
3020 @command('heads',
3020 [('r', 'rev', '',
3021 [('r', 'rev', '',
3021 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3022 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3022 ('t', 'topo', False, _('show topological heads only')),
3023 ('t', 'topo', False, _('show topological heads only')),
3023 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3024 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3024 ('c', 'closed', False, _('show normal and closed branch heads')),
3025 ('c', 'closed', False, _('show normal and closed branch heads')),
3025 ] + templateopts,
3026 ] + templateopts,
3026 _('[-ct] [-r STARTREV] [REV]...'),
3027 _('[-ct] [-r STARTREV] [REV]...'),
3027 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3028 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3028 intents={INTENT_READONLY})
3029 intents={INTENT_READONLY})
3029 def heads(ui, repo, *branchrevs, **opts):
3030 def heads(ui, repo, *branchrevs, **opts):
3030 """show branch heads
3031 """show branch heads
3031
3032
3032 With no arguments, show all open branch heads in the repository.
3033 With no arguments, show all open branch heads in the repository.
3033 Branch heads are changesets that have no descendants on the
3034 Branch heads are changesets that have no descendants on the
3034 same branch. They are where development generally takes place and
3035 same branch. They are where development generally takes place and
3035 are the usual targets for update and merge operations.
3036 are the usual targets for update and merge operations.
3036
3037
3037 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
3038 branches associated with the specified changesets are shown. This
3039 branches associated with the specified changesets are shown. This
3039 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
3040 currently checked-out branch.
3041 currently checked-out branch.
3041
3042
3042 If -c/--closed is specified, also show branch heads marked closed
3043 If -c/--closed is specified, also show branch heads marked closed
3043 (see :hg:`commit --close-branch`).
3044 (see :hg:`commit --close-branch`).
3044
3045
3045 If STARTREV is specified, only those heads that are descendants of
3046 If STARTREV is specified, only those heads that are descendants of
3046 STARTREV will be displayed.
3047 STARTREV will be displayed.
3047
3048
3048 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
3049 topological heads (changesets with no children) will be shown.
3050 topological heads (changesets with no children) will be shown.
3050
3051
3051 Returns 0 if matching heads are found, 1 if not.
3052 Returns 0 if matching heads are found, 1 if not.
3052 """
3053 """
3053
3054
3054 opts = pycompat.byteskwargs(opts)
3055 opts = pycompat.byteskwargs(opts)
3055 start = None
3056 start = None
3056 rev = opts.get('rev')
3057 rev = opts.get('rev')
3057 if rev:
3058 if rev:
3058 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3059 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3059 start = scmutil.revsingle(repo, rev, None).node()
3060 start = scmutil.revsingle(repo, rev, None).node()
3060
3061
3061 if opts.get('topo'):
3062 if opts.get('topo'):
3062 heads = [repo[h] for h in repo.heads(start)]
3063 heads = [repo[h] for h in repo.heads(start)]
3063 else:
3064 else:
3064 heads = []
3065 heads = []
3065 for branch in repo.branchmap():
3066 for branch in repo.branchmap():
3066 heads += repo.branchheads(branch, start, opts.get('closed'))
3067 heads += repo.branchheads(branch, start, opts.get('closed'))
3067 heads = [repo[h] for h in heads]
3068 heads = [repo[h] for h in heads]
3068
3069
3069 if branchrevs:
3070 if branchrevs:
3070 branches = set(repo[r].branch()
3071 branches = set(repo[r].branch()
3071 for r in scmutil.revrange(repo, branchrevs))
3072 for r in scmutil.revrange(repo, branchrevs))
3072 heads = [h for h in heads if h.branch() in branches]
3073 heads = [h for h in heads if h.branch() in branches]
3073
3074
3074 if opts.get('active') and branchrevs:
3075 if opts.get('active') and branchrevs:
3075 dagheads = repo.heads(start)
3076 dagheads = repo.heads(start)
3076 heads = [h for h in heads if h.node() in dagheads]
3077 heads = [h for h in heads if h.node() in dagheads]
3077
3078
3078 if branchrevs:
3079 if branchrevs:
3079 haveheads = set(h.branch() for h in heads)
3080 haveheads = set(h.branch() for h in heads)
3080 if branches - haveheads:
3081 if branches - haveheads:
3081 headless = ', '.join(b for b in branches - haveheads)
3082 headless = ', '.join(b for b in branches - haveheads)
3082 msg = _('no open branch heads found on branches %s')
3083 msg = _('no open branch heads found on branches %s')
3083 if opts.get('rev'):
3084 if opts.get('rev'):
3084 msg += _(' (started at %s)') % opts['rev']
3085 msg += _(' (started at %s)') % opts['rev']
3085 ui.warn((msg + '\n') % headless)
3086 ui.warn((msg + '\n') % headless)
3086
3087
3087 if not heads:
3088 if not heads:
3088 return 1
3089 return 1
3089
3090
3090 ui.pager('heads')
3091 ui.pager('heads')
3091 heads = sorted(heads, key=lambda x: -x.rev())
3092 heads = sorted(heads, key=lambda x: -x.rev())
3092 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3093 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3093 for ctx in heads:
3094 for ctx in heads:
3094 displayer.show(ctx)
3095 displayer.show(ctx)
3095 displayer.close()
3096 displayer.close()
3096
3097
3097 @command('help',
3098 @command('help',
3098 [('e', 'extension', None, _('show only help for extensions')),
3099 [('e', 'extension', None, _('show only help for extensions')),
3099 ('c', 'command', None, _('show only help for commands')),
3100 ('c', 'command', None, _('show only help for commands')),
3100 ('k', 'keyword', None, _('show topics matching keyword')),
3101 ('k', 'keyword', None, _('show topics matching keyword')),
3101 ('s', 'system', [],
3102 ('s', 'system', [],
3102 _('show help for specific platform(s)'), _('PLATFORM')),
3103 _('show help for specific platform(s)'), _('PLATFORM')),
3103 ],
3104 ],
3104 _('[-eck] [-s PLATFORM] [TOPIC]'),
3105 _('[-eck] [-s PLATFORM] [TOPIC]'),
3105 helpcategory=command.CATEGORY_HELP,
3106 helpcategory=command.CATEGORY_HELP,
3106 norepo=True,
3107 norepo=True,
3107 intents={INTENT_READONLY})
3108 intents={INTENT_READONLY})
3108 def help_(ui, name=None, **opts):
3109 def help_(ui, name=None, **opts):
3109 """show help for a given topic or a help overview
3110 """show help for a given topic or a help overview
3110
3111
3111 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.
3112
3113
3113 Given a topic, extension, or command name, print help for that
3114 Given a topic, extension, or command name, print help for that
3114 topic.
3115 topic.
3115
3116
3116 Returns 0 if successful.
3117 Returns 0 if successful.
3117 """
3118 """
3118
3119
3119 keep = opts.get(r'system') or []
3120 keep = opts.get(r'system') or []
3120 if len(keep) == 0:
3121 if len(keep) == 0:
3121 if pycompat.sysplatform.startswith('win'):
3122 if pycompat.sysplatform.startswith('win'):
3122 keep.append('windows')
3123 keep.append('windows')
3123 elif pycompat.sysplatform == 'OpenVMS':
3124 elif pycompat.sysplatform == 'OpenVMS':
3124 keep.append('vms')
3125 keep.append('vms')
3125 elif pycompat.sysplatform == 'plan9':
3126 elif pycompat.sysplatform == 'plan9':
3126 keep.append('plan9')
3127 keep.append('plan9')
3127 else:
3128 else:
3128 keep.append('unix')
3129 keep.append('unix')
3129 keep.append(pycompat.sysplatform.lower())
3130 keep.append(pycompat.sysplatform.lower())
3130 if ui.verbose:
3131 if ui.verbose:
3131 keep.append('verbose')
3132 keep.append('verbose')
3132
3133
3133 commands = sys.modules[__name__]
3134 commands = sys.modules[__name__]
3134 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3135 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3135 ui.pager('help')
3136 ui.pager('help')
3136 ui.write(formatted)
3137 ui.write(formatted)
3137
3138
3138
3139
3139 @command('identify|id',
3140 @command('identify|id',
3140 [('r', 'rev', '',
3141 [('r', 'rev', '',
3141 _('identify the specified revision'), _('REV')),
3142 _('identify the specified revision'), _('REV')),
3142 ('n', 'num', None, _('show local revision number')),
3143 ('n', 'num', None, _('show local revision number')),
3143 ('i', 'id', None, _('show global revision id')),
3144 ('i', 'id', None, _('show global revision id')),
3144 ('b', 'branch', None, _('show branch')),
3145 ('b', 'branch', None, _('show branch')),
3145 ('t', 'tags', None, _('show tags')),
3146 ('t', 'tags', None, _('show tags')),
3146 ('B', 'bookmarks', None, _('show bookmarks')),
3147 ('B', 'bookmarks', None, _('show bookmarks')),
3147 ] + remoteopts + formatteropts,
3148 ] + remoteopts + formatteropts,
3148 _('[-nibtB] [-r REV] [SOURCE]'),
3149 _('[-nibtB] [-r REV] [SOURCE]'),
3149 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3150 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3150 optionalrepo=True,
3151 optionalrepo=True,
3151 intents={INTENT_READONLY})
3152 intents={INTENT_READONLY})
3152 def identify(ui, repo, source=None, rev=None,
3153 def identify(ui, repo, source=None, rev=None,
3153 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3154 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3154 """identify the working directory or specified revision
3155 """identify the working directory or specified revision
3155
3156
3156 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
3157 two parent hash identifiers, followed by a "+" if the working
3158 two parent hash identifiers, followed by a "+" if the working
3158 directory has uncommitted changes, the branch name (if not default),
3159 directory has uncommitted changes, the branch name (if not default),
3159 a list of tags, and a list of bookmarks.
3160 a list of tags, and a list of bookmarks.
3160
3161
3161 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
3162 repository including the working directory. Specify -r. to get information
3163 repository including the working directory. Specify -r. to get information
3163 of the working directory parent without scanning uncommitted changes.
3164 of the working directory parent without scanning uncommitted changes.
3164
3165
3165 Specifying a path to a repository root or Mercurial bundle will
3166 Specifying a path to a repository root or Mercurial bundle will
3166 cause lookup to operate on that repository/bundle.
3167 cause lookup to operate on that repository/bundle.
3167
3168
3168 .. container:: verbose
3169 .. container:: verbose
3169
3170
3170 Template:
3171 Template:
3171
3172
3172 The following keywords are supported in addition to the common template
3173 The following keywords are supported in addition to the common template
3173 keywords and functions. See also :hg:`help templates`.
3174 keywords and functions. See also :hg:`help templates`.
3174
3175
3175 :dirty: String. Character ``+`` denoting if the working directory has
3176 :dirty: String. Character ``+`` denoting if the working directory has
3176 uncommitted changes.
3177 uncommitted changes.
3177 :id: String. One or two nodes, optionally followed by ``+``.
3178 :id: String. One or two nodes, optionally followed by ``+``.
3178 :parents: List of strings. Parent nodes of the changeset.
3179 :parents: List of strings. Parent nodes of the changeset.
3179
3180
3180 Examples:
3181 Examples:
3181
3182
3182 - generate a build identifier for the working directory::
3183 - generate a build identifier for the working directory::
3183
3184
3184 hg id --id > build-id.dat
3185 hg id --id > build-id.dat
3185
3186
3186 - find the revision corresponding to a tag::
3187 - find the revision corresponding to a tag::
3187
3188
3188 hg id -n -r 1.3
3189 hg id -n -r 1.3
3189
3190
3190 - check the most recent revision of a remote repository::
3191 - check the most recent revision of a remote repository::
3191
3192
3192 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3193 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3193
3194
3194 See :hg:`log` for generating more information about specific revisions,
3195 See :hg:`log` for generating more information about specific revisions,
3195 including full hash identifiers.
3196 including full hash identifiers.
3196
3197
3197 Returns 0 if successful.
3198 Returns 0 if successful.
3198 """
3199 """
3199
3200
3200 opts = pycompat.byteskwargs(opts)
3201 opts = pycompat.byteskwargs(opts)
3201 if not repo and not source:
3202 if not repo and not source:
3202 raise error.Abort(_("there is no Mercurial repository here "
3203 raise error.Abort(_("there is no Mercurial repository here "
3203 "(.hg not found)"))
3204 "(.hg not found)"))
3204
3205
3205 default = not (num or id or branch or tags or bookmarks)
3206 default = not (num or id or branch or tags or bookmarks)
3206 output = []
3207 output = []
3207 revs = []
3208 revs = []
3208
3209
3209 if source:
3210 if source:
3210 source, branches = hg.parseurl(ui.expandpath(source))
3211 source, branches = hg.parseurl(ui.expandpath(source))
3211 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
3212 repo = peer.local()
3213 repo = peer.local()
3213 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3214 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3214
3215
3215 fm = ui.formatter('identify', opts)
3216 fm = ui.formatter('identify', opts)
3216 fm.startitem()
3217 fm.startitem()
3217
3218
3218 if not repo:
3219 if not repo:
3219 if num or branch or tags:
3220 if num or branch or tags:
3220 raise error.Abort(
3221 raise error.Abort(
3221 _("can't query remote revision number, branch, or tags"))
3222 _("can't query remote revision number, branch, or tags"))
3222 if not rev and revs:
3223 if not rev and revs:
3223 rev = revs[0]
3224 rev = revs[0]
3224 if not rev:
3225 if not rev:
3225 rev = "tip"
3226 rev = "tip"
3226
3227
3227 remoterev = peer.lookup(rev)
3228 remoterev = peer.lookup(rev)
3228 hexrev = fm.hexfunc(remoterev)
3229 hexrev = fm.hexfunc(remoterev)
3229 if default or id:
3230 if default or id:
3230 output = [hexrev]
3231 output = [hexrev]
3231 fm.data(id=hexrev)
3232 fm.data(id=hexrev)
3232
3233
3233 @util.cachefunc
3234 @util.cachefunc
3234 def getbms():
3235 def getbms():
3235 bms = []
3236 bms = []
3236
3237
3237 if 'bookmarks' in peer.listkeys('namespaces'):
3238 if 'bookmarks' in peer.listkeys('namespaces'):
3238 hexremoterev = hex(remoterev)
3239 hexremoterev = hex(remoterev)
3239 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3240 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3240 if bmr == hexremoterev]
3241 if bmr == hexremoterev]
3241
3242
3242 return sorted(bms)
3243 return sorted(bms)
3243
3244
3244 if fm.isplain():
3245 if fm.isplain():
3245 if bookmarks:
3246 if bookmarks:
3246 output.extend(getbms())
3247 output.extend(getbms())
3247 elif default and not ui.quiet:
3248 elif default and not ui.quiet:
3248 # multiple bookmarks for a single parent separated by '/'
3249 # multiple bookmarks for a single parent separated by '/'
3249 bm = '/'.join(getbms())
3250 bm = '/'.join(getbms())
3250 if bm:
3251 if bm:
3251 output.append(bm)
3252 output.append(bm)
3252 else:
3253 else:
3253 fm.data(node=hex(remoterev))
3254 fm.data(node=hex(remoterev))
3254 if bookmarks or 'bookmarks' in fm.datahint():
3255 if bookmarks or 'bookmarks' in fm.datahint():
3255 fm.data(bookmarks=fm.formatlist(getbms(), name='bookmark'))
3256 fm.data(bookmarks=fm.formatlist(getbms(), name='bookmark'))
3256 else:
3257 else:
3257 if rev:
3258 if rev:
3258 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3259 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3259 ctx = scmutil.revsingle(repo, rev, None)
3260 ctx = scmutil.revsingle(repo, rev, None)
3260
3261
3261 if ctx.rev() is None:
3262 if ctx.rev() is None:
3262 ctx = repo[None]
3263 ctx = repo[None]
3263 parents = ctx.parents()
3264 parents = ctx.parents()
3264 taglist = []
3265 taglist = []
3265 for p in parents:
3266 for p in parents:
3266 taglist.extend(p.tags())
3267 taglist.extend(p.tags())
3267
3268
3268 dirty = ""
3269 dirty = ""
3269 if ctx.dirty(missing=True, merge=False, branch=False):
3270 if ctx.dirty(missing=True, merge=False, branch=False):
3270 dirty = '+'
3271 dirty = '+'
3271 fm.data(dirty=dirty)
3272 fm.data(dirty=dirty)
3272
3273
3273 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3274 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3274 if default or id:
3275 if default or id:
3275 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
3276 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
3276 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
3277 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
3277
3278
3278 if num:
3279 if num:
3279 numoutput = ["%d" % p.rev() for p in parents]
3280 numoutput = ["%d" % p.rev() for p in parents]
3280 output.append("%s%s" % ('+'.join(numoutput), dirty))
3281 output.append("%s%s" % ('+'.join(numoutput), dirty))
3281
3282
3282 fm.data(parents=fm.formatlist([fm.hexfunc(p.node())
3283 fm.data(parents=fm.formatlist([fm.hexfunc(p.node())
3283 for p in parents], name='node'))
3284 for p in parents], name='node'))
3284 else:
3285 else:
3285 hexoutput = fm.hexfunc(ctx.node())
3286 hexoutput = fm.hexfunc(ctx.node())
3286 if default or id:
3287 if default or id:
3287 output = [hexoutput]
3288 output = [hexoutput]
3288 fm.data(id=hexoutput)
3289 fm.data(id=hexoutput)
3289
3290
3290 if num:
3291 if num:
3291 output.append(pycompat.bytestr(ctx.rev()))
3292 output.append(pycompat.bytestr(ctx.rev()))
3292 taglist = ctx.tags()
3293 taglist = ctx.tags()
3293
3294
3294 if default and not ui.quiet:
3295 if default and not ui.quiet:
3295 b = ctx.branch()
3296 b = ctx.branch()
3296 if b != 'default':
3297 if b != 'default':
3297 output.append("(%s)" % b)
3298 output.append("(%s)" % b)
3298
3299
3299 # multiple tags for a single parent separated by '/'
3300 # multiple tags for a single parent separated by '/'
3300 t = '/'.join(taglist)
3301 t = '/'.join(taglist)
3301 if t:
3302 if t:
3302 output.append(t)
3303 output.append(t)
3303
3304
3304 # multiple bookmarks for a single parent separated by '/'
3305 # multiple bookmarks for a single parent separated by '/'
3305 bm = '/'.join(ctx.bookmarks())
3306 bm = '/'.join(ctx.bookmarks())
3306 if bm:
3307 if bm:
3307 output.append(bm)
3308 output.append(bm)
3308 else:
3309 else:
3309 if branch:
3310 if branch:
3310 output.append(ctx.branch())
3311 output.append(ctx.branch())
3311
3312
3312 if tags:
3313 if tags:
3313 output.extend(taglist)
3314 output.extend(taglist)
3314
3315
3315 if bookmarks:
3316 if bookmarks:
3316 output.extend(ctx.bookmarks())
3317 output.extend(ctx.bookmarks())
3317
3318
3318 fm.data(node=ctx.hex())
3319 fm.data(node=ctx.hex())
3319 fm.data(branch=ctx.branch())
3320 fm.data(branch=ctx.branch())
3320 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
3321 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
3321 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
3322 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
3322 fm.context(ctx=ctx)
3323 fm.context(ctx=ctx)
3323
3324
3324 fm.plain("%s\n" % ' '.join(output))
3325 fm.plain("%s\n" % ' '.join(output))
3325 fm.end()
3326 fm.end()
3326
3327
3327 @command('import|patch',
3328 @command('import|patch',
3328 [('p', 'strip', 1,
3329 [('p', 'strip', 1,
3329 _('directory strip option for patch. This has the same '
3330 _('directory strip option for patch. This has the same '
3330 'meaning as the corresponding patch option'), _('NUM')),
3331 'meaning as the corresponding patch option'), _('NUM')),
3331 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3332 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3332 ('e', 'edit', False, _('invoke editor on commit messages')),
3333 ('e', 'edit', False, _('invoke editor on commit messages')),
3333 ('f', 'force', None,
3334 ('f', 'force', None,
3334 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
3335 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
3335 ('', 'no-commit', None,
3336 ('', 'no-commit', None,
3336 _("don't commit, just update the working directory")),
3337 _("don't commit, just update the working directory")),
3337 ('', 'bypass', None,
3338 ('', 'bypass', None,
3338 _("apply patch without touching the working directory")),
3339 _("apply patch without touching the working directory")),
3339 ('', 'partial', None,
3340 ('', 'partial', None,
3340 _('commit even if some hunks fail')),
3341 _('commit even if some hunks fail')),
3341 ('', 'exact', None,
3342 ('', 'exact', None,
3342 _('abort if patch would apply lossily')),
3343 _('abort if patch would apply lossily')),
3343 ('', 'prefix', '',
3344 ('', 'prefix', '',
3344 _('apply patch to subdirectory'), _('DIR')),
3345 _('apply patch to subdirectory'), _('DIR')),
3345 ('', 'import-branch', None,
3346 ('', 'import-branch', None,
3346 _('use any branch information in patch (implied by --exact)'))] +
3347 _('use any branch information in patch (implied by --exact)'))] +
3347 commitopts + commitopts2 + similarityopts,
3348 commitopts + commitopts2 + similarityopts,
3348 _('[OPTION]... PATCH...'),
3349 _('[OPTION]... PATCH...'),
3349 helpcategory=command.CATEGORY_IMPORT_EXPORT)
3350 helpcategory=command.CATEGORY_IMPORT_EXPORT)
3350 def import_(ui, repo, patch1=None, *patches, **opts):
3351 def import_(ui, repo, patch1=None, *patches, **opts):
3351 """import an ordered set of patches
3352 """import an ordered set of patches
3352
3353
3353 Import a list of patches and commit them individually (unless
3354 Import a list of patches and commit them individually (unless
3354 --no-commit is specified).
3355 --no-commit is specified).
3355
3356
3356 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
3357 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
3358 there.
3359 there.
3359
3360
3360 Import first applies changes to the working directory (unless
3361 Import first applies changes to the working directory (unless
3361 --bypass is specified), import will abort if there are outstanding
3362 --bypass is specified), import will abort if there are outstanding
3362 changes.
3363 changes.
3363
3364
3364 Use --bypass to apply and commit patches directly to the
3365 Use --bypass to apply and commit patches directly to the
3365 repository, without affecting the working directory. Without
3366 repository, without affecting the working directory. Without
3366 --exact, patches will be applied on top of the working directory
3367 --exact, patches will be applied on top of the working directory
3367 parent revision.
3368 parent revision.
3368
3369
3369 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
3370 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
3371 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
3372 message are used as default committer and commit message. All
3373 message are used as default committer and commit message. All
3373 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
3374 message.
3375 message.
3375
3376
3376 If the imported patch was generated by :hg:`export`, user and
3377 If the imported patch was generated by :hg:`export`, user and
3377 description from patch override values from message headers and
3378 description from patch override values from message headers and
3378 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
3379 override these.
3380 override these.
3380
3381
3381 If --exact is specified, import will set the working directory to
3382 If --exact is specified, import will set the working directory to
3382 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
3383 resulting changeset has a different ID than the one recorded in
3384 resulting changeset has a different ID than the one recorded in
3384 the patch. This will guard against various ways that portable
3385 the patch. This will guard against various ways that portable
3385 patch formats and mail systems might fail to transfer Mercurial
3386 patch formats and mail systems might fail to transfer Mercurial
3386 data or metadata. See :hg:`bundle` for lossless transmission.
3387 data or metadata. See :hg:`bundle` for lossless transmission.
3387
3388
3388 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
3389 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
3390 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
3391 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
3392 changeset. This flag exists to let people import patches that
3393 changeset. This flag exists to let people import patches that
3393 partially apply without losing the associated metadata (author,
3394 partially apply without losing the associated metadata (author,
3394 date, description, ...).
3395 date, description, ...).
3395
3396
3396 .. note::
3397 .. note::
3397
3398
3398 When no hunks apply cleanly, :hg:`import --partial` will create
3399 When no hunks apply cleanly, :hg:`import --partial` will create
3399 an empty changeset, importing only the patch metadata.
3400 an empty changeset, importing only the patch metadata.
3400
3401
3401 With -s/--similarity, hg will attempt to discover renames and
3402 With -s/--similarity, hg will attempt to discover renames and
3402 copies in the patch in the same way as :hg:`addremove`.
3403 copies in the patch in the same way as :hg:`addremove`.
3403
3404
3404 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
3405 by setting the ``ui.patch`` configuration option. For the default
3406 by setting the ``ui.patch`` configuration option. For the default
3406 internal tool, the fuzz can also be configured via ``patch.fuzz``.
3407 internal tool, the fuzz can also be configured via ``patch.fuzz``.
3407 See :hg:`help config` for more information about configuration
3408 See :hg:`help config` for more information about configuration
3408 files and how to use these options.
3409 files and how to use these options.
3409
3410
3410 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.
3411
3412
3412 .. container:: verbose
3413 .. container:: verbose
3413
3414
3414 Examples:
3415 Examples:
3415
3416
3416 - import a traditional patch from a website and detect renames::
3417 - import a traditional patch from a website and detect renames::
3417
3418
3418 hg import -s 80 http://example.com/bugfix.patch
3419 hg import -s 80 http://example.com/bugfix.patch
3419
3420
3420 - import a changeset from an hgweb server::
3421 - import a changeset from an hgweb server::
3421
3422
3422 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3423 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3423
3424
3424 - import all the patches in an Unix-style mbox::
3425 - import all the patches in an Unix-style mbox::
3425
3426
3426 hg import incoming-patches.mbox
3427 hg import incoming-patches.mbox
3427
3428
3428 - import patches from stdin::
3429 - import patches from stdin::
3429
3430
3430 hg import -
3431 hg import -
3431
3432
3432 - attempt to exactly restore an exported changeset (not always
3433 - attempt to exactly restore an exported changeset (not always
3433 possible)::
3434 possible)::
3434
3435
3435 hg import --exact proposed-fix.patch
3436 hg import --exact proposed-fix.patch
3436
3437
3437 - 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
3438 the default internal tool.
3439 the default internal tool.
3439
3440
3440 hg import --config ui.patch="patch --merge" fuzzy.patch
3441 hg import --config ui.patch="patch --merge" fuzzy.patch
3441
3442
3442 - change the default fuzzing from 2 to a less strict 7
3443 - change the default fuzzing from 2 to a less strict 7
3443
3444
3444 hg import --config ui.fuzz=7 fuzz.patch
3445 hg import --config ui.fuzz=7 fuzz.patch
3445
3446
3446 Returns 0 on success, 1 on partial success (see --partial).
3447 Returns 0 on success, 1 on partial success (see --partial).
3447 """
3448 """
3448
3449
3449 opts = pycompat.byteskwargs(opts)
3450 opts = pycompat.byteskwargs(opts)
3450 if not patch1:
3451 if not patch1:
3451 raise error.Abort(_('need at least one patch to import'))
3452 raise error.Abort(_('need at least one patch to import'))
3452
3453
3453 patches = (patch1,) + patches
3454 patches = (patch1,) + patches
3454
3455
3455 date = opts.get('date')
3456 date = opts.get('date')
3456 if date:
3457 if date:
3457 opts['date'] = dateutil.parsedate(date)
3458 opts['date'] = dateutil.parsedate(date)
3458
3459
3459 exact = opts.get('exact')
3460 exact = opts.get('exact')
3460 update = not opts.get('bypass')
3461 update = not opts.get('bypass')
3461 if not update and opts.get('no_commit'):
3462 if not update and opts.get('no_commit'):
3462 raise error.Abort(_('cannot use --no-commit with --bypass'))
3463 raise error.Abort(_('cannot use --no-commit with --bypass'))
3463 try:
3464 try:
3464 sim = float(opts.get('similarity') or 0)
3465 sim = float(opts.get('similarity') or 0)
3465 except ValueError:
3466 except ValueError:
3466 raise error.Abort(_('similarity must be a number'))
3467 raise error.Abort(_('similarity must be a number'))
3467 if sim < 0 or sim > 100:
3468 if sim < 0 or sim > 100:
3468 raise error.Abort(_('similarity must be between 0 and 100'))
3469 raise error.Abort(_('similarity must be between 0 and 100'))
3469 if sim and not update:
3470 if sim and not update:
3470 raise error.Abort(_('cannot use --similarity with --bypass'))
3471 raise error.Abort(_('cannot use --similarity with --bypass'))
3471 if exact:
3472 if exact:
3472 if opts.get('edit'):
3473 if opts.get('edit'):
3473 raise error.Abort(_('cannot use --exact with --edit'))
3474 raise error.Abort(_('cannot use --exact with --edit'))
3474 if opts.get('prefix'):
3475 if opts.get('prefix'):
3475 raise error.Abort(_('cannot use --exact with --prefix'))
3476 raise error.Abort(_('cannot use --exact with --prefix'))
3476
3477
3477 base = opts["base"]
3478 base = opts["base"]
3478 msgs = []
3479 msgs = []
3479 ret = 0
3480 ret = 0
3480
3481
3481 with repo.wlock():
3482 with repo.wlock():
3482 if update:
3483 if update:
3483 cmdutil.checkunfinished(repo)
3484 cmdutil.checkunfinished(repo)
3484 if (exact or not opts.get('force')):
3485 if (exact or not opts.get('force')):
3485 cmdutil.bailifchanged(repo)
3486 cmdutil.bailifchanged(repo)
3486
3487
3487 if not opts.get('no_commit'):
3488 if not opts.get('no_commit'):
3488 lock = repo.lock
3489 lock = repo.lock
3489 tr = lambda: repo.transaction('import')
3490 tr = lambda: repo.transaction('import')
3490 dsguard = util.nullcontextmanager
3491 dsguard = util.nullcontextmanager
3491 else:
3492 else:
3492 lock = util.nullcontextmanager
3493 lock = util.nullcontextmanager
3493 tr = util.nullcontextmanager
3494 tr = util.nullcontextmanager
3494 dsguard = lambda: dirstateguard.dirstateguard(repo, 'import')
3495 dsguard = lambda: dirstateguard.dirstateguard(repo, 'import')
3495 with lock(), tr(), dsguard():
3496 with lock(), tr(), dsguard():
3496 parents = repo[None].parents()
3497 parents = repo[None].parents()
3497 for patchurl in patches:
3498 for patchurl in patches:
3498 if patchurl == '-':
3499 if patchurl == '-':
3499 ui.status(_('applying patch from stdin\n'))
3500 ui.status(_('applying patch from stdin\n'))
3500 patchfile = ui.fin
3501 patchfile = ui.fin
3501 patchurl = 'stdin' # for error message
3502 patchurl = 'stdin' # for error message
3502 else:
3503 else:
3503 patchurl = os.path.join(base, patchurl)
3504 patchurl = os.path.join(base, patchurl)
3504 ui.status(_('applying %s\n') % patchurl)
3505 ui.status(_('applying %s\n') % patchurl)
3505 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
3506 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
3506
3507
3507 haspatch = False
3508 haspatch = False
3508 for hunk in patch.split(patchfile):
3509 for hunk in patch.split(patchfile):
3509 with patch.extract(ui, hunk) as patchdata:
3510 with patch.extract(ui, hunk) as patchdata:
3510 msg, node, rej = cmdutil.tryimportone(ui, repo,
3511 msg, node, rej = cmdutil.tryimportone(ui, repo,
3511 patchdata,
3512 patchdata,
3512 parents, opts,
3513 parents, opts,
3513 msgs, hg.clean)
3514 msgs, hg.clean)
3514 if msg:
3515 if msg:
3515 haspatch = True
3516 haspatch = True
3516 ui.note(msg + '\n')
3517 ui.note(msg + '\n')
3517 if update or exact:
3518 if update or exact:
3518 parents = repo[None].parents()
3519 parents = repo[None].parents()
3519 else:
3520 else:
3520 parents = [repo[node]]
3521 parents = [repo[node]]
3521 if rej:
3522 if rej:
3522 ui.write_err(_("patch applied partially\n"))
3523 ui.write_err(_("patch applied partially\n"))
3523 ui.write_err(_("(fix the .rej files and run "
3524 ui.write_err(_("(fix the .rej files and run "
3524 "`hg commit --amend`)\n"))
3525 "`hg commit --amend`)\n"))
3525 ret = 1
3526 ret = 1
3526 break
3527 break
3527
3528
3528 if not haspatch:
3529 if not haspatch:
3529 raise error.Abort(_('%s: no diffs found') % patchurl)
3530 raise error.Abort(_('%s: no diffs found') % patchurl)
3530
3531
3531 if msgs:
3532 if msgs:
3532 repo.savecommitmessage('\n* * *\n'.join(msgs))
3533 repo.savecommitmessage('\n* * *\n'.join(msgs))
3533 return ret
3534 return ret
3534
3535
3535 @command('incoming|in',
3536 @command('incoming|in',
3536 [('f', 'force', None,
3537 [('f', 'force', None,
3537 _('run even if remote repository is unrelated')),
3538 _('run even if remote repository is unrelated')),
3538 ('n', 'newest-first', None, _('show newest record first')),
3539 ('n', 'newest-first', None, _('show newest record first')),
3539 ('', 'bundle', '',
3540 ('', 'bundle', '',
3540 _('file to store the bundles into'), _('FILE')),
3541 _('file to store the bundles into'), _('FILE')),
3541 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3542 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3542 ('B', 'bookmarks', False, _("compare bookmarks")),
3543 ('B', 'bookmarks', False, _("compare bookmarks")),
3543 ('b', 'branch', [],
3544 ('b', 'branch', [],
3544 _('a specific branch you would like to pull'), _('BRANCH')),
3545 _('a specific branch you would like to pull'), _('BRANCH')),
3545 ] + logopts + remoteopts + subrepoopts,
3546 ] + logopts + remoteopts + subrepoopts,
3546 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
3547 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
3547 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
3548 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
3548 def incoming(ui, repo, source="default", **opts):
3549 def incoming(ui, repo, source="default", **opts):
3549 """show new changesets found in source
3550 """show new changesets found in source
3550
3551
3551 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
3552 pull location. These are the changesets that would have been pulled
3553 pull location. These are the changesets that would have been pulled
3553 by :hg:`pull` at the time you issued this command.
3554 by :hg:`pull` at the time you issued this command.
3554
3555
3555 See pull for valid source format details.
3556 See pull for valid source format details.
3556
3557
3557 .. container:: verbose
3558 .. container:: verbose
3558
3559
3559 With -B/--bookmarks, the result of bookmark comparison between
3560 With -B/--bookmarks, the result of bookmark comparison between
3560 local and remote repositories is displayed. With -v/--verbose,
3561 local and remote repositories is displayed. With -v/--verbose,
3561 status is also displayed for each bookmark like below::
3562 status is also displayed for each bookmark like below::
3562
3563
3563 BM1 01234567890a added
3564 BM1 01234567890a added
3564 BM2 1234567890ab advanced
3565 BM2 1234567890ab advanced
3565 BM3 234567890abc diverged
3566 BM3 234567890abc diverged
3566 BM4 34567890abcd changed
3567 BM4 34567890abcd changed
3567
3568
3568 The action taken locally when pulling depends on the
3569 The action taken locally when pulling depends on the
3569 status of each bookmark:
3570 status of each bookmark:
3570
3571
3571 :``added``: pull will create it
3572 :``added``: pull will create it
3572 :``advanced``: pull will update it
3573 :``advanced``: pull will update it
3573 :``diverged``: pull will create a divergent bookmark
3574 :``diverged``: pull will create a divergent bookmark
3574 :``changed``: result depends on remote changesets
3575 :``changed``: result depends on remote changesets
3575
3576
3576 From the point of view of pulling behavior, bookmark
3577 From the point of view of pulling behavior, bookmark
3577 existing only in the remote repository are treated as ``added``,
3578 existing only in the remote repository are treated as ``added``,
3578 even if it is in fact locally deleted.
3579 even if it is in fact locally deleted.
3579
3580
3580 .. container:: verbose
3581 .. container:: verbose
3581
3582
3582 For remote repository, using --bundle avoids downloading the
3583 For remote repository, using --bundle avoids downloading the
3583 changesets twice if the incoming is followed by a pull.
3584 changesets twice if the incoming is followed by a pull.
3584
3585
3585 Examples:
3586 Examples:
3586
3587
3587 - show incoming changes with patches and full description::
3588 - show incoming changes with patches and full description::
3588
3589
3589 hg incoming -vp
3590 hg incoming -vp
3590
3591
3591 - show incoming changes excluding merges, store a bundle::
3592 - show incoming changes excluding merges, store a bundle::
3592
3593
3593 hg in -vpM --bundle incoming.hg
3594 hg in -vpM --bundle incoming.hg
3594 hg pull incoming.hg
3595 hg pull incoming.hg
3595
3596
3596 - briefly list changes inside a bundle::
3597 - briefly list changes inside a bundle::
3597
3598
3598 hg in changes.hg -T "{desc|firstline}\\n"
3599 hg in changes.hg -T "{desc|firstline}\\n"
3599
3600
3600 Returns 0 if there are incoming changes, 1 otherwise.
3601 Returns 0 if there are incoming changes, 1 otherwise.
3601 """
3602 """
3602 opts = pycompat.byteskwargs(opts)
3603 opts = pycompat.byteskwargs(opts)
3603 if opts.get('graph'):
3604 if opts.get('graph'):
3604 logcmdutil.checkunsupportedgraphflags([], opts)
3605 logcmdutil.checkunsupportedgraphflags([], opts)
3605 def display(other, chlist, displayer):
3606 def display(other, chlist, displayer):
3606 revdag = logcmdutil.graphrevs(other, chlist, opts)
3607 revdag = logcmdutil.graphrevs(other, chlist, opts)
3607 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3608 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3608 graphmod.asciiedges)
3609 graphmod.asciiedges)
3609
3610
3610 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3611 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3611 return 0
3612 return 0
3612
3613
3613 if opts.get('bundle') and opts.get('subrepos'):
3614 if opts.get('bundle') and opts.get('subrepos'):
3614 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3615 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3615
3616
3616 if opts.get('bookmarks'):
3617 if opts.get('bookmarks'):
3617 source, branches = hg.parseurl(ui.expandpath(source),
3618 source, branches = hg.parseurl(ui.expandpath(source),
3618 opts.get('branch'))
3619 opts.get('branch'))
3619 other = hg.peer(repo, opts, source)
3620 other = hg.peer(repo, opts, source)
3620 if 'bookmarks' not in other.listkeys('namespaces'):
3621 if 'bookmarks' not in other.listkeys('namespaces'):
3621 ui.warn(_("remote doesn't support bookmarks\n"))
3622 ui.warn(_("remote doesn't support bookmarks\n"))
3622 return 0
3623 return 0
3623 ui.pager('incoming')
3624 ui.pager('incoming')
3624 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3625 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3625 return bookmarks.incoming(ui, repo, other)
3626 return bookmarks.incoming(ui, repo, other)
3626
3627
3627 repo._subtoppath = ui.expandpath(source)
3628 repo._subtoppath = ui.expandpath(source)
3628 try:
3629 try:
3629 return hg.incoming(ui, repo, source, opts)
3630 return hg.incoming(ui, repo, source, opts)
3630 finally:
3631 finally:
3631 del repo._subtoppath
3632 del repo._subtoppath
3632
3633
3633
3634
3634 @command('init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3635 @command('init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3635 helpcategory=command.CATEGORY_REPO_CREATION,
3636 helpcategory=command.CATEGORY_REPO_CREATION,
3636 helpbasic=True, norepo=True)
3637 helpbasic=True, norepo=True)
3637 def init(ui, dest=".", **opts):
3638 def init(ui, dest=".", **opts):
3638 """create a new repository in the given directory
3639 """create a new repository in the given directory
3639
3640
3640 Initialize a new repository in the given directory. If the given
3641 Initialize a new repository in the given directory. If the given
3641 directory does not exist, it will be created.
3642 directory does not exist, it will be created.
3642
3643
3643 If no directory is given, the current directory is used.
3644 If no directory is given, the current directory is used.
3644
3645
3645 It is possible to specify an ``ssh://`` URL as the destination.
3646 It is possible to specify an ``ssh://`` URL as the destination.
3646 See :hg:`help urls` for more information.
3647 See :hg:`help urls` for more information.
3647
3648
3648 Returns 0 on success.
3649 Returns 0 on success.
3649 """
3650 """
3650 opts = pycompat.byteskwargs(opts)
3651 opts = pycompat.byteskwargs(opts)
3651 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3652 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3652
3653
3653 @command('locate',
3654 @command('locate',
3654 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3655 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3655 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3656 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3656 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3657 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3657 ] + walkopts,
3658 ] + walkopts,
3658 _('[OPTION]... [PATTERN]...'),
3659 _('[OPTION]... [PATTERN]...'),
3659 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
3660 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
3660 def locate(ui, repo, *pats, **opts):
3661 def locate(ui, repo, *pats, **opts):
3661 """locate files matching specific patterns (DEPRECATED)
3662 """locate files matching specific patterns (DEPRECATED)
3662
3663
3663 Print files under Mercurial control in the working directory whose
3664 Print files under Mercurial control in the working directory whose
3664 names match the given patterns.
3665 names match the given patterns.
3665
3666
3666 By default, this command searches all directories in the working
3667 By default, this command searches all directories in the working
3667 directory. To search just the current directory and its
3668 directory. To search just the current directory and its
3668 subdirectories, use "--include .".
3669 subdirectories, use "--include .".
3669
3670
3670 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
3671 of all files under Mercurial control in the working directory.
3672 of all files under Mercurial control in the working directory.
3672
3673
3673 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"
3674 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
3675 will avoid the problem of "xargs" treating single filenames that
3676 will avoid the problem of "xargs" treating single filenames that
3676 contain whitespace as multiple filenames.
3677 contain whitespace as multiple filenames.
3677
3678
3678 See :hg:`help files` for a more versatile command.
3679 See :hg:`help files` for a more versatile command.
3679
3680
3680 Returns 0 if a match is found, 1 otherwise.
3681 Returns 0 if a match is found, 1 otherwise.
3681 """
3682 """
3682 opts = pycompat.byteskwargs(opts)
3683 opts = pycompat.byteskwargs(opts)
3683 if opts.get('print0'):
3684 if opts.get('print0'):
3684 end = '\0'
3685 end = '\0'
3685 else:
3686 else:
3686 end = '\n'
3687 end = '\n'
3687 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3688 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3688
3689
3689 ret = 1
3690 ret = 1
3690 m = scmutil.match(ctx, pats, opts, default='relglob',
3691 m = scmutil.match(ctx, pats, opts, default='relglob',
3691 badfn=lambda x, y: False)
3692 badfn=lambda x, y: False)
3692
3693
3693 ui.pager('locate')
3694 ui.pager('locate')
3694 if ctx.rev() is None:
3695 if ctx.rev() is None:
3695 # When run on the working copy, "locate" includes removed files, so
3696 # When run on the working copy, "locate" includes removed files, so
3696 # we get the list of files from the dirstate.
3697 # we get the list of files from the dirstate.
3697 filesgen = sorted(repo.dirstate.matches(m))
3698 filesgen = sorted(repo.dirstate.matches(m))
3698 else:
3699 else:
3699 filesgen = ctx.matches(m)
3700 filesgen = ctx.matches(m)
3700 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
3701 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
3701 for abs in filesgen:
3702 for abs in filesgen:
3702 if opts.get('fullpath'):
3703 if opts.get('fullpath'):
3703 ui.write(repo.wjoin(abs), end)
3704 ui.write(repo.wjoin(abs), end)
3704 else:
3705 else:
3705 ui.write(uipathfn(abs), end)
3706 ui.write(uipathfn(abs), end)
3706 ret = 0
3707 ret = 0
3707
3708
3708 return ret
3709 return ret
3709
3710
3710 @command('log|history',
3711 @command('log|history',
3711 [('f', 'follow', None,
3712 [('f', 'follow', None,
3712 _('follow changeset history, or file history across copies and renames')),
3713 _('follow changeset history, or file history across copies and renames')),
3713 ('', 'follow-first', None,
3714 ('', 'follow-first', None,
3714 _('only follow the first parent of merge changesets (DEPRECATED)')),
3715 _('only follow the first parent of merge changesets (DEPRECATED)')),
3715 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3716 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3716 ('C', 'copies', None, _('show copied files')),
3717 ('C', 'copies', None, _('show copied files')),
3717 ('k', 'keyword', [],
3718 ('k', 'keyword', [],
3718 _('do case-insensitive search for a given text'), _('TEXT')),
3719 _('do case-insensitive search for a given text'), _('TEXT')),
3719 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3720 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3720 ('L', 'line-range', [],
3721 ('L', 'line-range', [],
3721 _('follow line range of specified file (EXPERIMENTAL)'),
3722 _('follow line range of specified file (EXPERIMENTAL)'),
3722 _('FILE,RANGE')),
3723 _('FILE,RANGE')),
3723 ('', 'removed', None, _('include revisions where files were removed')),
3724 ('', 'removed', None, _('include revisions where files were removed')),
3724 ('m', 'only-merges', None,
3725 ('m', 'only-merges', None,
3725 _('show only merges (DEPRECATED) (use -r "merge()" instead)')),
3726 _('show only merges (DEPRECATED) (use -r "merge()" instead)')),
3726 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3727 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3727 ('', 'only-branch', [],
3728 ('', 'only-branch', [],
3728 _('show only changesets within the given named branch (DEPRECATED)'),
3729 _('show only changesets within the given named branch (DEPRECATED)'),
3729 _('BRANCH')),
3730 _('BRANCH')),
3730 ('b', 'branch', [],
3731 ('b', 'branch', [],
3731 _('show changesets within the given named branch'), _('BRANCH')),
3732 _('show changesets within the given named branch'), _('BRANCH')),
3732 ('P', 'prune', [],
3733 ('P', 'prune', [],
3733 _('do not display revision or any of its ancestors'), _('REV')),
3734 _('do not display revision or any of its ancestors'), _('REV')),
3734 ] + logopts + walkopts,
3735 ] + logopts + walkopts,
3735 _('[OPTION]... [FILE]'),
3736 _('[OPTION]... [FILE]'),
3736 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3737 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3737 helpbasic=True, inferrepo=True,
3738 helpbasic=True, inferrepo=True,
3738 intents={INTENT_READONLY})
3739 intents={INTENT_READONLY})
3739 def log(ui, repo, *pats, **opts):
3740 def log(ui, repo, *pats, **opts):
3740 """show revision history of entire repository or files
3741 """show revision history of entire repository or files
3741
3742
3742 Print the revision history of the specified files or the entire
3743 Print the revision history of the specified files or the entire
3743 project.
3744 project.
3744
3745
3745 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
3746 --follow is set, in which case the working directory parent is
3747 --follow is set, in which case the working directory parent is
3747 used as the starting revision.
3748 used as the starting revision.
3748
3749
3749 File history is shown without following rename or copy history of
3750 File history is shown without following rename or copy history of
3750 files. Use -f/--follow with a filename to follow history across
3751 files. Use -f/--follow with a filename to follow history across
3751 renames and copies. --follow without a filename will only show
3752 renames and copies. --follow without a filename will only show
3752 ancestors of the starting revision.
3753 ancestors of the starting revision.
3753
3754
3754 By default this command prints revision number and changeset id,
3755 By default this command prints revision number and changeset id,
3755 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
3756 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
3757 changed files and full commit message are shown.
3758 changed files and full commit message are shown.
3758
3759
3759 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
3760 recent changeset at the top.
3761 recent changeset at the top.
3761 '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,
3762 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
3763 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
3763 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
3764 line.
3765 line.
3765 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
3766 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.
3767
3768
3768 .. container:: verbose
3769 .. container:: verbose
3769
3770
3770 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
3771 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
3772 specified line range will be shown. This option requires --follow;
3773 specified line range will be shown. This option requires --follow;
3773 it can be specified multiple times. Currently, this option is not
3774 it can be specified multiple times. Currently, this option is not
3774 compatible with --graph. This option is experimental.
3775 compatible with --graph. This option is experimental.
3775
3776
3776 .. note::
3777 .. note::
3777
3778
3778 :hg:`log --patch` may generate unexpected diff output for merge
3779 :hg:`log --patch` may generate unexpected diff output for merge
3779 changesets, as it will only compare the merge changeset against
3780 changesets, as it will only compare the merge changeset against
3780 its first parent. Also, only files different from BOTH parents
3781 its first parent. Also, only files different from BOTH parents
3781 will appear in files:.
3782 will appear in files:.
3782
3783
3783 .. note::
3784 .. note::
3784
3785
3785 For performance reasons, :hg:`log FILE` may omit duplicate changes
3786 For performance reasons, :hg:`log FILE` may omit duplicate changes
3786 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
3787 see all such changes, use the --removed switch.
3788 see all such changes, use the --removed switch.
3788
3789
3789 .. container:: verbose
3790 .. container:: verbose
3790
3791
3791 .. note::
3792 .. note::
3792
3793
3793 The history resulting from -L/--line-range options depends on diff
3794 The history resulting from -L/--line-range options depends on diff
3794 options; for instance if white-spaces are ignored, respective changes
3795 options; for instance if white-spaces are ignored, respective changes
3795 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.
3796
3797
3797 .. container:: verbose
3798 .. container:: verbose
3798
3799
3799 Some examples:
3800 Some examples:
3800
3801
3801 - changesets with full descriptions and file lists::
3802 - changesets with full descriptions and file lists::
3802
3803
3803 hg log -v
3804 hg log -v
3804
3805
3805 - changesets ancestral to the working directory::
3806 - changesets ancestral to the working directory::
3806
3807
3807 hg log -f
3808 hg log -f
3808
3809
3809 - last 10 commits on the current branch::
3810 - last 10 commits on the current branch::
3810
3811
3811 hg log -l 10 -b .
3812 hg log -l 10 -b .
3812
3813
3813 - changesets showing all modifications of a file, including removals::
3814 - changesets showing all modifications of a file, including removals::
3814
3815
3815 hg log --removed file.c
3816 hg log --removed file.c
3816
3817
3817 - all changesets that touch a directory, with diffs, excluding merges::
3818 - all changesets that touch a directory, with diffs, excluding merges::
3818
3819
3819 hg log -Mp lib/
3820 hg log -Mp lib/
3820
3821
3821 - all revision numbers that match a keyword::
3822 - all revision numbers that match a keyword::
3822
3823
3823 hg log -k bug --template "{rev}\\n"
3824 hg log -k bug --template "{rev}\\n"
3824
3825
3825 - the full hash identifier of the working directory parent::
3826 - the full hash identifier of the working directory parent::
3826
3827
3827 hg log -r . --template "{node}\\n"
3828 hg log -r . --template "{node}\\n"
3828
3829
3829 - list available log templates::
3830 - list available log templates::
3830
3831
3831 hg log -T list
3832 hg log -T list
3832
3833
3833 - check if a given changeset is included in a tagged release::
3834 - check if a given changeset is included in a tagged release::
3834
3835
3835 hg log -r "a21ccf and ancestor(1.9)"
3836 hg log -r "a21ccf and ancestor(1.9)"
3836
3837
3837 - find all changesets by some user in a date range::
3838 - find all changesets by some user in a date range::
3838
3839
3839 hg log -k alice -d "may 2008 to jul 2008"
3840 hg log -k alice -d "may 2008 to jul 2008"
3840
3841
3841 - summary of all changesets after the last tag::
3842 - summary of all changesets after the last tag::
3842
3843
3843 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3844 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3844
3845
3845 - changesets touching lines 13 to 23 for file.c::
3846 - changesets touching lines 13 to 23 for file.c::
3846
3847
3847 hg log -L file.c,13:23
3848 hg log -L file.c,13:23
3848
3849
3849 - 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
3850 main.c with patch::
3851 main.c with patch::
3851
3852
3852 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
3853
3854
3854 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.
3855
3856
3856 See :hg:`help revisions` for more about specifying and ordering
3857 See :hg:`help revisions` for more about specifying and ordering
3857 revisions.
3858 revisions.
3858
3859
3859 See :hg:`help templates` for more about pre-packaged styles and
3860 See :hg:`help templates` for more about pre-packaged styles and
3860 specifying custom templates. The default template used by the log
3861 specifying custom templates. The default template used by the log
3861 command can be customized via the ``ui.logtemplate`` configuration
3862 command can be customized via the ``ui.logtemplate`` configuration
3862 setting.
3863 setting.
3863
3864
3864 Returns 0 on success.
3865 Returns 0 on success.
3865
3866
3866 """
3867 """
3867 opts = pycompat.byteskwargs(opts)
3868 opts = pycompat.byteskwargs(opts)
3868 linerange = opts.get('line_range')
3869 linerange = opts.get('line_range')
3869
3870
3870 if linerange and not opts.get('follow'):
3871 if linerange and not opts.get('follow'):
3871 raise error.Abort(_('--line-range requires --follow'))
3872 raise error.Abort(_('--line-range requires --follow'))
3872
3873
3873 if linerange and pats:
3874 if linerange and pats:
3874 # TODO: take pats as patterns with no line-range filter
3875 # TODO: take pats as patterns with no line-range filter
3875 raise error.Abort(
3876 raise error.Abort(
3876 _('FILE arguments are not compatible with --line-range option')
3877 _('FILE arguments are not compatible with --line-range option')
3877 )
3878 )
3878
3879
3879 repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
3880 repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
3880 revs, differ = logcmdutil.getrevs(repo, pats, opts)
3881 revs, differ = logcmdutil.getrevs(repo, pats, opts)
3881 if linerange:
3882 if linerange:
3882 # TODO: should follow file history from logcmdutil._initialrevs(),
3883 # TODO: should follow file history from logcmdutil._initialrevs(),
3883 # then filter the result by logcmdutil._makerevset() and --limit
3884 # then filter the result by logcmdutil._makerevset() and --limit
3884 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
3885 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
3885
3886
3886 getcopies = None
3887 getcopies = None
3887 if opts.get('copies'):
3888 if opts.get('copies'):
3888 endrev = None
3889 endrev = None
3889 if revs:
3890 if revs:
3890 endrev = revs.max() + 1
3891 endrev = revs.max() + 1
3891 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
3892 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
3892
3893
3893 ui.pager('log')
3894 ui.pager('log')
3894 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, differ,
3895 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, differ,
3895 buffered=True)
3896 buffered=True)
3896 if opts.get('graph'):
3897 if opts.get('graph'):
3897 displayfn = logcmdutil.displaygraphrevs
3898 displayfn = logcmdutil.displaygraphrevs
3898 else:
3899 else:
3899 displayfn = logcmdutil.displayrevs
3900 displayfn = logcmdutil.displayrevs
3900 displayfn(ui, repo, revs, displayer, getcopies)
3901 displayfn(ui, repo, revs, displayer, getcopies)
3901
3902
3902 @command('manifest',
3903 @command('manifest',
3903 [('r', 'rev', '', _('revision to display'), _('REV')),
3904 [('r', 'rev', '', _('revision to display'), _('REV')),
3904 ('', 'all', False, _("list files from all revisions"))]
3905 ('', 'all', False, _("list files from all revisions"))]
3905 + formatteropts,
3906 + formatteropts,
3906 _('[-r REV]'),
3907 _('[-r REV]'),
3907 helpcategory=command.CATEGORY_MAINTENANCE,
3908 helpcategory=command.CATEGORY_MAINTENANCE,
3908 intents={INTENT_READONLY})
3909 intents={INTENT_READONLY})
3909 def manifest(ui, repo, node=None, rev=None, **opts):
3910 def manifest(ui, repo, node=None, rev=None, **opts):
3910 """output the current or given revision of the project manifest
3911 """output the current or given revision of the project manifest
3911
3912
3912 Print a list of version controlled files for the given revision.
3913 Print a list of version controlled files for the given revision.
3913 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
3914 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.
3915
3916
3916 With -v, print file permissions, symlink and executable bits.
3917 With -v, print file permissions, symlink and executable bits.
3917 With --debug, print file revision hashes.
3918 With --debug, print file revision hashes.
3918
3919
3919 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
3920 is printed. This includes deleted and renamed files.
3921 is printed. This includes deleted and renamed files.
3921
3922
3922 Returns 0 on success.
3923 Returns 0 on success.
3923 """
3924 """
3924 opts = pycompat.byteskwargs(opts)
3925 opts = pycompat.byteskwargs(opts)
3925 fm = ui.formatter('manifest', opts)
3926 fm = ui.formatter('manifest', opts)
3926
3927
3927 if opts.get('all'):
3928 if opts.get('all'):
3928 if rev or node:
3929 if rev or node:
3929 raise error.Abort(_("can't specify a revision with --all"))
3930 raise error.Abort(_("can't specify a revision with --all"))
3930
3931
3931 res = set()
3932 res = set()
3932 for rev in repo:
3933 for rev in repo:
3933 ctx = repo[rev]
3934 ctx = repo[rev]
3934 res |= set(ctx.files())
3935 res |= set(ctx.files())
3935
3936
3936 ui.pager('manifest')
3937 ui.pager('manifest')
3937 for f in sorted(res):
3938 for f in sorted(res):
3938 fm.startitem()
3939 fm.startitem()
3939 fm.write("path", '%s\n', f)
3940 fm.write("path", '%s\n', f)
3940 fm.end()
3941 fm.end()
3941 return
3942 return
3942
3943
3943 if rev and node:
3944 if rev and node:
3944 raise error.Abort(_("please specify just one revision"))
3945 raise error.Abort(_("please specify just one revision"))
3945
3946
3946 if not node:
3947 if not node:
3947 node = rev
3948 node = rev
3948
3949
3949 char = {'l': '@', 'x': '*', '': '', 't': 'd'}
3950 char = {'l': '@', 'x': '*', '': '', 't': 'd'}
3950 mode = {'l': '644', 'x': '755', '': '644', 't': '755'}
3951 mode = {'l': '644', 'x': '755', '': '644', 't': '755'}
3951 if node:
3952 if node:
3952 repo = scmutil.unhidehashlikerevs(repo, [node], 'nowarn')
3953 repo = scmutil.unhidehashlikerevs(repo, [node], 'nowarn')
3953 ctx = scmutil.revsingle(repo, node)
3954 ctx = scmutil.revsingle(repo, node)
3954 mf = ctx.manifest()
3955 mf = ctx.manifest()
3955 ui.pager('manifest')
3956 ui.pager('manifest')
3956 for f in ctx:
3957 for f in ctx:
3957 fm.startitem()
3958 fm.startitem()
3958 fm.context(ctx=ctx)
3959 fm.context(ctx=ctx)
3959 fl = ctx[f].flags()
3960 fl = ctx[f].flags()
3960 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3961 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3961 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])
3962 fm.write('path', '%s\n', f)
3963 fm.write('path', '%s\n', f)
3963 fm.end()
3964 fm.end()
3964
3965
3965 @command('merge',
3966 @command('merge',
3966 [('f', 'force', None,
3967 [('f', 'force', None,
3967 _('force a merge including outstanding changes (DEPRECATED)')),
3968 _('force a merge including outstanding changes (DEPRECATED)')),
3968 ('r', 'rev', '', _('revision to merge'), _('REV')),
3969 ('r', 'rev', '', _('revision to merge'), _('REV')),
3969 ('P', 'preview', None,
3970 ('P', 'preview', None,
3970 _('review revisions to merge (no merge is performed)')),
3971 _('review revisions to merge (no merge is performed)')),
3971 ('', 'abort', None, _('abort the ongoing merge')),
3972 ('', 'abort', None, _('abort the ongoing merge')),
3972 ] + mergetoolopts,
3973 ] + mergetoolopts,
3973 _('[-P] [[-r] REV]'),
3974 _('[-P] [[-r] REV]'),
3974 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT, helpbasic=True)
3975 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT, helpbasic=True)
3975 def merge(ui, repo, node=None, **opts):
3976 def merge(ui, repo, node=None, **opts):
3976 """merge another revision into working directory
3977 """merge another revision into working directory
3977
3978
3978 The current working directory is updated with all changes made in
3979 The current working directory is updated with all changes made in
3979 the requested revision since the last common predecessor revision.
3980 the requested revision since the last common predecessor revision.
3980
3981
3981 Files that changed between either parent are marked as changed for
3982 Files that changed between either parent are marked as changed for
3982 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
3983 updates to the repository are allowed. The next commit will have
3984 updates to the repository are allowed. The next commit will have
3984 two parents.
3985 two parents.
3985
3986
3986 ``--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
3987 merges. It overrides the HGMERGE environment variable and your
3988 merges. It overrides the HGMERGE environment variable and your
3988 configuration files. See :hg:`help merge-tools` for options.
3989 configuration files. See :hg:`help merge-tools` for options.
3989
3990
3990 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
3991 head revision, and the current branch contains exactly one other
3992 head revision, and the current branch contains exactly one other
3992 head, the other head is merged with by default. Otherwise, an
3993 head, the other head is merged with by default. Otherwise, an
3993 explicit revision with which to merge must be provided.
3994 explicit revision with which to merge must be provided.
3994
3995
3995 See :hg:`help resolve` for information on handling file conflicts.
3996 See :hg:`help resolve` for information on handling file conflicts.
3996
3997
3997 To undo an uncommitted merge, use :hg:`merge --abort` which
3998 To undo an uncommitted merge, use :hg:`merge --abort` which
3998 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
3999 all changes.
4000 all changes.
4000
4001
4001 Returns 0 on success, 1 if there are unresolved files.
4002 Returns 0 on success, 1 if there are unresolved files.
4002 """
4003 """
4003
4004
4004 opts = pycompat.byteskwargs(opts)
4005 opts = pycompat.byteskwargs(opts)
4005 abort = opts.get('abort')
4006 abort = opts.get('abort')
4006 if abort and repo.dirstate.p2() == nullid:
4007 if abort and repo.dirstate.p2() == nullid:
4007 cmdutil.wrongtooltocontinue(repo, _('merge'))
4008 cmdutil.wrongtooltocontinue(repo, _('merge'))
4008 if abort:
4009 if abort:
4009 state = cmdutil.getunfinishedstate(repo)
4010 state = cmdutil.getunfinishedstate(repo)
4010 if state and state._opname != 'merge':
4011 if state and state._opname != 'merge':
4011 raise error.Abort(_('cannot abort merge with %s in progress') %
4012 raise error.Abort(_('cannot abort merge with %s in progress') %
4012 (state._opname), hint=state.hint())
4013 (state._opname), hint=state.hint())
4013 if node:
4014 if node:
4014 raise error.Abort(_("cannot specify a node with --abort"))
4015 raise error.Abort(_("cannot specify a node with --abort"))
4015 if opts.get('rev'):
4016 if opts.get('rev'):
4016 raise error.Abort(_("cannot specify both --rev and --abort"))
4017 raise error.Abort(_("cannot specify both --rev and --abort"))
4017 if opts.get('preview'):
4018 if opts.get('preview'):
4018 raise error.Abort(_("cannot specify --preview with --abort"))
4019 raise error.Abort(_("cannot specify --preview with --abort"))
4019 if opts.get('rev') and node:
4020 if opts.get('rev') and node:
4020 raise error.Abort(_("please specify just one revision"))
4021 raise error.Abort(_("please specify just one revision"))
4021 if not node:
4022 if not node:
4022 node = opts.get('rev')
4023 node = opts.get('rev')
4023
4024
4024 if node:
4025 if node:
4025 node = scmutil.revsingle(repo, node).node()
4026 node = scmutil.revsingle(repo, node).node()
4026
4027
4027 if not node and not abort:
4028 if not node and not abort:
4028 node = repo[destutil.destmerge(repo)].node()
4029 node = repo[destutil.destmerge(repo)].node()
4029
4030
4030 if opts.get('preview'):
4031 if opts.get('preview'):
4031 # find nodes that are ancestors of p2 but not of p1
4032 # find nodes that are ancestors of p2 but not of p1
4032 p1 = repo.lookup('.')
4033 p1 = repo.lookup('.')
4033 p2 = node
4034 p2 = node
4034 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4035 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4035
4036
4036 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4037 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4037 for node in nodes:
4038 for node in nodes:
4038 displayer.show(repo[node])
4039 displayer.show(repo[node])
4039 displayer.close()
4040 displayer.close()
4040 return 0
4041 return 0
4041
4042
4042 # ui.forcemerge is an internal variable, do not document
4043 # ui.forcemerge is an internal variable, do not document
4043 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4044 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4044 with ui.configoverride(overrides, 'merge'):
4045 with ui.configoverride(overrides, 'merge'):
4045 force = opts.get('force')
4046 force = opts.get('force')
4046 labels = ['working copy', 'merge rev']
4047 labels = ['working copy', 'merge rev']
4047 return hg.merge(repo, node, force=force, mergeforce=force,
4048 return hg.merge(repo, node, force=force, mergeforce=force,
4048 labels=labels, abort=abort)
4049 labels=labels, abort=abort)
4049
4050
4050 statemod.addunfinished(
4051 statemod.addunfinished(
4051 'merge', fname=None, clearable=True, allowcommit=True,
4052 'merge', fname=None, clearable=True, allowcommit=True,
4052 cmdmsg=_('outstanding uncommitted merge'), abortfunc=hg.abortmerge,
4053 cmdmsg=_('outstanding uncommitted merge'), abortfunc=hg.abortmerge,
4053 statushint=_('To continue: hg commit\n'
4054 statushint=_('To continue: hg commit\n'
4054 'To abort: hg merge --abort'),
4055 'To abort: hg merge --abort'),
4055 cmdhint=_("use 'hg commit' or 'hg merge --abort'")
4056 cmdhint=_("use 'hg commit' or 'hg merge --abort'")
4056 )
4057 )
4057
4058
4058 @command('outgoing|out',
4059 @command('outgoing|out',
4059 [('f', 'force', None, _('run even when the destination is unrelated')),
4060 [('f', 'force', None, _('run even when the destination is unrelated')),
4060 ('r', 'rev', [],
4061 ('r', 'rev', [],
4061 _('a changeset intended to be included in the destination'), _('REV')),
4062 _('a changeset intended to be included in the destination'), _('REV')),
4062 ('n', 'newest-first', None, _('show newest record first')),
4063 ('n', 'newest-first', None, _('show newest record first')),
4063 ('B', 'bookmarks', False, _('compare bookmarks')),
4064 ('B', 'bookmarks', False, _('compare bookmarks')),
4064 ('b', 'branch', [], _('a specific branch you would like to push'),
4065 ('b', 'branch', [], _('a specific branch you would like to push'),
4065 _('BRANCH')),
4066 _('BRANCH')),
4066 ] + logopts + remoteopts + subrepoopts,
4067 ] + logopts + remoteopts + subrepoopts,
4067 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4068 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4068 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
4069 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
4069 def outgoing(ui, repo, dest=None, **opts):
4070 def outgoing(ui, repo, dest=None, **opts):
4070 """show changesets not found in the destination
4071 """show changesets not found in the destination
4071
4072
4072 Show changesets not found in the specified destination repository
4073 Show changesets not found in the specified destination repository
4073 or the default push location. These are the changesets that would
4074 or the default push location. These are the changesets that would
4074 be pushed if a push was requested.
4075 be pushed if a push was requested.
4075
4076
4076 See pull for details of valid destination formats.
4077 See pull for details of valid destination formats.
4077
4078
4078 .. container:: verbose
4079 .. container:: verbose
4079
4080
4080 With -B/--bookmarks, the result of bookmark comparison between
4081 With -B/--bookmarks, the result of bookmark comparison between
4081 local and remote repositories is displayed. With -v/--verbose,
4082 local and remote repositories is displayed. With -v/--verbose,
4082 status is also displayed for each bookmark like below::
4083 status is also displayed for each bookmark like below::
4083
4084
4084 BM1 01234567890a added
4085 BM1 01234567890a added
4085 BM2 deleted
4086 BM2 deleted
4086 BM3 234567890abc advanced
4087 BM3 234567890abc advanced
4087 BM4 34567890abcd diverged
4088 BM4 34567890abcd diverged
4088 BM5 4567890abcde changed
4089 BM5 4567890abcde changed
4089
4090
4090 The action taken when pushing depends on the
4091 The action taken when pushing depends on the
4091 status of each bookmark:
4092 status of each bookmark:
4092
4093
4093 :``added``: push with ``-B`` will create it
4094 :``added``: push with ``-B`` will create it
4094 :``deleted``: push with ``-B`` will delete it
4095 :``deleted``: push with ``-B`` will delete it
4095 :``advanced``: push will update it
4096 :``advanced``: push will update it
4096 :``diverged``: push with ``-B`` will update it
4097 :``diverged``: push with ``-B`` will update it
4097 :``changed``: push with ``-B`` will update it
4098 :``changed``: push with ``-B`` will update it
4098
4099
4099 From the point of view of pushing behavior, bookmarks
4100 From the point of view of pushing behavior, bookmarks
4100 existing only in the remote repository are treated as
4101 existing only in the remote repository are treated as
4101 ``deleted``, even if it is in fact added remotely.
4102 ``deleted``, even if it is in fact added remotely.
4102
4103
4103 Returns 0 if there are outgoing changes, 1 otherwise.
4104 Returns 0 if there are outgoing changes, 1 otherwise.
4104 """
4105 """
4105 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4106 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4106 # style URLs, so don't overwrite dest.
4107 # style URLs, so don't overwrite dest.
4107 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4108 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4108 if not path:
4109 if not path:
4109 raise error.Abort(_('default repository not configured!'),
4110 raise error.Abort(_('default repository not configured!'),
4110 hint=_("see 'hg help config.paths'"))
4111 hint=_("see 'hg help config.paths'"))
4111
4112
4112 opts = pycompat.byteskwargs(opts)
4113 opts = pycompat.byteskwargs(opts)
4113 if opts.get('graph'):
4114 if opts.get('graph'):
4114 logcmdutil.checkunsupportedgraphflags([], opts)
4115 logcmdutil.checkunsupportedgraphflags([], opts)
4115 o, other = hg._outgoing(ui, repo, dest, opts)
4116 o, other = hg._outgoing(ui, repo, dest, opts)
4116 if not o:
4117 if not o:
4117 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4118 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4118 return
4119 return
4119
4120
4120 revdag = logcmdutil.graphrevs(repo, o, opts)
4121 revdag = logcmdutil.graphrevs(repo, o, opts)
4121 ui.pager('outgoing')
4122 ui.pager('outgoing')
4122 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
4123 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
4123 logcmdutil.displaygraph(ui, repo, revdag, displayer,
4124 logcmdutil.displaygraph(ui, repo, revdag, displayer,
4124 graphmod.asciiedges)
4125 graphmod.asciiedges)
4125 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4126 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4126 return 0
4127 return 0
4127
4128
4128 if opts.get('bookmarks'):
4129 if opts.get('bookmarks'):
4129 dest = path.pushloc or path.loc
4130 dest = path.pushloc or path.loc
4130 other = hg.peer(repo, opts, dest)
4131 other = hg.peer(repo, opts, dest)
4131 if 'bookmarks' not in other.listkeys('namespaces'):
4132 if 'bookmarks' not in other.listkeys('namespaces'):
4132 ui.warn(_("remote doesn't support bookmarks\n"))
4133 ui.warn(_("remote doesn't support bookmarks\n"))
4133 return 0
4134 return 0
4134 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4135 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4135 ui.pager('outgoing')
4136 ui.pager('outgoing')
4136 return bookmarks.outgoing(ui, repo, other)
4137 return bookmarks.outgoing(ui, repo, other)
4137
4138
4138 repo._subtoppath = path.pushloc or path.loc
4139 repo._subtoppath = path.pushloc or path.loc
4139 try:
4140 try:
4140 return hg.outgoing(ui, repo, dest, opts)
4141 return hg.outgoing(ui, repo, dest, opts)
4141 finally:
4142 finally:
4142 del repo._subtoppath
4143 del repo._subtoppath
4143
4144
4144 @command('parents',
4145 @command('parents',
4145 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4146 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4146 ] + templateopts,
4147 ] + templateopts,
4147 _('[-r REV] [FILE]'),
4148 _('[-r REV] [FILE]'),
4148 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4149 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4149 inferrepo=True)
4150 inferrepo=True)
4150 def parents(ui, repo, file_=None, **opts):
4151 def parents(ui, repo, file_=None, **opts):
4151 """show the parents of the working directory or revision (DEPRECATED)
4152 """show the parents of the working directory or revision (DEPRECATED)
4152
4153
4153 Print the working directory's parent revisions. If a revision is
4154 Print the working directory's parent revisions. If a revision is
4154 given via -r/--rev, the parent of that revision will be printed.
4155 given via -r/--rev, the parent of that revision will be printed.
4155 If a file argument is given, the revision in which the file was
4156 If a file argument is given, the revision in which the file was
4156 last changed (before the working directory revision or the
4157 last changed (before the working directory revision or the
4157 argument to --rev if given) is printed.
4158 argument to --rev if given) is printed.
4158
4159
4159 This command is equivalent to::
4160 This command is equivalent to::
4160
4161
4161 hg log -r "p1()+p2()" or
4162 hg log -r "p1()+p2()" or
4162 hg log -r "p1(REV)+p2(REV)" or
4163 hg log -r "p1(REV)+p2(REV)" or
4163 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
4164 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
4164 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
4165 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
4165
4166
4166 See :hg:`summary` and :hg:`help revsets` for related information.
4167 See :hg:`summary` and :hg:`help revsets` for related information.
4167
4168
4168 Returns 0 on success.
4169 Returns 0 on success.
4169 """
4170 """
4170
4171
4171 opts = pycompat.byteskwargs(opts)
4172 opts = pycompat.byteskwargs(opts)
4172 rev = opts.get('rev')
4173 rev = opts.get('rev')
4173 if rev:
4174 if rev:
4174 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
4175 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
4175 ctx = scmutil.revsingle(repo, rev, None)
4176 ctx = scmutil.revsingle(repo, rev, None)
4176
4177
4177 if file_:
4178 if file_:
4178 m = scmutil.match(ctx, (file_,), opts)
4179 m = scmutil.match(ctx, (file_,), opts)
4179 if m.anypats() or len(m.files()) != 1:
4180 if m.anypats() or len(m.files()) != 1:
4180 raise error.Abort(_('can only specify an explicit filename'))
4181 raise error.Abort(_('can only specify an explicit filename'))
4181 file_ = m.files()[0]
4182 file_ = m.files()[0]
4182 filenodes = []
4183 filenodes = []
4183 for cp in ctx.parents():
4184 for cp in ctx.parents():
4184 if not cp:
4185 if not cp:
4185 continue
4186 continue
4186 try:
4187 try:
4187 filenodes.append(cp.filenode(file_))
4188 filenodes.append(cp.filenode(file_))
4188 except error.LookupError:
4189 except error.LookupError:
4189 pass
4190 pass
4190 if not filenodes:
4191 if not filenodes:
4191 raise error.Abort(_("'%s' not found in manifest!") % file_)
4192 raise error.Abort(_("'%s' not found in manifest!") % file_)
4192 p = []
4193 p = []
4193 for fn in filenodes:
4194 for fn in filenodes:
4194 fctx = repo.filectx(file_, fileid=fn)
4195 fctx = repo.filectx(file_, fileid=fn)
4195 p.append(fctx.node())
4196 p.append(fctx.node())
4196 else:
4197 else:
4197 p = [cp.node() for cp in ctx.parents()]
4198 p = [cp.node() for cp in ctx.parents()]
4198
4199
4199 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4200 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4200 for n in p:
4201 for n in p:
4201 if n != nullid:
4202 if n != nullid:
4202 displayer.show(repo[n])
4203 displayer.show(repo[n])
4203 displayer.close()
4204 displayer.close()
4204
4205
4205 @command('paths', formatteropts, _('[NAME]'),
4206 @command('paths', formatteropts, _('[NAME]'),
4206 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4207 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4207 optionalrepo=True, intents={INTENT_READONLY})
4208 optionalrepo=True, intents={INTENT_READONLY})
4208 def paths(ui, repo, search=None, **opts):
4209 def paths(ui, repo, search=None, **opts):
4209 """show aliases for remote repositories
4210 """show aliases for remote repositories
4210
4211
4211 Show definition of symbolic path name NAME. If no name is given,
4212 Show definition of symbolic path name NAME. If no name is given,
4212 show definition of all available names.
4213 show definition of all available names.
4213
4214
4214 Option -q/--quiet suppresses all output when searching for NAME
4215 Option -q/--quiet suppresses all output when searching for NAME
4215 and shows only the path names when listing all definitions.
4216 and shows only the path names when listing all definitions.
4216
4217
4217 Path names are defined in the [paths] section of your
4218 Path names are defined in the [paths] section of your
4218 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4219 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4219 repository, ``.hg/hgrc`` is used, too.
4220 repository, ``.hg/hgrc`` is used, too.
4220
4221
4221 The path names ``default`` and ``default-push`` have a special
4222 The path names ``default`` and ``default-push`` have a special
4222 meaning. When performing a push or pull operation, they are used
4223 meaning. When performing a push or pull operation, they are used
4223 as fallbacks if no location is specified on the command-line.
4224 as fallbacks if no location is specified on the command-line.
4224 When ``default-push`` is set, it will be used for push and
4225 When ``default-push`` is set, it will be used for push and
4225 ``default`` will be used for pull; otherwise ``default`` is used
4226 ``default`` will be used for pull; otherwise ``default`` is used
4226 as the fallback for both. When cloning a repository, the clone
4227 as the fallback for both. When cloning a repository, the clone
4227 source is written as ``default`` in ``.hg/hgrc``.
4228 source is written as ``default`` in ``.hg/hgrc``.
4228
4229
4229 .. note::
4230 .. note::
4230
4231
4231 ``default`` and ``default-push`` apply to all inbound (e.g.
4232 ``default`` and ``default-push`` apply to all inbound (e.g.
4232 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
4233 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
4233 and :hg:`bundle`) operations.
4234 and :hg:`bundle`) operations.
4234
4235
4235 See :hg:`help urls` for more information.
4236 See :hg:`help urls` for more information.
4236
4237
4237 .. container:: verbose
4238 .. container:: verbose
4238
4239
4239 Template:
4240 Template:
4240
4241
4241 The following keywords are supported. See also :hg:`help templates`.
4242 The following keywords are supported. See also :hg:`help templates`.
4242
4243
4243 :name: String. Symbolic name of the path alias.
4244 :name: String. Symbolic name of the path alias.
4244 :pushurl: String. URL for push operations.
4245 :pushurl: String. URL for push operations.
4245 :url: String. URL or directory path for the other operations.
4246 :url: String. URL or directory path for the other operations.
4246
4247
4247 Returns 0 on success.
4248 Returns 0 on success.
4248 """
4249 """
4249
4250
4250 opts = pycompat.byteskwargs(opts)
4251 opts = pycompat.byteskwargs(opts)
4251 ui.pager('paths')
4252 ui.pager('paths')
4252 if search:
4253 if search:
4253 pathitems = [(name, path) for name, path in ui.paths.iteritems()
4254 pathitems = [(name, path) for name, path in ui.paths.iteritems()
4254 if name == search]
4255 if name == search]
4255 else:
4256 else:
4256 pathitems = sorted(ui.paths.iteritems())
4257 pathitems = sorted(ui.paths.iteritems())
4257
4258
4258 fm = ui.formatter('paths', opts)
4259 fm = ui.formatter('paths', opts)
4259 if fm.isplain():
4260 if fm.isplain():
4260 hidepassword = util.hidepassword
4261 hidepassword = util.hidepassword
4261 else:
4262 else:
4262 hidepassword = bytes
4263 hidepassword = bytes
4263 if ui.quiet:
4264 if ui.quiet:
4264 namefmt = '%s\n'
4265 namefmt = '%s\n'
4265 else:
4266 else:
4266 namefmt = '%s = '
4267 namefmt = '%s = '
4267 showsubopts = not search and not ui.quiet
4268 showsubopts = not search and not ui.quiet
4268
4269
4269 for name, path in pathitems:
4270 for name, path in pathitems:
4270 fm.startitem()
4271 fm.startitem()
4271 fm.condwrite(not search, 'name', namefmt, name)
4272 fm.condwrite(not search, 'name', namefmt, name)
4272 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
4273 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
4273 for subopt, value in sorted(path.suboptions.items()):
4274 for subopt, value in sorted(path.suboptions.items()):
4274 assert subopt not in ('name', 'url')
4275 assert subopt not in ('name', 'url')
4275 if showsubopts:
4276 if showsubopts:
4276 fm.plain('%s:%s = ' % (name, subopt))
4277 fm.plain('%s:%s = ' % (name, subopt))
4277 fm.condwrite(showsubopts, subopt, '%s\n', value)
4278 fm.condwrite(showsubopts, subopt, '%s\n', value)
4278
4279
4279 fm.end()
4280 fm.end()
4280
4281
4281 if search and not pathitems:
4282 if search and not pathitems:
4282 if not ui.quiet:
4283 if not ui.quiet:
4283 ui.warn(_("not found!\n"))
4284 ui.warn(_("not found!\n"))
4284 return 1
4285 return 1
4285 else:
4286 else:
4286 return 0
4287 return 0
4287
4288
4288 @command('phase',
4289 @command('phase',
4289 [('p', 'public', False, _('set changeset phase to public')),
4290 [('p', 'public', False, _('set changeset phase to public')),
4290 ('d', 'draft', False, _('set changeset phase to draft')),
4291 ('d', 'draft', False, _('set changeset phase to draft')),
4291 ('s', 'secret', False, _('set changeset phase to secret')),
4292 ('s', 'secret', False, _('set changeset phase to secret')),
4292 ('f', 'force', False, _('allow to move boundary backward')),
4293 ('f', 'force', False, _('allow to move boundary backward')),
4293 ('r', 'rev', [], _('target revision'), _('REV')),
4294 ('r', 'rev', [], _('target revision'), _('REV')),
4294 ],
4295 ],
4295 _('[-p|-d|-s] [-f] [-r] [REV...]'),
4296 _('[-p|-d|-s] [-f] [-r] [REV...]'),
4296 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
4297 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
4297 def phase(ui, repo, *revs, **opts):
4298 def phase(ui, repo, *revs, **opts):
4298 """set or show the current phase name
4299 """set or show the current phase name
4299
4300
4300 With no argument, show the phase name of the current revision(s).
4301 With no argument, show the phase name of the current revision(s).
4301
4302
4302 With one of -p/--public, -d/--draft or -s/--secret, change the
4303 With one of -p/--public, -d/--draft or -s/--secret, change the
4303 phase value of the specified revisions.
4304 phase value of the specified revisions.
4304
4305
4305 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
4306 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
4306 lower phase to a higher phase. Phases are ordered as follows::
4307 lower phase to a higher phase. Phases are ordered as follows::
4307
4308
4308 public < draft < secret
4309 public < draft < secret
4309
4310
4310 Returns 0 on success, 1 if some phases could not be changed.
4311 Returns 0 on success, 1 if some phases could not be changed.
4311
4312
4312 (For more information about the phases concept, see :hg:`help phases`.)
4313 (For more information about the phases concept, see :hg:`help phases`.)
4313 """
4314 """
4314 opts = pycompat.byteskwargs(opts)
4315 opts = pycompat.byteskwargs(opts)
4315 # search for a unique phase argument
4316 # search for a unique phase argument
4316 targetphase = None
4317 targetphase = None
4317 for idx, name in enumerate(phases.cmdphasenames):
4318 for idx, name in enumerate(phases.cmdphasenames):
4318 if opts[name]:
4319 if opts[name]:
4319 if targetphase is not None:
4320 if targetphase is not None:
4320 raise error.Abort(_('only one phase can be specified'))
4321 raise error.Abort(_('only one phase can be specified'))
4321 targetphase = idx
4322 targetphase = idx
4322
4323
4323 # look for specified revision
4324 # look for specified revision
4324 revs = list(revs)
4325 revs = list(revs)
4325 revs.extend(opts['rev'])
4326 revs.extend(opts['rev'])
4326 if not revs:
4327 if not revs:
4327 # display both parents as the second parent phase can influence
4328 # display both parents as the second parent phase can influence
4328 # the phase of a merge commit
4329 # the phase of a merge commit
4329 revs = [c.rev() for c in repo[None].parents()]
4330 revs = [c.rev() for c in repo[None].parents()]
4330
4331
4331 revs = scmutil.revrange(repo, revs)
4332 revs = scmutil.revrange(repo, revs)
4332
4333
4333 ret = 0
4334 ret = 0
4334 if targetphase is None:
4335 if targetphase is None:
4335 # display
4336 # display
4336 for r in revs:
4337 for r in revs:
4337 ctx = repo[r]
4338 ctx = repo[r]
4338 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4339 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4339 else:
4340 else:
4340 with repo.lock(), repo.transaction("phase") as tr:
4341 with repo.lock(), repo.transaction("phase") as tr:
4341 # set phase
4342 # set phase
4342 if not revs:
4343 if not revs:
4343 raise error.Abort(_('empty revision set'))
4344 raise error.Abort(_('empty revision set'))
4344 nodes = [repo[r].node() for r in revs]
4345 nodes = [repo[r].node() for r in revs]
4345 # moving revision from public to draft may hide them
4346 # moving revision from public to draft may hide them
4346 # We have to check result on an unfiltered repository
4347 # We have to check result on an unfiltered repository
4347 unfi = repo.unfiltered()
4348 unfi = repo.unfiltered()
4348 getphase = unfi._phasecache.phase
4349 getphase = unfi._phasecache.phase
4349 olddata = [getphase(unfi, r) for r in unfi]
4350 olddata = [getphase(unfi, r) for r in unfi]
4350 phases.advanceboundary(repo, tr, targetphase, nodes)
4351 phases.advanceboundary(repo, tr, targetphase, nodes)
4351 if opts['force']:
4352 if opts['force']:
4352 phases.retractboundary(repo, tr, targetphase, nodes)
4353 phases.retractboundary(repo, tr, targetphase, nodes)
4353 getphase = unfi._phasecache.phase
4354 getphase = unfi._phasecache.phase
4354 newdata = [getphase(unfi, r) for r in unfi]
4355 newdata = [getphase(unfi, r) for r in unfi]
4355 changes = sum(newdata[r] != olddata[r] for r in unfi)
4356 changes = sum(newdata[r] != olddata[r] for r in unfi)
4356 cl = unfi.changelog
4357 cl = unfi.changelog
4357 rejected = [n for n in nodes
4358 rejected = [n for n in nodes
4358 if newdata[cl.rev(n)] < targetphase]
4359 if newdata[cl.rev(n)] < targetphase]
4359 if rejected:
4360 if rejected:
4360 ui.warn(_('cannot move %i changesets to a higher '
4361 ui.warn(_('cannot move %i changesets to a higher '
4361 'phase, use --force\n') % len(rejected))
4362 'phase, use --force\n') % len(rejected))
4362 ret = 1
4363 ret = 1
4363 if changes:
4364 if changes:
4364 msg = _('phase changed for %i changesets\n') % changes
4365 msg = _('phase changed for %i changesets\n') % changes
4365 if ret:
4366 if ret:
4366 ui.status(msg)
4367 ui.status(msg)
4367 else:
4368 else:
4368 ui.note(msg)
4369 ui.note(msg)
4369 else:
4370 else:
4370 ui.warn(_('no phases changed\n'))
4371 ui.warn(_('no phases changed\n'))
4371 return ret
4372 return ret
4372
4373
4373 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
4374 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
4374 """Run after a changegroup has been added via pull/unbundle
4375 """Run after a changegroup has been added via pull/unbundle
4375
4376
4376 This takes arguments below:
4377 This takes arguments below:
4377
4378
4378 :modheads: change of heads by pull/unbundle
4379 :modheads: change of heads by pull/unbundle
4379 :optupdate: updating working directory is needed or not
4380 :optupdate: updating working directory is needed or not
4380 :checkout: update destination revision (or None to default destination)
4381 :checkout: update destination revision (or None to default destination)
4381 :brev: a name, which might be a bookmark to be activated after updating
4382 :brev: a name, which might be a bookmark to be activated after updating
4382 """
4383 """
4383 if modheads == 0:
4384 if modheads == 0:
4384 return
4385 return
4385 if optupdate:
4386 if optupdate:
4386 try:
4387 try:
4387 return hg.updatetotally(ui, repo, checkout, brev)
4388 return hg.updatetotally(ui, repo, checkout, brev)
4388 except error.UpdateAbort as inst:
4389 except error.UpdateAbort as inst:
4389 msg = _("not updating: %s") % stringutil.forcebytestr(inst)
4390 msg = _("not updating: %s") % stringutil.forcebytestr(inst)
4390 hint = inst.hint
4391 hint = inst.hint
4391 raise error.UpdateAbort(msg, hint=hint)
4392 raise error.UpdateAbort(msg, hint=hint)
4392 if modheads is not None and modheads > 1:
4393 if modheads is not None and modheads > 1:
4393 currentbranchheads = len(repo.branchheads())
4394 currentbranchheads = len(repo.branchheads())
4394 if currentbranchheads == modheads:
4395 if currentbranchheads == modheads:
4395 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4396 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4396 elif currentbranchheads > 1:
4397 elif currentbranchheads > 1:
4397 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4398 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4398 "merge)\n"))
4399 "merge)\n"))
4399 else:
4400 else:
4400 ui.status(_("(run 'hg heads' to see heads)\n"))
4401 ui.status(_("(run 'hg heads' to see heads)\n"))
4401 elif not ui.configbool('commands', 'update.requiredest'):
4402 elif not ui.configbool('commands', 'update.requiredest'):
4402 ui.status(_("(run 'hg update' to get a working copy)\n"))
4403 ui.status(_("(run 'hg update' to get a working copy)\n"))
4403
4404
4404 @command('pull',
4405 @command('pull',
4405 [('u', 'update', None,
4406 [('u', 'update', None,
4406 _('update to new branch head if new descendants were pulled')),
4407 _('update to new branch head if new descendants were pulled')),
4407 ('f', 'force', None, _('run even when remote repository is unrelated')),
4408 ('f', 'force', None, _('run even when remote repository is unrelated')),
4408 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4409 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4409 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4410 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4410 ('b', 'branch', [], _('a specific branch you would like to pull'),
4411 ('b', 'branch', [], _('a specific branch you would like to pull'),
4411 _('BRANCH')),
4412 _('BRANCH')),
4412 ] + remoteopts,
4413 ] + remoteopts,
4413 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
4414 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
4414 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4415 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4415 helpbasic=True)
4416 helpbasic=True)
4416 def pull(ui, repo, source="default", **opts):
4417 def pull(ui, repo, source="default", **opts):
4417 """pull changes from the specified source
4418 """pull changes from the specified source
4418
4419
4419 Pull changes from a remote repository to a local one.
4420 Pull changes from a remote repository to a local one.
4420
4421
4421 This finds all changes from the repository at the specified path
4422 This finds all changes from the repository at the specified path
4422 or URL and adds them to a local repository (the current one unless
4423 or URL and adds them to a local repository (the current one unless
4423 -R is specified). By default, this does not update the copy of the
4424 -R is specified). By default, this does not update the copy of the
4424 project in the working directory.
4425 project in the working directory.
4425
4426
4426 When cloning from servers that support it, Mercurial may fetch
4427 When cloning from servers that support it, Mercurial may fetch
4427 pre-generated data. When this is done, hooks operating on incoming
4428 pre-generated data. When this is done, hooks operating on incoming
4428 changesets and changegroups may fire more than once, once for each
4429 changesets and changegroups may fire more than once, once for each
4429 pre-generated bundle and as well as for any additional remaining
4430 pre-generated bundle and as well as for any additional remaining
4430 data. See :hg:`help -e clonebundles` for more.
4431 data. See :hg:`help -e clonebundles` for more.
4431
4432
4432 Use :hg:`incoming` if you want to see what would have been added
4433 Use :hg:`incoming` if you want to see what would have been added
4433 by a pull at the time you issued this command. If you then decide
4434 by a pull at the time you issued this command. If you then decide
4434 to add those changes to the repository, you should use :hg:`pull
4435 to add those changes to the repository, you should use :hg:`pull
4435 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4436 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4436
4437
4437 If SOURCE is omitted, the 'default' path will be used.
4438 If SOURCE is omitted, the 'default' path will be used.
4438 See :hg:`help urls` for more information.
4439 See :hg:`help urls` for more information.
4439
4440
4440 Specifying bookmark as ``.`` is equivalent to specifying the active
4441 Specifying bookmark as ``.`` is equivalent to specifying the active
4441 bookmark's name.
4442 bookmark's name.
4442
4443
4443 Returns 0 on success, 1 if an update had unresolved files.
4444 Returns 0 on success, 1 if an update had unresolved files.
4444 """
4445 """
4445
4446
4446 opts = pycompat.byteskwargs(opts)
4447 opts = pycompat.byteskwargs(opts)
4447 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
4448 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
4448 msg = _('update destination required by configuration')
4449 msg = _('update destination required by configuration')
4449 hint = _('use hg pull followed by hg update DEST')
4450 hint = _('use hg pull followed by hg update DEST')
4450 raise error.Abort(msg, hint=hint)
4451 raise error.Abort(msg, hint=hint)
4451
4452
4452 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4453 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4453 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4454 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4454 other = hg.peer(repo, opts, source)
4455 other = hg.peer(repo, opts, source)
4455 try:
4456 try:
4456 revs, checkout = hg.addbranchrevs(repo, other, branches,
4457 revs, checkout = hg.addbranchrevs(repo, other, branches,
4457 opts.get('rev'))
4458 opts.get('rev'))
4458
4459
4459 pullopargs = {}
4460 pullopargs = {}
4460
4461
4461 nodes = None
4462 nodes = None
4462 if opts.get('bookmark') or revs:
4463 if opts.get('bookmark') or revs:
4463 # The list of bookmark used here is the same used to actually update
4464 # The list of bookmark used here is the same used to actually update
4464 # the bookmark names, to avoid the race from issue 4689 and we do
4465 # the bookmark names, to avoid the race from issue 4689 and we do
4465 # all lookup and bookmark queries in one go so they see the same
4466 # all lookup and bookmark queries in one go so they see the same
4466 # version of the server state (issue 4700).
4467 # version of the server state (issue 4700).
4467 nodes = []
4468 nodes = []
4468 fnodes = []
4469 fnodes = []
4469 revs = revs or []
4470 revs = revs or []
4470 if revs and not other.capable('lookup'):
4471 if revs and not other.capable('lookup'):
4471 err = _("other repository doesn't support revision lookup, "
4472 err = _("other repository doesn't support revision lookup, "
4472 "so a rev cannot be specified.")
4473 "so a rev cannot be specified.")
4473 raise error.Abort(err)
4474 raise error.Abort(err)
4474 with other.commandexecutor() as e:
4475 with other.commandexecutor() as e:
4475 fremotebookmarks = e.callcommand('listkeys', {
4476 fremotebookmarks = e.callcommand('listkeys', {
4476 'namespace': 'bookmarks'
4477 'namespace': 'bookmarks'
4477 })
4478 })
4478 for r in revs:
4479 for r in revs:
4479 fnodes.append(e.callcommand('lookup', {'key': r}))
4480 fnodes.append(e.callcommand('lookup', {'key': r}))
4480 remotebookmarks = fremotebookmarks.result()
4481 remotebookmarks = fremotebookmarks.result()
4481 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
4482 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
4482 pullopargs['remotebookmarks'] = remotebookmarks
4483 pullopargs['remotebookmarks'] = remotebookmarks
4483 for b in opts.get('bookmark', []):
4484 for b in opts.get('bookmark', []):
4484 b = repo._bookmarks.expandname(b)
4485 b = repo._bookmarks.expandname(b)
4485 if b not in remotebookmarks:
4486 if b not in remotebookmarks:
4486 raise error.Abort(_('remote bookmark %s not found!') % b)
4487 raise error.Abort(_('remote bookmark %s not found!') % b)
4487 nodes.append(remotebookmarks[b])
4488 nodes.append(remotebookmarks[b])
4488 for i, rev in enumerate(revs):
4489 for i, rev in enumerate(revs):
4489 node = fnodes[i].result()
4490 node = fnodes[i].result()
4490 nodes.append(node)
4491 nodes.append(node)
4491 if rev == checkout:
4492 if rev == checkout:
4492 checkout = node
4493 checkout = node
4493
4494
4494 wlock = util.nullcontextmanager()
4495 wlock = util.nullcontextmanager()
4495 if opts.get('update'):
4496 if opts.get('update'):
4496 wlock = repo.wlock()
4497 wlock = repo.wlock()
4497 with wlock:
4498 with wlock:
4498 pullopargs.update(opts.get('opargs', {}))
4499 pullopargs.update(opts.get('opargs', {}))
4499 modheads = exchange.pull(repo, other, heads=nodes,
4500 modheads = exchange.pull(repo, other, heads=nodes,
4500 force=opts.get('force'),
4501 force=opts.get('force'),
4501 bookmarks=opts.get('bookmark', ()),
4502 bookmarks=opts.get('bookmark', ()),
4502 opargs=pullopargs).cgresult
4503 opargs=pullopargs).cgresult
4503
4504
4504 # brev is a name, which might be a bookmark to be activated at
4505 # brev is a name, which might be a bookmark to be activated at
4505 # the end of the update. In other words, it is an explicit
4506 # the end of the update. In other words, it is an explicit
4506 # destination of the update
4507 # destination of the update
4507 brev = None
4508 brev = None
4508
4509
4509 if checkout:
4510 if checkout:
4510 checkout = repo.unfiltered().changelog.rev(checkout)
4511 checkout = repo.unfiltered().changelog.rev(checkout)
4511
4512
4512 # order below depends on implementation of
4513 # order below depends on implementation of
4513 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4514 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4514 # because 'checkout' is determined without it.
4515 # because 'checkout' is determined without it.
4515 if opts.get('rev'):
4516 if opts.get('rev'):
4516 brev = opts['rev'][0]
4517 brev = opts['rev'][0]
4517 elif opts.get('branch'):
4518 elif opts.get('branch'):
4518 brev = opts['branch'][0]
4519 brev = opts['branch'][0]
4519 else:
4520 else:
4520 brev = branches[0]
4521 brev = branches[0]
4521 repo._subtoppath = source
4522 repo._subtoppath = source
4522 try:
4523 try:
4523 ret = postincoming(ui, repo, modheads, opts.get('update'),
4524 ret = postincoming(ui, repo, modheads, opts.get('update'),
4524 checkout, brev)
4525 checkout, brev)
4525 except error.FilteredRepoLookupError as exc:
4526 except error.FilteredRepoLookupError as exc:
4526 msg = _('cannot update to target: %s') % exc.args[0]
4527 msg = _('cannot update to target: %s') % exc.args[0]
4527 exc.args = (msg,) + exc.args[1:]
4528 exc.args = (msg,) + exc.args[1:]
4528 raise
4529 raise
4529 finally:
4530 finally:
4530 del repo._subtoppath
4531 del repo._subtoppath
4531
4532
4532 finally:
4533 finally:
4533 other.close()
4534 other.close()
4534 return ret
4535 return ret
4535
4536
4536 @command('push',
4537 @command('push',
4537 [('f', 'force', None, _('force push')),
4538 [('f', 'force', None, _('force push')),
4538 ('r', 'rev', [],
4539 ('r', 'rev', [],
4539 _('a changeset intended to be included in the destination'),
4540 _('a changeset intended to be included in the destination'),
4540 _('REV')),
4541 _('REV')),
4541 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4542 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4542 ('b', 'branch', [],
4543 ('b', 'branch', [],
4543 _('a specific branch you would like to push'), _('BRANCH')),
4544 _('a specific branch you would like to push'), _('BRANCH')),
4544 ('', 'new-branch', False, _('allow pushing a new branch')),
4545 ('', 'new-branch', False, _('allow pushing a new branch')),
4545 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4546 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4546 ('', 'publish', False, _('push the changeset as public (EXPERIMENTAL)')),
4547 ('', 'publish', False, _('push the changeset as public (EXPERIMENTAL)')),
4547 ] + remoteopts,
4548 ] + remoteopts,
4548 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
4549 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
4549 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4550 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4550 helpbasic=True)
4551 helpbasic=True)
4551 def push(ui, repo, dest=None, **opts):
4552 def push(ui, repo, dest=None, **opts):
4552 """push changes to the specified destination
4553 """push changes to the specified destination
4553
4554
4554 Push changesets from the local repository to the specified
4555 Push changesets from the local repository to the specified
4555 destination.
4556 destination.
4556
4557
4557 This operation is symmetrical to pull: it is identical to a pull
4558 This operation is symmetrical to pull: it is identical to a pull
4558 in the destination repository from the current one.
4559 in the destination repository from the current one.
4559
4560
4560 By default, push will not allow creation of new heads at the
4561 By default, push will not allow creation of new heads at the
4561 destination, since multiple heads would make it unclear which head
4562 destination, since multiple heads would make it unclear which head
4562 to use. In this situation, it is recommended to pull and merge
4563 to use. In this situation, it is recommended to pull and merge
4563 before pushing.
4564 before pushing.
4564
4565
4565 Use --new-branch if you want to allow push to create a new named
4566 Use --new-branch if you want to allow push to create a new named
4566 branch that is not present at the destination. This allows you to
4567 branch that is not present at the destination. This allows you to
4567 only create a new branch without forcing other changes.
4568 only create a new branch without forcing other changes.
4568
4569
4569 .. note::
4570 .. note::
4570
4571
4571 Extra care should be taken with the -f/--force option,
4572 Extra care should be taken with the -f/--force option,
4572 which will push all new heads on all branches, an action which will
4573 which will push all new heads on all branches, an action which will
4573 almost always cause confusion for collaborators.
4574 almost always cause confusion for collaborators.
4574
4575
4575 If -r/--rev is used, the specified revision and all its ancestors
4576 If -r/--rev is used, the specified revision and all its ancestors
4576 will be pushed to the remote repository.
4577 will be pushed to the remote repository.
4577
4578
4578 If -B/--bookmark is used, the specified bookmarked revision, its
4579 If -B/--bookmark is used, the specified bookmarked revision, its
4579 ancestors, and the bookmark will be pushed to the remote
4580 ancestors, and the bookmark will be pushed to the remote
4580 repository. Specifying ``.`` is equivalent to specifying the active
4581 repository. Specifying ``.`` is equivalent to specifying the active
4581 bookmark's name.
4582 bookmark's name.
4582
4583
4583 Please see :hg:`help urls` for important details about ``ssh://``
4584 Please see :hg:`help urls` for important details about ``ssh://``
4584 URLs. If DESTINATION is omitted, a default path will be used.
4585 URLs. If DESTINATION is omitted, a default path will be used.
4585
4586
4586 .. container:: verbose
4587 .. container:: verbose
4587
4588
4588 The --pushvars option sends strings to the server that become
4589 The --pushvars option sends strings to the server that become
4589 environment variables prepended with ``HG_USERVAR_``. For example,
4590 environment variables prepended with ``HG_USERVAR_``. For example,
4590 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4591 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4591 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4592 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4592
4593
4593 pushvars can provide for user-overridable hooks as well as set debug
4594 pushvars can provide for user-overridable hooks as well as set debug
4594 levels. One example is having a hook that blocks commits containing
4595 levels. One example is having a hook that blocks commits containing
4595 conflict markers, but enables the user to override the hook if the file
4596 conflict markers, but enables the user to override the hook if the file
4596 is using conflict markers for testing purposes or the file format has
4597 is using conflict markers for testing purposes or the file format has
4597 strings that look like conflict markers.
4598 strings that look like conflict markers.
4598
4599
4599 By default, servers will ignore `--pushvars`. To enable it add the
4600 By default, servers will ignore `--pushvars`. To enable it add the
4600 following to your configuration file::
4601 following to your configuration file::
4601
4602
4602 [push]
4603 [push]
4603 pushvars.server = true
4604 pushvars.server = true
4604
4605
4605 Returns 0 if push was successful, 1 if nothing to push.
4606 Returns 0 if push was successful, 1 if nothing to push.
4606 """
4607 """
4607
4608
4608 opts = pycompat.byteskwargs(opts)
4609 opts = pycompat.byteskwargs(opts)
4609 if opts.get('bookmark'):
4610 if opts.get('bookmark'):
4610 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4611 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4611 for b in opts['bookmark']:
4612 for b in opts['bookmark']:
4612 # translate -B options to -r so changesets get pushed
4613 # translate -B options to -r so changesets get pushed
4613 b = repo._bookmarks.expandname(b)
4614 b = repo._bookmarks.expandname(b)
4614 if b in repo._bookmarks:
4615 if b in repo._bookmarks:
4615 opts.setdefault('rev', []).append(b)
4616 opts.setdefault('rev', []).append(b)
4616 else:
4617 else:
4617 # if we try to push a deleted bookmark, translate it to null
4618 # if we try to push a deleted bookmark, translate it to null
4618 # this lets simultaneous -r, -b options continue working
4619 # this lets simultaneous -r, -b options continue working
4619 opts.setdefault('rev', []).append("null")
4620 opts.setdefault('rev', []).append("null")
4620
4621
4621 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4622 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4622 if not path:
4623 if not path:
4623 raise error.Abort(_('default repository not configured!'),
4624 raise error.Abort(_('default repository not configured!'),
4624 hint=_("see 'hg help config.paths'"))
4625 hint=_("see 'hg help config.paths'"))
4625 dest = path.pushloc or path.loc
4626 dest = path.pushloc or path.loc
4626 branches = (path.branch, opts.get('branch') or [])
4627 branches = (path.branch, opts.get('branch') or [])
4627 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4628 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4628 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4629 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4629 other = hg.peer(repo, opts, dest)
4630 other = hg.peer(repo, opts, dest)
4630
4631
4631 if revs:
4632 if revs:
4632 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
4633 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
4633 if not revs:
4634 if not revs:
4634 raise error.Abort(_("specified revisions evaluate to an empty set"),
4635 raise error.Abort(_("specified revisions evaluate to an empty set"),
4635 hint=_("use different revision arguments"))
4636 hint=_("use different revision arguments"))
4636 elif path.pushrev:
4637 elif path.pushrev:
4637 # It doesn't make any sense to specify ancestor revisions. So limit
4638 # It doesn't make any sense to specify ancestor revisions. So limit
4638 # to DAG heads to make discovery simpler.
4639 # to DAG heads to make discovery simpler.
4639 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4640 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4640 revs = scmutil.revrange(repo, [expr])
4641 revs = scmutil.revrange(repo, [expr])
4641 revs = [repo[rev].node() for rev in revs]
4642 revs = [repo[rev].node() for rev in revs]
4642 if not revs:
4643 if not revs:
4643 raise error.Abort(_('default push revset for path evaluates to an '
4644 raise error.Abort(_('default push revset for path evaluates to an '
4644 'empty set'))
4645 'empty set'))
4645
4646
4646 repo._subtoppath = dest
4647 repo._subtoppath = dest
4647 try:
4648 try:
4648 # push subrepos depth-first for coherent ordering
4649 # push subrepos depth-first for coherent ordering
4649 c = repo['.']
4650 c = repo['.']
4650 subs = c.substate # only repos that are committed
4651 subs = c.substate # only repos that are committed
4651 for s in sorted(subs):
4652 for s in sorted(subs):
4652 result = c.sub(s).push(opts)
4653 result = c.sub(s).push(opts)
4653 if result == 0:
4654 if result == 0:
4654 return not result
4655 return not result
4655 finally:
4656 finally:
4656 del repo._subtoppath
4657 del repo._subtoppath
4657
4658
4658 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4659 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4659 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4660 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4660
4661
4661 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4662 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4662 newbranch=opts.get('new_branch'),
4663 newbranch=opts.get('new_branch'),
4663 bookmarks=opts.get('bookmark', ()),
4664 bookmarks=opts.get('bookmark', ()),
4664 publish=opts.get('publish'),
4665 publish=opts.get('publish'),
4665 opargs=opargs)
4666 opargs=opargs)
4666
4667
4667 result = not pushop.cgresult
4668 result = not pushop.cgresult
4668
4669
4669 if pushop.bkresult is not None:
4670 if pushop.bkresult is not None:
4670 if pushop.bkresult == 2:
4671 if pushop.bkresult == 2:
4671 result = 2
4672 result = 2
4672 elif not result and pushop.bkresult:
4673 elif not result and pushop.bkresult:
4673 result = 2
4674 result = 2
4674
4675
4675 return result
4676 return result
4676
4677
4677 @command('recover',
4678 @command('recover',
4678 [('','verify', True, "run `hg verify` after succesful recover"),
4679 [('','verify', True, "run `hg verify` after succesful recover"),
4679 ],
4680 ],
4680 helpcategory=command.CATEGORY_MAINTENANCE)
4681 helpcategory=command.CATEGORY_MAINTENANCE)
4681 def recover(ui, repo, **opts):
4682 def recover(ui, repo, **opts):
4682 """roll back an interrupted transaction
4683 """roll back an interrupted transaction
4683
4684
4684 Recover from an interrupted commit or pull.
4685 Recover from an interrupted commit or pull.
4685
4686
4686 This command tries to fix the repository status after an
4687 This command tries to fix the repository status after an
4687 interrupted operation. It should only be necessary when Mercurial
4688 interrupted operation. It should only be necessary when Mercurial
4688 suggests it.
4689 suggests it.
4689
4690
4690 Returns 0 if successful, 1 if nothing to recover or verify fails.
4691 Returns 0 if successful, 1 if nothing to recover or verify fails.
4691 """
4692 """
4692 ret = repo.recover()
4693 ret = repo.recover()
4693 if ret:
4694 if ret:
4694 if opts[r'verify']:
4695 if opts[r'verify']:
4695 return hg.verify(repo)
4696 return hg.verify(repo)
4696 else:
4697 else:
4697 msg = _("(verify step skipped, run `hg verify` to check your "
4698 msg = _("(verify step skipped, run `hg verify` to check your "
4698 "repository content)\n")
4699 "repository content)\n")
4699 ui.warn(msg)
4700 ui.warn(msg)
4700 return 0
4701 return 0
4701 return 1
4702 return 1
4702
4703
4703 @command('remove|rm',
4704 @command('remove|rm',
4704 [('A', 'after', None, _('record delete for missing files')),
4705 [('A', 'after', None, _('record delete for missing files')),
4705 ('f', 'force', None,
4706 ('f', 'force', None,
4706 _('forget added files, delete modified files')),
4707 _('forget added files, delete modified files')),
4707 ] + subrepoopts + walkopts + dryrunopts,
4708 ] + subrepoopts + walkopts + dryrunopts,
4708 _('[OPTION]... FILE...'),
4709 _('[OPTION]... FILE...'),
4709 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4710 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4710 helpbasic=True, inferrepo=True)
4711 helpbasic=True, inferrepo=True)
4711 def remove(ui, repo, *pats, **opts):
4712 def remove(ui, repo, *pats, **opts):
4712 """remove the specified files on the next commit
4713 """remove the specified files on the next commit
4713
4714
4714 Schedule the indicated files for removal from the current branch.
4715 Schedule the indicated files for removal from the current branch.
4715
4716
4716 This command schedules the files to be removed at the next commit.
4717 This command schedules the files to be removed at the next commit.
4717 To undo a remove before that, see :hg:`revert`. To undo added
4718 To undo a remove before that, see :hg:`revert`. To undo added
4718 files, see :hg:`forget`.
4719 files, see :hg:`forget`.
4719
4720
4720 .. container:: verbose
4721 .. container:: verbose
4721
4722
4722 -A/--after can be used to remove only files that have already
4723 -A/--after can be used to remove only files that have already
4723 been deleted, -f/--force can be used to force deletion, and -Af
4724 been deleted, -f/--force can be used to force deletion, and -Af
4724 can be used to remove files from the next revision without
4725 can be used to remove files from the next revision without
4725 deleting them from the working directory.
4726 deleting them from the working directory.
4726
4727
4727 The following table details the behavior of remove for different
4728 The following table details the behavior of remove for different
4728 file states (columns) and option combinations (rows). The file
4729 file states (columns) and option combinations (rows). The file
4729 states are Added [A], Clean [C], Modified [M] and Missing [!]
4730 states are Added [A], Clean [C], Modified [M] and Missing [!]
4730 (as reported by :hg:`status`). The actions are Warn, Remove
4731 (as reported by :hg:`status`). The actions are Warn, Remove
4731 (from branch) and Delete (from disk):
4732 (from branch) and Delete (from disk):
4732
4733
4733 ========= == == == ==
4734 ========= == == == ==
4734 opt/state A C M !
4735 opt/state A C M !
4735 ========= == == == ==
4736 ========= == == == ==
4736 none W RD W R
4737 none W RD W R
4737 -f R RD RD R
4738 -f R RD RD R
4738 -A W W W R
4739 -A W W W R
4739 -Af R R R R
4740 -Af R R R R
4740 ========= == == == ==
4741 ========= == == == ==
4741
4742
4742 .. note::
4743 .. note::
4743
4744
4744 :hg:`remove` never deletes files in Added [A] state from the
4745 :hg:`remove` never deletes files in Added [A] state from the
4745 working directory, not even if ``--force`` is specified.
4746 working directory, not even if ``--force`` is specified.
4746
4747
4747 Returns 0 on success, 1 if any warnings encountered.
4748 Returns 0 on success, 1 if any warnings encountered.
4748 """
4749 """
4749
4750
4750 opts = pycompat.byteskwargs(opts)
4751 opts = pycompat.byteskwargs(opts)
4751 after, force = opts.get('after'), opts.get('force')
4752 after, force = opts.get('after'), opts.get('force')
4752 dryrun = opts.get('dry_run')
4753 dryrun = opts.get('dry_run')
4753 if not pats and not after:
4754 if not pats and not after:
4754 raise error.Abort(_('no files specified'))
4755 raise error.Abort(_('no files specified'))
4755
4756
4756 m = scmutil.match(repo[None], pats, opts)
4757 m = scmutil.match(repo[None], pats, opts)
4757 subrepos = opts.get('subrepos')
4758 subrepos = opts.get('subrepos')
4758 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
4759 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
4759 return cmdutil.remove(ui, repo, m, "", uipathfn, after, force, subrepos,
4760 return cmdutil.remove(ui, repo, m, "", uipathfn, after, force, subrepos,
4760 dryrun=dryrun)
4761 dryrun=dryrun)
4761
4762
4762 @command('rename|move|mv',
4763 @command('rename|move|mv',
4763 [('A', 'after', None, _('record a rename that has already occurred')),
4764 [('A', 'after', None, _('record a rename that has already occurred')),
4764 ('f', 'force', None, _('forcibly move over an existing managed file')),
4765 ('f', 'force', None, _('forcibly move over an existing managed file')),
4765 ] + walkopts + dryrunopts,
4766 ] + walkopts + dryrunopts,
4766 _('[OPTION]... SOURCE... DEST'),
4767 _('[OPTION]... SOURCE... DEST'),
4767 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
4768 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
4768 def rename(ui, repo, *pats, **opts):
4769 def rename(ui, repo, *pats, **opts):
4769 """rename files; equivalent of copy + remove
4770 """rename files; equivalent of copy + remove
4770
4771
4771 Mark dest as copies of sources; mark sources for deletion. If dest
4772 Mark dest as copies of sources; mark sources for deletion. If dest
4772 is a directory, copies are put in that directory. If dest is a
4773 is a directory, copies are put in that directory. If dest is a
4773 file, there can only be one source.
4774 file, there can only be one source.
4774
4775
4775 By default, this command copies the contents of files as they
4776 By default, this command copies the contents of files as they
4776 exist in the working directory. If invoked with -A/--after, the
4777 exist in the working directory. If invoked with -A/--after, the
4777 operation is recorded, but no copying is performed.
4778 operation is recorded, but no copying is performed.
4778
4779
4779 This command takes effect at the next commit. To undo a rename
4780 This command takes effect at the next commit. To undo a rename
4780 before that, see :hg:`revert`.
4781 before that, see :hg:`revert`.
4781
4782
4782 Returns 0 on success, 1 if errors are encountered.
4783 Returns 0 on success, 1 if errors are encountered.
4783 """
4784 """
4784 opts = pycompat.byteskwargs(opts)
4785 opts = pycompat.byteskwargs(opts)
4785 with repo.wlock(False):
4786 with repo.wlock(False):
4786 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4787 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4787
4788
4788 @command('resolve',
4789 @command('resolve',
4789 [('a', 'all', None, _('select all unresolved files')),
4790 [('a', 'all', None, _('select all unresolved files')),
4790 ('l', 'list', None, _('list state of files needing merge')),
4791 ('l', 'list', None, _('list state of files needing merge')),
4791 ('m', 'mark', None, _('mark files as resolved')),
4792 ('m', 'mark', None, _('mark files as resolved')),
4792 ('u', 'unmark', None, _('mark files as unresolved')),
4793 ('u', 'unmark', None, _('mark files as unresolved')),
4793 ('n', 'no-status', None, _('hide status prefix')),
4794 ('n', 'no-status', None, _('hide status prefix')),
4794 ('', 're-merge', None, _('re-merge files'))]
4795 ('', 're-merge', None, _('re-merge files'))]
4795 + mergetoolopts + walkopts + formatteropts,
4796 + mergetoolopts + walkopts + formatteropts,
4796 _('[OPTION]... [FILE]...'),
4797 _('[OPTION]... [FILE]...'),
4797 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4798 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4798 inferrepo=True)
4799 inferrepo=True)
4799 def resolve(ui, repo, *pats, **opts):
4800 def resolve(ui, repo, *pats, **opts):
4800 """redo merges or set/view the merge status of files
4801 """redo merges or set/view the merge status of files
4801
4802
4802 Merges with unresolved conflicts are often the result of
4803 Merges with unresolved conflicts are often the result of
4803 non-interactive merging using the ``internal:merge`` configuration
4804 non-interactive merging using the ``internal:merge`` configuration
4804 setting, or a command-line merge tool like ``diff3``. The resolve
4805 setting, or a command-line merge tool like ``diff3``. The resolve
4805 command is used to manage the files involved in a merge, after
4806 command is used to manage the files involved in a merge, after
4806 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4807 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4807 working directory must have two parents). See :hg:`help
4808 working directory must have two parents). See :hg:`help
4808 merge-tools` for information on configuring merge tools.
4809 merge-tools` for information on configuring merge tools.
4809
4810
4810 The resolve command can be used in the following ways:
4811 The resolve command can be used in the following ways:
4811
4812
4812 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
4813 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
4813 the specified files, discarding any previous merge attempts. Re-merging
4814 the specified files, discarding any previous merge attempts. Re-merging
4814 is not performed for files already marked as resolved. Use ``--all/-a``
4815 is not performed for files already marked as resolved. Use ``--all/-a``
4815 to select all unresolved files. ``--tool`` can be used to specify
4816 to select all unresolved files. ``--tool`` can be used to specify
4816 the merge tool used for the given files. It overrides the HGMERGE
4817 the merge tool used for the given files. It overrides the HGMERGE
4817 environment variable and your configuration files. Previous file
4818 environment variable and your configuration files. Previous file
4818 contents are saved with a ``.orig`` suffix.
4819 contents are saved with a ``.orig`` suffix.
4819
4820
4820 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4821 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4821 (e.g. after having manually fixed-up the files). The default is
4822 (e.g. after having manually fixed-up the files). The default is
4822 to mark all unresolved files.
4823 to mark all unresolved files.
4823
4824
4824 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4825 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4825 default is to mark all resolved files.
4826 default is to mark all resolved files.
4826
4827
4827 - :hg:`resolve -l`: list files which had or still have conflicts.
4828 - :hg:`resolve -l`: list files which had or still have conflicts.
4828 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4829 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4829 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4830 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4830 the list. See :hg:`help filesets` for details.
4831 the list. See :hg:`help filesets` for details.
4831
4832
4832 .. note::
4833 .. note::
4833
4834
4834 Mercurial will not let you commit files with unresolved merge
4835 Mercurial will not let you commit files with unresolved merge
4835 conflicts. You must use :hg:`resolve -m ...` before you can
4836 conflicts. You must use :hg:`resolve -m ...` before you can
4836 commit after a conflicting merge.
4837 commit after a conflicting merge.
4837
4838
4838 .. container:: verbose
4839 .. container:: verbose
4839
4840
4840 Template:
4841 Template:
4841
4842
4842 The following keywords are supported in addition to the common template
4843 The following keywords are supported in addition to the common template
4843 keywords and functions. See also :hg:`help templates`.
4844 keywords and functions. See also :hg:`help templates`.
4844
4845
4845 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
4846 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
4846 :path: String. Repository-absolute path of the file.
4847 :path: String. Repository-absolute path of the file.
4847
4848
4848 Returns 0 on success, 1 if any files fail a resolve attempt.
4849 Returns 0 on success, 1 if any files fail a resolve attempt.
4849 """
4850 """
4850
4851
4851 opts = pycompat.byteskwargs(opts)
4852 opts = pycompat.byteskwargs(opts)
4852 confirm = ui.configbool('commands', 'resolve.confirm')
4853 confirm = ui.configbool('commands', 'resolve.confirm')
4853 flaglist = 'all mark unmark list no_status re_merge'.split()
4854 flaglist = 'all mark unmark list no_status re_merge'.split()
4854 all, mark, unmark, show, nostatus, remerge = [
4855 all, mark, unmark, show, nostatus, remerge = [
4855 opts.get(o) for o in flaglist]
4856 opts.get(o) for o in flaglist]
4856
4857
4857 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
4858 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
4858 if actioncount > 1:
4859 if actioncount > 1:
4859 raise error.Abort(_("too many actions specified"))
4860 raise error.Abort(_("too many actions specified"))
4860 elif (actioncount == 0
4861 elif (actioncount == 0
4861 and ui.configbool('commands', 'resolve.explicit-re-merge')):
4862 and ui.configbool('commands', 'resolve.explicit-re-merge')):
4862 hint = _('use --mark, --unmark, --list or --re-merge')
4863 hint = _('use --mark, --unmark, --list or --re-merge')
4863 raise error.Abort(_('no action specified'), hint=hint)
4864 raise error.Abort(_('no action specified'), hint=hint)
4864 if pats and all:
4865 if pats and all:
4865 raise error.Abort(_("can't specify --all and patterns"))
4866 raise error.Abort(_("can't specify --all and patterns"))
4866 if not (all or pats or show or mark or unmark):
4867 if not (all or pats or show or mark or unmark):
4867 raise error.Abort(_('no files or directories specified'),
4868 raise error.Abort(_('no files or directories specified'),
4868 hint=('use --all to re-merge all unresolved files'))
4869 hint=('use --all to re-merge all unresolved files'))
4869
4870
4870 if confirm:
4871 if confirm:
4871 if all:
4872 if all:
4872 if ui.promptchoice(_(b're-merge all unresolved files (yn)?'
4873 if ui.promptchoice(_(b're-merge all unresolved files (yn)?'
4873 b'$$ &Yes $$ &No')):
4874 b'$$ &Yes $$ &No')):
4874 raise error.Abort(_('user quit'))
4875 raise error.Abort(_('user quit'))
4875 if mark and not pats:
4876 if mark and not pats:
4876 if ui.promptchoice(_(b'mark all unresolved files as resolved (yn)?'
4877 if ui.promptchoice(_(b'mark all unresolved files as resolved (yn)?'
4877 b'$$ &Yes $$ &No')):
4878 b'$$ &Yes $$ &No')):
4878 raise error.Abort(_('user quit'))
4879 raise error.Abort(_('user quit'))
4879 if unmark and not pats:
4880 if unmark and not pats:
4880 if ui.promptchoice(_(b'mark all resolved files as unresolved (yn)?'
4881 if ui.promptchoice(_(b'mark all resolved files as unresolved (yn)?'
4881 b'$$ &Yes $$ &No')):
4882 b'$$ &Yes $$ &No')):
4882 raise error.Abort(_('user quit'))
4883 raise error.Abort(_('user quit'))
4883
4884
4884 uipathfn = scmutil.getuipathfn(repo)
4885 uipathfn = scmutil.getuipathfn(repo)
4885
4886
4886 if show:
4887 if show:
4887 ui.pager('resolve')
4888 ui.pager('resolve')
4888 fm = ui.formatter('resolve', opts)
4889 fm = ui.formatter('resolve', opts)
4889 ms = mergemod.mergestate.read(repo)
4890 ms = mergemod.mergestate.read(repo)
4890 wctx = repo[None]
4891 wctx = repo[None]
4891 m = scmutil.match(wctx, pats, opts)
4892 m = scmutil.match(wctx, pats, opts)
4892
4893
4893 # Labels and keys based on merge state. Unresolved path conflicts show
4894 # Labels and keys based on merge state. Unresolved path conflicts show
4894 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4895 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4895 # resolved conflicts.
4896 # resolved conflicts.
4896 mergestateinfo = {
4897 mergestateinfo = {
4897 mergemod.MERGE_RECORD_UNRESOLVED: ('resolve.unresolved', 'U'),
4898 mergemod.MERGE_RECORD_UNRESOLVED: ('resolve.unresolved', 'U'),
4898 mergemod.MERGE_RECORD_RESOLVED: ('resolve.resolved', 'R'),
4899 mergemod.MERGE_RECORD_RESOLVED: ('resolve.resolved', 'R'),
4899 mergemod.MERGE_RECORD_UNRESOLVED_PATH: ('resolve.unresolved', 'P'),
4900 mergemod.MERGE_RECORD_UNRESOLVED_PATH: ('resolve.unresolved', 'P'),
4900 mergemod.MERGE_RECORD_RESOLVED_PATH: ('resolve.resolved', 'R'),
4901 mergemod.MERGE_RECORD_RESOLVED_PATH: ('resolve.resolved', 'R'),
4901 mergemod.MERGE_RECORD_DRIVER_RESOLVED: ('resolve.driverresolved',
4902 mergemod.MERGE_RECORD_DRIVER_RESOLVED: ('resolve.driverresolved',
4902 'D'),
4903 'D'),
4903 }
4904 }
4904
4905
4905 for f in ms:
4906 for f in ms:
4906 if not m(f):
4907 if not m(f):
4907 continue
4908 continue
4908
4909
4909 label, key = mergestateinfo[ms[f]]
4910 label, key = mergestateinfo[ms[f]]
4910 fm.startitem()
4911 fm.startitem()
4911 fm.context(ctx=wctx)
4912 fm.context(ctx=wctx)
4912 fm.condwrite(not nostatus, 'mergestatus', '%s ', key, label=label)
4913 fm.condwrite(not nostatus, 'mergestatus', '%s ', key, label=label)
4913 fm.data(path=f)
4914 fm.data(path=f)
4914 fm.plain('%s\n' % uipathfn(f), label=label)
4915 fm.plain('%s\n' % uipathfn(f), label=label)
4915 fm.end()
4916 fm.end()
4916 return 0
4917 return 0
4917
4918
4918 with repo.wlock():
4919 with repo.wlock():
4919 ms = mergemod.mergestate.read(repo)
4920 ms = mergemod.mergestate.read(repo)
4920
4921
4921 if not (ms.active() or repo.dirstate.p2() != nullid):
4922 if not (ms.active() or repo.dirstate.p2() != nullid):
4922 raise error.Abort(
4923 raise error.Abort(
4923 _('resolve command not applicable when not merging'))
4924 _('resolve command not applicable when not merging'))
4924
4925
4925 wctx = repo[None]
4926 wctx = repo[None]
4926
4927
4927 if (ms.mergedriver
4928 if (ms.mergedriver
4928 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED):
4929 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED):
4929 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4930 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4930 ms.commit()
4931 ms.commit()
4931 # allow mark and unmark to go through
4932 # allow mark and unmark to go through
4932 if not mark and not unmark and not proceed:
4933 if not mark and not unmark and not proceed:
4933 return 1
4934 return 1
4934
4935
4935 m = scmutil.match(wctx, pats, opts)
4936 m = scmutil.match(wctx, pats, opts)
4936 ret = 0
4937 ret = 0
4937 didwork = False
4938 didwork = False
4938 runconclude = False
4939 runconclude = False
4939
4940
4940 tocomplete = []
4941 tocomplete = []
4941 hasconflictmarkers = []
4942 hasconflictmarkers = []
4942 if mark:
4943 if mark:
4943 markcheck = ui.config('commands', 'resolve.mark-check')
4944 markcheck = ui.config('commands', 'resolve.mark-check')
4944 if markcheck not in ['warn', 'abort']:
4945 if markcheck not in ['warn', 'abort']:
4945 # Treat all invalid / unrecognized values as 'none'.
4946 # Treat all invalid / unrecognized values as 'none'.
4946 markcheck = False
4947 markcheck = False
4947 for f in ms:
4948 for f in ms:
4948 if not m(f):
4949 if not m(f):
4949 continue
4950 continue
4950
4951
4951 didwork = True
4952 didwork = True
4952
4953
4953 # don't let driver-resolved files be marked, and run the conclude
4954 # don't let driver-resolved files be marked, and run the conclude
4954 # step if asked to resolve
4955 # step if asked to resolve
4955 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
4956 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
4956 exact = m.exact(f)
4957 exact = m.exact(f)
4957 if mark:
4958 if mark:
4958 if exact:
4959 if exact:
4959 ui.warn(_('not marking %s as it is driver-resolved\n')
4960 ui.warn(_('not marking %s as it is driver-resolved\n')
4960 % uipathfn(f))
4961 % uipathfn(f))
4961 elif unmark:
4962 elif unmark:
4962 if exact:
4963 if exact:
4963 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4964 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4964 % uipathfn(f))
4965 % uipathfn(f))
4965 else:
4966 else:
4966 runconclude = True
4967 runconclude = True
4967 continue
4968 continue
4968
4969
4969 # path conflicts must be resolved manually
4970 # path conflicts must be resolved manually
4970 if ms[f] in (mergemod.MERGE_RECORD_UNRESOLVED_PATH,
4971 if ms[f] in (mergemod.MERGE_RECORD_UNRESOLVED_PATH,
4971 mergemod.MERGE_RECORD_RESOLVED_PATH):
4972 mergemod.MERGE_RECORD_RESOLVED_PATH):
4972 if mark:
4973 if mark:
4973 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
4974 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
4974 elif unmark:
4975 elif unmark:
4975 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
4976 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
4976 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
4977 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
4977 ui.warn(_('%s: path conflict must be resolved manually\n')
4978 ui.warn(_('%s: path conflict must be resolved manually\n')
4978 % uipathfn(f))
4979 % uipathfn(f))
4979 continue
4980 continue
4980
4981
4981 if mark:
4982 if mark:
4982 if markcheck:
4983 if markcheck:
4983 fdata = repo.wvfs.tryread(f)
4984 fdata = repo.wvfs.tryread(f)
4984 if (filemerge.hasconflictmarkers(fdata) and
4985 if (filemerge.hasconflictmarkers(fdata) and
4985 ms[f] != mergemod.MERGE_RECORD_RESOLVED):
4986 ms[f] != mergemod.MERGE_RECORD_RESOLVED):
4986 hasconflictmarkers.append(f)
4987 hasconflictmarkers.append(f)
4987 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
4988 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
4988 elif unmark:
4989 elif unmark:
4989 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
4990 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
4990 else:
4991 else:
4991 # backup pre-resolve (merge uses .orig for its own purposes)
4992 # backup pre-resolve (merge uses .orig for its own purposes)
4992 a = repo.wjoin(f)
4993 a = repo.wjoin(f)
4993 try:
4994 try:
4994 util.copyfile(a, a + ".resolve")
4995 util.copyfile(a, a + ".resolve")
4995 except (IOError, OSError) as inst:
4996 except (IOError, OSError) as inst:
4996 if inst.errno != errno.ENOENT:
4997 if inst.errno != errno.ENOENT:
4997 raise
4998 raise
4998
4999
4999 try:
5000 try:
5000 # preresolve file
5001 # preresolve file
5001 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
5002 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
5002 with ui.configoverride(overrides, 'resolve'):
5003 with ui.configoverride(overrides, 'resolve'):
5003 complete, r = ms.preresolve(f, wctx)
5004 complete, r = ms.preresolve(f, wctx)
5004 if not complete:
5005 if not complete:
5005 tocomplete.append(f)
5006 tocomplete.append(f)
5006 elif r:
5007 elif r:
5007 ret = 1
5008 ret = 1
5008 finally:
5009 finally:
5009 ms.commit()
5010 ms.commit()
5010
5011
5011 # replace filemerge's .orig file with our resolve file, but only
5012 # replace filemerge's .orig file with our resolve file, but only
5012 # for merges that are complete
5013 # for merges that are complete
5013 if complete:
5014 if complete:
5014 try:
5015 try:
5015 util.rename(a + ".resolve",
5016 util.rename(a + ".resolve",
5016 scmutil.backuppath(ui, repo, f))
5017 scmutil.backuppath(ui, repo, f))
5017 except OSError as inst:
5018 except OSError as inst:
5018 if inst.errno != errno.ENOENT:
5019 if inst.errno != errno.ENOENT:
5019 raise
5020 raise
5020
5021
5021 if hasconflictmarkers:
5022 if hasconflictmarkers:
5022 ui.warn(_('warning: the following files still have conflict '
5023 ui.warn(_('warning: the following files still have conflict '
5023 'markers:\n') + ''.join(' ' + uipathfn(f) + '\n'
5024 'markers:\n') + ''.join(' ' + uipathfn(f) + '\n'
5024 for f in hasconflictmarkers))
5025 for f in hasconflictmarkers))
5025 if markcheck == 'abort' and not all and not pats:
5026 if markcheck == 'abort' and not all and not pats:
5026 raise error.Abort(_('conflict markers detected'),
5027 raise error.Abort(_('conflict markers detected'),
5027 hint=_('use --all to mark anyway'))
5028 hint=_('use --all to mark anyway'))
5028
5029
5029 for f in tocomplete:
5030 for f in tocomplete:
5030 try:
5031 try:
5031 # resolve file
5032 # resolve file
5032 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
5033 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
5033 with ui.configoverride(overrides, 'resolve'):
5034 with ui.configoverride(overrides, 'resolve'):
5034 r = ms.resolve(f, wctx)
5035 r = ms.resolve(f, wctx)
5035 if r:
5036 if r:
5036 ret = 1
5037 ret = 1
5037 finally:
5038 finally:
5038 ms.commit()
5039 ms.commit()
5039
5040
5040 # replace filemerge's .orig file with our resolve file
5041 # replace filemerge's .orig file with our resolve file
5041 a = repo.wjoin(f)
5042 a = repo.wjoin(f)
5042 try:
5043 try:
5043 util.rename(a + ".resolve", scmutil.backuppath(ui, repo, f))
5044 util.rename(a + ".resolve", scmutil.backuppath(ui, repo, f))
5044 except OSError as inst:
5045 except OSError as inst:
5045 if inst.errno != errno.ENOENT:
5046 if inst.errno != errno.ENOENT:
5046 raise
5047 raise
5047
5048
5048 ms.commit()
5049 ms.commit()
5049 ms.recordactions()
5050 ms.recordactions()
5050
5051
5051 if not didwork and pats:
5052 if not didwork and pats:
5052 hint = None
5053 hint = None
5053 if not any([p for p in pats if p.find(':') >= 0]):
5054 if not any([p for p in pats if p.find(':') >= 0]):
5054 pats = ['path:%s' % p for p in pats]
5055 pats = ['path:%s' % p for p in pats]
5055 m = scmutil.match(wctx, pats, opts)
5056 m = scmutil.match(wctx, pats, opts)
5056 for f in ms:
5057 for f in ms:
5057 if not m(f):
5058 if not m(f):
5058 continue
5059 continue
5059 def flag(o):
5060 def flag(o):
5060 if o == 're_merge':
5061 if o == 're_merge':
5061 return '--re-merge '
5062 return '--re-merge '
5062 return '-%s ' % o[0:1]
5063 return '-%s ' % o[0:1]
5063 flags = ''.join([flag(o) for o in flaglist if opts.get(o)])
5064 flags = ''.join([flag(o) for o in flaglist if opts.get(o)])
5064 hint = _("(try: hg resolve %s%s)\n") % (
5065 hint = _("(try: hg resolve %s%s)\n") % (
5065 flags,
5066 flags,
5066 ' '.join(pats))
5067 ' '.join(pats))
5067 break
5068 break
5068 ui.warn(_("arguments do not match paths that need resolving\n"))
5069 ui.warn(_("arguments do not match paths that need resolving\n"))
5069 if hint:
5070 if hint:
5070 ui.warn(hint)
5071 ui.warn(hint)
5071 elif ms.mergedriver and ms.mdstate() != 's':
5072 elif ms.mergedriver and ms.mdstate() != 's':
5072 # run conclude step when either a driver-resolved file is requested
5073 # run conclude step when either a driver-resolved file is requested
5073 # or there are no driver-resolved files
5074 # or there are no driver-resolved files
5074 # we can't use 'ret' to determine whether any files are unresolved
5075 # we can't use 'ret' to determine whether any files are unresolved
5075 # because we might not have tried to resolve some
5076 # because we might not have tried to resolve some
5076 if ((runconclude or not list(ms.driverresolved()))
5077 if ((runconclude or not list(ms.driverresolved()))
5077 and not list(ms.unresolved())):
5078 and not list(ms.unresolved())):
5078 proceed = mergemod.driverconclude(repo, ms, wctx)
5079 proceed = mergemod.driverconclude(repo, ms, wctx)
5079 ms.commit()
5080 ms.commit()
5080 if not proceed:
5081 if not proceed:
5081 return 1
5082 return 1
5082
5083
5083 # Nudge users into finishing an unfinished operation
5084 # Nudge users into finishing an unfinished operation
5084 unresolvedf = list(ms.unresolved())
5085 unresolvedf = list(ms.unresolved())
5085 driverresolvedf = list(ms.driverresolved())
5086 driverresolvedf = list(ms.driverresolved())
5086 if not unresolvedf and not driverresolvedf:
5087 if not unresolvedf and not driverresolvedf:
5087 ui.status(_('(no more unresolved files)\n'))
5088 ui.status(_('(no more unresolved files)\n'))
5088 cmdutil.checkafterresolved(repo)
5089 cmdutil.checkafterresolved(repo)
5089 elif not unresolvedf:
5090 elif not unresolvedf:
5090 ui.status(_('(no more unresolved files -- '
5091 ui.status(_('(no more unresolved files -- '
5091 'run "hg resolve --all" to conclude)\n'))
5092 'run "hg resolve --all" to conclude)\n'))
5092
5093
5093 return ret
5094 return ret
5094
5095
5095 @command('revert',
5096 @command('revert',
5096 [('a', 'all', None, _('revert all changes when no arguments given')),
5097 [('a', 'all', None, _('revert all changes when no arguments given')),
5097 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5098 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5098 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5099 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5099 ('C', 'no-backup', None, _('do not save backup copies of files')),
5100 ('C', 'no-backup', None, _('do not save backup copies of files')),
5100 ('i', 'interactive', None, _('interactively select the changes')),
5101 ('i', 'interactive', None, _('interactively select the changes')),
5101 ] + walkopts + dryrunopts,
5102 ] + walkopts + dryrunopts,
5102 _('[OPTION]... [-r REV] [NAME]...'),
5103 _('[OPTION]... [-r REV] [NAME]...'),
5103 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5104 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5104 def revert(ui, repo, *pats, **opts):
5105 def revert(ui, repo, *pats, **opts):
5105 """restore files to their checkout state
5106 """restore files to their checkout state
5106
5107
5107 .. note::
5108 .. note::
5108
5109
5109 To check out earlier revisions, you should use :hg:`update REV`.
5110 To check out earlier revisions, you should use :hg:`update REV`.
5110 To cancel an uncommitted merge (and lose your changes),
5111 To cancel an uncommitted merge (and lose your changes),
5111 use :hg:`merge --abort`.
5112 use :hg:`merge --abort`.
5112
5113
5113 With no revision specified, revert the specified files or directories
5114 With no revision specified, revert the specified files or directories
5114 to the contents they had in the parent of the working directory.
5115 to the contents they had in the parent of the working directory.
5115 This restores the contents of files to an unmodified
5116 This restores the contents of files to an unmodified
5116 state and unschedules adds, removes, copies, and renames. If the
5117 state and unschedules adds, removes, copies, and renames. If the
5117 working directory has two parents, you must explicitly specify a
5118 working directory has two parents, you must explicitly specify a
5118 revision.
5119 revision.
5119
5120
5120 Using the -r/--rev or -d/--date options, revert the given files or
5121 Using the -r/--rev or -d/--date options, revert the given files or
5121 directories to their states as of a specific revision. Because
5122 directories to their states as of a specific revision. Because
5122 revert does not change the working directory parents, this will
5123 revert does not change the working directory parents, this will
5123 cause these files to appear modified. This can be helpful to "back
5124 cause these files to appear modified. This can be helpful to "back
5124 out" some or all of an earlier change. See :hg:`backout` for a
5125 out" some or all of an earlier change. See :hg:`backout` for a
5125 related method.
5126 related method.
5126
5127
5127 Modified files are saved with a .orig suffix before reverting.
5128 Modified files are saved with a .orig suffix before reverting.
5128 To disable these backups, use --no-backup. It is possible to store
5129 To disable these backups, use --no-backup. It is possible to store
5129 the backup files in a custom directory relative to the root of the
5130 the backup files in a custom directory relative to the root of the
5130 repository by setting the ``ui.origbackuppath`` configuration
5131 repository by setting the ``ui.origbackuppath`` configuration
5131 option.
5132 option.
5132
5133
5133 See :hg:`help dates` for a list of formats valid for -d/--date.
5134 See :hg:`help dates` for a list of formats valid for -d/--date.
5134
5135
5135 See :hg:`help backout` for a way to reverse the effect of an
5136 See :hg:`help backout` for a way to reverse the effect of an
5136 earlier changeset.
5137 earlier changeset.
5137
5138
5138 Returns 0 on success.
5139 Returns 0 on success.
5139 """
5140 """
5140
5141
5141 opts = pycompat.byteskwargs(opts)
5142 opts = pycompat.byteskwargs(opts)
5142 if opts.get("date"):
5143 if opts.get("date"):
5143 if opts.get("rev"):
5144 if opts.get("rev"):
5144 raise error.Abort(_("you can't specify a revision and a date"))
5145 raise error.Abort(_("you can't specify a revision and a date"))
5145 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5146 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5146
5147
5147 parent, p2 = repo.dirstate.parents()
5148 parent, p2 = repo.dirstate.parents()
5148 if not opts.get('rev') and p2 != nullid:
5149 if not opts.get('rev') and p2 != nullid:
5149 # revert after merge is a trap for new users (issue2915)
5150 # revert after merge is a trap for new users (issue2915)
5150 raise error.Abort(_('uncommitted merge with no revision specified'),
5151 raise error.Abort(_('uncommitted merge with no revision specified'),
5151 hint=_("use 'hg update' or see 'hg help revert'"))
5152 hint=_("use 'hg update' or see 'hg help revert'"))
5152
5153
5153 rev = opts.get('rev')
5154 rev = opts.get('rev')
5154 if rev:
5155 if rev:
5155 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
5156 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
5156 ctx = scmutil.revsingle(repo, rev)
5157 ctx = scmutil.revsingle(repo, rev)
5157
5158
5158 if (not (pats or opts.get('include') or opts.get('exclude') or
5159 if (not (pats or opts.get('include') or opts.get('exclude') or
5159 opts.get('all') or opts.get('interactive'))):
5160 opts.get('all') or opts.get('interactive'))):
5160 msg = _("no files or directories specified")
5161 msg = _("no files or directories specified")
5161 if p2 != nullid:
5162 if p2 != nullid:
5162 hint = _("uncommitted merge, use --all to discard all changes,"
5163 hint = _("uncommitted merge, use --all to discard all changes,"
5163 " or 'hg update -C .' to abort the merge")
5164 " or 'hg update -C .' to abort the merge")
5164 raise error.Abort(msg, hint=hint)
5165 raise error.Abort(msg, hint=hint)
5165 dirty = any(repo.status())
5166 dirty = any(repo.status())
5166 node = ctx.node()
5167 node = ctx.node()
5167 if node != parent:
5168 if node != parent:
5168 if dirty:
5169 if dirty:
5169 hint = _("uncommitted changes, use --all to discard all"
5170 hint = _("uncommitted changes, use --all to discard all"
5170 " changes, or 'hg update %d' to update") % ctx.rev()
5171 " changes, or 'hg update %d' to update") % ctx.rev()
5171 else:
5172 else:
5172 hint = _("use --all to revert all files,"
5173 hint = _("use --all to revert all files,"
5173 " or 'hg update %d' to update") % ctx.rev()
5174 " or 'hg update %d' to update") % ctx.rev()
5174 elif dirty:
5175 elif dirty:
5175 hint = _("uncommitted changes, use --all to discard all changes")
5176 hint = _("uncommitted changes, use --all to discard all changes")
5176 else:
5177 else:
5177 hint = _("use --all to revert all files")
5178 hint = _("use --all to revert all files")
5178 raise error.Abort(msg, hint=hint)
5179 raise error.Abort(msg, hint=hint)
5179
5180
5180 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats,
5181 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats,
5181 **pycompat.strkwargs(opts))
5182 **pycompat.strkwargs(opts))
5182
5183
5183 @command(
5184 @command(
5184 'rollback',
5185 'rollback',
5185 dryrunopts + [('f', 'force', False, _('ignore safety measures'))],
5186 dryrunopts + [('f', 'force', False, _('ignore safety measures'))],
5186 helpcategory=command.CATEGORY_MAINTENANCE)
5187 helpcategory=command.CATEGORY_MAINTENANCE)
5187 def rollback(ui, repo, **opts):
5188 def rollback(ui, repo, **opts):
5188 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5189 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5189
5190
5190 Please use :hg:`commit --amend` instead of rollback to correct
5191 Please use :hg:`commit --amend` instead of rollback to correct
5191 mistakes in the last commit.
5192 mistakes in the last commit.
5192
5193
5193 This command should be used with care. There is only one level of
5194 This command should be used with care. There is only one level of
5194 rollback, and there is no way to undo a rollback. It will also
5195 rollback, and there is no way to undo a rollback. It will also
5195 restore the dirstate at the time of the last transaction, losing
5196 restore the dirstate at the time of the last transaction, losing
5196 any dirstate changes since that time. This command does not alter
5197 any dirstate changes since that time. This command does not alter
5197 the working directory.
5198 the working directory.
5198
5199
5199 Transactions are used to encapsulate the effects of all commands
5200 Transactions are used to encapsulate the effects of all commands
5200 that create new changesets or propagate existing changesets into a
5201 that create new changesets or propagate existing changesets into a
5201 repository.
5202 repository.
5202
5203
5203 .. container:: verbose
5204 .. container:: verbose
5204
5205
5205 For example, the following commands are transactional, and their
5206 For example, the following commands are transactional, and their
5206 effects can be rolled back:
5207 effects can be rolled back:
5207
5208
5208 - commit
5209 - commit
5209 - import
5210 - import
5210 - pull
5211 - pull
5211 - push (with this repository as the destination)
5212 - push (with this repository as the destination)
5212 - unbundle
5213 - unbundle
5213
5214
5214 To avoid permanent data loss, rollback will refuse to rollback a
5215 To avoid permanent data loss, rollback will refuse to rollback a
5215 commit transaction if it isn't checked out. Use --force to
5216 commit transaction if it isn't checked out. Use --force to
5216 override this protection.
5217 override this protection.
5217
5218
5218 The rollback command can be entirely disabled by setting the
5219 The rollback command can be entirely disabled by setting the
5219 ``ui.rollback`` configuration setting to false. If you're here
5220 ``ui.rollback`` configuration setting to false. If you're here
5220 because you want to use rollback and it's disabled, you can
5221 because you want to use rollback and it's disabled, you can
5221 re-enable the command by setting ``ui.rollback`` to true.
5222 re-enable the command by setting ``ui.rollback`` to true.
5222
5223
5223 This command is not intended for use on public repositories. Once
5224 This command is not intended for use on public repositories. Once
5224 changes are visible for pull by other users, rolling a transaction
5225 changes are visible for pull by other users, rolling a transaction
5225 back locally is ineffective (someone else may already have pulled
5226 back locally is ineffective (someone else may already have pulled
5226 the changes). Furthermore, a race is possible with readers of the
5227 the changes). Furthermore, a race is possible with readers of the
5227 repository; for example an in-progress pull from the repository
5228 repository; for example an in-progress pull from the repository
5228 may fail if a rollback is performed.
5229 may fail if a rollback is performed.
5229
5230
5230 Returns 0 on success, 1 if no rollback data is available.
5231 Returns 0 on success, 1 if no rollback data is available.
5231 """
5232 """
5232 if not ui.configbool('ui', 'rollback'):
5233 if not ui.configbool('ui', 'rollback'):
5233 raise error.Abort(_('rollback is disabled because it is unsafe'),
5234 raise error.Abort(_('rollback is disabled because it is unsafe'),
5234 hint=('see `hg help -v rollback` for information'))
5235 hint=('see `hg help -v rollback` for information'))
5235 return repo.rollback(dryrun=opts.get(r'dry_run'),
5236 return repo.rollback(dryrun=opts.get(r'dry_run'),
5236 force=opts.get(r'force'))
5237 force=opts.get(r'force'))
5237
5238
5238 @command(
5239 @command(
5239 'root', [] + formatteropts, intents={INTENT_READONLY},
5240 'root', [] + formatteropts, intents={INTENT_READONLY},
5240 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5241 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5241 def root(ui, repo, **opts):
5242 def root(ui, repo, **opts):
5242 """print the root (top) of the current working directory
5243 """print the root (top) of the current working directory
5243
5244
5244 Print the root directory of the current repository.
5245 Print the root directory of the current repository.
5245
5246
5246 .. container:: verbose
5247 .. container:: verbose
5247
5248
5248 Template:
5249 Template:
5249
5250
5250 The following keywords are supported in addition to the common template
5251 The following keywords are supported in addition to the common template
5251 keywords and functions. See also :hg:`help templates`.
5252 keywords and functions. See also :hg:`help templates`.
5252
5253
5253 :hgpath: String. Path to the .hg directory.
5254 :hgpath: String. Path to the .hg directory.
5254 :storepath: String. Path to the directory holding versioned data.
5255 :storepath: String. Path to the directory holding versioned data.
5255
5256
5256 Returns 0 on success.
5257 Returns 0 on success.
5257 """
5258 """
5258 opts = pycompat.byteskwargs(opts)
5259 opts = pycompat.byteskwargs(opts)
5259 with ui.formatter('root', opts) as fm:
5260 with ui.formatter('root', opts) as fm:
5260 fm.startitem()
5261 fm.startitem()
5261 fm.write('reporoot', '%s\n', repo.root)
5262 fm.write('reporoot', '%s\n', repo.root)
5262 fm.data(hgpath=repo.path, storepath=repo.spath)
5263 fm.data(hgpath=repo.path, storepath=repo.spath)
5263
5264
5264 @command('serve',
5265 @command('serve',
5265 [('A', 'accesslog', '', _('name of access log file to write to'),
5266 [('A', 'accesslog', '', _('name of access log file to write to'),
5266 _('FILE')),
5267 _('FILE')),
5267 ('d', 'daemon', None, _('run server in background')),
5268 ('d', 'daemon', None, _('run server in background')),
5268 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
5269 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
5269 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5270 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5270 # use string type, then we can check if something was passed
5271 # use string type, then we can check if something was passed
5271 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5272 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5272 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5273 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5273 _('ADDR')),
5274 _('ADDR')),
5274 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5275 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5275 _('PREFIX')),
5276 _('PREFIX')),
5276 ('n', 'name', '',
5277 ('n', 'name', '',
5277 _('name to show in web pages (default: working directory)'), _('NAME')),
5278 _('name to show in web pages (default: working directory)'), _('NAME')),
5278 ('', 'web-conf', '',
5279 ('', 'web-conf', '',
5279 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
5280 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
5280 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5281 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5281 _('FILE')),
5282 _('FILE')),
5282 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5283 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5283 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
5284 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
5284 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
5285 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
5285 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5286 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5286 ('', 'style', '', _('template style to use'), _('STYLE')),
5287 ('', 'style', '', _('template style to use'), _('STYLE')),
5287 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5288 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5288 ('', 'certificate', '', _('SSL certificate file'), _('FILE')),
5289 ('', 'certificate', '', _('SSL certificate file'), _('FILE')),
5289 ('', 'print-url', None, _('start and print only the URL'))]
5290 ('', 'print-url', None, _('start and print only the URL'))]
5290 + subrepoopts,
5291 + subrepoopts,
5291 _('[OPTION]...'),
5292 _('[OPTION]...'),
5292 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5293 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5293 helpbasic=True, optionalrepo=True)
5294 helpbasic=True, optionalrepo=True)
5294 def serve(ui, repo, **opts):
5295 def serve(ui, repo, **opts):
5295 """start stand-alone webserver
5296 """start stand-alone webserver
5296
5297
5297 Start a local HTTP repository browser and pull server. You can use
5298 Start a local HTTP repository browser and pull server. You can use
5298 this for ad-hoc sharing and browsing of repositories. It is
5299 this for ad-hoc sharing and browsing of repositories. It is
5299 recommended to use a real web server to serve a repository for
5300 recommended to use a real web server to serve a repository for
5300 longer periods of time.
5301 longer periods of time.
5301
5302
5302 Please note that the server does not implement access control.
5303 Please note that the server does not implement access control.
5303 This means that, by default, anybody can read from the server and
5304 This means that, by default, anybody can read from the server and
5304 nobody can write to it by default. Set the ``web.allow-push``
5305 nobody can write to it by default. Set the ``web.allow-push``
5305 option to ``*`` to allow everybody to push to the server. You
5306 option to ``*`` to allow everybody to push to the server. You
5306 should use a real web server if you need to authenticate users.
5307 should use a real web server if you need to authenticate users.
5307
5308
5308 By default, the server logs accesses to stdout and errors to
5309 By default, the server logs accesses to stdout and errors to
5309 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5310 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5310 files.
5311 files.
5311
5312
5312 To have the server choose a free port number to listen on, specify
5313 To have the server choose a free port number to listen on, specify
5313 a port number of 0; in this case, the server will print the port
5314 a port number of 0; in this case, the server will print the port
5314 number it uses.
5315 number it uses.
5315
5316
5316 Returns 0 on success.
5317 Returns 0 on success.
5317 """
5318 """
5318
5319
5319 opts = pycompat.byteskwargs(opts)
5320 opts = pycompat.byteskwargs(opts)
5320 if opts["stdio"] and opts["cmdserver"]:
5321 if opts["stdio"] and opts["cmdserver"]:
5321 raise error.Abort(_("cannot use --stdio with --cmdserver"))
5322 raise error.Abort(_("cannot use --stdio with --cmdserver"))
5322 if opts["print_url"] and ui.verbose:
5323 if opts["print_url"] and ui.verbose:
5323 raise error.Abort(_("cannot use --print-url with --verbose"))
5324 raise error.Abort(_("cannot use --print-url with --verbose"))
5324
5325
5325 if opts["stdio"]:
5326 if opts["stdio"]:
5326 if repo is None:
5327 if repo is None:
5327 raise error.RepoError(_("there is no Mercurial repository here"
5328 raise error.RepoError(_("there is no Mercurial repository here"
5328 " (.hg not found)"))
5329 " (.hg not found)"))
5329 s = wireprotoserver.sshserver(ui, repo)
5330 s = wireprotoserver.sshserver(ui, repo)
5330 s.serve_forever()
5331 s.serve_forever()
5331
5332
5332 service = server.createservice(ui, repo, opts)
5333 service = server.createservice(ui, repo, opts)
5333 return server.runservice(opts, initfn=service.init, runfn=service.run)
5334 return server.runservice(opts, initfn=service.init, runfn=service.run)
5334
5335
5335 @command('shelve',
5336 @command('shelve',
5336 [('A', 'addremove', None,
5337 [('A', 'addremove', None,
5337 _('mark new/missing files as added/removed before shelving')),
5338 _('mark new/missing files as added/removed before shelving')),
5338 ('u', 'unknown', None,
5339 ('u', 'unknown', None,
5339 _('store unknown files in the shelve')),
5340 _('store unknown files in the shelve')),
5340 ('', 'cleanup', None,
5341 ('', 'cleanup', None,
5341 _('delete all shelved changes')),
5342 _('delete all shelved changes')),
5342 ('', 'date', '',
5343 ('', 'date', '',
5343 _('shelve with the specified commit date'), _('DATE')),
5344 _('shelve with the specified commit date'), _('DATE')),
5344 ('d', 'delete', None,
5345 ('d', 'delete', None,
5345 _('delete the named shelved change(s)')),
5346 _('delete the named shelved change(s)')),
5346 ('e', 'edit', False,
5347 ('e', 'edit', False,
5347 _('invoke editor on commit messages')),
5348 _('invoke editor on commit messages')),
5348 ('k', 'keep', False,
5349 ('k', 'keep', False,
5349 _('shelve, but keep changes in the working directory')),
5350 _('shelve, but keep changes in the working directory')),
5350 ('l', 'list', None,
5351 ('l', 'list', None,
5351 _('list current shelves')),
5352 _('list current shelves')),
5352 ('m', 'message', '',
5353 ('m', 'message', '',
5353 _('use text as shelve message'), _('TEXT')),
5354 _('use text as shelve message'), _('TEXT')),
5354 ('n', 'name', '',
5355 ('n', 'name', '',
5355 _('use the given name for the shelved commit'), _('NAME')),
5356 _('use the given name for the shelved commit'), _('NAME')),
5356 ('p', 'patch', None,
5357 ('p', 'patch', None,
5357 _('output patches for changes (provide the names of the shelved '
5358 _('output patches for changes (provide the names of the shelved '
5358 'changes as positional arguments)')),
5359 'changes as positional arguments)')),
5359 ('i', 'interactive', None,
5360 ('i', 'interactive', None,
5360 _('interactive mode')),
5361 _('interactive mode')),
5361 ('', 'stat', None,
5362 ('', 'stat', None,
5362 _('output diffstat-style summary of changes (provide the names of '
5363 _('output diffstat-style summary of changes (provide the names of '
5363 'the shelved changes as positional arguments)')
5364 'the shelved changes as positional arguments)')
5364 )] + cmdutil.walkopts,
5365 )] + cmdutil.walkopts,
5365 _('hg shelve [OPTION]... [FILE]...'),
5366 _('hg shelve [OPTION]... [FILE]...'),
5366 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5367 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5367 def shelve(ui, repo, *pats, **opts):
5368 def shelve(ui, repo, *pats, **opts):
5368 '''save and set aside changes from the working directory
5369 '''save and set aside changes from the working directory
5369
5370
5370 Shelving takes files that "hg status" reports as not clean, saves
5371 Shelving takes files that "hg status" reports as not clean, saves
5371 the modifications to a bundle (a shelved change), and reverts the
5372 the modifications to a bundle (a shelved change), and reverts the
5372 files so that their state in the working directory becomes clean.
5373 files so that their state in the working directory becomes clean.
5373
5374
5374 To restore these changes to the working directory, using "hg
5375 To restore these changes to the working directory, using "hg
5375 unshelve"; this will work even if you switch to a different
5376 unshelve"; this will work even if you switch to a different
5376 commit.
5377 commit.
5377
5378
5378 When no files are specified, "hg shelve" saves all not-clean
5379 When no files are specified, "hg shelve" saves all not-clean
5379 files. If specific files or directories are named, only changes to
5380 files. If specific files or directories are named, only changes to
5380 those files are shelved.
5381 those files are shelved.
5381
5382
5382 In bare shelve (when no files are specified, without interactive,
5383 In bare shelve (when no files are specified, without interactive,
5383 include and exclude option), shelving remembers information if the
5384 include and exclude option), shelving remembers information if the
5384 working directory was on newly created branch, in other words working
5385 working directory was on newly created branch, in other words working
5385 directory was on different branch than its first parent. In this
5386 directory was on different branch than its first parent. In this
5386 situation unshelving restores branch information to the working directory.
5387 situation unshelving restores branch information to the working directory.
5387
5388
5388 Each shelved change has a name that makes it easier to find later.
5389 Each shelved change has a name that makes it easier to find later.
5389 The name of a shelved change defaults to being based on the active
5390 The name of a shelved change defaults to being based on the active
5390 bookmark, or if there is no active bookmark, the current named
5391 bookmark, or if there is no active bookmark, the current named
5391 branch. To specify a different name, use ``--name``.
5392 branch. To specify a different name, use ``--name``.
5392
5393
5393 To see a list of existing shelved changes, use the ``--list``
5394 To see a list of existing shelved changes, use the ``--list``
5394 option. For each shelved change, this will print its name, age,
5395 option. For each shelved change, this will print its name, age,
5395 and description; use ``--patch`` or ``--stat`` for more details.
5396 and description; use ``--patch`` or ``--stat`` for more details.
5396
5397
5397 To delete specific shelved changes, use ``--delete``. To delete
5398 To delete specific shelved changes, use ``--delete``. To delete
5398 all shelved changes, use ``--cleanup``.
5399 all shelved changes, use ``--cleanup``.
5399 '''
5400 '''
5400 opts = pycompat.byteskwargs(opts)
5401 opts = pycompat.byteskwargs(opts)
5401 allowables = [
5402 allowables = [
5402 ('addremove', {'create'}), # 'create' is pseudo action
5403 ('addremove', {'create'}), # 'create' is pseudo action
5403 ('unknown', {'create'}),
5404 ('unknown', {'create'}),
5404 ('cleanup', {'cleanup'}),
5405 ('cleanup', {'cleanup'}),
5405 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
5406 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
5406 ('delete', {'delete'}),
5407 ('delete', {'delete'}),
5407 ('edit', {'create'}),
5408 ('edit', {'create'}),
5408 ('keep', {'create'}),
5409 ('keep', {'create'}),
5409 ('list', {'list'}),
5410 ('list', {'list'}),
5410 ('message', {'create'}),
5411 ('message', {'create'}),
5411 ('name', {'create'}),
5412 ('name', {'create'}),
5412 ('patch', {'patch', 'list'}),
5413 ('patch', {'patch', 'list'}),
5413 ('stat', {'stat', 'list'}),
5414 ('stat', {'stat', 'list'}),
5414 ]
5415 ]
5415 def checkopt(opt):
5416 def checkopt(opt):
5416 if opts.get(opt):
5417 if opts.get(opt):
5417 for i, allowable in allowables:
5418 for i, allowable in allowables:
5418 if opts[i] and opt not in allowable:
5419 if opts[i] and opt not in allowable:
5419 raise error.Abort(_("options '--%s' and '--%s' may not be "
5420 raise error.Abort(_("options '--%s' and '--%s' may not be "
5420 "used together") % (opt, i))
5421 "used together") % (opt, i))
5421 return True
5422 return True
5422 if checkopt('cleanup'):
5423 if checkopt('cleanup'):
5423 if pats:
5424 if pats:
5424 raise error.Abort(_("cannot specify names when using '--cleanup'"))
5425 raise error.Abort(_("cannot specify names when using '--cleanup'"))
5425 return shelvemod.cleanupcmd(ui, repo)
5426 return shelvemod.cleanupcmd(ui, repo)
5426 elif checkopt('delete'):
5427 elif checkopt('delete'):
5427 return shelvemod.deletecmd(ui, repo, pats)
5428 return shelvemod.deletecmd(ui, repo, pats)
5428 elif checkopt('list'):
5429 elif checkopt('list'):
5429 return shelvemod.listcmd(ui, repo, pats, opts)
5430 return shelvemod.listcmd(ui, repo, pats, opts)
5430 elif checkopt('patch') or checkopt('stat'):
5431 elif checkopt('patch') or checkopt('stat'):
5431 return shelvemod.patchcmds(ui, repo, pats, opts)
5432 return shelvemod.patchcmds(ui, repo, pats, opts)
5432 else:
5433 else:
5433 return shelvemod.createcmd(ui, repo, pats, opts)
5434 return shelvemod.createcmd(ui, repo, pats, opts)
5434
5435
5435 _NOTTERSE = 'nothing'
5436 _NOTTERSE = 'nothing'
5436
5437
5437 @command('status|st',
5438 @command('status|st',
5438 [('A', 'all', None, _('show status of all files')),
5439 [('A', 'all', None, _('show status of all files')),
5439 ('m', 'modified', None, _('show only modified files')),
5440 ('m', 'modified', None, _('show only modified files')),
5440 ('a', 'added', None, _('show only added files')),
5441 ('a', 'added', None, _('show only added files')),
5441 ('r', 'removed', None, _('show only removed files')),
5442 ('r', 'removed', None, _('show only removed files')),
5442 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5443 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5443 ('c', 'clean', None, _('show only files without changes')),
5444 ('c', 'clean', None, _('show only files without changes')),
5444 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5445 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5445 ('i', 'ignored', None, _('show only ignored files')),
5446 ('i', 'ignored', None, _('show only ignored files')),
5446 ('n', 'no-status', None, _('hide status prefix')),
5447 ('n', 'no-status', None, _('hide status prefix')),
5447 ('t', 'terse', _NOTTERSE, _('show the terse output (EXPERIMENTAL)')),
5448 ('t', 'terse', _NOTTERSE, _('show the terse output (EXPERIMENTAL)')),
5448 ('C', 'copies', None, _('show source of copied files')),
5449 ('C', 'copies', None, _('show source of copied files')),
5449 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5450 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5450 ('', 'rev', [], _('show difference from revision'), _('REV')),
5451 ('', 'rev', [], _('show difference from revision'), _('REV')),
5451 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5452 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5452 ] + walkopts + subrepoopts + formatteropts,
5453 ] + walkopts + subrepoopts + formatteropts,
5453 _('[OPTION]... [FILE]...'),
5454 _('[OPTION]... [FILE]...'),
5454 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5455 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5455 helpbasic=True, inferrepo=True,
5456 helpbasic=True, inferrepo=True,
5456 intents={INTENT_READONLY})
5457 intents={INTENT_READONLY})
5457 def status(ui, repo, *pats, **opts):
5458 def status(ui, repo, *pats, **opts):
5458 """show changed files in the working directory
5459 """show changed files in the working directory
5459
5460
5460 Show status of files in the repository. If names are given, only
5461 Show status of files in the repository. If names are given, only
5461 files that match are shown. Files that are clean or ignored or
5462 files that match are shown. Files that are clean or ignored or
5462 the source of a copy/move operation, are not listed unless
5463 the source of a copy/move operation, are not listed unless
5463 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5464 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5464 Unless options described with "show only ..." are given, the
5465 Unless options described with "show only ..." are given, the
5465 options -mardu are used.
5466 options -mardu are used.
5466
5467
5467 Option -q/--quiet hides untracked (unknown and ignored) files
5468 Option -q/--quiet hides untracked (unknown and ignored) files
5468 unless explicitly requested with -u/--unknown or -i/--ignored.
5469 unless explicitly requested with -u/--unknown or -i/--ignored.
5469
5470
5470 .. note::
5471 .. note::
5471
5472
5472 :hg:`status` may appear to disagree with diff if permissions have
5473 :hg:`status` may appear to disagree with diff if permissions have
5473 changed or a merge has occurred. The standard diff format does
5474 changed or a merge has occurred. The standard diff format does
5474 not report permission changes and diff only reports changes
5475 not report permission changes and diff only reports changes
5475 relative to one merge parent.
5476 relative to one merge parent.
5476
5477
5477 If one revision is given, it is used as the base revision.
5478 If one revision is given, it is used as the base revision.
5478 If two revisions are given, the differences between them are
5479 If two revisions are given, the differences between them are
5479 shown. The --change option can also be used as a shortcut to list
5480 shown. The --change option can also be used as a shortcut to list
5480 the changed files of a revision from its first parent.
5481 the changed files of a revision from its first parent.
5481
5482
5482 The codes used to show the status of files are::
5483 The codes used to show the status of files are::
5483
5484
5484 M = modified
5485 M = modified
5485 A = added
5486 A = added
5486 R = removed
5487 R = removed
5487 C = clean
5488 C = clean
5488 ! = missing (deleted by non-hg command, but still tracked)
5489 ! = missing (deleted by non-hg command, but still tracked)
5489 ? = not tracked
5490 ? = not tracked
5490 I = ignored
5491 I = ignored
5491 = origin of the previous file (with --copies)
5492 = origin of the previous file (with --copies)
5492
5493
5493 .. container:: verbose
5494 .. container:: verbose
5494
5495
5495 The -t/--terse option abbreviates the output by showing only the directory
5496 The -t/--terse option abbreviates the output by showing only the directory
5496 name if all the files in it share the same status. The option takes an
5497 name if all the files in it share the same status. The option takes an
5497 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
5498 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
5498 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
5499 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
5499 for 'ignored' and 'c' for clean.
5500 for 'ignored' and 'c' for clean.
5500
5501
5501 It abbreviates only those statuses which are passed. Note that clean and
5502 It abbreviates only those statuses which are passed. Note that clean and
5502 ignored files are not displayed with '--terse ic' unless the -c/--clean
5503 ignored files are not displayed with '--terse ic' unless the -c/--clean
5503 and -i/--ignored options are also used.
5504 and -i/--ignored options are also used.
5504
5505
5505 The -v/--verbose option shows information when the repository is in an
5506 The -v/--verbose option shows information when the repository is in an
5506 unfinished merge, shelve, rebase state etc. You can have this behavior
5507 unfinished merge, shelve, rebase state etc. You can have this behavior
5507 turned on by default by enabling the ``commands.status.verbose`` option.
5508 turned on by default by enabling the ``commands.status.verbose`` option.
5508
5509
5509 You can skip displaying some of these states by setting
5510 You can skip displaying some of these states by setting
5510 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
5511 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
5511 'histedit', 'merge', 'rebase', or 'unshelve'.
5512 'histedit', 'merge', 'rebase', or 'unshelve'.
5512
5513
5513 Template:
5514 Template:
5514
5515
5515 The following keywords are supported in addition to the common template
5516 The following keywords are supported in addition to the common template
5516 keywords and functions. See also :hg:`help templates`.
5517 keywords and functions. See also :hg:`help templates`.
5517
5518
5518 :path: String. Repository-absolute path of the file.
5519 :path: String. Repository-absolute path of the file.
5519 :source: String. Repository-absolute path of the file originated from.
5520 :source: String. Repository-absolute path of the file originated from.
5520 Available if ``--copies`` is specified.
5521 Available if ``--copies`` is specified.
5521 :status: String. Character denoting file's status.
5522 :status: String. Character denoting file's status.
5522
5523
5523 Examples:
5524 Examples:
5524
5525
5525 - show changes in the working directory relative to a
5526 - show changes in the working directory relative to a
5526 changeset::
5527 changeset::
5527
5528
5528 hg status --rev 9353
5529 hg status --rev 9353
5529
5530
5530 - show changes in the working directory relative to the
5531 - show changes in the working directory relative to the
5531 current directory (see :hg:`help patterns` for more information)::
5532 current directory (see :hg:`help patterns` for more information)::
5532
5533
5533 hg status re:
5534 hg status re:
5534
5535
5535 - show all changes including copies in an existing changeset::
5536 - show all changes including copies in an existing changeset::
5536
5537
5537 hg status --copies --change 9353
5538 hg status --copies --change 9353
5538
5539
5539 - get a NUL separated list of added files, suitable for xargs::
5540 - get a NUL separated list of added files, suitable for xargs::
5540
5541
5541 hg status -an0
5542 hg status -an0
5542
5543
5543 - show more information about the repository status, abbreviating
5544 - show more information about the repository status, abbreviating
5544 added, removed, modified, deleted, and untracked paths::
5545 added, removed, modified, deleted, and untracked paths::
5545
5546
5546 hg status -v -t mardu
5547 hg status -v -t mardu
5547
5548
5548 Returns 0 on success.
5549 Returns 0 on success.
5549
5550
5550 """
5551 """
5551
5552
5552 opts = pycompat.byteskwargs(opts)
5553 opts = pycompat.byteskwargs(opts)
5553 revs = opts.get('rev')
5554 revs = opts.get('rev')
5554 change = opts.get('change')
5555 change = opts.get('change')
5555 terse = opts.get('terse')
5556 terse = opts.get('terse')
5556 if terse is _NOTTERSE:
5557 if terse is _NOTTERSE:
5557 if revs:
5558 if revs:
5558 terse = ''
5559 terse = ''
5559 else:
5560 else:
5560 terse = ui.config('commands', 'status.terse')
5561 terse = ui.config('commands', 'status.terse')
5561
5562
5562 if revs and change:
5563 if revs and change:
5563 msg = _('cannot specify --rev and --change at the same time')
5564 msg = _('cannot specify --rev and --change at the same time')
5564 raise error.Abort(msg)
5565 raise error.Abort(msg)
5565 elif revs and terse:
5566 elif revs and terse:
5566 msg = _('cannot use --terse with --rev')
5567 msg = _('cannot use --terse with --rev')
5567 raise error.Abort(msg)
5568 raise error.Abort(msg)
5568 elif change:
5569 elif change:
5569 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
5570 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
5570 ctx2 = scmutil.revsingle(repo, change, None)
5571 ctx2 = scmutil.revsingle(repo, change, None)
5571 ctx1 = ctx2.p1()
5572 ctx1 = ctx2.p1()
5572 else:
5573 else:
5573 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
5574 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
5574 ctx1, ctx2 = scmutil.revpair(repo, revs)
5575 ctx1, ctx2 = scmutil.revpair(repo, revs)
5575
5576
5576 forcerelativevalue = None
5577 forcerelativevalue = None
5577 if ui.hasconfig('commands', 'status.relative'):
5578 if ui.hasconfig('commands', 'status.relative'):
5578 forcerelativevalue = ui.configbool('commands', 'status.relative')
5579 forcerelativevalue = ui.configbool('commands', 'status.relative')
5579 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats),
5580 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats),
5580 forcerelativevalue=forcerelativevalue)
5581 forcerelativevalue=forcerelativevalue)
5581
5582
5582 if opts.get('print0'):
5583 if opts.get('print0'):
5583 end = '\0'
5584 end = '\0'
5584 else:
5585 else:
5585 end = '\n'
5586 end = '\n'
5586 copy = {}
5587 copy = {}
5587 states = 'modified added removed deleted unknown ignored clean'.split()
5588 states = 'modified added removed deleted unknown ignored clean'.split()
5588 show = [k for k in states if opts.get(k)]
5589 show = [k for k in states if opts.get(k)]
5589 if opts.get('all'):
5590 if opts.get('all'):
5590 show += ui.quiet and (states[:4] + ['clean']) or states
5591 show += ui.quiet and (states[:4] + ['clean']) or states
5591
5592
5592 if not show:
5593 if not show:
5593 if ui.quiet:
5594 if ui.quiet:
5594 show = states[:4]
5595 show = states[:4]
5595 else:
5596 else:
5596 show = states[:5]
5597 show = states[:5]
5597
5598
5598 m = scmutil.match(ctx2, pats, opts)
5599 m = scmutil.match(ctx2, pats, opts)
5599 if terse:
5600 if terse:
5600 # we need to compute clean and unknown to terse
5601 # we need to compute clean and unknown to terse
5601 stat = repo.status(ctx1.node(), ctx2.node(), m,
5602 stat = repo.status(ctx1.node(), ctx2.node(), m,
5602 'ignored' in show or 'i' in terse,
5603 'ignored' in show or 'i' in terse,
5603 clean=True, unknown=True,
5604 clean=True, unknown=True,
5604 listsubrepos=opts.get('subrepos'))
5605 listsubrepos=opts.get('subrepos'))
5605
5606
5606 stat = cmdutil.tersedir(stat, terse)
5607 stat = cmdutil.tersedir(stat, terse)
5607 else:
5608 else:
5608 stat = repo.status(ctx1.node(), ctx2.node(), m,
5609 stat = repo.status(ctx1.node(), ctx2.node(), m,
5609 'ignored' in show, 'clean' in show,
5610 'ignored' in show, 'clean' in show,
5610 'unknown' in show, opts.get('subrepos'))
5611 'unknown' in show, opts.get('subrepos'))
5611
5612
5612 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
5613 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
5613
5614
5614 if (opts.get('all') or opts.get('copies')
5615 if (opts.get('all') or opts.get('copies')
5615 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5616 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5616 copy = copies.pathcopies(ctx1, ctx2, m)
5617 copy = copies.pathcopies(ctx1, ctx2, m)
5617
5618
5618 ui.pager('status')
5619 ui.pager('status')
5619 fm = ui.formatter('status', opts)
5620 fm = ui.formatter('status', opts)
5620 fmt = '%s' + end
5621 fmt = '%s' + end
5621 showchar = not opts.get('no_status')
5622 showchar = not opts.get('no_status')
5622
5623
5623 for state, char, files in changestates:
5624 for state, char, files in changestates:
5624 if state in show:
5625 if state in show:
5625 label = 'status.' + state
5626 label = 'status.' + state
5626 for f in files:
5627 for f in files:
5627 fm.startitem()
5628 fm.startitem()
5628 fm.context(ctx=ctx2)
5629 fm.context(ctx=ctx2)
5629 fm.data(path=f)
5630 fm.data(path=f)
5630 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5631 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5631 fm.plain(fmt % uipathfn(f), label=label)
5632 fm.plain(fmt % uipathfn(f), label=label)
5632 if f in copy:
5633 if f in copy:
5633 fm.data(source=copy[f])
5634 fm.data(source=copy[f])
5634 fm.plain((' %s' + end) % uipathfn(copy[f]),
5635 fm.plain((' %s' + end) % uipathfn(copy[f]),
5635 label='status.copied')
5636 label='status.copied')
5636
5637
5637 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
5638 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
5638 and not ui.plain()):
5639 and not ui.plain()):
5639 cmdutil.morestatus(repo, fm)
5640 cmdutil.morestatus(repo, fm)
5640 fm.end()
5641 fm.end()
5641
5642
5642 @command('summary|sum',
5643 @command('summary|sum',
5643 [('', 'remote', None, _('check for push and pull'))],
5644 [('', 'remote', None, _('check for push and pull'))],
5644 '[--remote]',
5645 '[--remote]',
5645 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5646 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5646 helpbasic=True,
5647 helpbasic=True,
5647 intents={INTENT_READONLY})
5648 intents={INTENT_READONLY})
5648 def summary(ui, repo, **opts):
5649 def summary(ui, repo, **opts):
5649 """summarize working directory state
5650 """summarize working directory state
5650
5651
5651 This generates a brief summary of the working directory state,
5652 This generates a brief summary of the working directory state,
5652 including parents, branch, commit status, phase and available updates.
5653 including parents, branch, commit status, phase and available updates.
5653
5654
5654 With the --remote option, this will check the default paths for
5655 With the --remote option, this will check the default paths for
5655 incoming and outgoing changes. This can be time-consuming.
5656 incoming and outgoing changes. This can be time-consuming.
5656
5657
5657 Returns 0 on success.
5658 Returns 0 on success.
5658 """
5659 """
5659
5660
5660 opts = pycompat.byteskwargs(opts)
5661 opts = pycompat.byteskwargs(opts)
5661 ui.pager('summary')
5662 ui.pager('summary')
5662 ctx = repo[None]
5663 ctx = repo[None]
5663 parents = ctx.parents()
5664 parents = ctx.parents()
5664 pnode = parents[0].node()
5665 pnode = parents[0].node()
5665 marks = []
5666 marks = []
5666
5667
5667 try:
5668 try:
5668 ms = mergemod.mergestate.read(repo)
5669 ms = mergemod.mergestate.read(repo)
5669 except error.UnsupportedMergeRecords as e:
5670 except error.UnsupportedMergeRecords as e:
5670 s = ' '.join(e.recordtypes)
5671 s = ' '.join(e.recordtypes)
5671 ui.warn(
5672 ui.warn(
5672 _('warning: merge state has unsupported record types: %s\n') % s)
5673 _('warning: merge state has unsupported record types: %s\n') % s)
5673 unresolved = []
5674 unresolved = []
5674 else:
5675 else:
5675 unresolved = list(ms.unresolved())
5676 unresolved = list(ms.unresolved())
5676
5677
5677 for p in parents:
5678 for p in parents:
5678 # label with log.changeset (instead of log.parent) since this
5679 # label with log.changeset (instead of log.parent) since this
5679 # shows a working directory parent *changeset*:
5680 # shows a working directory parent *changeset*:
5680 # i18n: column positioning for "hg summary"
5681 # i18n: column positioning for "hg summary"
5681 ui.write(_('parent: %d:%s ') % (p.rev(), p),
5682 ui.write(_('parent: %d:%s ') % (p.rev(), p),
5682 label=logcmdutil.changesetlabels(p))
5683 label=logcmdutil.changesetlabels(p))
5683 ui.write(' '.join(p.tags()), label='log.tag')
5684 ui.write(' '.join(p.tags()), label='log.tag')
5684 if p.bookmarks():
5685 if p.bookmarks():
5685 marks.extend(p.bookmarks())
5686 marks.extend(p.bookmarks())
5686 if p.rev() == -1:
5687 if p.rev() == -1:
5687 if not len(repo):
5688 if not len(repo):
5688 ui.write(_(' (empty repository)'))
5689 ui.write(_(' (empty repository)'))
5689 else:
5690 else:
5690 ui.write(_(' (no revision checked out)'))
5691 ui.write(_(' (no revision checked out)'))
5691 if p.obsolete():
5692 if p.obsolete():
5692 ui.write(_(' (obsolete)'))
5693 ui.write(_(' (obsolete)'))
5693 if p.isunstable():
5694 if p.isunstable():
5694 instabilities = (ui.label(instability, 'trouble.%s' % instability)
5695 instabilities = (ui.label(instability, 'trouble.%s' % instability)
5695 for instability in p.instabilities())
5696 for instability in p.instabilities())
5696 ui.write(' ('
5697 ui.write(' ('
5697 + ', '.join(instabilities)
5698 + ', '.join(instabilities)
5698 + ')')
5699 + ')')
5699 ui.write('\n')
5700 ui.write('\n')
5700 if p.description():
5701 if p.description():
5701 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5702 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5702 label='log.summary')
5703 label='log.summary')
5703
5704
5704 branch = ctx.branch()
5705 branch = ctx.branch()
5705 bheads = repo.branchheads(branch)
5706 bheads = repo.branchheads(branch)
5706 # i18n: column positioning for "hg summary"
5707 # i18n: column positioning for "hg summary"
5707 m = _('branch: %s\n') % branch
5708 m = _('branch: %s\n') % branch
5708 if branch != 'default':
5709 if branch != 'default':
5709 ui.write(m, label='log.branch')
5710 ui.write(m, label='log.branch')
5710 else:
5711 else:
5711 ui.status(m, label='log.branch')
5712 ui.status(m, label='log.branch')
5712
5713
5713 if marks:
5714 if marks:
5714 active = repo._activebookmark
5715 active = repo._activebookmark
5715 # i18n: column positioning for "hg summary"
5716 # i18n: column positioning for "hg summary"
5716 ui.write(_('bookmarks:'), label='log.bookmark')
5717 ui.write(_('bookmarks:'), label='log.bookmark')
5717 if active is not None:
5718 if active is not None:
5718 if active in marks:
5719 if active in marks:
5719 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
5720 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
5720 marks.remove(active)
5721 marks.remove(active)
5721 else:
5722 else:
5722 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
5723 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
5723 for m in marks:
5724 for m in marks:
5724 ui.write(' ' + m, label='log.bookmark')
5725 ui.write(' ' + m, label='log.bookmark')
5725 ui.write('\n', label='log.bookmark')
5726 ui.write('\n', label='log.bookmark')
5726
5727
5727 status = repo.status(unknown=True)
5728 status = repo.status(unknown=True)
5728
5729
5729 c = repo.dirstate.copies()
5730 c = repo.dirstate.copies()
5730 copied, renamed = [], []
5731 copied, renamed = [], []
5731 for d, s in c.iteritems():
5732 for d, s in c.iteritems():
5732 if s in status.removed:
5733 if s in status.removed:
5733 status.removed.remove(s)
5734 status.removed.remove(s)
5734 renamed.append(d)
5735 renamed.append(d)
5735 else:
5736 else:
5736 copied.append(d)
5737 copied.append(d)
5737 if d in status.added:
5738 if d in status.added:
5738 status.added.remove(d)
5739 status.added.remove(d)
5739
5740
5740 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5741 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5741
5742
5742 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5743 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5743 (ui.label(_('%d added'), 'status.added'), status.added),
5744 (ui.label(_('%d added'), 'status.added'), status.added),
5744 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5745 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5745 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5746 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5746 (ui.label(_('%d copied'), 'status.copied'), copied),
5747 (ui.label(_('%d copied'), 'status.copied'), copied),
5747 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5748 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5748 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5749 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5749 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5750 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5750 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5751 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5751 t = []
5752 t = []
5752 for l, s in labels:
5753 for l, s in labels:
5753 if s:
5754 if s:
5754 t.append(l % len(s))
5755 t.append(l % len(s))
5755
5756
5756 t = ', '.join(t)
5757 t = ', '.join(t)
5757 cleanworkdir = False
5758 cleanworkdir = False
5758
5759
5759 if repo.vfs.exists('graftstate'):
5760 if repo.vfs.exists('graftstate'):
5760 t += _(' (graft in progress)')
5761 t += _(' (graft in progress)')
5761 if repo.vfs.exists('updatestate'):
5762 if repo.vfs.exists('updatestate'):
5762 t += _(' (interrupted update)')
5763 t += _(' (interrupted update)')
5763 elif len(parents) > 1:
5764 elif len(parents) > 1:
5764 t += _(' (merge)')
5765 t += _(' (merge)')
5765 elif branch != parents[0].branch():
5766 elif branch != parents[0].branch():
5766 t += _(' (new branch)')
5767 t += _(' (new branch)')
5767 elif (parents[0].closesbranch() and
5768 elif (parents[0].closesbranch() and
5768 pnode in repo.branchheads(branch, closed=True)):
5769 pnode in repo.branchheads(branch, closed=True)):
5769 t += _(' (head closed)')
5770 t += _(' (head closed)')
5770 elif not (status.modified or status.added or status.removed or renamed or
5771 elif not (status.modified or status.added or status.removed or renamed or
5771 copied or subs):
5772 copied or subs):
5772 t += _(' (clean)')
5773 t += _(' (clean)')
5773 cleanworkdir = True
5774 cleanworkdir = True
5774 elif pnode not in bheads:
5775 elif pnode not in bheads:
5775 t += _(' (new branch head)')
5776 t += _(' (new branch head)')
5776
5777
5777 if parents:
5778 if parents:
5778 pendingphase = max(p.phase() for p in parents)
5779 pendingphase = max(p.phase() for p in parents)
5779 else:
5780 else:
5780 pendingphase = phases.public
5781 pendingphase = phases.public
5781
5782
5782 if pendingphase > phases.newcommitphase(ui):
5783 if pendingphase > phases.newcommitphase(ui):
5783 t += ' (%s)' % phases.phasenames[pendingphase]
5784 t += ' (%s)' % phases.phasenames[pendingphase]
5784
5785
5785 if cleanworkdir:
5786 if cleanworkdir:
5786 # i18n: column positioning for "hg summary"
5787 # i18n: column positioning for "hg summary"
5787 ui.status(_('commit: %s\n') % t.strip())
5788 ui.status(_('commit: %s\n') % t.strip())
5788 else:
5789 else:
5789 # i18n: column positioning for "hg summary"
5790 # i18n: column positioning for "hg summary"
5790 ui.write(_('commit: %s\n') % t.strip())
5791 ui.write(_('commit: %s\n') % t.strip())
5791
5792
5792 # all ancestors of branch heads - all ancestors of parent = new csets
5793 # all ancestors of branch heads - all ancestors of parent = new csets
5793 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5794 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5794 bheads))
5795 bheads))
5795
5796
5796 if new == 0:
5797 if new == 0:
5797 # i18n: column positioning for "hg summary"
5798 # i18n: column positioning for "hg summary"
5798 ui.status(_('update: (current)\n'))
5799 ui.status(_('update: (current)\n'))
5799 elif pnode not in bheads:
5800 elif pnode not in bheads:
5800 # i18n: column positioning for "hg summary"
5801 # i18n: column positioning for "hg summary"
5801 ui.write(_('update: %d new changesets (update)\n') % new)
5802 ui.write(_('update: %d new changesets (update)\n') % new)
5802 else:
5803 else:
5803 # i18n: column positioning for "hg summary"
5804 # i18n: column positioning for "hg summary"
5804 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5805 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5805 (new, len(bheads)))
5806 (new, len(bheads)))
5806
5807
5807 t = []
5808 t = []
5808 draft = len(repo.revs('draft()'))
5809 draft = len(repo.revs('draft()'))
5809 if draft:
5810 if draft:
5810 t.append(_('%d draft') % draft)
5811 t.append(_('%d draft') % draft)
5811 secret = len(repo.revs('secret()'))
5812 secret = len(repo.revs('secret()'))
5812 if secret:
5813 if secret:
5813 t.append(_('%d secret') % secret)
5814 t.append(_('%d secret') % secret)
5814
5815
5815 if draft or secret:
5816 if draft or secret:
5816 ui.status(_('phases: %s\n') % ', '.join(t))
5817 ui.status(_('phases: %s\n') % ', '.join(t))
5817
5818
5818 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5819 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5819 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5820 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5820 numtrouble = len(repo.revs(trouble + "()"))
5821 numtrouble = len(repo.revs(trouble + "()"))
5821 # We write all the possibilities to ease translation
5822 # We write all the possibilities to ease translation
5822 troublemsg = {
5823 troublemsg = {
5823 "orphan": _("orphan: %d changesets"),
5824 "orphan": _("orphan: %d changesets"),
5824 "contentdivergent": _("content-divergent: %d changesets"),
5825 "contentdivergent": _("content-divergent: %d changesets"),
5825 "phasedivergent": _("phase-divergent: %d changesets"),
5826 "phasedivergent": _("phase-divergent: %d changesets"),
5826 }
5827 }
5827 if numtrouble > 0:
5828 if numtrouble > 0:
5828 ui.status(troublemsg[trouble] % numtrouble + "\n")
5829 ui.status(troublemsg[trouble] % numtrouble + "\n")
5829
5830
5830 cmdutil.summaryhooks(ui, repo)
5831 cmdutil.summaryhooks(ui, repo)
5831
5832
5832 if opts.get('remote'):
5833 if opts.get('remote'):
5833 needsincoming, needsoutgoing = True, True
5834 needsincoming, needsoutgoing = True, True
5834 else:
5835 else:
5835 needsincoming, needsoutgoing = False, False
5836 needsincoming, needsoutgoing = False, False
5836 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5837 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5837 if i:
5838 if i:
5838 needsincoming = True
5839 needsincoming = True
5839 if o:
5840 if o:
5840 needsoutgoing = True
5841 needsoutgoing = True
5841 if not needsincoming and not needsoutgoing:
5842 if not needsincoming and not needsoutgoing:
5842 return
5843 return
5843
5844
5844 def getincoming():
5845 def getincoming():
5845 source, branches = hg.parseurl(ui.expandpath('default'))
5846 source, branches = hg.parseurl(ui.expandpath('default'))
5846 sbranch = branches[0]
5847 sbranch = branches[0]
5847 try:
5848 try:
5848 other = hg.peer(repo, {}, source)
5849 other = hg.peer(repo, {}, source)
5849 except error.RepoError:
5850 except error.RepoError:
5850 if opts.get('remote'):
5851 if opts.get('remote'):
5851 raise
5852 raise
5852 return source, sbranch, None, None, None
5853 return source, sbranch, None, None, None
5853 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5854 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5854 if revs:
5855 if revs:
5855 revs = [other.lookup(rev) for rev in revs]
5856 revs = [other.lookup(rev) for rev in revs]
5856 ui.debug('comparing with %s\n' % util.hidepassword(source))
5857 ui.debug('comparing with %s\n' % util.hidepassword(source))
5857 repo.ui.pushbuffer()
5858 repo.ui.pushbuffer()
5858 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5859 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5859 repo.ui.popbuffer()
5860 repo.ui.popbuffer()
5860 return source, sbranch, other, commoninc, commoninc[1]
5861 return source, sbranch, other, commoninc, commoninc[1]
5861
5862
5862 if needsincoming:
5863 if needsincoming:
5863 source, sbranch, sother, commoninc, incoming = getincoming()
5864 source, sbranch, sother, commoninc, incoming = getincoming()
5864 else:
5865 else:
5865 source = sbranch = sother = commoninc = incoming = None
5866 source = sbranch = sother = commoninc = incoming = None
5866
5867
5867 def getoutgoing():
5868 def getoutgoing():
5868 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5869 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5869 dbranch = branches[0]
5870 dbranch = branches[0]
5870 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5871 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5871 if source != dest:
5872 if source != dest:
5872 try:
5873 try:
5873 dother = hg.peer(repo, {}, dest)
5874 dother = hg.peer(repo, {}, dest)
5874 except error.RepoError:
5875 except error.RepoError:
5875 if opts.get('remote'):
5876 if opts.get('remote'):
5876 raise
5877 raise
5877 return dest, dbranch, None, None
5878 return dest, dbranch, None, None
5878 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5879 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5879 elif sother is None:
5880 elif sother is None:
5880 # there is no explicit destination peer, but source one is invalid
5881 # there is no explicit destination peer, but source one is invalid
5881 return dest, dbranch, None, None
5882 return dest, dbranch, None, None
5882 else:
5883 else:
5883 dother = sother
5884 dother = sother
5884 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5885 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5885 common = None
5886 common = None
5886 else:
5887 else:
5887 common = commoninc
5888 common = commoninc
5888 if revs:
5889 if revs:
5889 revs = [repo.lookup(rev) for rev in revs]
5890 revs = [repo.lookup(rev) for rev in revs]
5890 repo.ui.pushbuffer()
5891 repo.ui.pushbuffer()
5891 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5892 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5892 commoninc=common)
5893 commoninc=common)
5893 repo.ui.popbuffer()
5894 repo.ui.popbuffer()
5894 return dest, dbranch, dother, outgoing
5895 return dest, dbranch, dother, outgoing
5895
5896
5896 if needsoutgoing:
5897 if needsoutgoing:
5897 dest, dbranch, dother, outgoing = getoutgoing()
5898 dest, dbranch, dother, outgoing = getoutgoing()
5898 else:
5899 else:
5899 dest = dbranch = dother = outgoing = None
5900 dest = dbranch = dother = outgoing = None
5900
5901
5901 if opts.get('remote'):
5902 if opts.get('remote'):
5902 t = []
5903 t = []
5903 if incoming:
5904 if incoming:
5904 t.append(_('1 or more incoming'))
5905 t.append(_('1 or more incoming'))
5905 o = outgoing.missing
5906 o = outgoing.missing
5906 if o:
5907 if o:
5907 t.append(_('%d outgoing') % len(o))
5908 t.append(_('%d outgoing') % len(o))
5908 other = dother or sother
5909 other = dother or sother
5909 if 'bookmarks' in other.listkeys('namespaces'):
5910 if 'bookmarks' in other.listkeys('namespaces'):
5910 counts = bookmarks.summary(repo, other)
5911 counts = bookmarks.summary(repo, other)
5911 if counts[0] > 0:
5912 if counts[0] > 0:
5912 t.append(_('%d incoming bookmarks') % counts[0])
5913 t.append(_('%d incoming bookmarks') % counts[0])
5913 if counts[1] > 0:
5914 if counts[1] > 0:
5914 t.append(_('%d outgoing bookmarks') % counts[1])
5915 t.append(_('%d outgoing bookmarks') % counts[1])
5915
5916
5916 if t:
5917 if t:
5917 # i18n: column positioning for "hg summary"
5918 # i18n: column positioning for "hg summary"
5918 ui.write(_('remote: %s\n') % (', '.join(t)))
5919 ui.write(_('remote: %s\n') % (', '.join(t)))
5919 else:
5920 else:
5920 # i18n: column positioning for "hg summary"
5921 # i18n: column positioning for "hg summary"
5921 ui.status(_('remote: (synced)\n'))
5922 ui.status(_('remote: (synced)\n'))
5922
5923
5923 cmdutil.summaryremotehooks(ui, repo, opts,
5924 cmdutil.summaryremotehooks(ui, repo, opts,
5924 ((source, sbranch, sother, commoninc),
5925 ((source, sbranch, sother, commoninc),
5925 (dest, dbranch, dother, outgoing)))
5926 (dest, dbranch, dother, outgoing)))
5926
5927
5927 @command('tag',
5928 @command('tag',
5928 [('f', 'force', None, _('force tag')),
5929 [('f', 'force', None, _('force tag')),
5929 ('l', 'local', None, _('make the tag local')),
5930 ('l', 'local', None, _('make the tag local')),
5930 ('r', 'rev', '', _('revision to tag'), _('REV')),
5931 ('r', 'rev', '', _('revision to tag'), _('REV')),
5931 ('', 'remove', None, _('remove a tag')),
5932 ('', 'remove', None, _('remove a tag')),
5932 # -l/--local is already there, commitopts cannot be used
5933 # -l/--local is already there, commitopts cannot be used
5933 ('e', 'edit', None, _('invoke editor on commit messages')),
5934 ('e', 'edit', None, _('invoke editor on commit messages')),
5934 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5935 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5935 ] + commitopts2,
5936 ] + commitopts2,
5936 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
5937 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
5937 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
5938 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
5938 def tag(ui, repo, name1, *names, **opts):
5939 def tag(ui, repo, name1, *names, **opts):
5939 """add one or more tags for the current or given revision
5940 """add one or more tags for the current or given revision
5940
5941
5941 Name a particular revision using <name>.
5942 Name a particular revision using <name>.
5942
5943
5943 Tags are used to name particular revisions of the repository and are
5944 Tags are used to name particular revisions of the repository and are
5944 very useful to compare different revisions, to go back to significant
5945 very useful to compare different revisions, to go back to significant
5945 earlier versions or to mark branch points as releases, etc. Changing
5946 earlier versions or to mark branch points as releases, etc. Changing
5946 an existing tag is normally disallowed; use -f/--force to override.
5947 an existing tag is normally disallowed; use -f/--force to override.
5947
5948
5948 If no revision is given, the parent of the working directory is
5949 If no revision is given, the parent of the working directory is
5949 used.
5950 used.
5950
5951
5951 To facilitate version control, distribution, and merging of tags,
5952 To facilitate version control, distribution, and merging of tags,
5952 they are stored as a file named ".hgtags" which is managed similarly
5953 they are stored as a file named ".hgtags" which is managed similarly
5953 to other project files and can be hand-edited if necessary. This
5954 to other project files and can be hand-edited if necessary. This
5954 also means that tagging creates a new commit. The file
5955 also means that tagging creates a new commit. The file
5955 ".hg/localtags" is used for local tags (not shared among
5956 ".hg/localtags" is used for local tags (not shared among
5956 repositories).
5957 repositories).
5957
5958
5958 Tag commits are usually made at the head of a branch. If the parent
5959 Tag commits are usually made at the head of a branch. If the parent
5959 of the working directory is not a branch head, :hg:`tag` aborts; use
5960 of the working directory is not a branch head, :hg:`tag` aborts; use
5960 -f/--force to force the tag commit to be based on a non-head
5961 -f/--force to force the tag commit to be based on a non-head
5961 changeset.
5962 changeset.
5962
5963
5963 See :hg:`help dates` for a list of formats valid for -d/--date.
5964 See :hg:`help dates` for a list of formats valid for -d/--date.
5964
5965
5965 Since tag names have priority over branch names during revision
5966 Since tag names have priority over branch names during revision
5966 lookup, using an existing branch name as a tag name is discouraged.
5967 lookup, using an existing branch name as a tag name is discouraged.
5967
5968
5968 Returns 0 on success.
5969 Returns 0 on success.
5969 """
5970 """
5970 opts = pycompat.byteskwargs(opts)
5971 opts = pycompat.byteskwargs(opts)
5971 with repo.wlock(), repo.lock():
5972 with repo.wlock(), repo.lock():
5972 rev_ = "."
5973 rev_ = "."
5973 names = [t.strip() for t in (name1,) + names]
5974 names = [t.strip() for t in (name1,) + names]
5974 if len(names) != len(set(names)):
5975 if len(names) != len(set(names)):
5975 raise error.Abort(_('tag names must be unique'))
5976 raise error.Abort(_('tag names must be unique'))
5976 for n in names:
5977 for n in names:
5977 scmutil.checknewlabel(repo, n, 'tag')
5978 scmutil.checknewlabel(repo, n, 'tag')
5978 if not n:
5979 if not n:
5979 raise error.Abort(_('tag names cannot consist entirely of '
5980 raise error.Abort(_('tag names cannot consist entirely of '
5980 'whitespace'))
5981 'whitespace'))
5981 if opts.get('rev') and opts.get('remove'):
5982 if opts.get('rev') and opts.get('remove'):
5982 raise error.Abort(_("--rev and --remove are incompatible"))
5983 raise error.Abort(_("--rev and --remove are incompatible"))
5983 if opts.get('rev'):
5984 if opts.get('rev'):
5984 rev_ = opts['rev']
5985 rev_ = opts['rev']
5985 message = opts.get('message')
5986 message = opts.get('message')
5986 if opts.get('remove'):
5987 if opts.get('remove'):
5987 if opts.get('local'):
5988 if opts.get('local'):
5988 expectedtype = 'local'
5989 expectedtype = 'local'
5989 else:
5990 else:
5990 expectedtype = 'global'
5991 expectedtype = 'global'
5991
5992
5992 for n in names:
5993 for n in names:
5993 if repo.tagtype(n) == 'global':
5994 if repo.tagtype(n) == 'global':
5994 alltags = tagsmod.findglobaltags(ui, repo)
5995 alltags = tagsmod.findglobaltags(ui, repo)
5995 if alltags[n][0] == nullid:
5996 if alltags[n][0] == nullid:
5996 raise error.Abort(_("tag '%s' is already removed") % n)
5997 raise error.Abort(_("tag '%s' is already removed") % n)
5997 if not repo.tagtype(n):
5998 if not repo.tagtype(n):
5998 raise error.Abort(_("tag '%s' does not exist") % n)
5999 raise error.Abort(_("tag '%s' does not exist") % n)
5999 if repo.tagtype(n) != expectedtype:
6000 if repo.tagtype(n) != expectedtype:
6000 if expectedtype == 'global':
6001 if expectedtype == 'global':
6001 raise error.Abort(_("tag '%s' is not a global tag") % n)
6002 raise error.Abort(_("tag '%s' is not a global tag") % n)
6002 else:
6003 else:
6003 raise error.Abort(_("tag '%s' is not a local tag") % n)
6004 raise error.Abort(_("tag '%s' is not a local tag") % n)
6004 rev_ = 'null'
6005 rev_ = 'null'
6005 if not message:
6006 if not message:
6006 # we don't translate commit messages
6007 # we don't translate commit messages
6007 message = 'Removed tag %s' % ', '.join(names)
6008 message = 'Removed tag %s' % ', '.join(names)
6008 elif not opts.get('force'):
6009 elif not opts.get('force'):
6009 for n in names:
6010 for n in names:
6010 if n in repo.tags():
6011 if n in repo.tags():
6011 raise error.Abort(_("tag '%s' already exists "
6012 raise error.Abort(_("tag '%s' already exists "
6012 "(use -f to force)") % n)
6013 "(use -f to force)") % n)
6013 if not opts.get('local'):
6014 if not opts.get('local'):
6014 p1, p2 = repo.dirstate.parents()
6015 p1, p2 = repo.dirstate.parents()
6015 if p2 != nullid:
6016 if p2 != nullid:
6016 raise error.Abort(_('uncommitted merge'))
6017 raise error.Abort(_('uncommitted merge'))
6017 bheads = repo.branchheads()
6018 bheads = repo.branchheads()
6018 if not opts.get('force') and bheads and p1 not in bheads:
6019 if not opts.get('force') and bheads and p1 not in bheads:
6019 raise error.Abort(_('working directory is not at a branch head '
6020 raise error.Abort(_('working directory is not at a branch head '
6020 '(use -f to force)'))
6021 '(use -f to force)'))
6021 node = scmutil.revsingle(repo, rev_).node()
6022 node = scmutil.revsingle(repo, rev_).node()
6022
6023
6023 if not message:
6024 if not message:
6024 # we don't translate commit messages
6025 # we don't translate commit messages
6025 message = ('Added tag %s for changeset %s' %
6026 message = ('Added tag %s for changeset %s' %
6026 (', '.join(names), short(node)))
6027 (', '.join(names), short(node)))
6027
6028
6028 date = opts.get('date')
6029 date = opts.get('date')
6029 if date:
6030 if date:
6030 date = dateutil.parsedate(date)
6031 date = dateutil.parsedate(date)
6031
6032
6032 if opts.get('remove'):
6033 if opts.get('remove'):
6033 editform = 'tag.remove'
6034 editform = 'tag.remove'
6034 else:
6035 else:
6035 editform = 'tag.add'
6036 editform = 'tag.add'
6036 editor = cmdutil.getcommiteditor(editform=editform,
6037 editor = cmdutil.getcommiteditor(editform=editform,
6037 **pycompat.strkwargs(opts))
6038 **pycompat.strkwargs(opts))
6038
6039
6039 # don't allow tagging the null rev
6040 # don't allow tagging the null rev
6040 if (not opts.get('remove') and
6041 if (not opts.get('remove') and
6041 scmutil.revsingle(repo, rev_).rev() == nullrev):
6042 scmutil.revsingle(repo, rev_).rev() == nullrev):
6042 raise error.Abort(_("cannot tag null revision"))
6043 raise error.Abort(_("cannot tag null revision"))
6043
6044
6044 tagsmod.tag(repo, names, node, message, opts.get('local'),
6045 tagsmod.tag(repo, names, node, message, opts.get('local'),
6045 opts.get('user'), date, editor=editor)
6046 opts.get('user'), date, editor=editor)
6046
6047
6047 @command(
6048 @command(
6048 'tags', formatteropts, '',
6049 'tags', formatteropts, '',
6049 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
6050 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
6050 intents={INTENT_READONLY})
6051 intents={INTENT_READONLY})
6051 def tags(ui, repo, **opts):
6052 def tags(ui, repo, **opts):
6052 """list repository tags
6053 """list repository tags
6053
6054
6054 This lists both regular and local tags. When the -v/--verbose
6055 This lists both regular and local tags. When the -v/--verbose
6055 switch is used, a third column "local" is printed for local tags.
6056 switch is used, a third column "local" is printed for local tags.
6056 When the -q/--quiet switch is used, only the tag name is printed.
6057 When the -q/--quiet switch is used, only the tag name is printed.
6057
6058
6058 .. container:: verbose
6059 .. container:: verbose
6059
6060
6060 Template:
6061 Template:
6061
6062
6062 The following keywords are supported in addition to the common template
6063 The following keywords are supported in addition to the common template
6063 keywords and functions such as ``{tag}``. See also
6064 keywords and functions such as ``{tag}``. See also
6064 :hg:`help templates`.
6065 :hg:`help templates`.
6065
6066
6066 :type: String. ``local`` for local tags.
6067 :type: String. ``local`` for local tags.
6067
6068
6068 Returns 0 on success.
6069 Returns 0 on success.
6069 """
6070 """
6070
6071
6071 opts = pycompat.byteskwargs(opts)
6072 opts = pycompat.byteskwargs(opts)
6072 ui.pager('tags')
6073 ui.pager('tags')
6073 fm = ui.formatter('tags', opts)
6074 fm = ui.formatter('tags', opts)
6074 hexfunc = fm.hexfunc
6075 hexfunc = fm.hexfunc
6075
6076
6076 for t, n in reversed(repo.tagslist()):
6077 for t, n in reversed(repo.tagslist()):
6077 hn = hexfunc(n)
6078 hn = hexfunc(n)
6078 label = 'tags.normal'
6079 label = 'tags.normal'
6079 tagtype = ''
6080 tagtype = ''
6080 if repo.tagtype(t) == 'local':
6081 if repo.tagtype(t) == 'local':
6081 label = 'tags.local'
6082 label = 'tags.local'
6082 tagtype = 'local'
6083 tagtype = 'local'
6083
6084
6084 fm.startitem()
6085 fm.startitem()
6085 fm.context(repo=repo)
6086 fm.context(repo=repo)
6086 fm.write('tag', '%s', t, label=label)
6087 fm.write('tag', '%s', t, label=label)
6087 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6088 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6088 fm.condwrite(not ui.quiet, 'rev node', fmt,
6089 fm.condwrite(not ui.quiet, 'rev node', fmt,
6089 repo.changelog.rev(n), hn, label=label)
6090 repo.changelog.rev(n), hn, label=label)
6090 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6091 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6091 tagtype, label=label)
6092 tagtype, label=label)
6092 fm.plain('\n')
6093 fm.plain('\n')
6093 fm.end()
6094 fm.end()
6094
6095
6095 @command('tip',
6096 @command('tip',
6096 [('p', 'patch', None, _('show patch')),
6097 [('p', 'patch', None, _('show patch')),
6097 ('g', 'git', None, _('use git extended diff format')),
6098 ('g', 'git', None, _('use git extended diff format')),
6098 ] + templateopts,
6099 ] + templateopts,
6099 _('[-p] [-g]'),
6100 _('[-p] [-g]'),
6100 helpcategory=command.CATEGORY_CHANGE_NAVIGATION)
6101 helpcategory=command.CATEGORY_CHANGE_NAVIGATION)
6101 def tip(ui, repo, **opts):
6102 def tip(ui, repo, **opts):
6102 """show the tip revision (DEPRECATED)
6103 """show the tip revision (DEPRECATED)
6103
6104
6104 The tip revision (usually just called the tip) is the changeset
6105 The tip revision (usually just called the tip) is the changeset
6105 most recently added to the repository (and therefore the most
6106 most recently added to the repository (and therefore the most
6106 recently changed head).
6107 recently changed head).
6107
6108
6108 If you have just made a commit, that commit will be the tip. If
6109 If you have just made a commit, that commit will be the tip. If
6109 you have just pulled changes from another repository, the tip of
6110 you have just pulled changes from another repository, the tip of
6110 that repository becomes the current tip. The "tip" tag is special
6111 that repository becomes the current tip. The "tip" tag is special
6111 and cannot be renamed or assigned to a different changeset.
6112 and cannot be renamed or assigned to a different changeset.
6112
6113
6113 This command is deprecated, please use :hg:`heads` instead.
6114 This command is deprecated, please use :hg:`heads` instead.
6114
6115
6115 Returns 0 on success.
6116 Returns 0 on success.
6116 """
6117 """
6117 opts = pycompat.byteskwargs(opts)
6118 opts = pycompat.byteskwargs(opts)
6118 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
6119 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
6119 displayer.show(repo['tip'])
6120 displayer.show(repo['tip'])
6120 displayer.close()
6121 displayer.close()
6121
6122
6122 @command('unbundle',
6123 @command('unbundle',
6123 [('u', 'update', None,
6124 [('u', 'update', None,
6124 _('update to new branch head if changesets were unbundled'))],
6125 _('update to new branch head if changesets were unbundled'))],
6125 _('[-u] FILE...'),
6126 _('[-u] FILE...'),
6126 helpcategory=command.CATEGORY_IMPORT_EXPORT)
6127 helpcategory=command.CATEGORY_IMPORT_EXPORT)
6127 def unbundle(ui, repo, fname1, *fnames, **opts):
6128 def unbundle(ui, repo, fname1, *fnames, **opts):
6128 """apply one or more bundle files
6129 """apply one or more bundle files
6129
6130
6130 Apply one or more bundle files generated by :hg:`bundle`.
6131 Apply one or more bundle files generated by :hg:`bundle`.
6131
6132
6132 Returns 0 on success, 1 if an update has unresolved files.
6133 Returns 0 on success, 1 if an update has unresolved files.
6133 """
6134 """
6134 fnames = (fname1,) + fnames
6135 fnames = (fname1,) + fnames
6135
6136
6136 with repo.lock():
6137 with repo.lock():
6137 for fname in fnames:
6138 for fname in fnames:
6138 f = hg.openpath(ui, fname)
6139 f = hg.openpath(ui, fname)
6139 gen = exchange.readbundle(ui, f, fname)
6140 gen = exchange.readbundle(ui, f, fname)
6140 if isinstance(gen, streamclone.streamcloneapplier):
6141 if isinstance(gen, streamclone.streamcloneapplier):
6141 raise error.Abort(
6142 raise error.Abort(
6142 _('packed bundles cannot be applied with '
6143 _('packed bundles cannot be applied with '
6143 '"hg unbundle"'),
6144 '"hg unbundle"'),
6144 hint=_('use "hg debugapplystreamclonebundle"'))
6145 hint=_('use "hg debugapplystreamclonebundle"'))
6145 url = 'bundle:' + fname
6146 url = 'bundle:' + fname
6146 try:
6147 try:
6147 txnname = 'unbundle'
6148 txnname = 'unbundle'
6148 if not isinstance(gen, bundle2.unbundle20):
6149 if not isinstance(gen, bundle2.unbundle20):
6149 txnname = 'unbundle\n%s' % util.hidepassword(url)
6150 txnname = 'unbundle\n%s' % util.hidepassword(url)
6150 with repo.transaction(txnname) as tr:
6151 with repo.transaction(txnname) as tr:
6151 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
6152 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
6152 url=url)
6153 url=url)
6153 except error.BundleUnknownFeatureError as exc:
6154 except error.BundleUnknownFeatureError as exc:
6154 raise error.Abort(
6155 raise error.Abort(
6155 _('%s: unknown bundle feature, %s') % (fname, exc),
6156 _('%s: unknown bundle feature, %s') % (fname, exc),
6156 hint=_("see https://mercurial-scm.org/"
6157 hint=_("see https://mercurial-scm.org/"
6157 "wiki/BundleFeature for more "
6158 "wiki/BundleFeature for more "
6158 "information"))
6159 "information"))
6159 modheads = bundle2.combinechangegroupresults(op)
6160 modheads = bundle2.combinechangegroupresults(op)
6160
6161
6161 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
6162 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
6162
6163
6163 @command('unshelve',
6164 @command('unshelve',
6164 [('a', 'abort', None,
6165 [('a', 'abort', None,
6165 _('abort an incomplete unshelve operation')),
6166 _('abort an incomplete unshelve operation')),
6166 ('c', 'continue', None,
6167 ('c', 'continue', None,
6167 _('continue an incomplete unshelve operation')),
6168 _('continue an incomplete unshelve operation')),
6168 ('i', 'interactive', None,
6169 ('i', 'interactive', None,
6169 _('use interactive mode (EXPERIMENTAL)')),
6170 _('use interactive mode (EXPERIMENTAL)')),
6170 ('k', 'keep', None,
6171 ('k', 'keep', None,
6171 _('keep shelve after unshelving')),
6172 _('keep shelve after unshelving')),
6172 ('n', 'name', '',
6173 ('n', 'name', '',
6173 _('restore shelved change with given name'), _('NAME')),
6174 _('restore shelved change with given name'), _('NAME')),
6174 ('t', 'tool', '', _('specify merge tool')),
6175 ('t', 'tool', '', _('specify merge tool')),
6175 ('', 'date', '',
6176 ('', 'date', '',
6176 _('set date for temporary commits (DEPRECATED)'), _('DATE'))],
6177 _('set date for temporary commits (DEPRECATED)'), _('DATE'))],
6177 _('hg unshelve [OPTION]... [FILE]... [-n SHELVED]'),
6178 _('hg unshelve [OPTION]... [FILE]... [-n SHELVED]'),
6178 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
6179 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
6179 def unshelve(ui, repo, *shelved, **opts):
6180 def unshelve(ui, repo, *shelved, **opts):
6180 """restore a shelved change to the working directory
6181 """restore a shelved change to the working directory
6181
6182
6182 This command accepts an optional name of a shelved change to
6183 This command accepts an optional name of a shelved change to
6183 restore. If none is given, the most recent shelved change is used.
6184 restore. If none is given, the most recent shelved change is used.
6184
6185
6185 If a shelved change is applied successfully, the bundle that
6186 If a shelved change is applied successfully, the bundle that
6186 contains the shelved changes is moved to a backup location
6187 contains the shelved changes is moved to a backup location
6187 (.hg/shelve-backup).
6188 (.hg/shelve-backup).
6188
6189
6189 Since you can restore a shelved change on top of an arbitrary
6190 Since you can restore a shelved change on top of an arbitrary
6190 commit, it is possible that unshelving will result in a conflict
6191 commit, it is possible that unshelving will result in a conflict
6191 between your changes and the commits you are unshelving onto. If
6192 between your changes and the commits you are unshelving onto. If
6192 this occurs, you must resolve the conflict, then use
6193 this occurs, you must resolve the conflict, then use
6193 ``--continue`` to complete the unshelve operation. (The bundle
6194 ``--continue`` to complete the unshelve operation. (The bundle
6194 will not be moved until you successfully complete the unshelve.)
6195 will not be moved until you successfully complete the unshelve.)
6195
6196
6196 (Alternatively, you can use ``--abort`` to abandon an unshelve
6197 (Alternatively, you can use ``--abort`` to abandon an unshelve
6197 that causes a conflict. This reverts the unshelved changes, and
6198 that causes a conflict. This reverts the unshelved changes, and
6198 leaves the bundle in place.)
6199 leaves the bundle in place.)
6199
6200
6200 If bare shelved change (when no files are specified, without interactive,
6201 If bare shelved change (when no files are specified, without interactive,
6201 include and exclude option) was done on newly created branch it would
6202 include and exclude option) was done on newly created branch it would
6202 restore branch information to the working directory.
6203 restore branch information to the working directory.
6203
6204
6204 After a successful unshelve, the shelved changes are stored in a
6205 After a successful unshelve, the shelved changes are stored in a
6205 backup directory. Only the N most recent backups are kept. N
6206 backup directory. Only the N most recent backups are kept. N
6206 defaults to 10 but can be overridden using the ``shelve.maxbackups``
6207 defaults to 10 but can be overridden using the ``shelve.maxbackups``
6207 configuration option.
6208 configuration option.
6208
6209
6209 .. container:: verbose
6210 .. container:: verbose
6210
6211
6211 Timestamp in seconds is used to decide order of backups. More
6212 Timestamp in seconds is used to decide order of backups. More
6212 than ``maxbackups`` backups are kept, if same timestamp
6213 than ``maxbackups`` backups are kept, if same timestamp
6213 prevents from deciding exact order of them, for safety.
6214 prevents from deciding exact order of them, for safety.
6214
6215
6215 Selected changes can be unshelved with ``--interactive`` flag.
6216 Selected changes can be unshelved with ``--interactive`` flag.
6216 The working directory is updated with the selected changes, and
6217 The working directory is updated with the selected changes, and
6217 only the unselected changes remain shelved.
6218 only the unselected changes remain shelved.
6218 Note: The whole shelve is applied to working directory first before
6219 Note: The whole shelve is applied to working directory first before
6219 running interactively. So, this will bring up all the conflicts between
6220 running interactively. So, this will bring up all the conflicts between
6220 working directory and the shelve, irrespective of which changes will be
6221 working directory and the shelve, irrespective of which changes will be
6221 unshelved.
6222 unshelved.
6222 """
6223 """
6223 with repo.wlock():
6224 with repo.wlock():
6224 return shelvemod.dounshelve(ui, repo, *shelved, **opts)
6225 return shelvemod.dounshelve(ui, repo, *shelved, **opts)
6225
6226
6226 statemod.addunfinished(
6227 statemod.addunfinished(
6227 'unshelve', fname='shelvedstate', continueflag=True,
6228 'unshelve', fname='shelvedstate', continueflag=True,
6228 abortfunc=shelvemod.hgabortunshelve,
6229 abortfunc=shelvemod.hgabortunshelve,
6229 continuefunc=shelvemod.hgcontinueunshelve,
6230 continuefunc=shelvemod.hgcontinueunshelve,
6230 cmdmsg=_('unshelve already in progress'),
6231 cmdmsg=_('unshelve already in progress'),
6231 )
6232 )
6232
6233
6233 @command('update|up|checkout|co',
6234 @command('update|up|checkout|co',
6234 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6235 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6235 ('c', 'check', None, _('require clean working directory')),
6236 ('c', 'check', None, _('require clean working directory')),
6236 ('m', 'merge', None, _('merge uncommitted changes')),
6237 ('m', 'merge', None, _('merge uncommitted changes')),
6237 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6238 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6238 ('r', 'rev', '', _('revision'), _('REV'))
6239 ('r', 'rev', '', _('revision'), _('REV'))
6239 ] + mergetoolopts,
6240 ] + mergetoolopts,
6240 _('[-C|-c|-m] [-d DATE] [[-r] REV]'),
6241 _('[-C|-c|-m] [-d DATE] [[-r] REV]'),
6241 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6242 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6242 helpbasic=True)
6243 helpbasic=True)
6243 def update(ui, repo, node=None, **opts):
6244 def update(ui, repo, node=None, **opts):
6244 """update working directory (or switch revisions)
6245 """update working directory (or switch revisions)
6245
6246
6246 Update the repository's working directory to the specified
6247 Update the repository's working directory to the specified
6247 changeset. If no changeset is specified, update to the tip of the
6248 changeset. If no changeset is specified, update to the tip of the
6248 current named branch and move the active bookmark (see :hg:`help
6249 current named branch and move the active bookmark (see :hg:`help
6249 bookmarks`).
6250 bookmarks`).
6250
6251
6251 Update sets the working directory's parent revision to the specified
6252 Update sets the working directory's parent revision to the specified
6252 changeset (see :hg:`help parents`).
6253 changeset (see :hg:`help parents`).
6253
6254
6254 If the changeset is not a descendant or ancestor of the working
6255 If the changeset is not a descendant or ancestor of the working
6255 directory's parent and there are uncommitted changes, the update is
6256 directory's parent and there are uncommitted changes, the update is
6256 aborted. With the -c/--check option, the working directory is checked
6257 aborted. With the -c/--check option, the working directory is checked
6257 for uncommitted changes; if none are found, the working directory is
6258 for uncommitted changes; if none are found, the working directory is
6258 updated to the specified changeset.
6259 updated to the specified changeset.
6259
6260
6260 .. container:: verbose
6261 .. container:: verbose
6261
6262
6262 The -C/--clean, -c/--check, and -m/--merge options control what
6263 The -C/--clean, -c/--check, and -m/--merge options control what
6263 happens if the working directory contains uncommitted changes.
6264 happens if the working directory contains uncommitted changes.
6264 At most of one of them can be specified.
6265 At most of one of them can be specified.
6265
6266
6266 1. If no option is specified, and if
6267 1. If no option is specified, and if
6267 the requested changeset is an ancestor or descendant of
6268 the requested changeset is an ancestor or descendant of
6268 the working directory's parent, the uncommitted changes
6269 the working directory's parent, the uncommitted changes
6269 are merged into the requested changeset and the merged
6270 are merged into the requested changeset and the merged
6270 result is left uncommitted. If the requested changeset is
6271 result is left uncommitted. If the requested changeset is
6271 not an ancestor or descendant (that is, it is on another
6272 not an ancestor or descendant (that is, it is on another
6272 branch), the update is aborted and the uncommitted changes
6273 branch), the update is aborted and the uncommitted changes
6273 are preserved.
6274 are preserved.
6274
6275
6275 2. With the -m/--merge option, the update is allowed even if the
6276 2. With the -m/--merge option, the update is allowed even if the
6276 requested changeset is not an ancestor or descendant of
6277 requested changeset is not an ancestor or descendant of
6277 the working directory's parent.
6278 the working directory's parent.
6278
6279
6279 3. With the -c/--check option, the update is aborted and the
6280 3. With the -c/--check option, the update is aborted and the
6280 uncommitted changes are preserved.
6281 uncommitted changes are preserved.
6281
6282
6282 4. With the -C/--clean option, uncommitted changes are discarded and
6283 4. With the -C/--clean option, uncommitted changes are discarded and
6283 the working directory is updated to the requested changeset.
6284 the working directory is updated to the requested changeset.
6284
6285
6285 To cancel an uncommitted merge (and lose your changes), use
6286 To cancel an uncommitted merge (and lose your changes), use
6286 :hg:`merge --abort`.
6287 :hg:`merge --abort`.
6287
6288
6288 Use null as the changeset to remove the working directory (like
6289 Use null as the changeset to remove the working directory (like
6289 :hg:`clone -U`).
6290 :hg:`clone -U`).
6290
6291
6291 If you want to revert just one file to an older revision, use
6292 If you want to revert just one file to an older revision, use
6292 :hg:`revert [-r REV] NAME`.
6293 :hg:`revert [-r REV] NAME`.
6293
6294
6294 See :hg:`help dates` for a list of formats valid for -d/--date.
6295 See :hg:`help dates` for a list of formats valid for -d/--date.
6295
6296
6296 Returns 0 on success, 1 if there are unresolved files.
6297 Returns 0 on success, 1 if there are unresolved files.
6297 """
6298 """
6298 rev = opts.get(r'rev')
6299 rev = opts.get(r'rev')
6299 date = opts.get(r'date')
6300 date = opts.get(r'date')
6300 clean = opts.get(r'clean')
6301 clean = opts.get(r'clean')
6301 check = opts.get(r'check')
6302 check = opts.get(r'check')
6302 merge = opts.get(r'merge')
6303 merge = opts.get(r'merge')
6303 if rev and node:
6304 if rev and node:
6304 raise error.Abort(_("please specify just one revision"))
6305 raise error.Abort(_("please specify just one revision"))
6305
6306
6306 if ui.configbool('commands', 'update.requiredest'):
6307 if ui.configbool('commands', 'update.requiredest'):
6307 if not node and not rev and not date:
6308 if not node and not rev and not date:
6308 raise error.Abort(_('you must specify a destination'),
6309 raise error.Abort(_('you must specify a destination'),
6309 hint=_('for example: hg update ".::"'))
6310 hint=_('for example: hg update ".::"'))
6310
6311
6311 if rev is None or rev == '':
6312 if rev is None or rev == '':
6312 rev = node
6313 rev = node
6313
6314
6314 if date and rev is not None:
6315 if date and rev is not None:
6315 raise error.Abort(_("you can't specify a revision and a date"))
6316 raise error.Abort(_("you can't specify a revision and a date"))
6316
6317
6317 if len([x for x in (clean, check, merge) if x]) > 1:
6318 if len([x for x in (clean, check, merge) if x]) > 1:
6318 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
6319 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
6319 "or -m/--merge"))
6320 "or -m/--merge"))
6320
6321
6321 updatecheck = None
6322 updatecheck = None
6322 if check:
6323 if check:
6323 updatecheck = 'abort'
6324 updatecheck = 'abort'
6324 elif merge:
6325 elif merge:
6325 updatecheck = 'none'
6326 updatecheck = 'none'
6326
6327
6327 with repo.wlock():
6328 with repo.wlock():
6328 cmdutil.clearunfinished(repo)
6329 cmdutil.clearunfinished(repo)
6329 if date:
6330 if date:
6330 rev = cmdutil.finddate(ui, repo, date)
6331 rev = cmdutil.finddate(ui, repo, date)
6331
6332
6332 # if we defined a bookmark, we have to remember the original name
6333 # if we defined a bookmark, we have to remember the original name
6333 brev = rev
6334 brev = rev
6334 if rev:
6335 if rev:
6335 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
6336 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
6336 ctx = scmutil.revsingle(repo, rev, default=None)
6337 ctx = scmutil.revsingle(repo, rev, default=None)
6337 rev = ctx.rev()
6338 rev = ctx.rev()
6338 hidden = ctx.hidden()
6339 hidden = ctx.hidden()
6339 overrides = {('ui', 'forcemerge'): opts.get(r'tool', '')}
6340 overrides = {('ui', 'forcemerge'): opts.get(r'tool', '')}
6340 with ui.configoverride(overrides, 'update'):
6341 with ui.configoverride(overrides, 'update'):
6341 ret = hg.updatetotally(ui, repo, rev, brev, clean=clean,
6342 ret = hg.updatetotally(ui, repo, rev, brev, clean=clean,
6342 updatecheck=updatecheck)
6343 updatecheck=updatecheck)
6343 if hidden:
6344 if hidden:
6344 ctxstr = ctx.hex()[:12]
6345 ctxstr = ctx.hex()[:12]
6345 ui.warn(_("updated to hidden changeset %s\n") % ctxstr)
6346 ui.warn(_("updated to hidden changeset %s\n") % ctxstr)
6346
6347
6347 if ctx.obsolete():
6348 if ctx.obsolete():
6348 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
6349 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
6349 ui.warn("(%s)\n" % obsfatemsg)
6350 ui.warn("(%s)\n" % obsfatemsg)
6350 return ret
6351 return ret
6351
6352
6352 @command('verify',
6353 @command('verify',
6353 [('', 'full', False, 'perform more checks (EXPERIMENTAL)')],
6354 [('', 'full', False, 'perform more checks (EXPERIMENTAL)')],
6354 helpcategory=command.CATEGORY_MAINTENANCE)
6355 helpcategory=command.CATEGORY_MAINTENANCE)
6355 def verify(ui, repo, **opts):
6356 def verify(ui, repo, **opts):
6356 """verify the integrity of the repository
6357 """verify the integrity of the repository
6357
6358
6358 Verify the integrity of the current repository.
6359 Verify the integrity of the current repository.
6359
6360
6360 This will perform an extensive check of the repository's
6361 This will perform an extensive check of the repository's
6361 integrity, validating the hashes and checksums of each entry in
6362 integrity, validating the hashes and checksums of each entry in
6362 the changelog, manifest, and tracked files, as well as the
6363 the changelog, manifest, and tracked files, as well as the
6363 integrity of their crosslinks and indices.
6364 integrity of their crosslinks and indices.
6364
6365
6365 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
6366 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
6366 for more information about recovery from corruption of the
6367 for more information about recovery from corruption of the
6367 repository.
6368 repository.
6368
6369
6369 Returns 0 on success, 1 if errors are encountered.
6370 Returns 0 on success, 1 if errors are encountered.
6370 """
6371 """
6371 opts = pycompat.byteskwargs(opts)
6372 opts = pycompat.byteskwargs(opts)
6372
6373
6373 level = None
6374 level = None
6374 if opts['full']:
6375 if opts['full']:
6375 level = verifymod.VERIFY_FULL
6376 level = verifymod.VERIFY_FULL
6376 return hg.verify(repo, level)
6377 return hg.verify(repo, level)
6377
6378
6378 @command(
6379 @command(
6379 'version', [] + formatteropts, helpcategory=command.CATEGORY_HELP,
6380 'version', [] + formatteropts, helpcategory=command.CATEGORY_HELP,
6380 norepo=True, intents={INTENT_READONLY})
6381 norepo=True, intents={INTENT_READONLY})
6381 def version_(ui, **opts):
6382 def version_(ui, **opts):
6382 """output version and copyright information
6383 """output version and copyright information
6383
6384
6384 .. container:: verbose
6385 .. container:: verbose
6385
6386
6386 Template:
6387 Template:
6387
6388
6388 The following keywords are supported. See also :hg:`help templates`.
6389 The following keywords are supported. See also :hg:`help templates`.
6389
6390
6390 :extensions: List of extensions.
6391 :extensions: List of extensions.
6391 :ver: String. Version number.
6392 :ver: String. Version number.
6392
6393
6393 And each entry of ``{extensions}`` provides the following sub-keywords
6394 And each entry of ``{extensions}`` provides the following sub-keywords
6394 in addition to ``{ver}``.
6395 in addition to ``{ver}``.
6395
6396
6396 :bundled: Boolean. True if included in the release.
6397 :bundled: Boolean. True if included in the release.
6397 :name: String. Extension name.
6398 :name: String. Extension name.
6398 """
6399 """
6399 opts = pycompat.byteskwargs(opts)
6400 opts = pycompat.byteskwargs(opts)
6400 if ui.verbose:
6401 if ui.verbose:
6401 ui.pager('version')
6402 ui.pager('version')
6402 fm = ui.formatter("version", opts)
6403 fm = ui.formatter("version", opts)
6403 fm.startitem()
6404 fm.startitem()
6404 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
6405 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
6405 util.version())
6406 util.version())
6406 license = _(
6407 license = _(
6407 "(see https://mercurial-scm.org for more information)\n"
6408 "(see https://mercurial-scm.org for more information)\n"
6408 "\nCopyright (C) 2005-2019 Matt Mackall and others\n"
6409 "\nCopyright (C) 2005-2019 Matt Mackall and others\n"
6409 "This is free software; see the source for copying conditions. "
6410 "This is free software; see the source for copying conditions. "
6410 "There is NO\nwarranty; "
6411 "There is NO\nwarranty; "
6411 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6412 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6412 )
6413 )
6413 if not ui.quiet:
6414 if not ui.quiet:
6414 fm.plain(license)
6415 fm.plain(license)
6415
6416
6416 if ui.verbose:
6417 if ui.verbose:
6417 fm.plain(_("\nEnabled extensions:\n\n"))
6418 fm.plain(_("\nEnabled extensions:\n\n"))
6418 # format names and versions into columns
6419 # format names and versions into columns
6419 names = []
6420 names = []
6420 vers = []
6421 vers = []
6421 isinternals = []
6422 isinternals = []
6422 for name, module in extensions.extensions():
6423 for name, module in extensions.extensions():
6423 names.append(name)
6424 names.append(name)
6424 vers.append(extensions.moduleversion(module) or None)
6425 vers.append(extensions.moduleversion(module) or None)
6425 isinternals.append(extensions.ismoduleinternal(module))
6426 isinternals.append(extensions.ismoduleinternal(module))
6426 fn = fm.nested("extensions", tmpl='{name}\n')
6427 fn = fm.nested("extensions", tmpl='{name}\n')
6427 if names:
6428 if names:
6428 namefmt = " %%-%ds " % max(len(n) for n in names)
6429 namefmt = " %%-%ds " % max(len(n) for n in names)
6429 places = [_("external"), _("internal")]
6430 places = [_("external"), _("internal")]
6430 for n, v, p in zip(names, vers, isinternals):
6431 for n, v, p in zip(names, vers, isinternals):
6431 fn.startitem()
6432 fn.startitem()
6432 fn.condwrite(ui.verbose, "name", namefmt, n)
6433 fn.condwrite(ui.verbose, "name", namefmt, n)
6433 if ui.verbose:
6434 if ui.verbose:
6434 fn.plain("%s " % places[p])
6435 fn.plain("%s " % places[p])
6435 fn.data(bundled=p)
6436 fn.data(bundled=p)
6436 fn.condwrite(ui.verbose and v, "ver", "%s", v)
6437 fn.condwrite(ui.verbose and v, "ver", "%s", v)
6437 if ui.verbose:
6438 if ui.verbose:
6438 fn.plain("\n")
6439 fn.plain("\n")
6439 fn.end()
6440 fn.end()
6440 fm.end()
6441 fm.end()
6441
6442
6442 def loadcmdtable(ui, name, cmdtable):
6443 def loadcmdtable(ui, name, cmdtable):
6443 """Load command functions from specified cmdtable
6444 """Load command functions from specified cmdtable
6444 """
6445 """
6445 overrides = [cmd for cmd in cmdtable if cmd in table]
6446 overrides = [cmd for cmd in cmdtable if cmd in table]
6446 if overrides:
6447 if overrides:
6447 ui.warn(_("extension '%s' overrides commands: %s\n")
6448 ui.warn(_("extension '%s' overrides commands: %s\n")
6448 % (name, " ".join(overrides)))
6449 % (name, " ".join(overrides)))
6449 table.update(cmdtable)
6450 table.update(cmdtable)
@@ -1,224 +1,226 b''
1 hide outer repo
1 hide outer repo
2 $ hg init
2 $ hg init
3
3
4 Invalid syntax: no value
4 Invalid syntax: no value
5
5
6 $ cat > .hg/hgrc << EOF
6 $ cat > .hg/hgrc << EOF
7 > novaluekey
7 > novaluekey
8 > EOF
8 > EOF
9 $ hg showconfig
9 $ hg showconfig
10 hg: parse error at $TESTTMP/.hg/hgrc:1: novaluekey
10 hg: parse error at $TESTTMP/.hg/hgrc:1: novaluekey
11 [255]
11 [255]
12
12
13 Invalid syntax: no key
13 Invalid syntax: no key
14
14
15 $ cat > .hg/hgrc << EOF
15 $ cat > .hg/hgrc << EOF
16 > =nokeyvalue
16 > =nokeyvalue
17 > EOF
17 > EOF
18 $ hg showconfig
18 $ hg showconfig
19 hg: parse error at $TESTTMP/.hg/hgrc:1: =nokeyvalue
19 hg: parse error at $TESTTMP/.hg/hgrc:1: =nokeyvalue
20 [255]
20 [255]
21
21
22 Test hint about invalid syntax from leading white space
22 Test hint about invalid syntax from leading white space
23
23
24 $ cat > .hg/hgrc << EOF
24 $ cat > .hg/hgrc << EOF
25 > key=value
25 > key=value
26 > EOF
26 > EOF
27 $ hg showconfig
27 $ hg showconfig
28 hg: parse error at $TESTTMP/.hg/hgrc:1: key=value
28 hg: parse error at $TESTTMP/.hg/hgrc:1: key=value
29 unexpected leading whitespace
29 unexpected leading whitespace
30 [255]
30 [255]
31
31
32 $ cat > .hg/hgrc << EOF
32 $ cat > .hg/hgrc << EOF
33 > [section]
33 > [section]
34 > key=value
34 > key=value
35 > EOF
35 > EOF
36 $ hg showconfig
36 $ hg showconfig
37 hg: parse error at $TESTTMP/.hg/hgrc:1: [section]
37 hg: parse error at $TESTTMP/.hg/hgrc:1: [section]
38 unexpected leading whitespace
38 unexpected leading whitespace
39 [255]
39 [255]
40
40
41 Reset hgrc
41 Reset hgrc
42
42
43 $ echo > .hg/hgrc
43 $ echo > .hg/hgrc
44
44
45 Test case sensitive configuration
45 Test case sensitive configuration
46
46
47 $ cat <<EOF >> $HGRCPATH
47 $ cat <<EOF >> $HGRCPATH
48 > [Section]
48 > [Section]
49 > KeY = Case Sensitive
49 > KeY = Case Sensitive
50 > key = lower case
50 > key = lower case
51 > EOF
51 > EOF
52
52
53 $ hg showconfig Section
53 $ hg showconfig Section
54 Section.KeY=Case Sensitive
54 Section.KeY=Case Sensitive
55 Section.key=lower case
55 Section.key=lower case
56
56
57 $ hg showconfig Section -Tjson
57 $ hg showconfig Section -Tjson
58 [
58 [
59 {
59 {
60 "defaultvalue": null,
60 "name": "Section.KeY",
61 "name": "Section.KeY",
61 "source": "*.hgrc:*", (glob)
62 "source": "*.hgrc:*", (glob)
62 "value": "Case Sensitive"
63 "value": "Case Sensitive"
63 },
64 },
64 {
65 {
66 "defaultvalue": null,
65 "name": "Section.key",
67 "name": "Section.key",
66 "source": "*.hgrc:*", (glob)
68 "source": "*.hgrc:*", (glob)
67 "value": "lower case"
69 "value": "lower case"
68 }
70 }
69 ]
71 ]
70 $ hg showconfig Section.KeY -Tjson
72 $ hg showconfig Section.KeY -Tjson
71 [
73 [
72 {
74 {
73 "defaultvalue": null,
75 "defaultvalue": null,
74 "name": "Section.KeY",
76 "name": "Section.KeY",
75 "source": "*.hgrc:*", (glob)
77 "source": "*.hgrc:*", (glob)
76 "value": "Case Sensitive"
78 "value": "Case Sensitive"
77 }
79 }
78 ]
80 ]
79 $ hg showconfig -Tjson | tail -7
81 $ hg showconfig -Tjson | tail -7
80 },
81 {
82 {
83 "defaultvalue": null,
82 "name": "*", (glob)
84 "name": "*", (glob)
83 "source": "*", (glob)
85 "source": "*", (glob)
84 "value": "*" (glob)
86 "value": "*" (glob)
85 }
87 }
86 ]
88 ]
87
89
88 Test empty config source:
90 Test empty config source:
89
91
90 $ cat <<EOF > emptysource.py
92 $ cat <<EOF > emptysource.py
91 > def reposetup(ui, repo):
93 > def reposetup(ui, repo):
92 > ui.setconfig(b'empty', b'source', b'value')
94 > ui.setconfig(b'empty', b'source', b'value')
93 > EOF
95 > EOF
94 $ cp .hg/hgrc .hg/hgrc.orig
96 $ cp .hg/hgrc .hg/hgrc.orig
95 $ cat <<EOF >> .hg/hgrc
97 $ cat <<EOF >> .hg/hgrc
96 > [extensions]
98 > [extensions]
97 > emptysource = `pwd`/emptysource.py
99 > emptysource = `pwd`/emptysource.py
98 > EOF
100 > EOF
99
101
100 $ hg config --debug empty.source
102 $ hg config --debug empty.source
101 read config from: * (glob)
103 read config from: * (glob)
102 none: value
104 none: value
103 $ hg config empty.source -Tjson
105 $ hg config empty.source -Tjson
104 [
106 [
105 {
107 {
106 "defaultvalue": null,
108 "defaultvalue": null,
107 "name": "empty.source",
109 "name": "empty.source",
108 "source": "",
110 "source": "",
109 "value": "value"
111 "value": "value"
110 }
112 }
111 ]
113 ]
112
114
113 $ cp .hg/hgrc.orig .hg/hgrc
115 $ cp .hg/hgrc.orig .hg/hgrc
114
116
115 Test "%unset"
117 Test "%unset"
116
118
117 $ cat >> $HGRCPATH <<EOF
119 $ cat >> $HGRCPATH <<EOF
118 > [unsettest]
120 > [unsettest]
119 > local-hgrcpath = should be unset (HGRCPATH)
121 > local-hgrcpath = should be unset (HGRCPATH)
120 > %unset local-hgrcpath
122 > %unset local-hgrcpath
121 >
123 >
122 > global = should be unset (HGRCPATH)
124 > global = should be unset (HGRCPATH)
123 >
125 >
124 > both = should be unset (HGRCPATH)
126 > both = should be unset (HGRCPATH)
125 >
127 >
126 > set-after-unset = should be unset (HGRCPATH)
128 > set-after-unset = should be unset (HGRCPATH)
127 > EOF
129 > EOF
128
130
129 $ cat >> .hg/hgrc <<EOF
131 $ cat >> .hg/hgrc <<EOF
130 > [unsettest]
132 > [unsettest]
131 > local-hgrc = should be unset (.hg/hgrc)
133 > local-hgrc = should be unset (.hg/hgrc)
132 > %unset local-hgrc
134 > %unset local-hgrc
133 >
135 >
134 > %unset global
136 > %unset global
135 >
137 >
136 > both = should be unset (.hg/hgrc)
138 > both = should be unset (.hg/hgrc)
137 > %unset both
139 > %unset both
138 >
140 >
139 > set-after-unset = should be unset (.hg/hgrc)
141 > set-after-unset = should be unset (.hg/hgrc)
140 > %unset set-after-unset
142 > %unset set-after-unset
141 > set-after-unset = should be set (.hg/hgrc)
143 > set-after-unset = should be set (.hg/hgrc)
142 > EOF
144 > EOF
143
145
144 $ hg showconfig unsettest
146 $ hg showconfig unsettest
145 unsettest.set-after-unset=should be set (.hg/hgrc)
147 unsettest.set-after-unset=should be set (.hg/hgrc)
146
148
147 Test exit code when no config matches
149 Test exit code when no config matches
148
150
149 $ hg config Section.idontexist
151 $ hg config Section.idontexist
150 [1]
152 [1]
151
153
152 sub-options in [paths] aren't expanded
154 sub-options in [paths] aren't expanded
153
155
154 $ cat > .hg/hgrc << EOF
156 $ cat > .hg/hgrc << EOF
155 > [paths]
157 > [paths]
156 > foo = ~/foo
158 > foo = ~/foo
157 > foo:suboption = ~/foo
159 > foo:suboption = ~/foo
158 > EOF
160 > EOF
159
161
160 $ hg showconfig paths
162 $ hg showconfig paths
161 paths.foo:suboption=~/foo
163 paths.foo:suboption=~/foo
162 paths.foo=$TESTTMP/foo
164 paths.foo=$TESTTMP/foo
163
165
164 edit failure
166 edit failure
165
167
166 $ HGEDITOR=false hg config --edit
168 $ HGEDITOR=false hg config --edit
167 abort: edit failed: false exited with status 1
169 abort: edit failed: false exited with status 1
168 [255]
170 [255]
169
171
170 config affected by environment variables
172 config affected by environment variables
171
173
172 $ EDITOR=e1 VISUAL=e2 hg config --debug | grep 'ui\.editor'
174 $ EDITOR=e1 VISUAL=e2 hg config --debug | grep 'ui\.editor'
173 $VISUAL: ui.editor=e2
175 $VISUAL: ui.editor=e2
174
176
175 $ VISUAL=e2 hg config --debug --config ui.editor=e3 | grep 'ui\.editor'
177 $ VISUAL=e2 hg config --debug --config ui.editor=e3 | grep 'ui\.editor'
176 --config: ui.editor=e3
178 --config: ui.editor=e3
177
179
178 $ PAGER=p1 hg config --debug | grep 'pager\.pager'
180 $ PAGER=p1 hg config --debug | grep 'pager\.pager'
179 $PAGER: pager.pager=p1
181 $PAGER: pager.pager=p1
180
182
181 $ PAGER=p1 hg config --debug --config pager.pager=p2 | grep 'pager\.pager'
183 $ PAGER=p1 hg config --debug --config pager.pager=p2 | grep 'pager\.pager'
182 --config: pager.pager=p2
184 --config: pager.pager=p2
183
185
184 verify that aliases are evaluated as well
186 verify that aliases are evaluated as well
185
187
186 $ hg init aliastest
188 $ hg init aliastest
187 $ cd aliastest
189 $ cd aliastest
188 $ cat > .hg/hgrc << EOF
190 $ cat > .hg/hgrc << EOF
189 > [ui]
191 > [ui]
190 > user = repo user
192 > user = repo user
191 > EOF
193 > EOF
192 $ touch index
194 $ touch index
193 $ unset HGUSER
195 $ unset HGUSER
194 $ hg ci -Am test
196 $ hg ci -Am test
195 adding index
197 adding index
196 $ hg log --template '{author}\n'
198 $ hg log --template '{author}\n'
197 repo user
199 repo user
198 $ cd ..
200 $ cd ..
199
201
200 alias has lower priority
202 alias has lower priority
201
203
202 $ hg init aliaspriority
204 $ hg init aliaspriority
203 $ cd aliaspriority
205 $ cd aliaspriority
204 $ cat > .hg/hgrc << EOF
206 $ cat > .hg/hgrc << EOF
205 > [ui]
207 > [ui]
206 > user = alias user
208 > user = alias user
207 > username = repo user
209 > username = repo user
208 > EOF
210 > EOF
209 $ touch index
211 $ touch index
210 $ unset HGUSER
212 $ unset HGUSER
211 $ hg ci -Am test
213 $ hg ci -Am test
212 adding index
214 adding index
213 $ hg log --template '{author}\n'
215 $ hg log --template '{author}\n'
214 repo user
216 repo user
215 $ cd ..
217 $ cd ..
216
218
217 configs should be read in lexicographical order
219 configs should be read in lexicographical order
218
220
219 $ mkdir configs
221 $ mkdir configs
220 $ for i in `$TESTDIR/seq.py 10 99`; do
222 $ for i in `$TESTDIR/seq.py 10 99`; do
221 > printf "[section]\nkey=$i" > configs/$i.rc
223 > printf "[section]\nkey=$i" > configs/$i.rc
222 > done
224 > done
223 $ HGRCPATH=configs hg config section.key
225 $ HGRCPATH=configs hg config section.key
224 99
226 99
General Comments 0
You need to be logged in to leave comments. Login now