##// END OF EJS Templates
bookmarks: reject --delete with --inactive which makes no sense...
Yuya Nishihara -
r39788:2d478b05 default
parent child Browse files
Show More
@@ -1,5916 +1,5916 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 )
22 )
23 from . import (
23 from . import (
24 archival,
24 archival,
25 bookmarks,
25 bookmarks,
26 bundle2,
26 bundle2,
27 changegroup,
27 changegroup,
28 cmdutil,
28 cmdutil,
29 copies,
29 copies,
30 debugcommands as debugcommandsmod,
30 debugcommands as debugcommandsmod,
31 destutil,
31 destutil,
32 dirstateguard,
32 dirstateguard,
33 discovery,
33 discovery,
34 encoding,
34 encoding,
35 error,
35 error,
36 exchange,
36 exchange,
37 extensions,
37 extensions,
38 filemerge,
38 filemerge,
39 formatter,
39 formatter,
40 graphmod,
40 graphmod,
41 hbisect,
41 hbisect,
42 help,
42 help,
43 hg,
43 hg,
44 logcmdutil,
44 logcmdutil,
45 match as matchmod,
45 match as matchmod,
46 merge as mergemod,
46 merge as mergemod,
47 narrowspec,
47 narrowspec,
48 obsolete,
48 obsolete,
49 obsutil,
49 obsutil,
50 patch,
50 patch,
51 phases,
51 phases,
52 pycompat,
52 pycompat,
53 rcutil,
53 rcutil,
54 registrar,
54 registrar,
55 repair,
55 repair,
56 revsetlang,
56 revsetlang,
57 rewriteutil,
57 rewriteutil,
58 scmutil,
58 scmutil,
59 server,
59 server,
60 state as statemod,
60 state as statemod,
61 streamclone,
61 streamclone,
62 tags as tagsmod,
62 tags as tagsmod,
63 templatekw,
63 templatekw,
64 ui as uimod,
64 ui as uimod,
65 util,
65 util,
66 wireprotoserver,
66 wireprotoserver,
67 )
67 )
68 from .utils import (
68 from .utils import (
69 dateutil,
69 dateutil,
70 stringutil,
70 stringutil,
71 )
71 )
72
72
73 table = {}
73 table = {}
74 table.update(debugcommandsmod.command._table)
74 table.update(debugcommandsmod.command._table)
75
75
76 command = registrar.command(table)
76 command = registrar.command(table)
77 INTENT_READONLY = registrar.INTENT_READONLY
77 INTENT_READONLY = registrar.INTENT_READONLY
78
78
79 # common command options
79 # common command options
80
80
81 globalopts = [
81 globalopts = [
82 ('R', 'repository', '',
82 ('R', 'repository', '',
83 _('repository root directory or name of overlay bundle file'),
83 _('repository root directory or name of overlay bundle file'),
84 _('REPO')),
84 _('REPO')),
85 ('', 'cwd', '',
85 ('', 'cwd', '',
86 _('change working directory'), _('DIR')),
86 _('change working directory'), _('DIR')),
87 ('y', 'noninteractive', None,
87 ('y', 'noninteractive', None,
88 _('do not prompt, automatically pick the first choice for all prompts')),
88 _('do not prompt, automatically pick the first choice for all prompts')),
89 ('q', 'quiet', None, _('suppress output')),
89 ('q', 'quiet', None, _('suppress output')),
90 ('v', 'verbose', None, _('enable additional output')),
90 ('v', 'verbose', None, _('enable additional output')),
91 ('', 'color', '',
91 ('', 'color', '',
92 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
92 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
93 # and should not be translated
93 # and should not be translated
94 _("when to colorize (boolean, always, auto, never, or debug)"),
94 _("when to colorize (boolean, always, auto, never, or debug)"),
95 _('TYPE')),
95 _('TYPE')),
96 ('', 'config', [],
96 ('', 'config', [],
97 _('set/override config option (use \'section.name=value\')'),
97 _('set/override config option (use \'section.name=value\')'),
98 _('CONFIG')),
98 _('CONFIG')),
99 ('', 'debug', None, _('enable debugging output')),
99 ('', 'debug', None, _('enable debugging output')),
100 ('', 'debugger', None, _('start debugger')),
100 ('', 'debugger', None, _('start debugger')),
101 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
101 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
102 _('ENCODE')),
102 _('ENCODE')),
103 ('', 'encodingmode', encoding.encodingmode,
103 ('', 'encodingmode', encoding.encodingmode,
104 _('set the charset encoding mode'), _('MODE')),
104 _('set the charset encoding mode'), _('MODE')),
105 ('', 'traceback', None, _('always print a traceback on exception')),
105 ('', 'traceback', None, _('always print a traceback on exception')),
106 ('', 'time', None, _('time how long the command takes')),
106 ('', 'time', None, _('time how long the command takes')),
107 ('', 'profile', None, _('print command execution profile')),
107 ('', 'profile', None, _('print command execution profile')),
108 ('', 'version', None, _('output version information and exit')),
108 ('', 'version', None, _('output version information and exit')),
109 ('h', 'help', None, _('display help and exit')),
109 ('h', 'help', None, _('display help and exit')),
110 ('', 'hidden', False, _('consider hidden changesets')),
110 ('', 'hidden', False, _('consider hidden changesets')),
111 ('', 'pager', 'auto',
111 ('', 'pager', 'auto',
112 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
112 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
113 ]
113 ]
114
114
115 dryrunopts = cmdutil.dryrunopts
115 dryrunopts = cmdutil.dryrunopts
116 remoteopts = cmdutil.remoteopts
116 remoteopts = cmdutil.remoteopts
117 walkopts = cmdutil.walkopts
117 walkopts = cmdutil.walkopts
118 commitopts = cmdutil.commitopts
118 commitopts = cmdutil.commitopts
119 commitopts2 = cmdutil.commitopts2
119 commitopts2 = cmdutil.commitopts2
120 formatteropts = cmdutil.formatteropts
120 formatteropts = cmdutil.formatteropts
121 templateopts = cmdutil.templateopts
121 templateopts = cmdutil.templateopts
122 logopts = cmdutil.logopts
122 logopts = cmdutil.logopts
123 diffopts = cmdutil.diffopts
123 diffopts = cmdutil.diffopts
124 diffwsopts = cmdutil.diffwsopts
124 diffwsopts = cmdutil.diffwsopts
125 diffopts2 = cmdutil.diffopts2
125 diffopts2 = cmdutil.diffopts2
126 mergetoolopts = cmdutil.mergetoolopts
126 mergetoolopts = cmdutil.mergetoolopts
127 similarityopts = cmdutil.similarityopts
127 similarityopts = cmdutil.similarityopts
128 subrepoopts = cmdutil.subrepoopts
128 subrepoopts = cmdutil.subrepoopts
129 debugrevlogopts = cmdutil.debugrevlogopts
129 debugrevlogopts = cmdutil.debugrevlogopts
130
130
131 # Commands start here, listed alphabetically
131 # Commands start here, listed alphabetically
132
132
133 @command('^add',
133 @command('^add',
134 walkopts + subrepoopts + dryrunopts,
134 walkopts + subrepoopts + dryrunopts,
135 _('[OPTION]... [FILE]...'),
135 _('[OPTION]... [FILE]...'),
136 inferrepo=True)
136 inferrepo=True)
137 def add(ui, repo, *pats, **opts):
137 def add(ui, repo, *pats, **opts):
138 """add the specified files on the next commit
138 """add the specified files on the next commit
139
139
140 Schedule files to be version controlled and added to the
140 Schedule files to be version controlled and added to the
141 repository.
141 repository.
142
142
143 The files will be added to the repository at the next commit. To
143 The files will be added to the repository at the next commit. To
144 undo an add before that, see :hg:`forget`.
144 undo an add before that, see :hg:`forget`.
145
145
146 If no names are given, add all files to the repository (except
146 If no names are given, add all files to the repository (except
147 files matching ``.hgignore``).
147 files matching ``.hgignore``).
148
148
149 .. container:: verbose
149 .. container:: verbose
150
150
151 Examples:
151 Examples:
152
152
153 - New (unknown) files are added
153 - New (unknown) files are added
154 automatically by :hg:`add`::
154 automatically by :hg:`add`::
155
155
156 $ ls
156 $ ls
157 foo.c
157 foo.c
158 $ hg status
158 $ hg status
159 ? foo.c
159 ? foo.c
160 $ hg add
160 $ hg add
161 adding foo.c
161 adding foo.c
162 $ hg status
162 $ hg status
163 A foo.c
163 A foo.c
164
164
165 - Specific files to be added can be specified::
165 - Specific files to be added can be specified::
166
166
167 $ ls
167 $ ls
168 bar.c foo.c
168 bar.c foo.c
169 $ hg status
169 $ hg status
170 ? bar.c
170 ? bar.c
171 ? foo.c
171 ? foo.c
172 $ hg add bar.c
172 $ hg add bar.c
173 $ hg status
173 $ hg status
174 A bar.c
174 A bar.c
175 ? foo.c
175 ? foo.c
176
176
177 Returns 0 if all files are successfully added.
177 Returns 0 if all files are successfully added.
178 """
178 """
179
179
180 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
180 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
181 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
181 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
182 return rejected and 1 or 0
182 return rejected and 1 or 0
183
183
184 @command('addremove',
184 @command('addremove',
185 similarityopts + subrepoopts + walkopts + dryrunopts,
185 similarityopts + subrepoopts + walkopts + dryrunopts,
186 _('[OPTION]... [FILE]...'),
186 _('[OPTION]... [FILE]...'),
187 inferrepo=True)
187 inferrepo=True)
188 def addremove(ui, repo, *pats, **opts):
188 def addremove(ui, repo, *pats, **opts):
189 """add all new files, delete all missing files
189 """add all new files, delete all missing files
190
190
191 Add all new files and remove all missing files from the
191 Add all new files and remove all missing files from the
192 repository.
192 repository.
193
193
194 Unless names are given, new files are ignored if they match any of
194 Unless names are given, new files are ignored if they match any of
195 the patterns in ``.hgignore``. As with add, these changes take
195 the patterns in ``.hgignore``. As with add, these changes take
196 effect at the next commit.
196 effect at the next commit.
197
197
198 Use the -s/--similarity option to detect renamed files. This
198 Use the -s/--similarity option to detect renamed files. This
199 option takes a percentage between 0 (disabled) and 100 (files must
199 option takes a percentage between 0 (disabled) and 100 (files must
200 be identical) as its parameter. With a parameter greater than 0,
200 be identical) as its parameter. With a parameter greater than 0,
201 this compares every removed file with every added file and records
201 this compares every removed file with every added file and records
202 those similar enough as renames. Detecting renamed files this way
202 those similar enough as renames. Detecting renamed files this way
203 can be expensive. After using this option, :hg:`status -C` can be
203 can be expensive. After using this option, :hg:`status -C` can be
204 used to check which files were identified as moved or renamed. If
204 used to check which files were identified as moved or renamed. If
205 not specified, -s/--similarity defaults to 100 and only renames of
205 not specified, -s/--similarity defaults to 100 and only renames of
206 identical files are detected.
206 identical files are detected.
207
207
208 .. container:: verbose
208 .. container:: verbose
209
209
210 Examples:
210 Examples:
211
211
212 - A number of files (bar.c and foo.c) are new,
212 - A number of files (bar.c and foo.c) are new,
213 while foobar.c has been removed (without using :hg:`remove`)
213 while foobar.c has been removed (without using :hg:`remove`)
214 from the repository::
214 from the repository::
215
215
216 $ ls
216 $ ls
217 bar.c foo.c
217 bar.c foo.c
218 $ hg status
218 $ hg status
219 ! foobar.c
219 ! foobar.c
220 ? bar.c
220 ? bar.c
221 ? foo.c
221 ? foo.c
222 $ hg addremove
222 $ hg addremove
223 adding bar.c
223 adding bar.c
224 adding foo.c
224 adding foo.c
225 removing foobar.c
225 removing foobar.c
226 $ hg status
226 $ hg status
227 A bar.c
227 A bar.c
228 A foo.c
228 A foo.c
229 R foobar.c
229 R foobar.c
230
230
231 - A file foobar.c was moved to foo.c without using :hg:`rename`.
231 - A file foobar.c was moved to foo.c without using :hg:`rename`.
232 Afterwards, it was edited slightly::
232 Afterwards, it was edited slightly::
233
233
234 $ ls
234 $ ls
235 foo.c
235 foo.c
236 $ hg status
236 $ hg status
237 ! foobar.c
237 ! foobar.c
238 ? foo.c
238 ? foo.c
239 $ hg addremove --similarity 90
239 $ hg addremove --similarity 90
240 removing foobar.c
240 removing foobar.c
241 adding foo.c
241 adding foo.c
242 recording removal of foobar.c as rename to foo.c (94% similar)
242 recording removal of foobar.c as rename to foo.c (94% similar)
243 $ hg status -C
243 $ hg status -C
244 A foo.c
244 A foo.c
245 foobar.c
245 foobar.c
246 R foobar.c
246 R foobar.c
247
247
248 Returns 0 if all files are successfully added.
248 Returns 0 if all files are successfully added.
249 """
249 """
250 opts = pycompat.byteskwargs(opts)
250 opts = pycompat.byteskwargs(opts)
251 if not opts.get('similarity'):
251 if not opts.get('similarity'):
252 opts['similarity'] = '100'
252 opts['similarity'] = '100'
253 matcher = scmutil.match(repo[None], pats, opts)
253 matcher = scmutil.match(repo[None], pats, opts)
254 return scmutil.addremove(repo, matcher, "", opts)
254 return scmutil.addremove(repo, matcher, "", opts)
255
255
256 @command('^annotate|blame',
256 @command('^annotate|blame',
257 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
257 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
258 ('', 'follow', None,
258 ('', 'follow', None,
259 _('follow copies/renames and list the filename (DEPRECATED)')),
259 _('follow copies/renames and list the filename (DEPRECATED)')),
260 ('', 'no-follow', None, _("don't follow copies and renames")),
260 ('', 'no-follow', None, _("don't follow copies and renames")),
261 ('a', 'text', None, _('treat all files as text')),
261 ('a', 'text', None, _('treat all files as text')),
262 ('u', 'user', None, _('list the author (long with -v)')),
262 ('u', 'user', None, _('list the author (long with -v)')),
263 ('f', 'file', None, _('list the filename')),
263 ('f', 'file', None, _('list the filename')),
264 ('d', 'date', None, _('list the date (short with -q)')),
264 ('d', 'date', None, _('list the date (short with -q)')),
265 ('n', 'number', None, _('list the revision number (default)')),
265 ('n', 'number', None, _('list the revision number (default)')),
266 ('c', 'changeset', None, _('list the changeset')),
266 ('c', 'changeset', None, _('list the changeset')),
267 ('l', 'line-number', None, _('show line number at the first appearance')),
267 ('l', 'line-number', None, _('show line number at the first appearance')),
268 ('', 'skip', [], _('revision to not display (EXPERIMENTAL)'), _('REV')),
268 ('', 'skip', [], _('revision to not display (EXPERIMENTAL)'), _('REV')),
269 ] + diffwsopts + walkopts + formatteropts,
269 ] + diffwsopts + walkopts + formatteropts,
270 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
270 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
271 inferrepo=True)
271 inferrepo=True)
272 def annotate(ui, repo, *pats, **opts):
272 def annotate(ui, repo, *pats, **opts):
273 """show changeset information by line for each file
273 """show changeset information by line for each file
274
274
275 List changes in files, showing the revision id responsible for
275 List changes in files, showing the revision id responsible for
276 each line.
276 each line.
277
277
278 This command is useful for discovering when a change was made and
278 This command is useful for discovering when a change was made and
279 by whom.
279 by whom.
280
280
281 If you include --file, --user, or --date, the revision number is
281 If you include --file, --user, or --date, the revision number is
282 suppressed unless you also include --number.
282 suppressed unless you also include --number.
283
283
284 Without the -a/--text option, annotate will avoid processing files
284 Without the -a/--text option, annotate will avoid processing files
285 it detects as binary. With -a, annotate will annotate the file
285 it detects as binary. With -a, annotate will annotate the file
286 anyway, although the results will probably be neither useful
286 anyway, although the results will probably be neither useful
287 nor desirable.
287 nor desirable.
288
288
289 Returns 0 on success.
289 Returns 0 on success.
290 """
290 """
291 opts = pycompat.byteskwargs(opts)
291 opts = pycompat.byteskwargs(opts)
292 if not pats:
292 if not pats:
293 raise error.Abort(_('at least one filename or pattern is required'))
293 raise error.Abort(_('at least one filename or pattern is required'))
294
294
295 if opts.get('follow'):
295 if opts.get('follow'):
296 # --follow is deprecated and now just an alias for -f/--file
296 # --follow is deprecated and now just an alias for -f/--file
297 # to mimic the behavior of Mercurial before version 1.5
297 # to mimic the behavior of Mercurial before version 1.5
298 opts['file'] = True
298 opts['file'] = True
299
299
300 rev = opts.get('rev')
300 rev = opts.get('rev')
301 if rev:
301 if rev:
302 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
302 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
303 ctx = scmutil.revsingle(repo, rev)
303 ctx = scmutil.revsingle(repo, rev)
304
304
305 rootfm = ui.formatter('annotate', opts)
305 rootfm = ui.formatter('annotate', opts)
306 if ui.quiet:
306 if ui.quiet:
307 datefunc = dateutil.shortdate
307 datefunc = dateutil.shortdate
308 else:
308 else:
309 datefunc = dateutil.datestr
309 datefunc = dateutil.datestr
310 if ctx.rev() is None:
310 if ctx.rev() is None:
311 def hexfn(node):
311 def hexfn(node):
312 if node is None:
312 if node is None:
313 return None
313 return None
314 else:
314 else:
315 return rootfm.hexfunc(node)
315 return rootfm.hexfunc(node)
316 if opts.get('changeset'):
316 if opts.get('changeset'):
317 # omit "+" suffix which is appended to node hex
317 # omit "+" suffix which is appended to node hex
318 def formatrev(rev):
318 def formatrev(rev):
319 if rev is None:
319 if rev is None:
320 return '%d' % ctx.p1().rev()
320 return '%d' % ctx.p1().rev()
321 else:
321 else:
322 return '%d' % rev
322 return '%d' % rev
323 else:
323 else:
324 def formatrev(rev):
324 def formatrev(rev):
325 if rev is None:
325 if rev is None:
326 return '%d+' % ctx.p1().rev()
326 return '%d+' % ctx.p1().rev()
327 else:
327 else:
328 return '%d ' % rev
328 return '%d ' % rev
329 def formathex(hex):
329 def formathex(hex):
330 if hex is None:
330 if hex is None:
331 return '%s+' % rootfm.hexfunc(ctx.p1().node())
331 return '%s+' % rootfm.hexfunc(ctx.p1().node())
332 else:
332 else:
333 return '%s ' % hex
333 return '%s ' % hex
334 else:
334 else:
335 hexfn = rootfm.hexfunc
335 hexfn = rootfm.hexfunc
336 formatrev = formathex = pycompat.bytestr
336 formatrev = formathex = pycompat.bytestr
337
337
338 opmap = [('user', ' ', lambda x: x.fctx.user(), ui.shortuser),
338 opmap = [('user', ' ', lambda x: x.fctx.user(), ui.shortuser),
339 ('rev', ' ', lambda x: x.fctx.rev(), formatrev),
339 ('rev', ' ', lambda x: x.fctx.rev(), formatrev),
340 ('node', ' ', lambda x: hexfn(x.fctx.node()), formathex),
340 ('node', ' ', lambda x: hexfn(x.fctx.node()), formathex),
341 ('date', ' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
341 ('date', ' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
342 ('path', ' ', lambda x: x.fctx.path(), pycompat.bytestr),
342 ('path', ' ', lambda x: x.fctx.path(), pycompat.bytestr),
343 ('line_number', ':', lambda x: x.lineno, pycompat.bytestr),
343 ('line_number', ':', lambda x: x.lineno, pycompat.bytestr),
344 ]
344 ]
345 opnamemap = {'rev': 'number', 'node': 'changeset', 'path': 'file'}
345 opnamemap = {'rev': 'number', 'node': 'changeset', 'path': 'file'}
346
346
347 if (not opts.get('user') and not opts.get('changeset')
347 if (not opts.get('user') and not opts.get('changeset')
348 and not opts.get('date') and not opts.get('file')):
348 and not opts.get('date') and not opts.get('file')):
349 opts['number'] = True
349 opts['number'] = True
350
350
351 linenumber = opts.get('line_number') is not None
351 linenumber = opts.get('line_number') is not None
352 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
352 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
353 raise error.Abort(_('at least one of -n/-c is required for -l'))
353 raise error.Abort(_('at least one of -n/-c is required for -l'))
354
354
355 ui.pager('annotate')
355 ui.pager('annotate')
356
356
357 if rootfm.isplain():
357 if rootfm.isplain():
358 def makefunc(get, fmt):
358 def makefunc(get, fmt):
359 return lambda x: fmt(get(x))
359 return lambda x: fmt(get(x))
360 else:
360 else:
361 def makefunc(get, fmt):
361 def makefunc(get, fmt):
362 return get
362 return get
363 datahint = rootfm.datahint()
363 datahint = rootfm.datahint()
364 funcmap = [(makefunc(get, fmt), sep) for fn, sep, get, fmt in opmap
364 funcmap = [(makefunc(get, fmt), sep) for fn, sep, get, fmt in opmap
365 if opts.get(opnamemap.get(fn, fn)) or fn in datahint]
365 if opts.get(opnamemap.get(fn, fn)) or fn in datahint]
366 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
366 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
367 fields = ' '.join(fn for fn, sep, get, fmt in opmap
367 fields = ' '.join(fn for fn, sep, get, fmt in opmap
368 if opts.get(opnamemap.get(fn, fn)) or fn in datahint)
368 if opts.get(opnamemap.get(fn, fn)) or fn in datahint)
369
369
370 def bad(x, y):
370 def bad(x, y):
371 raise error.Abort("%s: %s" % (x, y))
371 raise error.Abort("%s: %s" % (x, y))
372
372
373 m = scmutil.match(ctx, pats, opts, badfn=bad)
373 m = scmutil.match(ctx, pats, opts, badfn=bad)
374
374
375 follow = not opts.get('no_follow')
375 follow = not opts.get('no_follow')
376 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
376 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
377 whitespace=True)
377 whitespace=True)
378 skiprevs = opts.get('skip')
378 skiprevs = opts.get('skip')
379 if skiprevs:
379 if skiprevs:
380 skiprevs = scmutil.revrange(repo, skiprevs)
380 skiprevs = scmutil.revrange(repo, skiprevs)
381
381
382 for abs in ctx.walk(m):
382 for abs in ctx.walk(m):
383 fctx = ctx[abs]
383 fctx = ctx[abs]
384 rootfm.startitem()
384 rootfm.startitem()
385 rootfm.data(path=abs)
385 rootfm.data(path=abs)
386 if not opts.get('text') and fctx.isbinary():
386 if not opts.get('text') and fctx.isbinary():
387 rootfm.plain(_("%s: binary file\n")
387 rootfm.plain(_("%s: binary file\n")
388 % ((pats and m.rel(abs)) or abs))
388 % ((pats and m.rel(abs)) or abs))
389 continue
389 continue
390
390
391 fm = rootfm.nested('lines', tmpl='{rev}: {line}')
391 fm = rootfm.nested('lines', tmpl='{rev}: {line}')
392 lines = fctx.annotate(follow=follow, skiprevs=skiprevs,
392 lines = fctx.annotate(follow=follow, skiprevs=skiprevs,
393 diffopts=diffopts)
393 diffopts=diffopts)
394 if not lines:
394 if not lines:
395 fm.end()
395 fm.end()
396 continue
396 continue
397 formats = []
397 formats = []
398 pieces = []
398 pieces = []
399
399
400 for f, sep in funcmap:
400 for f, sep in funcmap:
401 l = [f(n) for n in lines]
401 l = [f(n) for n in lines]
402 if fm.isplain():
402 if fm.isplain():
403 sizes = [encoding.colwidth(x) for x in l]
403 sizes = [encoding.colwidth(x) for x in l]
404 ml = max(sizes)
404 ml = max(sizes)
405 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
405 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
406 else:
406 else:
407 formats.append(['%s' for x in l])
407 formats.append(['%s' for x in l])
408 pieces.append(l)
408 pieces.append(l)
409
409
410 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
410 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
411 fm.startitem()
411 fm.startitem()
412 fm.context(fctx=n.fctx)
412 fm.context(fctx=n.fctx)
413 fm.write(fields, "".join(f), *p)
413 fm.write(fields, "".join(f), *p)
414 if n.skip:
414 if n.skip:
415 fmt = "* %s"
415 fmt = "* %s"
416 else:
416 else:
417 fmt = ": %s"
417 fmt = ": %s"
418 fm.write('line', fmt, n.text)
418 fm.write('line', fmt, n.text)
419
419
420 if not lines[-1].text.endswith('\n'):
420 if not lines[-1].text.endswith('\n'):
421 fm.plain('\n')
421 fm.plain('\n')
422 fm.end()
422 fm.end()
423
423
424 rootfm.end()
424 rootfm.end()
425
425
426 @command('archive',
426 @command('archive',
427 [('', 'no-decode', None, _('do not pass files through decoders')),
427 [('', 'no-decode', None, _('do not pass files through decoders')),
428 ('p', 'prefix', '', _('directory prefix for files in archive'),
428 ('p', 'prefix', '', _('directory prefix for files in archive'),
429 _('PREFIX')),
429 _('PREFIX')),
430 ('r', 'rev', '', _('revision to distribute'), _('REV')),
430 ('r', 'rev', '', _('revision to distribute'), _('REV')),
431 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
431 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
432 ] + subrepoopts + walkopts,
432 ] + subrepoopts + walkopts,
433 _('[OPTION]... DEST'))
433 _('[OPTION]... DEST'))
434 def archive(ui, repo, dest, **opts):
434 def archive(ui, repo, dest, **opts):
435 '''create an unversioned archive of a repository revision
435 '''create an unversioned archive of a repository revision
436
436
437 By default, the revision used is the parent of the working
437 By default, the revision used is the parent of the working
438 directory; use -r/--rev to specify a different revision.
438 directory; use -r/--rev to specify a different revision.
439
439
440 The archive type is automatically detected based on file
440 The archive type is automatically detected based on file
441 extension (to override, use -t/--type).
441 extension (to override, use -t/--type).
442
442
443 .. container:: verbose
443 .. container:: verbose
444
444
445 Examples:
445 Examples:
446
446
447 - create a zip file containing the 1.0 release::
447 - create a zip file containing the 1.0 release::
448
448
449 hg archive -r 1.0 project-1.0.zip
449 hg archive -r 1.0 project-1.0.zip
450
450
451 - create a tarball excluding .hg files::
451 - create a tarball excluding .hg files::
452
452
453 hg archive project.tar.gz -X ".hg*"
453 hg archive project.tar.gz -X ".hg*"
454
454
455 Valid types are:
455 Valid types are:
456
456
457 :``files``: a directory full of files (default)
457 :``files``: a directory full of files (default)
458 :``tar``: tar archive, uncompressed
458 :``tar``: tar archive, uncompressed
459 :``tbz2``: tar archive, compressed using bzip2
459 :``tbz2``: tar archive, compressed using bzip2
460 :``tgz``: tar archive, compressed using gzip
460 :``tgz``: tar archive, compressed using gzip
461 :``uzip``: zip archive, uncompressed
461 :``uzip``: zip archive, uncompressed
462 :``zip``: zip archive, compressed using deflate
462 :``zip``: zip archive, compressed using deflate
463
463
464 The exact name of the destination archive or directory is given
464 The exact name of the destination archive or directory is given
465 using a format string; see :hg:`help export` for details.
465 using a format string; see :hg:`help export` for details.
466
466
467 Each member added to an archive file has a directory prefix
467 Each member added to an archive file has a directory prefix
468 prepended. Use -p/--prefix to specify a format string for the
468 prepended. Use -p/--prefix to specify a format string for the
469 prefix. The default is the basename of the archive, with suffixes
469 prefix. The default is the basename of the archive, with suffixes
470 removed.
470 removed.
471
471
472 Returns 0 on success.
472 Returns 0 on success.
473 '''
473 '''
474
474
475 opts = pycompat.byteskwargs(opts)
475 opts = pycompat.byteskwargs(opts)
476 rev = opts.get('rev')
476 rev = opts.get('rev')
477 if rev:
477 if rev:
478 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
478 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
479 ctx = scmutil.revsingle(repo, rev)
479 ctx = scmutil.revsingle(repo, rev)
480 if not ctx:
480 if not ctx:
481 raise error.Abort(_('no working directory: please specify a revision'))
481 raise error.Abort(_('no working directory: please specify a revision'))
482 node = ctx.node()
482 node = ctx.node()
483 dest = cmdutil.makefilename(ctx, dest)
483 dest = cmdutil.makefilename(ctx, dest)
484 if os.path.realpath(dest) == repo.root:
484 if os.path.realpath(dest) == repo.root:
485 raise error.Abort(_('repository root cannot be destination'))
485 raise error.Abort(_('repository root cannot be destination'))
486
486
487 kind = opts.get('type') or archival.guesskind(dest) or 'files'
487 kind = opts.get('type') or archival.guesskind(dest) or 'files'
488 prefix = opts.get('prefix')
488 prefix = opts.get('prefix')
489
489
490 if dest == '-':
490 if dest == '-':
491 if kind == 'files':
491 if kind == 'files':
492 raise error.Abort(_('cannot archive plain files to stdout'))
492 raise error.Abort(_('cannot archive plain files to stdout'))
493 dest = cmdutil.makefileobj(ctx, dest)
493 dest = cmdutil.makefileobj(ctx, dest)
494 if not prefix:
494 if not prefix:
495 prefix = os.path.basename(repo.root) + '-%h'
495 prefix = os.path.basename(repo.root) + '-%h'
496
496
497 prefix = cmdutil.makefilename(ctx, prefix)
497 prefix = cmdutil.makefilename(ctx, prefix)
498 match = scmutil.match(ctx, [], opts)
498 match = scmutil.match(ctx, [], opts)
499 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
499 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
500 match, prefix, subrepos=opts.get('subrepos'))
500 match, prefix, subrepos=opts.get('subrepos'))
501
501
502 @command('backout',
502 @command('backout',
503 [('', 'merge', None, _('merge with old dirstate parent after backout')),
503 [('', 'merge', None, _('merge with old dirstate parent after backout')),
504 ('', 'commit', None,
504 ('', 'commit', None,
505 _('commit if no conflicts were encountered (DEPRECATED)')),
505 _('commit if no conflicts were encountered (DEPRECATED)')),
506 ('', 'no-commit', None, _('do not commit')),
506 ('', 'no-commit', None, _('do not commit')),
507 ('', 'parent', '',
507 ('', 'parent', '',
508 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
508 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
509 ('r', 'rev', '', _('revision to backout'), _('REV')),
509 ('r', 'rev', '', _('revision to backout'), _('REV')),
510 ('e', 'edit', False, _('invoke editor on commit messages')),
510 ('e', 'edit', False, _('invoke editor on commit messages')),
511 ] + mergetoolopts + walkopts + commitopts + commitopts2,
511 ] + mergetoolopts + walkopts + commitopts + commitopts2,
512 _('[OPTION]... [-r] REV'))
512 _('[OPTION]... [-r] REV'))
513 def backout(ui, repo, node=None, rev=None, **opts):
513 def backout(ui, repo, node=None, rev=None, **opts):
514 '''reverse effect of earlier changeset
514 '''reverse effect of earlier changeset
515
515
516 Prepare a new changeset with the effect of REV undone in the
516 Prepare a new changeset with the effect of REV undone in the
517 current working directory. If no conflicts were encountered,
517 current working directory. If no conflicts were encountered,
518 it will be committed immediately.
518 it will be committed immediately.
519
519
520 If REV is the parent of the working directory, then this new changeset
520 If REV is the parent of the working directory, then this new changeset
521 is committed automatically (unless --no-commit is specified).
521 is committed automatically (unless --no-commit is specified).
522
522
523 .. note::
523 .. note::
524
524
525 :hg:`backout` cannot be used to fix either an unwanted or
525 :hg:`backout` cannot be used to fix either an unwanted or
526 incorrect merge.
526 incorrect merge.
527
527
528 .. container:: verbose
528 .. container:: verbose
529
529
530 Examples:
530 Examples:
531
531
532 - Reverse the effect of the parent of the working directory.
532 - Reverse the effect of the parent of the working directory.
533 This backout will be committed immediately::
533 This backout will be committed immediately::
534
534
535 hg backout -r .
535 hg backout -r .
536
536
537 - Reverse the effect of previous bad revision 23::
537 - Reverse the effect of previous bad revision 23::
538
538
539 hg backout -r 23
539 hg backout -r 23
540
540
541 - Reverse the effect of previous bad revision 23 and
541 - Reverse the effect of previous bad revision 23 and
542 leave changes uncommitted::
542 leave changes uncommitted::
543
543
544 hg backout -r 23 --no-commit
544 hg backout -r 23 --no-commit
545 hg commit -m "Backout revision 23"
545 hg commit -m "Backout revision 23"
546
546
547 By default, the pending changeset will have one parent,
547 By default, the pending changeset will have one parent,
548 maintaining a linear history. With --merge, the pending
548 maintaining a linear history. With --merge, the pending
549 changeset will instead have two parents: the old parent of the
549 changeset will instead have two parents: the old parent of the
550 working directory and a new child of REV that simply undoes REV.
550 working directory and a new child of REV that simply undoes REV.
551
551
552 Before version 1.7, the behavior without --merge was equivalent
552 Before version 1.7, the behavior without --merge was equivalent
553 to specifying --merge followed by :hg:`update --clean .` to
553 to specifying --merge followed by :hg:`update --clean .` to
554 cancel the merge and leave the child of REV as a head to be
554 cancel the merge and leave the child of REV as a head to be
555 merged separately.
555 merged separately.
556
556
557 See :hg:`help dates` for a list of formats valid for -d/--date.
557 See :hg:`help dates` for a list of formats valid for -d/--date.
558
558
559 See :hg:`help revert` for a way to restore files to the state
559 See :hg:`help revert` for a way to restore files to the state
560 of another revision.
560 of another revision.
561
561
562 Returns 0 on success, 1 if nothing to backout or there are unresolved
562 Returns 0 on success, 1 if nothing to backout or there are unresolved
563 files.
563 files.
564 '''
564 '''
565 with repo.wlock(), repo.lock():
565 with repo.wlock(), repo.lock():
566 return _dobackout(ui, repo, node, rev, **opts)
566 return _dobackout(ui, repo, node, rev, **opts)
567
567
568 def _dobackout(ui, repo, node=None, rev=None, **opts):
568 def _dobackout(ui, repo, node=None, rev=None, **opts):
569 opts = pycompat.byteskwargs(opts)
569 opts = pycompat.byteskwargs(opts)
570 if opts.get('commit') and opts.get('no_commit'):
570 if opts.get('commit') and opts.get('no_commit'):
571 raise error.Abort(_("cannot use --commit with --no-commit"))
571 raise error.Abort(_("cannot use --commit with --no-commit"))
572 if opts.get('merge') and opts.get('no_commit'):
572 if opts.get('merge') and opts.get('no_commit'):
573 raise error.Abort(_("cannot use --merge with --no-commit"))
573 raise error.Abort(_("cannot use --merge with --no-commit"))
574
574
575 if rev and node:
575 if rev and node:
576 raise error.Abort(_("please specify just one revision"))
576 raise error.Abort(_("please specify just one revision"))
577
577
578 if not rev:
578 if not rev:
579 rev = node
579 rev = node
580
580
581 if not rev:
581 if not rev:
582 raise error.Abort(_("please specify a revision to backout"))
582 raise error.Abort(_("please specify a revision to backout"))
583
583
584 date = opts.get('date')
584 date = opts.get('date')
585 if date:
585 if date:
586 opts['date'] = dateutil.parsedate(date)
586 opts['date'] = dateutil.parsedate(date)
587
587
588 cmdutil.checkunfinished(repo)
588 cmdutil.checkunfinished(repo)
589 cmdutil.bailifchanged(repo)
589 cmdutil.bailifchanged(repo)
590 node = scmutil.revsingle(repo, rev).node()
590 node = scmutil.revsingle(repo, rev).node()
591
591
592 op1, op2 = repo.dirstate.parents()
592 op1, op2 = repo.dirstate.parents()
593 if not repo.changelog.isancestor(node, op1):
593 if not repo.changelog.isancestor(node, op1):
594 raise error.Abort(_('cannot backout change that is not an ancestor'))
594 raise error.Abort(_('cannot backout change that is not an ancestor'))
595
595
596 p1, p2 = repo.changelog.parents(node)
596 p1, p2 = repo.changelog.parents(node)
597 if p1 == nullid:
597 if p1 == nullid:
598 raise error.Abort(_('cannot backout a change with no parents'))
598 raise error.Abort(_('cannot backout a change with no parents'))
599 if p2 != nullid:
599 if p2 != nullid:
600 if not opts.get('parent'):
600 if not opts.get('parent'):
601 raise error.Abort(_('cannot backout a merge changeset'))
601 raise error.Abort(_('cannot backout a merge changeset'))
602 p = repo.lookup(opts['parent'])
602 p = repo.lookup(opts['parent'])
603 if p not in (p1, p2):
603 if p not in (p1, p2):
604 raise error.Abort(_('%s is not a parent of %s') %
604 raise error.Abort(_('%s is not a parent of %s') %
605 (short(p), short(node)))
605 (short(p), short(node)))
606 parent = p
606 parent = p
607 else:
607 else:
608 if opts.get('parent'):
608 if opts.get('parent'):
609 raise error.Abort(_('cannot use --parent on non-merge changeset'))
609 raise error.Abort(_('cannot use --parent on non-merge changeset'))
610 parent = p1
610 parent = p1
611
611
612 # the backout should appear on the same branch
612 # the backout should appear on the same branch
613 branch = repo.dirstate.branch()
613 branch = repo.dirstate.branch()
614 bheads = repo.branchheads(branch)
614 bheads = repo.branchheads(branch)
615 rctx = scmutil.revsingle(repo, hex(parent))
615 rctx = scmutil.revsingle(repo, hex(parent))
616 if not opts.get('merge') and op1 != node:
616 if not opts.get('merge') and op1 != node:
617 with dirstateguard.dirstateguard(repo, 'backout'):
617 with dirstateguard.dirstateguard(repo, 'backout'):
618 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
618 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
619 with ui.configoverride(overrides, 'backout'):
619 with ui.configoverride(overrides, 'backout'):
620 stats = mergemod.update(repo, parent, True, True, node, False)
620 stats = mergemod.update(repo, parent, True, True, node, False)
621 repo.setparents(op1, op2)
621 repo.setparents(op1, op2)
622 hg._showstats(repo, stats)
622 hg._showstats(repo, stats)
623 if stats.unresolvedcount:
623 if stats.unresolvedcount:
624 repo.ui.status(_("use 'hg resolve' to retry unresolved "
624 repo.ui.status(_("use 'hg resolve' to retry unresolved "
625 "file merges\n"))
625 "file merges\n"))
626 return 1
626 return 1
627 else:
627 else:
628 hg.clean(repo, node, show_stats=False)
628 hg.clean(repo, node, show_stats=False)
629 repo.dirstate.setbranch(branch)
629 repo.dirstate.setbranch(branch)
630 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
630 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
631
631
632 if opts.get('no_commit'):
632 if opts.get('no_commit'):
633 msg = _("changeset %s backed out, "
633 msg = _("changeset %s backed out, "
634 "don't forget to commit.\n")
634 "don't forget to commit.\n")
635 ui.status(msg % short(node))
635 ui.status(msg % short(node))
636 return 0
636 return 0
637
637
638 def commitfunc(ui, repo, message, match, opts):
638 def commitfunc(ui, repo, message, match, opts):
639 editform = 'backout'
639 editform = 'backout'
640 e = cmdutil.getcommiteditor(editform=editform,
640 e = cmdutil.getcommiteditor(editform=editform,
641 **pycompat.strkwargs(opts))
641 **pycompat.strkwargs(opts))
642 if not message:
642 if not message:
643 # we don't translate commit messages
643 # we don't translate commit messages
644 message = "Backed out changeset %s" % short(node)
644 message = "Backed out changeset %s" % short(node)
645 e = cmdutil.getcommiteditor(edit=True, editform=editform)
645 e = cmdutil.getcommiteditor(edit=True, editform=editform)
646 return repo.commit(message, opts.get('user'), opts.get('date'),
646 return repo.commit(message, opts.get('user'), opts.get('date'),
647 match, editor=e)
647 match, editor=e)
648 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
648 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
649 if not newnode:
649 if not newnode:
650 ui.status(_("nothing changed\n"))
650 ui.status(_("nothing changed\n"))
651 return 1
651 return 1
652 cmdutil.commitstatus(repo, newnode, branch, bheads)
652 cmdutil.commitstatus(repo, newnode, branch, bheads)
653
653
654 def nice(node):
654 def nice(node):
655 return '%d:%s' % (repo.changelog.rev(node), short(node))
655 return '%d:%s' % (repo.changelog.rev(node), short(node))
656 ui.status(_('changeset %s backs out changeset %s\n') %
656 ui.status(_('changeset %s backs out changeset %s\n') %
657 (nice(repo.changelog.tip()), nice(node)))
657 (nice(repo.changelog.tip()), nice(node)))
658 if opts.get('merge') and op1 != node:
658 if opts.get('merge') and op1 != node:
659 hg.clean(repo, op1, show_stats=False)
659 hg.clean(repo, op1, show_stats=False)
660 ui.status(_('merging with changeset %s\n')
660 ui.status(_('merging with changeset %s\n')
661 % nice(repo.changelog.tip()))
661 % nice(repo.changelog.tip()))
662 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
662 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
663 with ui.configoverride(overrides, 'backout'):
663 with ui.configoverride(overrides, 'backout'):
664 return hg.merge(repo, hex(repo.changelog.tip()))
664 return hg.merge(repo, hex(repo.changelog.tip()))
665 return 0
665 return 0
666
666
667 @command('bisect',
667 @command('bisect',
668 [('r', 'reset', False, _('reset bisect state')),
668 [('r', 'reset', False, _('reset bisect state')),
669 ('g', 'good', False, _('mark changeset good')),
669 ('g', 'good', False, _('mark changeset good')),
670 ('b', 'bad', False, _('mark changeset bad')),
670 ('b', 'bad', False, _('mark changeset bad')),
671 ('s', 'skip', False, _('skip testing changeset')),
671 ('s', 'skip', False, _('skip testing changeset')),
672 ('e', 'extend', False, _('extend the bisect range')),
672 ('e', 'extend', False, _('extend the bisect range')),
673 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
673 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
674 ('U', 'noupdate', False, _('do not update to target'))],
674 ('U', 'noupdate', False, _('do not update to target'))],
675 _("[-gbsr] [-U] [-c CMD] [REV]"))
675 _("[-gbsr] [-U] [-c CMD] [REV]"))
676 def bisect(ui, repo, rev=None, extra=None, command=None,
676 def bisect(ui, repo, rev=None, extra=None, command=None,
677 reset=None, good=None, bad=None, skip=None, extend=None,
677 reset=None, good=None, bad=None, skip=None, extend=None,
678 noupdate=None):
678 noupdate=None):
679 """subdivision search of changesets
679 """subdivision search of changesets
680
680
681 This command helps to find changesets which introduce problems. To
681 This command helps to find changesets which introduce problems. To
682 use, mark the earliest changeset you know exhibits the problem as
682 use, mark the earliest changeset you know exhibits the problem as
683 bad, then mark the latest changeset which is free from the problem
683 bad, then mark the latest changeset which is free from the problem
684 as good. Bisect will update your working directory to a revision
684 as good. Bisect will update your working directory to a revision
685 for testing (unless the -U/--noupdate option is specified). Once
685 for testing (unless the -U/--noupdate option is specified). Once
686 you have performed tests, mark the working directory as good or
686 you have performed tests, mark the working directory as good or
687 bad, and bisect will either update to another candidate changeset
687 bad, and bisect will either update to another candidate changeset
688 or announce that it has found the bad revision.
688 or announce that it has found the bad revision.
689
689
690 As a shortcut, you can also use the revision argument to mark a
690 As a shortcut, you can also use the revision argument to mark a
691 revision as good or bad without checking it out first.
691 revision as good or bad without checking it out first.
692
692
693 If you supply a command, it will be used for automatic bisection.
693 If you supply a command, it will be used for automatic bisection.
694 The environment variable HG_NODE will contain the ID of the
694 The environment variable HG_NODE will contain the ID of the
695 changeset being tested. The exit status of the command will be
695 changeset being tested. The exit status of the command will be
696 used to mark revisions as good or bad: status 0 means good, 125
696 used to mark revisions as good or bad: status 0 means good, 125
697 means to skip the revision, 127 (command not found) will abort the
697 means to skip the revision, 127 (command not found) will abort the
698 bisection, and any other non-zero exit status means the revision
698 bisection, and any other non-zero exit status means the revision
699 is bad.
699 is bad.
700
700
701 .. container:: verbose
701 .. container:: verbose
702
702
703 Some examples:
703 Some examples:
704
704
705 - start a bisection with known bad revision 34, and good revision 12::
705 - start a bisection with known bad revision 34, and good revision 12::
706
706
707 hg bisect --bad 34
707 hg bisect --bad 34
708 hg bisect --good 12
708 hg bisect --good 12
709
709
710 - advance the current bisection by marking current revision as good or
710 - advance the current bisection by marking current revision as good or
711 bad::
711 bad::
712
712
713 hg bisect --good
713 hg bisect --good
714 hg bisect --bad
714 hg bisect --bad
715
715
716 - mark the current revision, or a known revision, to be skipped (e.g. if
716 - mark the current revision, or a known revision, to be skipped (e.g. if
717 that revision is not usable because of another issue)::
717 that revision is not usable because of another issue)::
718
718
719 hg bisect --skip
719 hg bisect --skip
720 hg bisect --skip 23
720 hg bisect --skip 23
721
721
722 - skip all revisions that do not touch directories ``foo`` or ``bar``::
722 - skip all revisions that do not touch directories ``foo`` or ``bar``::
723
723
724 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
724 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
725
725
726 - forget the current bisection::
726 - forget the current bisection::
727
727
728 hg bisect --reset
728 hg bisect --reset
729
729
730 - use 'make && make tests' to automatically find the first broken
730 - use 'make && make tests' to automatically find the first broken
731 revision::
731 revision::
732
732
733 hg bisect --reset
733 hg bisect --reset
734 hg bisect --bad 34
734 hg bisect --bad 34
735 hg bisect --good 12
735 hg bisect --good 12
736 hg bisect --command "make && make tests"
736 hg bisect --command "make && make tests"
737
737
738 - see all changesets whose states are already known in the current
738 - see all changesets whose states are already known in the current
739 bisection::
739 bisection::
740
740
741 hg log -r "bisect(pruned)"
741 hg log -r "bisect(pruned)"
742
742
743 - see the changeset currently being bisected (especially useful
743 - see the changeset currently being bisected (especially useful
744 if running with -U/--noupdate)::
744 if running with -U/--noupdate)::
745
745
746 hg log -r "bisect(current)"
746 hg log -r "bisect(current)"
747
747
748 - see all changesets that took part in the current bisection::
748 - see all changesets that took part in the current bisection::
749
749
750 hg log -r "bisect(range)"
750 hg log -r "bisect(range)"
751
751
752 - you can even get a nice graph::
752 - you can even get a nice graph::
753
753
754 hg log --graph -r "bisect(range)"
754 hg log --graph -r "bisect(range)"
755
755
756 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
756 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
757
757
758 Returns 0 on success.
758 Returns 0 on success.
759 """
759 """
760 # backward compatibility
760 # backward compatibility
761 if rev in "good bad reset init".split():
761 if rev in "good bad reset init".split():
762 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
762 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
763 cmd, rev, extra = rev, extra, None
763 cmd, rev, extra = rev, extra, None
764 if cmd == "good":
764 if cmd == "good":
765 good = True
765 good = True
766 elif cmd == "bad":
766 elif cmd == "bad":
767 bad = True
767 bad = True
768 else:
768 else:
769 reset = True
769 reset = True
770 elif extra:
770 elif extra:
771 raise error.Abort(_('incompatible arguments'))
771 raise error.Abort(_('incompatible arguments'))
772
772
773 incompatibles = {
773 incompatibles = {
774 '--bad': bad,
774 '--bad': bad,
775 '--command': bool(command),
775 '--command': bool(command),
776 '--extend': extend,
776 '--extend': extend,
777 '--good': good,
777 '--good': good,
778 '--reset': reset,
778 '--reset': reset,
779 '--skip': skip,
779 '--skip': skip,
780 }
780 }
781
781
782 enabled = [x for x in incompatibles if incompatibles[x]]
782 enabled = [x for x in incompatibles if incompatibles[x]]
783
783
784 if len(enabled) > 1:
784 if len(enabled) > 1:
785 raise error.Abort(_('%s and %s are incompatible') %
785 raise error.Abort(_('%s and %s are incompatible') %
786 tuple(sorted(enabled)[0:2]))
786 tuple(sorted(enabled)[0:2]))
787
787
788 if reset:
788 if reset:
789 hbisect.resetstate(repo)
789 hbisect.resetstate(repo)
790 return
790 return
791
791
792 state = hbisect.load_state(repo)
792 state = hbisect.load_state(repo)
793
793
794 # update state
794 # update state
795 if good or bad or skip:
795 if good or bad or skip:
796 if rev:
796 if rev:
797 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
797 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
798 else:
798 else:
799 nodes = [repo.lookup('.')]
799 nodes = [repo.lookup('.')]
800 if good:
800 if good:
801 state['good'] += nodes
801 state['good'] += nodes
802 elif bad:
802 elif bad:
803 state['bad'] += nodes
803 state['bad'] += nodes
804 elif skip:
804 elif skip:
805 state['skip'] += nodes
805 state['skip'] += nodes
806 hbisect.save_state(repo, state)
806 hbisect.save_state(repo, state)
807 if not (state['good'] and state['bad']):
807 if not (state['good'] and state['bad']):
808 return
808 return
809
809
810 def mayupdate(repo, node, show_stats=True):
810 def mayupdate(repo, node, show_stats=True):
811 """common used update sequence"""
811 """common used update sequence"""
812 if noupdate:
812 if noupdate:
813 return
813 return
814 cmdutil.checkunfinished(repo)
814 cmdutil.checkunfinished(repo)
815 cmdutil.bailifchanged(repo)
815 cmdutil.bailifchanged(repo)
816 return hg.clean(repo, node, show_stats=show_stats)
816 return hg.clean(repo, node, show_stats=show_stats)
817
817
818 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
818 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
819
819
820 if command:
820 if command:
821 changesets = 1
821 changesets = 1
822 if noupdate:
822 if noupdate:
823 try:
823 try:
824 node = state['current'][0]
824 node = state['current'][0]
825 except LookupError:
825 except LookupError:
826 raise error.Abort(_('current bisect revision is unknown - '
826 raise error.Abort(_('current bisect revision is unknown - '
827 'start a new bisect to fix'))
827 'start a new bisect to fix'))
828 else:
828 else:
829 node, p2 = repo.dirstate.parents()
829 node, p2 = repo.dirstate.parents()
830 if p2 != nullid:
830 if p2 != nullid:
831 raise error.Abort(_('current bisect revision is a merge'))
831 raise error.Abort(_('current bisect revision is a merge'))
832 if rev:
832 if rev:
833 node = repo[scmutil.revsingle(repo, rev, node)].node()
833 node = repo[scmutil.revsingle(repo, rev, node)].node()
834 try:
834 try:
835 while changesets:
835 while changesets:
836 # update state
836 # update state
837 state['current'] = [node]
837 state['current'] = [node]
838 hbisect.save_state(repo, state)
838 hbisect.save_state(repo, state)
839 status = ui.system(command, environ={'HG_NODE': hex(node)},
839 status = ui.system(command, environ={'HG_NODE': hex(node)},
840 blockedtag='bisect_check')
840 blockedtag='bisect_check')
841 if status == 125:
841 if status == 125:
842 transition = "skip"
842 transition = "skip"
843 elif status == 0:
843 elif status == 0:
844 transition = "good"
844 transition = "good"
845 # status < 0 means process was killed
845 # status < 0 means process was killed
846 elif status == 127:
846 elif status == 127:
847 raise error.Abort(_("failed to execute %s") % command)
847 raise error.Abort(_("failed to execute %s") % command)
848 elif status < 0:
848 elif status < 0:
849 raise error.Abort(_("%s killed") % command)
849 raise error.Abort(_("%s killed") % command)
850 else:
850 else:
851 transition = "bad"
851 transition = "bad"
852 state[transition].append(node)
852 state[transition].append(node)
853 ctx = repo[node]
853 ctx = repo[node]
854 ui.status(_('changeset %d:%s: %s\n') % (ctx.rev(), ctx,
854 ui.status(_('changeset %d:%s: %s\n') % (ctx.rev(), ctx,
855 transition))
855 transition))
856 hbisect.checkstate(state)
856 hbisect.checkstate(state)
857 # bisect
857 # bisect
858 nodes, changesets, bgood = hbisect.bisect(repo, state)
858 nodes, changesets, bgood = hbisect.bisect(repo, state)
859 # update to next check
859 # update to next check
860 node = nodes[0]
860 node = nodes[0]
861 mayupdate(repo, node, show_stats=False)
861 mayupdate(repo, node, show_stats=False)
862 finally:
862 finally:
863 state['current'] = [node]
863 state['current'] = [node]
864 hbisect.save_state(repo, state)
864 hbisect.save_state(repo, state)
865 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
865 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
866 return
866 return
867
867
868 hbisect.checkstate(state)
868 hbisect.checkstate(state)
869
869
870 # actually bisect
870 # actually bisect
871 nodes, changesets, good = hbisect.bisect(repo, state)
871 nodes, changesets, good = hbisect.bisect(repo, state)
872 if extend:
872 if extend:
873 if not changesets:
873 if not changesets:
874 extendnode = hbisect.extendrange(repo, state, nodes, good)
874 extendnode = hbisect.extendrange(repo, state, nodes, good)
875 if extendnode is not None:
875 if extendnode is not None:
876 ui.write(_("Extending search to changeset %d:%s\n")
876 ui.write(_("Extending search to changeset %d:%s\n")
877 % (extendnode.rev(), extendnode))
877 % (extendnode.rev(), extendnode))
878 state['current'] = [extendnode.node()]
878 state['current'] = [extendnode.node()]
879 hbisect.save_state(repo, state)
879 hbisect.save_state(repo, state)
880 return mayupdate(repo, extendnode.node())
880 return mayupdate(repo, extendnode.node())
881 raise error.Abort(_("nothing to extend"))
881 raise error.Abort(_("nothing to extend"))
882
882
883 if changesets == 0:
883 if changesets == 0:
884 hbisect.printresult(ui, repo, state, displayer, nodes, good)
884 hbisect.printresult(ui, repo, state, displayer, nodes, good)
885 else:
885 else:
886 assert len(nodes) == 1 # only a single node can be tested next
886 assert len(nodes) == 1 # only a single node can be tested next
887 node = nodes[0]
887 node = nodes[0]
888 # compute the approximate number of remaining tests
888 # compute the approximate number of remaining tests
889 tests, size = 0, 2
889 tests, size = 0, 2
890 while size <= changesets:
890 while size <= changesets:
891 tests, size = tests + 1, size * 2
891 tests, size = tests + 1, size * 2
892 rev = repo.changelog.rev(node)
892 rev = repo.changelog.rev(node)
893 ui.write(_("Testing changeset %d:%s "
893 ui.write(_("Testing changeset %d:%s "
894 "(%d changesets remaining, ~%d tests)\n")
894 "(%d changesets remaining, ~%d tests)\n")
895 % (rev, short(node), changesets, tests))
895 % (rev, short(node), changesets, tests))
896 state['current'] = [node]
896 state['current'] = [node]
897 hbisect.save_state(repo, state)
897 hbisect.save_state(repo, state)
898 return mayupdate(repo, node)
898 return mayupdate(repo, node)
899
899
900 @command('bookmarks|bookmark',
900 @command('bookmarks|bookmark',
901 [('f', 'force', False, _('force')),
901 [('f', 'force', False, _('force')),
902 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
902 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
903 ('d', 'delete', False, _('delete a given bookmark')),
903 ('d', 'delete', False, _('delete a given bookmark')),
904 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
904 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
905 ('i', 'inactive', False, _('mark a bookmark inactive')),
905 ('i', 'inactive', False, _('mark a bookmark inactive')),
906 ('', 'active', False, _('display the active bookmark')),
906 ('', 'active', False, _('display the active bookmark')),
907 ] + formatteropts,
907 ] + formatteropts,
908 _('hg bookmarks [OPTIONS]... [NAME]...'))
908 _('hg bookmarks [OPTIONS]... [NAME]...'))
909 def bookmark(ui, repo, *names, **opts):
909 def bookmark(ui, repo, *names, **opts):
910 '''create a new bookmark or list existing bookmarks
910 '''create a new bookmark or list existing bookmarks
911
911
912 Bookmarks are labels on changesets to help track lines of development.
912 Bookmarks are labels on changesets to help track lines of development.
913 Bookmarks are unversioned and can be moved, renamed and deleted.
913 Bookmarks are unversioned and can be moved, renamed and deleted.
914 Deleting or moving a bookmark has no effect on the associated changesets.
914 Deleting or moving a bookmark has no effect on the associated changesets.
915
915
916 Creating or updating to a bookmark causes it to be marked as 'active'.
916 Creating or updating to a bookmark causes it to be marked as 'active'.
917 The active bookmark is indicated with a '*'.
917 The active bookmark is indicated with a '*'.
918 When a commit is made, the active bookmark will advance to the new commit.
918 When a commit is made, the active bookmark will advance to the new commit.
919 A plain :hg:`update` will also advance an active bookmark, if possible.
919 A plain :hg:`update` will also advance an active bookmark, if possible.
920 Updating away from a bookmark will cause it to be deactivated.
920 Updating away from a bookmark will cause it to be deactivated.
921
921
922 Bookmarks can be pushed and pulled between repositories (see
922 Bookmarks can be pushed and pulled between repositories (see
923 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
923 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
924 diverged, a new 'divergent bookmark' of the form 'name@path' will
924 diverged, a new 'divergent bookmark' of the form 'name@path' will
925 be created. Using :hg:`merge` will resolve the divergence.
925 be created. Using :hg:`merge` will resolve the divergence.
926
926
927 Specifying bookmark as '.' to -m or -d options is equivalent to specifying
927 Specifying bookmark as '.' to -m or -d options is equivalent to specifying
928 the active bookmark's name.
928 the active bookmark's name.
929
929
930 A bookmark named '@' has the special property that :hg:`clone` will
930 A bookmark named '@' has the special property that :hg:`clone` will
931 check it out by default if it exists.
931 check it out by default if it exists.
932
932
933 The '--active' flag will display the current bookmark or return non-zero,
933 The '--active' flag will display the current bookmark or return non-zero,
934 if combined with other action, they will be performed on the active
934 if combined with other action, they will be performed on the active
935 bookmark.
935 bookmark.
936
936
937 .. container:: verbose
937 .. container:: verbose
938
938
939 Examples:
939 Examples:
940
940
941 - create an active bookmark for a new line of development::
941 - create an active bookmark for a new line of development::
942
942
943 hg book new-feature
943 hg book new-feature
944
944
945 - create an inactive bookmark as a place marker::
945 - create an inactive bookmark as a place marker::
946
946
947 hg book -i reviewed
947 hg book -i reviewed
948
948
949 - create an inactive bookmark on another changeset::
949 - create an inactive bookmark on another changeset::
950
950
951 hg book -r .^ tested
951 hg book -r .^ tested
952
952
953 - rename bookmark turkey to dinner::
953 - rename bookmark turkey to dinner::
954
954
955 hg book -m turkey dinner
955 hg book -m turkey dinner
956
956
957 - move the '@' bookmark from another branch::
957 - move the '@' bookmark from another branch::
958
958
959 hg book -f @
959 hg book -f @
960 '''
960 '''
961 opts = pycompat.byteskwargs(opts)
961 opts = pycompat.byteskwargs(opts)
962 force = opts.get('force')
962 force = opts.get('force')
963 rev = opts.get('rev')
963 rev = opts.get('rev')
964 inactive = opts.get('inactive') # meaning add/rename to inactive bookmark
964 inactive = opts.get('inactive') # meaning add/rename to inactive bookmark
965
965
966 selactions = [k for k in ['delete', 'rename', 'active'] if opts.get(k)]
966 selactions = [k for k in ['delete', 'rename', 'active'] if opts.get(k)]
967 if len(selactions) > 1:
967 if len(selactions) > 1:
968 raise error.Abort(_('--%s and --%s are incompatible')
968 raise error.Abort(_('--%s and --%s are incompatible')
969 % tuple(selactions[:2]))
969 % tuple(selactions[:2]))
970 if selactions:
970 if selactions:
971 action = selactions[0]
971 action = selactions[0]
972 elif names or rev:
972 elif names or rev:
973 action = 'add'
973 action = 'add'
974 elif inactive:
974 elif inactive:
975 action = 'inactive' # meaning deactivate
975 action = 'inactive' # meaning deactivate
976 else:
976 else:
977 action = None
977 action = None
978
978
979 if rev and action in {'delete', 'rename', 'active'}:
979 if rev and action in {'delete', 'rename', 'active'}:
980 raise error.Abort(_("--rev is incompatible with --%s") % action)
980 raise error.Abort(_("--rev is incompatible with --%s") % action)
981 if names and action == 'active':
981 if names and action == 'active':
982 raise error.Abort(_("NAMES is incompatible with --active"))
982 raise error.Abort(_("NAMES is incompatible with --active"))
983 if inactive and action == 'active':
983 if inactive and action in {'delete', 'active'}:
984 raise error.Abort(_("--inactive is incompatible with --active"))
984 raise error.Abort(_("--inactive is incompatible with --%s") % action)
985 if not names and action in {'add', 'delete'}:
985 if not names and action in {'add', 'delete'}:
986 raise error.Abort(_("bookmark name required"))
986 raise error.Abort(_("bookmark name required"))
987
987
988 if action in {'add', 'delete', 'rename', 'inactive'}:
988 if action in {'add', 'delete', 'rename', 'inactive'}:
989 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
989 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
990 if action == 'delete':
990 if action == 'delete':
991 names = pycompat.maplist(repo._bookmarks.expandname, names)
991 names = pycompat.maplist(repo._bookmarks.expandname, names)
992 bookmarks.delete(repo, tr, names)
992 bookmarks.delete(repo, tr, names)
993 elif action == 'rename':
993 elif action == 'rename':
994 if not names:
994 if not names:
995 raise error.Abort(_("new bookmark name required"))
995 raise error.Abort(_("new bookmark name required"))
996 elif len(names) > 1:
996 elif len(names) > 1:
997 raise error.Abort(_("only one new bookmark name allowed"))
997 raise error.Abort(_("only one new bookmark name allowed"))
998 oldname = repo._bookmarks.expandname(opts['rename'])
998 oldname = repo._bookmarks.expandname(opts['rename'])
999 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
999 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1000 elif action == 'add':
1000 elif action == 'add':
1001 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1001 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1002 elif action == 'inactive':
1002 elif action == 'inactive':
1003 if len(repo._bookmarks) == 0:
1003 if len(repo._bookmarks) == 0:
1004 ui.status(_("no bookmarks set\n"))
1004 ui.status(_("no bookmarks set\n"))
1005 elif not repo._activebookmark:
1005 elif not repo._activebookmark:
1006 ui.status(_("no active bookmark\n"))
1006 ui.status(_("no active bookmark\n"))
1007 else:
1007 else:
1008 bookmarks.deactivate(repo)
1008 bookmarks.deactivate(repo)
1009 elif action == 'active':
1009 elif action == 'active':
1010 book = repo._activebookmark
1010 book = repo._activebookmark
1011 if book is None:
1011 if book is None:
1012 return 1
1012 return 1
1013 ui.write("%s\n" % book, label=bookmarks.activebookmarklabel)
1013 ui.write("%s\n" % book, label=bookmarks.activebookmarklabel)
1014 else: # show bookmarks
1014 else: # show bookmarks
1015 with ui.formatter('bookmarks', opts) as fm:
1015 with ui.formatter('bookmarks', opts) as fm:
1016 bookmarks.printbookmarks(ui, repo, fm)
1016 bookmarks.printbookmarks(ui, repo, fm)
1017
1017
1018 @command('branch',
1018 @command('branch',
1019 [('f', 'force', None,
1019 [('f', 'force', None,
1020 _('set branch name even if it shadows an existing branch')),
1020 _('set branch name even if it shadows an existing branch')),
1021 ('C', 'clean', None, _('reset branch name to parent branch name')),
1021 ('C', 'clean', None, _('reset branch name to parent branch name')),
1022 ('r', 'rev', [], _('change branches of the given revs (EXPERIMENTAL)')),
1022 ('r', 'rev', [], _('change branches of the given revs (EXPERIMENTAL)')),
1023 ],
1023 ],
1024 _('[-fC] [NAME]'))
1024 _('[-fC] [NAME]'))
1025 def branch(ui, repo, label=None, **opts):
1025 def branch(ui, repo, label=None, **opts):
1026 """set or show the current branch name
1026 """set or show the current branch name
1027
1027
1028 .. note::
1028 .. note::
1029
1029
1030 Branch names are permanent and global. Use :hg:`bookmark` to create a
1030 Branch names are permanent and global. Use :hg:`bookmark` to create a
1031 light-weight bookmark instead. See :hg:`help glossary` for more
1031 light-weight bookmark instead. See :hg:`help glossary` for more
1032 information about named branches and bookmarks.
1032 information about named branches and bookmarks.
1033
1033
1034 With no argument, show the current branch name. With one argument,
1034 With no argument, show the current branch name. With one argument,
1035 set the working directory branch name (the branch will not exist
1035 set the working directory branch name (the branch will not exist
1036 in the repository until the next commit). Standard practice
1036 in the repository until the next commit). Standard practice
1037 recommends that primary development take place on the 'default'
1037 recommends that primary development take place on the 'default'
1038 branch.
1038 branch.
1039
1039
1040 Unless -f/--force is specified, branch will not let you set a
1040 Unless -f/--force is specified, branch will not let you set a
1041 branch name that already exists.
1041 branch name that already exists.
1042
1042
1043 Use -C/--clean to reset the working directory branch to that of
1043 Use -C/--clean to reset the working directory branch to that of
1044 the parent of the working directory, negating a previous branch
1044 the parent of the working directory, negating a previous branch
1045 change.
1045 change.
1046
1046
1047 Use the command :hg:`update` to switch to an existing branch. Use
1047 Use the command :hg:`update` to switch to an existing branch. Use
1048 :hg:`commit --close-branch` to mark this branch head as closed.
1048 :hg:`commit --close-branch` to mark this branch head as closed.
1049 When all heads of a branch are closed, the branch will be
1049 When all heads of a branch are closed, the branch will be
1050 considered closed.
1050 considered closed.
1051
1051
1052 Returns 0 on success.
1052 Returns 0 on success.
1053 """
1053 """
1054 opts = pycompat.byteskwargs(opts)
1054 opts = pycompat.byteskwargs(opts)
1055 revs = opts.get('rev')
1055 revs = opts.get('rev')
1056 if label:
1056 if label:
1057 label = label.strip()
1057 label = label.strip()
1058
1058
1059 if not opts.get('clean') and not label:
1059 if not opts.get('clean') and not label:
1060 if revs:
1060 if revs:
1061 raise error.Abort(_("no branch name specified for the revisions"))
1061 raise error.Abort(_("no branch name specified for the revisions"))
1062 ui.write("%s\n" % repo.dirstate.branch())
1062 ui.write("%s\n" % repo.dirstate.branch())
1063 return
1063 return
1064
1064
1065 with repo.wlock():
1065 with repo.wlock():
1066 if opts.get('clean'):
1066 if opts.get('clean'):
1067 label = repo[None].p1().branch()
1067 label = repo[None].p1().branch()
1068 repo.dirstate.setbranch(label)
1068 repo.dirstate.setbranch(label)
1069 ui.status(_('reset working directory to branch %s\n') % label)
1069 ui.status(_('reset working directory to branch %s\n') % label)
1070 elif label:
1070 elif label:
1071
1071
1072 scmutil.checknewlabel(repo, label, 'branch')
1072 scmutil.checknewlabel(repo, label, 'branch')
1073 if revs:
1073 if revs:
1074 return cmdutil.changebranch(ui, repo, revs, label)
1074 return cmdutil.changebranch(ui, repo, revs, label)
1075
1075
1076 if not opts.get('force') and label in repo.branchmap():
1076 if not opts.get('force') and label in repo.branchmap():
1077 if label not in [p.branch() for p in repo[None].parents()]:
1077 if label not in [p.branch() for p in repo[None].parents()]:
1078 raise error.Abort(_('a branch of the same name already'
1078 raise error.Abort(_('a branch of the same name already'
1079 ' exists'),
1079 ' exists'),
1080 # i18n: "it" refers to an existing branch
1080 # i18n: "it" refers to an existing branch
1081 hint=_("use 'hg update' to switch to it"))
1081 hint=_("use 'hg update' to switch to it"))
1082
1082
1083 repo.dirstate.setbranch(label)
1083 repo.dirstate.setbranch(label)
1084 ui.status(_('marked working directory as branch %s\n') % label)
1084 ui.status(_('marked working directory as branch %s\n') % label)
1085
1085
1086 # find any open named branches aside from default
1086 # find any open named branches aside from default
1087 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1087 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1088 if n != "default" and not c]
1088 if n != "default" and not c]
1089 if not others:
1089 if not others:
1090 ui.status(_('(branches are permanent and global, '
1090 ui.status(_('(branches are permanent and global, '
1091 'did you want a bookmark?)\n'))
1091 'did you want a bookmark?)\n'))
1092
1092
1093 @command('branches',
1093 @command('branches',
1094 [('a', 'active', False,
1094 [('a', 'active', False,
1095 _('show only branches that have unmerged heads (DEPRECATED)')),
1095 _('show only branches that have unmerged heads (DEPRECATED)')),
1096 ('c', 'closed', False, _('show normal and closed branches')),
1096 ('c', 'closed', False, _('show normal and closed branches')),
1097 ] + formatteropts,
1097 ] + formatteropts,
1098 _('[-c]'),
1098 _('[-c]'),
1099 intents={INTENT_READONLY})
1099 intents={INTENT_READONLY})
1100 def branches(ui, repo, active=False, closed=False, **opts):
1100 def branches(ui, repo, active=False, closed=False, **opts):
1101 """list repository named branches
1101 """list repository named branches
1102
1102
1103 List the repository's named branches, indicating which ones are
1103 List the repository's named branches, indicating which ones are
1104 inactive. If -c/--closed is specified, also list branches which have
1104 inactive. If -c/--closed is specified, also list branches which have
1105 been marked closed (see :hg:`commit --close-branch`).
1105 been marked closed (see :hg:`commit --close-branch`).
1106
1106
1107 Use the command :hg:`update` to switch to an existing branch.
1107 Use the command :hg:`update` to switch to an existing branch.
1108
1108
1109 Returns 0.
1109 Returns 0.
1110 """
1110 """
1111
1111
1112 opts = pycompat.byteskwargs(opts)
1112 opts = pycompat.byteskwargs(opts)
1113 ui.pager('branches')
1113 ui.pager('branches')
1114 fm = ui.formatter('branches', opts)
1114 fm = ui.formatter('branches', opts)
1115 hexfunc = fm.hexfunc
1115 hexfunc = fm.hexfunc
1116
1116
1117 allheads = set(repo.heads())
1117 allheads = set(repo.heads())
1118 branches = []
1118 branches = []
1119 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1119 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1120 isactive = False
1120 isactive = False
1121 if not isclosed:
1121 if not isclosed:
1122 openheads = set(repo.branchmap().iteropen(heads))
1122 openheads = set(repo.branchmap().iteropen(heads))
1123 isactive = bool(openheads & allheads)
1123 isactive = bool(openheads & allheads)
1124 branches.append((tag, repo[tip], isactive, not isclosed))
1124 branches.append((tag, repo[tip], isactive, not isclosed))
1125 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1125 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1126 reverse=True)
1126 reverse=True)
1127
1127
1128 for tag, ctx, isactive, isopen in branches:
1128 for tag, ctx, isactive, isopen in branches:
1129 if active and not isactive:
1129 if active and not isactive:
1130 continue
1130 continue
1131 if isactive:
1131 if isactive:
1132 label = 'branches.active'
1132 label = 'branches.active'
1133 notice = ''
1133 notice = ''
1134 elif not isopen:
1134 elif not isopen:
1135 if not closed:
1135 if not closed:
1136 continue
1136 continue
1137 label = 'branches.closed'
1137 label = 'branches.closed'
1138 notice = _(' (closed)')
1138 notice = _(' (closed)')
1139 else:
1139 else:
1140 label = 'branches.inactive'
1140 label = 'branches.inactive'
1141 notice = _(' (inactive)')
1141 notice = _(' (inactive)')
1142 current = (tag == repo.dirstate.branch())
1142 current = (tag == repo.dirstate.branch())
1143 if current:
1143 if current:
1144 label = 'branches.current'
1144 label = 'branches.current'
1145
1145
1146 fm.startitem()
1146 fm.startitem()
1147 fm.write('branch', '%s', tag, label=label)
1147 fm.write('branch', '%s', tag, label=label)
1148 rev = ctx.rev()
1148 rev = ctx.rev()
1149 padsize = max(31 - len("%d" % rev) - encoding.colwidth(tag), 0)
1149 padsize = max(31 - len("%d" % rev) - encoding.colwidth(tag), 0)
1150 fmt = ' ' * padsize + ' %d:%s'
1150 fmt = ' ' * padsize + ' %d:%s'
1151 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1151 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1152 label='log.changeset changeset.%s' % ctx.phasestr())
1152 label='log.changeset changeset.%s' % ctx.phasestr())
1153 fm.context(ctx=ctx)
1153 fm.context(ctx=ctx)
1154 fm.data(active=isactive, closed=not isopen, current=current)
1154 fm.data(active=isactive, closed=not isopen, current=current)
1155 if not ui.quiet:
1155 if not ui.quiet:
1156 fm.plain(notice)
1156 fm.plain(notice)
1157 fm.plain('\n')
1157 fm.plain('\n')
1158 fm.end()
1158 fm.end()
1159
1159
1160 @command('bundle',
1160 @command('bundle',
1161 [('f', 'force', None, _('run even when the destination is unrelated')),
1161 [('f', 'force', None, _('run even when the destination is unrelated')),
1162 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1162 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1163 _('REV')),
1163 _('REV')),
1164 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1164 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1165 _('BRANCH')),
1165 _('BRANCH')),
1166 ('', 'base', [],
1166 ('', 'base', [],
1167 _('a base changeset assumed to be available at the destination'),
1167 _('a base changeset assumed to be available at the destination'),
1168 _('REV')),
1168 _('REV')),
1169 ('a', 'all', None, _('bundle all changesets in the repository')),
1169 ('a', 'all', None, _('bundle all changesets in the repository')),
1170 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1170 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1171 ] + remoteopts,
1171 ] + remoteopts,
1172 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1172 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1173 def bundle(ui, repo, fname, dest=None, **opts):
1173 def bundle(ui, repo, fname, dest=None, **opts):
1174 """create a bundle file
1174 """create a bundle file
1175
1175
1176 Generate a bundle file containing data to be transferred to another
1176 Generate a bundle file containing data to be transferred to another
1177 repository.
1177 repository.
1178
1178
1179 To create a bundle containing all changesets, use -a/--all
1179 To create a bundle containing all changesets, use -a/--all
1180 (or --base null). Otherwise, hg assumes the destination will have
1180 (or --base null). Otherwise, hg assumes the destination will have
1181 all the nodes you specify with --base parameters. Otherwise, hg
1181 all the nodes you specify with --base parameters. Otherwise, hg
1182 will assume the repository has all the nodes in destination, or
1182 will assume the repository has all the nodes in destination, or
1183 default-push/default if no destination is specified, where destination
1183 default-push/default if no destination is specified, where destination
1184 is the repository you provide through DEST option.
1184 is the repository you provide through DEST option.
1185
1185
1186 You can change bundle format with the -t/--type option. See
1186 You can change bundle format with the -t/--type option. See
1187 :hg:`help bundlespec` for documentation on this format. By default,
1187 :hg:`help bundlespec` for documentation on this format. By default,
1188 the most appropriate format is used and compression defaults to
1188 the most appropriate format is used and compression defaults to
1189 bzip2.
1189 bzip2.
1190
1190
1191 The bundle file can then be transferred using conventional means
1191 The bundle file can then be transferred using conventional means
1192 and applied to another repository with the unbundle or pull
1192 and applied to another repository with the unbundle or pull
1193 command. This is useful when direct push and pull are not
1193 command. This is useful when direct push and pull are not
1194 available or when exporting an entire repository is undesirable.
1194 available or when exporting an entire repository is undesirable.
1195
1195
1196 Applying bundles preserves all changeset contents including
1196 Applying bundles preserves all changeset contents including
1197 permissions, copy/rename information, and revision history.
1197 permissions, copy/rename information, and revision history.
1198
1198
1199 Returns 0 on success, 1 if no changes found.
1199 Returns 0 on success, 1 if no changes found.
1200 """
1200 """
1201 opts = pycompat.byteskwargs(opts)
1201 opts = pycompat.byteskwargs(opts)
1202 revs = None
1202 revs = None
1203 if 'rev' in opts:
1203 if 'rev' in opts:
1204 revstrings = opts['rev']
1204 revstrings = opts['rev']
1205 revs = scmutil.revrange(repo, revstrings)
1205 revs = scmutil.revrange(repo, revstrings)
1206 if revstrings and not revs:
1206 if revstrings and not revs:
1207 raise error.Abort(_('no commits to bundle'))
1207 raise error.Abort(_('no commits to bundle'))
1208
1208
1209 bundletype = opts.get('type', 'bzip2').lower()
1209 bundletype = opts.get('type', 'bzip2').lower()
1210 try:
1210 try:
1211 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1211 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1212 except error.UnsupportedBundleSpecification as e:
1212 except error.UnsupportedBundleSpecification as e:
1213 raise error.Abort(pycompat.bytestr(e),
1213 raise error.Abort(pycompat.bytestr(e),
1214 hint=_("see 'hg help bundlespec' for supported "
1214 hint=_("see 'hg help bundlespec' for supported "
1215 "values for --type"))
1215 "values for --type"))
1216 cgversion = bundlespec.contentopts["cg.version"]
1216 cgversion = bundlespec.contentopts["cg.version"]
1217
1217
1218 # Packed bundles are a pseudo bundle format for now.
1218 # Packed bundles are a pseudo bundle format for now.
1219 if cgversion == 's1':
1219 if cgversion == 's1':
1220 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1220 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1221 hint=_("use 'hg debugcreatestreamclonebundle'"))
1221 hint=_("use 'hg debugcreatestreamclonebundle'"))
1222
1222
1223 if opts.get('all'):
1223 if opts.get('all'):
1224 if dest:
1224 if dest:
1225 raise error.Abort(_("--all is incompatible with specifying "
1225 raise error.Abort(_("--all is incompatible with specifying "
1226 "a destination"))
1226 "a destination"))
1227 if opts.get('base'):
1227 if opts.get('base'):
1228 ui.warn(_("ignoring --base because --all was specified\n"))
1228 ui.warn(_("ignoring --base because --all was specified\n"))
1229 base = ['null']
1229 base = ['null']
1230 else:
1230 else:
1231 base = scmutil.revrange(repo, opts.get('base'))
1231 base = scmutil.revrange(repo, opts.get('base'))
1232 if cgversion not in changegroup.supportedoutgoingversions(repo):
1232 if cgversion not in changegroup.supportedoutgoingversions(repo):
1233 raise error.Abort(_("repository does not support bundle version %s") %
1233 raise error.Abort(_("repository does not support bundle version %s") %
1234 cgversion)
1234 cgversion)
1235
1235
1236 if base:
1236 if base:
1237 if dest:
1237 if dest:
1238 raise error.Abort(_("--base is incompatible with specifying "
1238 raise error.Abort(_("--base is incompatible with specifying "
1239 "a destination"))
1239 "a destination"))
1240 common = [repo[rev].node() for rev in base]
1240 common = [repo[rev].node() for rev in base]
1241 heads = [repo[r].node() for r in revs] if revs else None
1241 heads = [repo[r].node() for r in revs] if revs else None
1242 outgoing = discovery.outgoing(repo, common, heads)
1242 outgoing = discovery.outgoing(repo, common, heads)
1243 else:
1243 else:
1244 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1244 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1245 dest, branches = hg.parseurl(dest, opts.get('branch'))
1245 dest, branches = hg.parseurl(dest, opts.get('branch'))
1246 other = hg.peer(repo, opts, dest)
1246 other = hg.peer(repo, opts, dest)
1247 revs = [repo[r].hex() for r in revs]
1247 revs = [repo[r].hex() for r in revs]
1248 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1248 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1249 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1249 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1250 outgoing = discovery.findcommonoutgoing(repo, other,
1250 outgoing = discovery.findcommonoutgoing(repo, other,
1251 onlyheads=heads,
1251 onlyheads=heads,
1252 force=opts.get('force'),
1252 force=opts.get('force'),
1253 portable=True)
1253 portable=True)
1254
1254
1255 if not outgoing.missing:
1255 if not outgoing.missing:
1256 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1256 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1257 return 1
1257 return 1
1258
1258
1259 if cgversion == '01': #bundle1
1259 if cgversion == '01': #bundle1
1260 bversion = 'HG10' + bundlespec.wirecompression
1260 bversion = 'HG10' + bundlespec.wirecompression
1261 bcompression = None
1261 bcompression = None
1262 elif cgversion in ('02', '03'):
1262 elif cgversion in ('02', '03'):
1263 bversion = 'HG20'
1263 bversion = 'HG20'
1264 bcompression = bundlespec.wirecompression
1264 bcompression = bundlespec.wirecompression
1265 else:
1265 else:
1266 raise error.ProgrammingError(
1266 raise error.ProgrammingError(
1267 'bundle: unexpected changegroup version %s' % cgversion)
1267 'bundle: unexpected changegroup version %s' % cgversion)
1268
1268
1269 # TODO compression options should be derived from bundlespec parsing.
1269 # TODO compression options should be derived from bundlespec parsing.
1270 # This is a temporary hack to allow adjusting bundle compression
1270 # This is a temporary hack to allow adjusting bundle compression
1271 # level without a) formalizing the bundlespec changes to declare it
1271 # level without a) formalizing the bundlespec changes to declare it
1272 # b) introducing a command flag.
1272 # b) introducing a command flag.
1273 compopts = {}
1273 compopts = {}
1274 complevel = ui.configint('experimental',
1274 complevel = ui.configint('experimental',
1275 'bundlecomplevel.' + bundlespec.compression)
1275 'bundlecomplevel.' + bundlespec.compression)
1276 if complevel is None:
1276 if complevel is None:
1277 complevel = ui.configint('experimental', 'bundlecomplevel')
1277 complevel = ui.configint('experimental', 'bundlecomplevel')
1278 if complevel is not None:
1278 if complevel is not None:
1279 compopts['level'] = complevel
1279 compopts['level'] = complevel
1280
1280
1281 # Allow overriding the bundling of obsmarker in phases through
1281 # Allow overriding the bundling of obsmarker in phases through
1282 # configuration while we don't have a bundle version that include them
1282 # configuration while we don't have a bundle version that include them
1283 if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker'):
1283 if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker'):
1284 bundlespec.contentopts['obsolescence'] = True
1284 bundlespec.contentopts['obsolescence'] = True
1285 if repo.ui.configbool('experimental', 'bundle-phases'):
1285 if repo.ui.configbool('experimental', 'bundle-phases'):
1286 bundlespec.contentopts['phases'] = True
1286 bundlespec.contentopts['phases'] = True
1287
1287
1288 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1288 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1289 bundlespec.contentopts, compression=bcompression,
1289 bundlespec.contentopts, compression=bcompression,
1290 compopts=compopts)
1290 compopts=compopts)
1291
1291
1292 @command('cat',
1292 @command('cat',
1293 [('o', 'output', '',
1293 [('o', 'output', '',
1294 _('print output to file with formatted name'), _('FORMAT')),
1294 _('print output to file with formatted name'), _('FORMAT')),
1295 ('r', 'rev', '', _('print the given revision'), _('REV')),
1295 ('r', 'rev', '', _('print the given revision'), _('REV')),
1296 ('', 'decode', None, _('apply any matching decode filter')),
1296 ('', 'decode', None, _('apply any matching decode filter')),
1297 ] + walkopts + formatteropts,
1297 ] + walkopts + formatteropts,
1298 _('[OPTION]... FILE...'),
1298 _('[OPTION]... FILE...'),
1299 inferrepo=True,
1299 inferrepo=True,
1300 intents={INTENT_READONLY})
1300 intents={INTENT_READONLY})
1301 def cat(ui, repo, file1, *pats, **opts):
1301 def cat(ui, repo, file1, *pats, **opts):
1302 """output the current or given revision of files
1302 """output the current or given revision of files
1303
1303
1304 Print the specified files as they were at the given revision. If
1304 Print the specified files as they were at the given revision. If
1305 no revision is given, the parent of the working directory is used.
1305 no revision is given, the parent of the working directory is used.
1306
1306
1307 Output may be to a file, in which case the name of the file is
1307 Output may be to a file, in which case the name of the file is
1308 given using a template string. See :hg:`help templates`. In addition
1308 given using a template string. See :hg:`help templates`. In addition
1309 to the common template keywords, the following formatting rules are
1309 to the common template keywords, the following formatting rules are
1310 supported:
1310 supported:
1311
1311
1312 :``%%``: literal "%" character
1312 :``%%``: literal "%" character
1313 :``%s``: basename of file being printed
1313 :``%s``: basename of file being printed
1314 :``%d``: dirname of file being printed, or '.' if in repository root
1314 :``%d``: dirname of file being printed, or '.' if in repository root
1315 :``%p``: root-relative path name of file being printed
1315 :``%p``: root-relative path name of file being printed
1316 :``%H``: changeset hash (40 hexadecimal digits)
1316 :``%H``: changeset hash (40 hexadecimal digits)
1317 :``%R``: changeset revision number
1317 :``%R``: changeset revision number
1318 :``%h``: short-form changeset hash (12 hexadecimal digits)
1318 :``%h``: short-form changeset hash (12 hexadecimal digits)
1319 :``%r``: zero-padded changeset revision number
1319 :``%r``: zero-padded changeset revision number
1320 :``%b``: basename of the exporting repository
1320 :``%b``: basename of the exporting repository
1321 :``\\``: literal "\\" character
1321 :``\\``: literal "\\" character
1322
1322
1323 Returns 0 on success.
1323 Returns 0 on success.
1324 """
1324 """
1325 opts = pycompat.byteskwargs(opts)
1325 opts = pycompat.byteskwargs(opts)
1326 rev = opts.get('rev')
1326 rev = opts.get('rev')
1327 if rev:
1327 if rev:
1328 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
1328 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
1329 ctx = scmutil.revsingle(repo, rev)
1329 ctx = scmutil.revsingle(repo, rev)
1330 m = scmutil.match(ctx, (file1,) + pats, opts)
1330 m = scmutil.match(ctx, (file1,) + pats, opts)
1331 fntemplate = opts.pop('output', '')
1331 fntemplate = opts.pop('output', '')
1332 if cmdutil.isstdiofilename(fntemplate):
1332 if cmdutil.isstdiofilename(fntemplate):
1333 fntemplate = ''
1333 fntemplate = ''
1334
1334
1335 if fntemplate:
1335 if fntemplate:
1336 fm = formatter.nullformatter(ui, 'cat', opts)
1336 fm = formatter.nullformatter(ui, 'cat', opts)
1337 else:
1337 else:
1338 ui.pager('cat')
1338 ui.pager('cat')
1339 fm = ui.formatter('cat', opts)
1339 fm = ui.formatter('cat', opts)
1340 with fm:
1340 with fm:
1341 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '',
1341 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '',
1342 **pycompat.strkwargs(opts))
1342 **pycompat.strkwargs(opts))
1343
1343
1344 @command('^clone',
1344 @command('^clone',
1345 [('U', 'noupdate', None, _('the clone will include an empty working '
1345 [('U', 'noupdate', None, _('the clone will include an empty working '
1346 'directory (only a repository)')),
1346 'directory (only a repository)')),
1347 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1347 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1348 _('REV')),
1348 _('REV')),
1349 ('r', 'rev', [], _('do not clone everything, but include this changeset'
1349 ('r', 'rev', [], _('do not clone everything, but include this changeset'
1350 ' and its ancestors'), _('REV')),
1350 ' and its ancestors'), _('REV')),
1351 ('b', 'branch', [], _('do not clone everything, but include this branch\'s'
1351 ('b', 'branch', [], _('do not clone everything, but include this branch\'s'
1352 ' changesets and their ancestors'), _('BRANCH')),
1352 ' changesets and their ancestors'), _('BRANCH')),
1353 ('', 'pull', None, _('use pull protocol to copy metadata')),
1353 ('', 'pull', None, _('use pull protocol to copy metadata')),
1354 ('', 'uncompressed', None,
1354 ('', 'uncompressed', None,
1355 _('an alias to --stream (DEPRECATED)')),
1355 _('an alias to --stream (DEPRECATED)')),
1356 ('', 'stream', None,
1356 ('', 'stream', None,
1357 _('clone with minimal data processing')),
1357 _('clone with minimal data processing')),
1358 ] + remoteopts,
1358 ] + remoteopts,
1359 _('[OPTION]... SOURCE [DEST]'),
1359 _('[OPTION]... SOURCE [DEST]'),
1360 norepo=True)
1360 norepo=True)
1361 def clone(ui, source, dest=None, **opts):
1361 def clone(ui, source, dest=None, **opts):
1362 """make a copy of an existing repository
1362 """make a copy of an existing repository
1363
1363
1364 Create a copy of an existing repository in a new directory.
1364 Create a copy of an existing repository in a new directory.
1365
1365
1366 If no destination directory name is specified, it defaults to the
1366 If no destination directory name is specified, it defaults to the
1367 basename of the source.
1367 basename of the source.
1368
1368
1369 The location of the source is added to the new repository's
1369 The location of the source is added to the new repository's
1370 ``.hg/hgrc`` file, as the default to be used for future pulls.
1370 ``.hg/hgrc`` file, as the default to be used for future pulls.
1371
1371
1372 Only local paths and ``ssh://`` URLs are supported as
1372 Only local paths and ``ssh://`` URLs are supported as
1373 destinations. For ``ssh://`` destinations, no working directory or
1373 destinations. For ``ssh://`` destinations, no working directory or
1374 ``.hg/hgrc`` will be created on the remote side.
1374 ``.hg/hgrc`` will be created on the remote side.
1375
1375
1376 If the source repository has a bookmark called '@' set, that
1376 If the source repository has a bookmark called '@' set, that
1377 revision will be checked out in the new repository by default.
1377 revision will be checked out in the new repository by default.
1378
1378
1379 To check out a particular version, use -u/--update, or
1379 To check out a particular version, use -u/--update, or
1380 -U/--noupdate to create a clone with no working directory.
1380 -U/--noupdate to create a clone with no working directory.
1381
1381
1382 To pull only a subset of changesets, specify one or more revisions
1382 To pull only a subset of changesets, specify one or more revisions
1383 identifiers with -r/--rev or branches with -b/--branch. The
1383 identifiers with -r/--rev or branches with -b/--branch. The
1384 resulting clone will contain only the specified changesets and
1384 resulting clone will contain only the specified changesets and
1385 their ancestors. These options (or 'clone src#rev dest') imply
1385 their ancestors. These options (or 'clone src#rev dest') imply
1386 --pull, even for local source repositories.
1386 --pull, even for local source repositories.
1387
1387
1388 In normal clone mode, the remote normalizes repository data into a common
1388 In normal clone mode, the remote normalizes repository data into a common
1389 exchange format and the receiving end translates this data into its local
1389 exchange format and the receiving end translates this data into its local
1390 storage format. --stream activates a different clone mode that essentially
1390 storage format. --stream activates a different clone mode that essentially
1391 copies repository files from the remote with minimal data processing. This
1391 copies repository files from the remote with minimal data processing. This
1392 significantly reduces the CPU cost of a clone both remotely and locally.
1392 significantly reduces the CPU cost of a clone both remotely and locally.
1393 However, it often increases the transferred data size by 30-40%. This can
1393 However, it often increases the transferred data size by 30-40%. This can
1394 result in substantially faster clones where I/O throughput is plentiful,
1394 result in substantially faster clones where I/O throughput is plentiful,
1395 especially for larger repositories. A side-effect of --stream clones is
1395 especially for larger repositories. A side-effect of --stream clones is
1396 that storage settings and requirements on the remote are applied locally:
1396 that storage settings and requirements on the remote are applied locally:
1397 a modern client may inherit legacy or inefficient storage used by the
1397 a modern client may inherit legacy or inefficient storage used by the
1398 remote or a legacy Mercurial client may not be able to clone from a
1398 remote or a legacy Mercurial client may not be able to clone from a
1399 modern Mercurial remote.
1399 modern Mercurial remote.
1400
1400
1401 .. note::
1401 .. note::
1402
1402
1403 Specifying a tag will include the tagged changeset but not the
1403 Specifying a tag will include the tagged changeset but not the
1404 changeset containing the tag.
1404 changeset containing the tag.
1405
1405
1406 .. container:: verbose
1406 .. container:: verbose
1407
1407
1408 For efficiency, hardlinks are used for cloning whenever the
1408 For efficiency, hardlinks are used for cloning whenever the
1409 source and destination are on the same filesystem (note this
1409 source and destination are on the same filesystem (note this
1410 applies only to the repository data, not to the working
1410 applies only to the repository data, not to the working
1411 directory). Some filesystems, such as AFS, implement hardlinking
1411 directory). Some filesystems, such as AFS, implement hardlinking
1412 incorrectly, but do not report errors. In these cases, use the
1412 incorrectly, but do not report errors. In these cases, use the
1413 --pull option to avoid hardlinking.
1413 --pull option to avoid hardlinking.
1414
1414
1415 Mercurial will update the working directory to the first applicable
1415 Mercurial will update the working directory to the first applicable
1416 revision from this list:
1416 revision from this list:
1417
1417
1418 a) null if -U or the source repository has no changesets
1418 a) null if -U or the source repository has no changesets
1419 b) if -u . and the source repository is local, the first parent of
1419 b) if -u . and the source repository is local, the first parent of
1420 the source repository's working directory
1420 the source repository's working directory
1421 c) the changeset specified with -u (if a branch name, this means the
1421 c) the changeset specified with -u (if a branch name, this means the
1422 latest head of that branch)
1422 latest head of that branch)
1423 d) the changeset specified with -r
1423 d) the changeset specified with -r
1424 e) the tipmost head specified with -b
1424 e) the tipmost head specified with -b
1425 f) the tipmost head specified with the url#branch source syntax
1425 f) the tipmost head specified with the url#branch source syntax
1426 g) the revision marked with the '@' bookmark, if present
1426 g) the revision marked with the '@' bookmark, if present
1427 h) the tipmost head of the default branch
1427 h) the tipmost head of the default branch
1428 i) tip
1428 i) tip
1429
1429
1430 When cloning from servers that support it, Mercurial may fetch
1430 When cloning from servers that support it, Mercurial may fetch
1431 pre-generated data from a server-advertised URL or inline from the
1431 pre-generated data from a server-advertised URL or inline from the
1432 same stream. When this is done, hooks operating on incoming changesets
1432 same stream. When this is done, hooks operating on incoming changesets
1433 and changegroups may fire more than once, once for each pre-generated
1433 and changegroups may fire more than once, once for each pre-generated
1434 bundle and as well as for any additional remaining data. In addition,
1434 bundle and as well as for any additional remaining data. In addition,
1435 if an error occurs, the repository may be rolled back to a partial
1435 if an error occurs, the repository may be rolled back to a partial
1436 clone. This behavior may change in future releases.
1436 clone. This behavior may change in future releases.
1437 See :hg:`help -e clonebundles` for more.
1437 See :hg:`help -e clonebundles` for more.
1438
1438
1439 Examples:
1439 Examples:
1440
1440
1441 - clone a remote repository to a new directory named hg/::
1441 - clone a remote repository to a new directory named hg/::
1442
1442
1443 hg clone https://www.mercurial-scm.org/repo/hg/
1443 hg clone https://www.mercurial-scm.org/repo/hg/
1444
1444
1445 - create a lightweight local clone::
1445 - create a lightweight local clone::
1446
1446
1447 hg clone project/ project-feature/
1447 hg clone project/ project-feature/
1448
1448
1449 - clone from an absolute path on an ssh server (note double-slash)::
1449 - clone from an absolute path on an ssh server (note double-slash)::
1450
1450
1451 hg clone ssh://user@server//home/projects/alpha/
1451 hg clone ssh://user@server//home/projects/alpha/
1452
1452
1453 - do a streaming clone while checking out a specified version::
1453 - do a streaming clone while checking out a specified version::
1454
1454
1455 hg clone --stream http://server/repo -u 1.5
1455 hg clone --stream http://server/repo -u 1.5
1456
1456
1457 - create a repository without changesets after a particular revision::
1457 - create a repository without changesets after a particular revision::
1458
1458
1459 hg clone -r 04e544 experimental/ good/
1459 hg clone -r 04e544 experimental/ good/
1460
1460
1461 - clone (and track) a particular named branch::
1461 - clone (and track) a particular named branch::
1462
1462
1463 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1463 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1464
1464
1465 See :hg:`help urls` for details on specifying URLs.
1465 See :hg:`help urls` for details on specifying URLs.
1466
1466
1467 Returns 0 on success.
1467 Returns 0 on success.
1468 """
1468 """
1469 opts = pycompat.byteskwargs(opts)
1469 opts = pycompat.byteskwargs(opts)
1470 if opts.get('noupdate') and opts.get('updaterev'):
1470 if opts.get('noupdate') and opts.get('updaterev'):
1471 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1471 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1472
1472
1473 # --include/--exclude can come from narrow or sparse.
1473 # --include/--exclude can come from narrow or sparse.
1474 includepats, excludepats = None, None
1474 includepats, excludepats = None, None
1475
1475
1476 # hg.clone() differentiates between None and an empty set. So make sure
1476 # hg.clone() differentiates between None and an empty set. So make sure
1477 # patterns are sets if narrow is requested without patterns.
1477 # patterns are sets if narrow is requested without patterns.
1478 if opts.get('narrow'):
1478 if opts.get('narrow'):
1479 includepats = set()
1479 includepats = set()
1480 excludepats = set()
1480 excludepats = set()
1481
1481
1482 if opts.get('include'):
1482 if opts.get('include'):
1483 includepats = narrowspec.parsepatterns(opts.get('include'))
1483 includepats = narrowspec.parsepatterns(opts.get('include'))
1484 if opts.get('exclude'):
1484 if opts.get('exclude'):
1485 excludepats = narrowspec.parsepatterns(opts.get('exclude'))
1485 excludepats = narrowspec.parsepatterns(opts.get('exclude'))
1486
1486
1487 r = hg.clone(ui, opts, source, dest,
1487 r = hg.clone(ui, opts, source, dest,
1488 pull=opts.get('pull'),
1488 pull=opts.get('pull'),
1489 stream=opts.get('stream') or opts.get('uncompressed'),
1489 stream=opts.get('stream') or opts.get('uncompressed'),
1490 revs=opts.get('rev'),
1490 revs=opts.get('rev'),
1491 update=opts.get('updaterev') or not opts.get('noupdate'),
1491 update=opts.get('updaterev') or not opts.get('noupdate'),
1492 branch=opts.get('branch'),
1492 branch=opts.get('branch'),
1493 shareopts=opts.get('shareopts'),
1493 shareopts=opts.get('shareopts'),
1494 storeincludepats=includepats,
1494 storeincludepats=includepats,
1495 storeexcludepats=excludepats)
1495 storeexcludepats=excludepats)
1496
1496
1497 return r is None
1497 return r is None
1498
1498
1499 @command('^commit|ci',
1499 @command('^commit|ci',
1500 [('A', 'addremove', None,
1500 [('A', 'addremove', None,
1501 _('mark new/missing files as added/removed before committing')),
1501 _('mark new/missing files as added/removed before committing')),
1502 ('', 'close-branch', None,
1502 ('', 'close-branch', None,
1503 _('mark a branch head as closed')),
1503 _('mark a branch head as closed')),
1504 ('', 'amend', None, _('amend the parent of the working directory')),
1504 ('', 'amend', None, _('amend the parent of the working directory')),
1505 ('s', 'secret', None, _('use the secret phase for committing')),
1505 ('s', 'secret', None, _('use the secret phase for committing')),
1506 ('e', 'edit', None, _('invoke editor on commit messages')),
1506 ('e', 'edit', None, _('invoke editor on commit messages')),
1507 ('i', 'interactive', None, _('use interactive mode')),
1507 ('i', 'interactive', None, _('use interactive mode')),
1508 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1508 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1509 _('[OPTION]... [FILE]...'),
1509 _('[OPTION]... [FILE]...'),
1510 inferrepo=True)
1510 inferrepo=True)
1511 def commit(ui, repo, *pats, **opts):
1511 def commit(ui, repo, *pats, **opts):
1512 """commit the specified files or all outstanding changes
1512 """commit the specified files or all outstanding changes
1513
1513
1514 Commit changes to the given files into the repository. Unlike a
1514 Commit changes to the given files into the repository. Unlike a
1515 centralized SCM, this operation is a local operation. See
1515 centralized SCM, this operation is a local operation. See
1516 :hg:`push` for a way to actively distribute your changes.
1516 :hg:`push` for a way to actively distribute your changes.
1517
1517
1518 If a list of files is omitted, all changes reported by :hg:`status`
1518 If a list of files is omitted, all changes reported by :hg:`status`
1519 will be committed.
1519 will be committed.
1520
1520
1521 If you are committing the result of a merge, do not provide any
1521 If you are committing the result of a merge, do not provide any
1522 filenames or -I/-X filters.
1522 filenames or -I/-X filters.
1523
1523
1524 If no commit message is specified, Mercurial starts your
1524 If no commit message is specified, Mercurial starts your
1525 configured editor where you can enter a message. In case your
1525 configured editor where you can enter a message. In case your
1526 commit fails, you will find a backup of your message in
1526 commit fails, you will find a backup of your message in
1527 ``.hg/last-message.txt``.
1527 ``.hg/last-message.txt``.
1528
1528
1529 The --close-branch flag can be used to mark the current branch
1529 The --close-branch flag can be used to mark the current branch
1530 head closed. When all heads of a branch are closed, the branch
1530 head closed. When all heads of a branch are closed, the branch
1531 will be considered closed and no longer listed.
1531 will be considered closed and no longer listed.
1532
1532
1533 The --amend flag can be used to amend the parent of the
1533 The --amend flag can be used to amend the parent of the
1534 working directory with a new commit that contains the changes
1534 working directory with a new commit that contains the changes
1535 in the parent in addition to those currently reported by :hg:`status`,
1535 in the parent in addition to those currently reported by :hg:`status`,
1536 if there are any. The old commit is stored in a backup bundle in
1536 if there are any. The old commit is stored in a backup bundle in
1537 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1537 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1538 on how to restore it).
1538 on how to restore it).
1539
1539
1540 Message, user and date are taken from the amended commit unless
1540 Message, user and date are taken from the amended commit unless
1541 specified. When a message isn't specified on the command line,
1541 specified. When a message isn't specified on the command line,
1542 the editor will open with the message of the amended commit.
1542 the editor will open with the message of the amended commit.
1543
1543
1544 It is not possible to amend public changesets (see :hg:`help phases`)
1544 It is not possible to amend public changesets (see :hg:`help phases`)
1545 or changesets that have children.
1545 or changesets that have children.
1546
1546
1547 See :hg:`help dates` for a list of formats valid for -d/--date.
1547 See :hg:`help dates` for a list of formats valid for -d/--date.
1548
1548
1549 Returns 0 on success, 1 if nothing changed.
1549 Returns 0 on success, 1 if nothing changed.
1550
1550
1551 .. container:: verbose
1551 .. container:: verbose
1552
1552
1553 Examples:
1553 Examples:
1554
1554
1555 - commit all files ending in .py::
1555 - commit all files ending in .py::
1556
1556
1557 hg commit --include "set:**.py"
1557 hg commit --include "set:**.py"
1558
1558
1559 - commit all non-binary files::
1559 - commit all non-binary files::
1560
1560
1561 hg commit --exclude "set:binary()"
1561 hg commit --exclude "set:binary()"
1562
1562
1563 - amend the current commit and set the date to now::
1563 - amend the current commit and set the date to now::
1564
1564
1565 hg commit --amend --date now
1565 hg commit --amend --date now
1566 """
1566 """
1567 with repo.wlock(), repo.lock():
1567 with repo.wlock(), repo.lock():
1568 return _docommit(ui, repo, *pats, **opts)
1568 return _docommit(ui, repo, *pats, **opts)
1569
1569
1570 def _docommit(ui, repo, *pats, **opts):
1570 def _docommit(ui, repo, *pats, **opts):
1571 if opts.get(r'interactive'):
1571 if opts.get(r'interactive'):
1572 opts.pop(r'interactive')
1572 opts.pop(r'interactive')
1573 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1573 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1574 cmdutil.recordfilter, *pats,
1574 cmdutil.recordfilter, *pats,
1575 **opts)
1575 **opts)
1576 # ret can be 0 (no changes to record) or the value returned by
1576 # ret can be 0 (no changes to record) or the value returned by
1577 # commit(), 1 if nothing changed or None on success.
1577 # commit(), 1 if nothing changed or None on success.
1578 return 1 if ret == 0 else ret
1578 return 1 if ret == 0 else ret
1579
1579
1580 opts = pycompat.byteskwargs(opts)
1580 opts = pycompat.byteskwargs(opts)
1581 if opts.get('subrepos'):
1581 if opts.get('subrepos'):
1582 if opts.get('amend'):
1582 if opts.get('amend'):
1583 raise error.Abort(_('cannot amend with --subrepos'))
1583 raise error.Abort(_('cannot amend with --subrepos'))
1584 # Let --subrepos on the command line override config setting.
1584 # Let --subrepos on the command line override config setting.
1585 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1585 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1586
1586
1587 cmdutil.checkunfinished(repo, commit=True)
1587 cmdutil.checkunfinished(repo, commit=True)
1588
1588
1589 branch = repo[None].branch()
1589 branch = repo[None].branch()
1590 bheads = repo.branchheads(branch)
1590 bheads = repo.branchheads(branch)
1591
1591
1592 extra = {}
1592 extra = {}
1593 if opts.get('close_branch'):
1593 if opts.get('close_branch'):
1594 extra['close'] = '1'
1594 extra['close'] = '1'
1595
1595
1596 if not bheads:
1596 if not bheads:
1597 raise error.Abort(_('can only close branch heads'))
1597 raise error.Abort(_('can only close branch heads'))
1598 elif opts.get('amend'):
1598 elif opts.get('amend'):
1599 if repo[None].parents()[0].p1().branch() != branch and \
1599 if repo[None].parents()[0].p1().branch() != branch and \
1600 repo[None].parents()[0].p2().branch() != branch:
1600 repo[None].parents()[0].p2().branch() != branch:
1601 raise error.Abort(_('can only close branch heads'))
1601 raise error.Abort(_('can only close branch heads'))
1602
1602
1603 if opts.get('amend'):
1603 if opts.get('amend'):
1604 if ui.configbool('ui', 'commitsubrepos'):
1604 if ui.configbool('ui', 'commitsubrepos'):
1605 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1605 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1606
1606
1607 old = repo['.']
1607 old = repo['.']
1608 rewriteutil.precheck(repo, [old.rev()], 'amend')
1608 rewriteutil.precheck(repo, [old.rev()], 'amend')
1609
1609
1610 # Currently histedit gets confused if an amend happens while histedit
1610 # Currently histedit gets confused if an amend happens while histedit
1611 # is in progress. Since we have a checkunfinished command, we are
1611 # is in progress. Since we have a checkunfinished command, we are
1612 # temporarily honoring it.
1612 # temporarily honoring it.
1613 #
1613 #
1614 # Note: eventually this guard will be removed. Please do not expect
1614 # Note: eventually this guard will be removed. Please do not expect
1615 # this behavior to remain.
1615 # this behavior to remain.
1616 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1616 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1617 cmdutil.checkunfinished(repo)
1617 cmdutil.checkunfinished(repo)
1618
1618
1619 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
1619 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
1620 if node == old.node():
1620 if node == old.node():
1621 ui.status(_("nothing changed\n"))
1621 ui.status(_("nothing changed\n"))
1622 return 1
1622 return 1
1623 else:
1623 else:
1624 def commitfunc(ui, repo, message, match, opts):
1624 def commitfunc(ui, repo, message, match, opts):
1625 overrides = {}
1625 overrides = {}
1626 if opts.get('secret'):
1626 if opts.get('secret'):
1627 overrides[('phases', 'new-commit')] = 'secret'
1627 overrides[('phases', 'new-commit')] = 'secret'
1628
1628
1629 baseui = repo.baseui
1629 baseui = repo.baseui
1630 with baseui.configoverride(overrides, 'commit'):
1630 with baseui.configoverride(overrides, 'commit'):
1631 with ui.configoverride(overrides, 'commit'):
1631 with ui.configoverride(overrides, 'commit'):
1632 editform = cmdutil.mergeeditform(repo[None],
1632 editform = cmdutil.mergeeditform(repo[None],
1633 'commit.normal')
1633 'commit.normal')
1634 editor = cmdutil.getcommiteditor(
1634 editor = cmdutil.getcommiteditor(
1635 editform=editform, **pycompat.strkwargs(opts))
1635 editform=editform, **pycompat.strkwargs(opts))
1636 return repo.commit(message,
1636 return repo.commit(message,
1637 opts.get('user'),
1637 opts.get('user'),
1638 opts.get('date'),
1638 opts.get('date'),
1639 match,
1639 match,
1640 editor=editor,
1640 editor=editor,
1641 extra=extra)
1641 extra=extra)
1642
1642
1643 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1643 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1644
1644
1645 if not node:
1645 if not node:
1646 stat = cmdutil.postcommitstatus(repo, pats, opts)
1646 stat = cmdutil.postcommitstatus(repo, pats, opts)
1647 if stat[3]:
1647 if stat[3]:
1648 ui.status(_("nothing changed (%d missing files, see "
1648 ui.status(_("nothing changed (%d missing files, see "
1649 "'hg status')\n") % len(stat[3]))
1649 "'hg status')\n") % len(stat[3]))
1650 else:
1650 else:
1651 ui.status(_("nothing changed\n"))
1651 ui.status(_("nothing changed\n"))
1652 return 1
1652 return 1
1653
1653
1654 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1654 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1655
1655
1656 @command('config|showconfig|debugconfig',
1656 @command('config|showconfig|debugconfig',
1657 [('u', 'untrusted', None, _('show untrusted configuration options')),
1657 [('u', 'untrusted', None, _('show untrusted configuration options')),
1658 ('e', 'edit', None, _('edit user config')),
1658 ('e', 'edit', None, _('edit user config')),
1659 ('l', 'local', None, _('edit repository config')),
1659 ('l', 'local', None, _('edit repository config')),
1660 ('g', 'global', None, _('edit global config'))] + formatteropts,
1660 ('g', 'global', None, _('edit global config'))] + formatteropts,
1661 _('[-u] [NAME]...'),
1661 _('[-u] [NAME]...'),
1662 optionalrepo=True,
1662 optionalrepo=True,
1663 intents={INTENT_READONLY})
1663 intents={INTENT_READONLY})
1664 def config(ui, repo, *values, **opts):
1664 def config(ui, repo, *values, **opts):
1665 """show combined config settings from all hgrc files
1665 """show combined config settings from all hgrc files
1666
1666
1667 With no arguments, print names and values of all config items.
1667 With no arguments, print names and values of all config items.
1668
1668
1669 With one argument of the form section.name, print just the value
1669 With one argument of the form section.name, print just the value
1670 of that config item.
1670 of that config item.
1671
1671
1672 With multiple arguments, print names and values of all config
1672 With multiple arguments, print names and values of all config
1673 items with matching section names or section.names.
1673 items with matching section names or section.names.
1674
1674
1675 With --edit, start an editor on the user-level config file. With
1675 With --edit, start an editor on the user-level config file. With
1676 --global, edit the system-wide config file. With --local, edit the
1676 --global, edit the system-wide config file. With --local, edit the
1677 repository-level config file.
1677 repository-level config file.
1678
1678
1679 With --debug, the source (filename and line number) is printed
1679 With --debug, the source (filename and line number) is printed
1680 for each config item.
1680 for each config item.
1681
1681
1682 See :hg:`help config` for more information about config files.
1682 See :hg:`help config` for more information about config files.
1683
1683
1684 Returns 0 on success, 1 if NAME does not exist.
1684 Returns 0 on success, 1 if NAME does not exist.
1685
1685
1686 """
1686 """
1687
1687
1688 opts = pycompat.byteskwargs(opts)
1688 opts = pycompat.byteskwargs(opts)
1689 if opts.get('edit') or opts.get('local') or opts.get('global'):
1689 if opts.get('edit') or opts.get('local') or opts.get('global'):
1690 if opts.get('local') and opts.get('global'):
1690 if opts.get('local') and opts.get('global'):
1691 raise error.Abort(_("can't use --local and --global together"))
1691 raise error.Abort(_("can't use --local and --global together"))
1692
1692
1693 if opts.get('local'):
1693 if opts.get('local'):
1694 if not repo:
1694 if not repo:
1695 raise error.Abort(_("can't use --local outside a repository"))
1695 raise error.Abort(_("can't use --local outside a repository"))
1696 paths = [repo.vfs.join('hgrc')]
1696 paths = [repo.vfs.join('hgrc')]
1697 elif opts.get('global'):
1697 elif opts.get('global'):
1698 paths = rcutil.systemrcpath()
1698 paths = rcutil.systemrcpath()
1699 else:
1699 else:
1700 paths = rcutil.userrcpath()
1700 paths = rcutil.userrcpath()
1701
1701
1702 for f in paths:
1702 for f in paths:
1703 if os.path.exists(f):
1703 if os.path.exists(f):
1704 break
1704 break
1705 else:
1705 else:
1706 if opts.get('global'):
1706 if opts.get('global'):
1707 samplehgrc = uimod.samplehgrcs['global']
1707 samplehgrc = uimod.samplehgrcs['global']
1708 elif opts.get('local'):
1708 elif opts.get('local'):
1709 samplehgrc = uimod.samplehgrcs['local']
1709 samplehgrc = uimod.samplehgrcs['local']
1710 else:
1710 else:
1711 samplehgrc = uimod.samplehgrcs['user']
1711 samplehgrc = uimod.samplehgrcs['user']
1712
1712
1713 f = paths[0]
1713 f = paths[0]
1714 fp = open(f, "wb")
1714 fp = open(f, "wb")
1715 fp.write(util.tonativeeol(samplehgrc))
1715 fp.write(util.tonativeeol(samplehgrc))
1716 fp.close()
1716 fp.close()
1717
1717
1718 editor = ui.geteditor()
1718 editor = ui.geteditor()
1719 ui.system("%s \"%s\"" % (editor, f),
1719 ui.system("%s \"%s\"" % (editor, f),
1720 onerr=error.Abort, errprefix=_("edit failed"),
1720 onerr=error.Abort, errprefix=_("edit failed"),
1721 blockedtag='config_edit')
1721 blockedtag='config_edit')
1722 return
1722 return
1723 ui.pager('config')
1723 ui.pager('config')
1724 fm = ui.formatter('config', opts)
1724 fm = ui.formatter('config', opts)
1725 for t, f in rcutil.rccomponents():
1725 for t, f in rcutil.rccomponents():
1726 if t == 'path':
1726 if t == 'path':
1727 ui.debug('read config from: %s\n' % f)
1727 ui.debug('read config from: %s\n' % f)
1728 elif t == 'items':
1728 elif t == 'items':
1729 for section, name, value, source in f:
1729 for section, name, value, source in f:
1730 ui.debug('set config by: %s\n' % source)
1730 ui.debug('set config by: %s\n' % source)
1731 else:
1731 else:
1732 raise error.ProgrammingError('unknown rctype: %s' % t)
1732 raise error.ProgrammingError('unknown rctype: %s' % t)
1733 untrusted = bool(opts.get('untrusted'))
1733 untrusted = bool(opts.get('untrusted'))
1734
1734
1735 selsections = selentries = []
1735 selsections = selentries = []
1736 if values:
1736 if values:
1737 selsections = [v for v in values if '.' not in v]
1737 selsections = [v for v in values if '.' not in v]
1738 selentries = [v for v in values if '.' in v]
1738 selentries = [v for v in values if '.' in v]
1739 uniquesel = (len(selentries) == 1 and not selsections)
1739 uniquesel = (len(selentries) == 1 and not selsections)
1740 selsections = set(selsections)
1740 selsections = set(selsections)
1741 selentries = set(selentries)
1741 selentries = set(selentries)
1742
1742
1743 matched = False
1743 matched = False
1744 for section, name, value in ui.walkconfig(untrusted=untrusted):
1744 for section, name, value in ui.walkconfig(untrusted=untrusted):
1745 source = ui.configsource(section, name, untrusted)
1745 source = ui.configsource(section, name, untrusted)
1746 value = pycompat.bytestr(value)
1746 value = pycompat.bytestr(value)
1747 if fm.isplain():
1747 if fm.isplain():
1748 source = source or 'none'
1748 source = source or 'none'
1749 value = value.replace('\n', '\\n')
1749 value = value.replace('\n', '\\n')
1750 entryname = section + '.' + name
1750 entryname = section + '.' + name
1751 if values and not (section in selsections or entryname in selentries):
1751 if values and not (section in selsections or entryname in selentries):
1752 continue
1752 continue
1753 fm.startitem()
1753 fm.startitem()
1754 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1754 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1755 if uniquesel:
1755 if uniquesel:
1756 fm.data(name=entryname)
1756 fm.data(name=entryname)
1757 fm.write('value', '%s\n', value)
1757 fm.write('value', '%s\n', value)
1758 else:
1758 else:
1759 fm.write('name value', '%s=%s\n', entryname, value)
1759 fm.write('name value', '%s=%s\n', entryname, value)
1760 matched = True
1760 matched = True
1761 fm.end()
1761 fm.end()
1762 if matched:
1762 if matched:
1763 return 0
1763 return 0
1764 return 1
1764 return 1
1765
1765
1766 @command('copy|cp',
1766 @command('copy|cp',
1767 [('A', 'after', None, _('record a copy that has already occurred')),
1767 [('A', 'after', None, _('record a copy that has already occurred')),
1768 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1768 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1769 ] + walkopts + dryrunopts,
1769 ] + walkopts + dryrunopts,
1770 _('[OPTION]... [SOURCE]... DEST'))
1770 _('[OPTION]... [SOURCE]... DEST'))
1771 def copy(ui, repo, *pats, **opts):
1771 def copy(ui, repo, *pats, **opts):
1772 """mark files as copied for the next commit
1772 """mark files as copied for the next commit
1773
1773
1774 Mark dest as having copies of source files. If dest is a
1774 Mark dest as having copies of source files. If dest is a
1775 directory, copies are put in that directory. If dest is a file,
1775 directory, copies are put in that directory. If dest is a file,
1776 the source must be a single file.
1776 the source must be a single file.
1777
1777
1778 By default, this command copies the contents of files as they
1778 By default, this command copies the contents of files as they
1779 exist in the working directory. If invoked with -A/--after, the
1779 exist in the working directory. If invoked with -A/--after, the
1780 operation is recorded, but no copying is performed.
1780 operation is recorded, but no copying is performed.
1781
1781
1782 This command takes effect with the next commit. To undo a copy
1782 This command takes effect with the next commit. To undo a copy
1783 before that, see :hg:`revert`.
1783 before that, see :hg:`revert`.
1784
1784
1785 Returns 0 on success, 1 if errors are encountered.
1785 Returns 0 on success, 1 if errors are encountered.
1786 """
1786 """
1787 opts = pycompat.byteskwargs(opts)
1787 opts = pycompat.byteskwargs(opts)
1788 with repo.wlock(False):
1788 with repo.wlock(False):
1789 return cmdutil.copy(ui, repo, pats, opts)
1789 return cmdutil.copy(ui, repo, pats, opts)
1790
1790
1791 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1791 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1792 def debugcommands(ui, cmd='', *args):
1792 def debugcommands(ui, cmd='', *args):
1793 """list all available commands and options"""
1793 """list all available commands and options"""
1794 for cmd, vals in sorted(table.iteritems()):
1794 for cmd, vals in sorted(table.iteritems()):
1795 cmd = cmd.split('|')[0].strip('^')
1795 cmd = cmd.split('|')[0].strip('^')
1796 opts = ', '.join([i[1] for i in vals[1]])
1796 opts = ', '.join([i[1] for i in vals[1]])
1797 ui.write('%s: %s\n' % (cmd, opts))
1797 ui.write('%s: %s\n' % (cmd, opts))
1798
1798
1799 @command('debugcomplete',
1799 @command('debugcomplete',
1800 [('o', 'options', None, _('show the command options'))],
1800 [('o', 'options', None, _('show the command options'))],
1801 _('[-o] CMD'),
1801 _('[-o] CMD'),
1802 norepo=True)
1802 norepo=True)
1803 def debugcomplete(ui, cmd='', **opts):
1803 def debugcomplete(ui, cmd='', **opts):
1804 """returns the completion list associated with the given command"""
1804 """returns the completion list associated with the given command"""
1805
1805
1806 if opts.get(r'options'):
1806 if opts.get(r'options'):
1807 options = []
1807 options = []
1808 otables = [globalopts]
1808 otables = [globalopts]
1809 if cmd:
1809 if cmd:
1810 aliases, entry = cmdutil.findcmd(cmd, table, False)
1810 aliases, entry = cmdutil.findcmd(cmd, table, False)
1811 otables.append(entry[1])
1811 otables.append(entry[1])
1812 for t in otables:
1812 for t in otables:
1813 for o in t:
1813 for o in t:
1814 if "(DEPRECATED)" in o[3]:
1814 if "(DEPRECATED)" in o[3]:
1815 continue
1815 continue
1816 if o[0]:
1816 if o[0]:
1817 options.append('-%s' % o[0])
1817 options.append('-%s' % o[0])
1818 options.append('--%s' % o[1])
1818 options.append('--%s' % o[1])
1819 ui.write("%s\n" % "\n".join(options))
1819 ui.write("%s\n" % "\n".join(options))
1820 return
1820 return
1821
1821
1822 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1822 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1823 if ui.verbose:
1823 if ui.verbose:
1824 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1824 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1825 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1825 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1826
1826
1827 @command('^diff',
1827 @command('^diff',
1828 [('r', 'rev', [], _('revision'), _('REV')),
1828 [('r', 'rev', [], _('revision'), _('REV')),
1829 ('c', 'change', '', _('change made by revision'), _('REV'))
1829 ('c', 'change', '', _('change made by revision'), _('REV'))
1830 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1830 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1831 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1831 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1832 inferrepo=True,
1832 inferrepo=True,
1833 intents={INTENT_READONLY})
1833 intents={INTENT_READONLY})
1834 def diff(ui, repo, *pats, **opts):
1834 def diff(ui, repo, *pats, **opts):
1835 """diff repository (or selected files)
1835 """diff repository (or selected files)
1836
1836
1837 Show differences between revisions for the specified files.
1837 Show differences between revisions for the specified files.
1838
1838
1839 Differences between files are shown using the unified diff format.
1839 Differences between files are shown using the unified diff format.
1840
1840
1841 .. note::
1841 .. note::
1842
1842
1843 :hg:`diff` may generate unexpected results for merges, as it will
1843 :hg:`diff` may generate unexpected results for merges, as it will
1844 default to comparing against the working directory's first
1844 default to comparing against the working directory's first
1845 parent changeset if no revisions are specified.
1845 parent changeset if no revisions are specified.
1846
1846
1847 When two revision arguments are given, then changes are shown
1847 When two revision arguments are given, then changes are shown
1848 between those revisions. If only one revision is specified then
1848 between those revisions. If only one revision is specified then
1849 that revision is compared to the working directory, and, when no
1849 that revision is compared to the working directory, and, when no
1850 revisions are specified, the working directory files are compared
1850 revisions are specified, the working directory files are compared
1851 to its first parent.
1851 to its first parent.
1852
1852
1853 Alternatively you can specify -c/--change with a revision to see
1853 Alternatively you can specify -c/--change with a revision to see
1854 the changes in that changeset relative to its first parent.
1854 the changes in that changeset relative to its first parent.
1855
1855
1856 Without the -a/--text option, diff will avoid generating diffs of
1856 Without the -a/--text option, diff will avoid generating diffs of
1857 files it detects as binary. With -a, diff will generate a diff
1857 files it detects as binary. With -a, diff will generate a diff
1858 anyway, probably with undesirable results.
1858 anyway, probably with undesirable results.
1859
1859
1860 Use the -g/--git option to generate diffs in the git extended diff
1860 Use the -g/--git option to generate diffs in the git extended diff
1861 format. For more information, read :hg:`help diffs`.
1861 format. For more information, read :hg:`help diffs`.
1862
1862
1863 .. container:: verbose
1863 .. container:: verbose
1864
1864
1865 Examples:
1865 Examples:
1866
1866
1867 - compare a file in the current working directory to its parent::
1867 - compare a file in the current working directory to its parent::
1868
1868
1869 hg diff foo.c
1869 hg diff foo.c
1870
1870
1871 - compare two historical versions of a directory, with rename info::
1871 - compare two historical versions of a directory, with rename info::
1872
1872
1873 hg diff --git -r 1.0:1.2 lib/
1873 hg diff --git -r 1.0:1.2 lib/
1874
1874
1875 - get change stats relative to the last change on some date::
1875 - get change stats relative to the last change on some date::
1876
1876
1877 hg diff --stat -r "date('may 2')"
1877 hg diff --stat -r "date('may 2')"
1878
1878
1879 - diff all newly-added files that contain a keyword::
1879 - diff all newly-added files that contain a keyword::
1880
1880
1881 hg diff "set:added() and grep(GNU)"
1881 hg diff "set:added() and grep(GNU)"
1882
1882
1883 - compare a revision and its parents::
1883 - compare a revision and its parents::
1884
1884
1885 hg diff -c 9353 # compare against first parent
1885 hg diff -c 9353 # compare against first parent
1886 hg diff -r 9353^:9353 # same using revset syntax
1886 hg diff -r 9353^:9353 # same using revset syntax
1887 hg diff -r 9353^2:9353 # compare against the second parent
1887 hg diff -r 9353^2:9353 # compare against the second parent
1888
1888
1889 Returns 0 on success.
1889 Returns 0 on success.
1890 """
1890 """
1891
1891
1892 opts = pycompat.byteskwargs(opts)
1892 opts = pycompat.byteskwargs(opts)
1893 revs = opts.get('rev')
1893 revs = opts.get('rev')
1894 change = opts.get('change')
1894 change = opts.get('change')
1895 stat = opts.get('stat')
1895 stat = opts.get('stat')
1896 reverse = opts.get('reverse')
1896 reverse = opts.get('reverse')
1897
1897
1898 if revs and change:
1898 if revs and change:
1899 msg = _('cannot specify --rev and --change at the same time')
1899 msg = _('cannot specify --rev and --change at the same time')
1900 raise error.Abort(msg)
1900 raise error.Abort(msg)
1901 elif change:
1901 elif change:
1902 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
1902 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
1903 ctx2 = scmutil.revsingle(repo, change, None)
1903 ctx2 = scmutil.revsingle(repo, change, None)
1904 ctx1 = ctx2.p1()
1904 ctx1 = ctx2.p1()
1905 else:
1905 else:
1906 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
1906 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
1907 ctx1, ctx2 = scmutil.revpair(repo, revs)
1907 ctx1, ctx2 = scmutil.revpair(repo, revs)
1908 node1, node2 = ctx1.node(), ctx2.node()
1908 node1, node2 = ctx1.node(), ctx2.node()
1909
1909
1910 if reverse:
1910 if reverse:
1911 node1, node2 = node2, node1
1911 node1, node2 = node2, node1
1912
1912
1913 diffopts = patch.diffallopts(ui, opts)
1913 diffopts = patch.diffallopts(ui, opts)
1914 m = scmutil.match(ctx2, pats, opts)
1914 m = scmutil.match(ctx2, pats, opts)
1915 m = matchmod.intersectmatchers(m, repo.narrowmatch())
1915 m = matchmod.intersectmatchers(m, repo.narrowmatch())
1916 ui.pager('diff')
1916 ui.pager('diff')
1917 logcmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1917 logcmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1918 listsubrepos=opts.get('subrepos'),
1918 listsubrepos=opts.get('subrepos'),
1919 root=opts.get('root'))
1919 root=opts.get('root'))
1920
1920
1921 @command('^export',
1921 @command('^export',
1922 [('B', 'bookmark', '',
1922 [('B', 'bookmark', '',
1923 _('export changes only reachable by given bookmark')),
1923 _('export changes only reachable by given bookmark')),
1924 ('o', 'output', '',
1924 ('o', 'output', '',
1925 _('print output to file with formatted name'), _('FORMAT')),
1925 _('print output to file with formatted name'), _('FORMAT')),
1926 ('', 'switch-parent', None, _('diff against the second parent')),
1926 ('', 'switch-parent', None, _('diff against the second parent')),
1927 ('r', 'rev', [], _('revisions to export'), _('REV')),
1927 ('r', 'rev', [], _('revisions to export'), _('REV')),
1928 ] + diffopts + formatteropts,
1928 ] + diffopts + formatteropts,
1929 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
1929 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
1930 intents={INTENT_READONLY})
1930 intents={INTENT_READONLY})
1931 def export(ui, repo, *changesets, **opts):
1931 def export(ui, repo, *changesets, **opts):
1932 """dump the header and diffs for one or more changesets
1932 """dump the header and diffs for one or more changesets
1933
1933
1934 Print the changeset header and diffs for one or more revisions.
1934 Print the changeset header and diffs for one or more revisions.
1935 If no revision is given, the parent of the working directory is used.
1935 If no revision is given, the parent of the working directory is used.
1936
1936
1937 The information shown in the changeset header is: author, date,
1937 The information shown in the changeset header is: author, date,
1938 branch name (if non-default), changeset hash, parent(s) and commit
1938 branch name (if non-default), changeset hash, parent(s) and commit
1939 comment.
1939 comment.
1940
1940
1941 .. note::
1941 .. note::
1942
1942
1943 :hg:`export` may generate unexpected diff output for merge
1943 :hg:`export` may generate unexpected diff output for merge
1944 changesets, as it will compare the merge changeset against its
1944 changesets, as it will compare the merge changeset against its
1945 first parent only.
1945 first parent only.
1946
1946
1947 Output may be to a file, in which case the name of the file is
1947 Output may be to a file, in which case the name of the file is
1948 given using a template string. See :hg:`help templates`. In addition
1948 given using a template string. See :hg:`help templates`. In addition
1949 to the common template keywords, the following formatting rules are
1949 to the common template keywords, the following formatting rules are
1950 supported:
1950 supported:
1951
1951
1952 :``%%``: literal "%" character
1952 :``%%``: literal "%" character
1953 :``%H``: changeset hash (40 hexadecimal digits)
1953 :``%H``: changeset hash (40 hexadecimal digits)
1954 :``%N``: number of patches being generated
1954 :``%N``: number of patches being generated
1955 :``%R``: changeset revision number
1955 :``%R``: changeset revision number
1956 :``%b``: basename of the exporting repository
1956 :``%b``: basename of the exporting repository
1957 :``%h``: short-form changeset hash (12 hexadecimal digits)
1957 :``%h``: short-form changeset hash (12 hexadecimal digits)
1958 :``%m``: first line of the commit message (only alphanumeric characters)
1958 :``%m``: first line of the commit message (only alphanumeric characters)
1959 :``%n``: zero-padded sequence number, starting at 1
1959 :``%n``: zero-padded sequence number, starting at 1
1960 :``%r``: zero-padded changeset revision number
1960 :``%r``: zero-padded changeset revision number
1961 :``\\``: literal "\\" character
1961 :``\\``: literal "\\" character
1962
1962
1963 Without the -a/--text option, export will avoid generating diffs
1963 Without the -a/--text option, export will avoid generating diffs
1964 of files it detects as binary. With -a, export will generate a
1964 of files it detects as binary. With -a, export will generate a
1965 diff anyway, probably with undesirable results.
1965 diff anyway, probably with undesirable results.
1966
1966
1967 With -B/--bookmark changesets reachable by the given bookmark are
1967 With -B/--bookmark changesets reachable by the given bookmark are
1968 selected.
1968 selected.
1969
1969
1970 Use the -g/--git option to generate diffs in the git extended diff
1970 Use the -g/--git option to generate diffs in the git extended diff
1971 format. See :hg:`help diffs` for more information.
1971 format. See :hg:`help diffs` for more information.
1972
1972
1973 With the --switch-parent option, the diff will be against the
1973 With the --switch-parent option, the diff will be against the
1974 second parent. It can be useful to review a merge.
1974 second parent. It can be useful to review a merge.
1975
1975
1976 .. container:: verbose
1976 .. container:: verbose
1977
1977
1978 Examples:
1978 Examples:
1979
1979
1980 - use export and import to transplant a bugfix to the current
1980 - use export and import to transplant a bugfix to the current
1981 branch::
1981 branch::
1982
1982
1983 hg export -r 9353 | hg import -
1983 hg export -r 9353 | hg import -
1984
1984
1985 - export all the changesets between two revisions to a file with
1985 - export all the changesets between two revisions to a file with
1986 rename information::
1986 rename information::
1987
1987
1988 hg export --git -r 123:150 > changes.txt
1988 hg export --git -r 123:150 > changes.txt
1989
1989
1990 - split outgoing changes into a series of patches with
1990 - split outgoing changes into a series of patches with
1991 descriptive names::
1991 descriptive names::
1992
1992
1993 hg export -r "outgoing()" -o "%n-%m.patch"
1993 hg export -r "outgoing()" -o "%n-%m.patch"
1994
1994
1995 Returns 0 on success.
1995 Returns 0 on success.
1996 """
1996 """
1997 opts = pycompat.byteskwargs(opts)
1997 opts = pycompat.byteskwargs(opts)
1998 bookmark = opts.get('bookmark')
1998 bookmark = opts.get('bookmark')
1999 changesets += tuple(opts.get('rev', []))
1999 changesets += tuple(opts.get('rev', []))
2000
2000
2001 if bookmark and changesets:
2001 if bookmark and changesets:
2002 raise error.Abort(_("-r and -B are mutually exclusive"))
2002 raise error.Abort(_("-r and -B are mutually exclusive"))
2003
2003
2004 if bookmark:
2004 if bookmark:
2005 if bookmark not in repo._bookmarks:
2005 if bookmark not in repo._bookmarks:
2006 raise error.Abort(_("bookmark '%s' not found") % bookmark)
2006 raise error.Abort(_("bookmark '%s' not found") % bookmark)
2007
2007
2008 revs = scmutil.bookmarkrevs(repo, bookmark)
2008 revs = scmutil.bookmarkrevs(repo, bookmark)
2009 else:
2009 else:
2010 if not changesets:
2010 if not changesets:
2011 changesets = ['.']
2011 changesets = ['.']
2012
2012
2013 repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn')
2013 repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn')
2014 revs = scmutil.revrange(repo, changesets)
2014 revs = scmutil.revrange(repo, changesets)
2015
2015
2016 if not revs:
2016 if not revs:
2017 raise error.Abort(_("export requires at least one changeset"))
2017 raise error.Abort(_("export requires at least one changeset"))
2018 if len(revs) > 1:
2018 if len(revs) > 1:
2019 ui.note(_('exporting patches:\n'))
2019 ui.note(_('exporting patches:\n'))
2020 else:
2020 else:
2021 ui.note(_('exporting patch:\n'))
2021 ui.note(_('exporting patch:\n'))
2022
2022
2023 fntemplate = opts.get('output')
2023 fntemplate = opts.get('output')
2024 if cmdutil.isstdiofilename(fntemplate):
2024 if cmdutil.isstdiofilename(fntemplate):
2025 fntemplate = ''
2025 fntemplate = ''
2026
2026
2027 if fntemplate:
2027 if fntemplate:
2028 fm = formatter.nullformatter(ui, 'export', opts)
2028 fm = formatter.nullformatter(ui, 'export', opts)
2029 else:
2029 else:
2030 ui.pager('export')
2030 ui.pager('export')
2031 fm = ui.formatter('export', opts)
2031 fm = ui.formatter('export', opts)
2032 with fm:
2032 with fm:
2033 cmdutil.export(repo, revs, fm, fntemplate=fntemplate,
2033 cmdutil.export(repo, revs, fm, fntemplate=fntemplate,
2034 switch_parent=opts.get('switch_parent'),
2034 switch_parent=opts.get('switch_parent'),
2035 opts=patch.diffallopts(ui, opts))
2035 opts=patch.diffallopts(ui, opts))
2036
2036
2037 @command('files',
2037 @command('files',
2038 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
2038 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
2039 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2039 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2040 ] + walkopts + formatteropts + subrepoopts,
2040 ] + walkopts + formatteropts + subrepoopts,
2041 _('[OPTION]... [FILE]...'),
2041 _('[OPTION]... [FILE]...'),
2042 intents={INTENT_READONLY})
2042 intents={INTENT_READONLY})
2043 def files(ui, repo, *pats, **opts):
2043 def files(ui, repo, *pats, **opts):
2044 """list tracked files
2044 """list tracked files
2045
2045
2046 Print files under Mercurial control in the working directory or
2046 Print files under Mercurial control in the working directory or
2047 specified revision for given files (excluding removed files).
2047 specified revision for given files (excluding removed files).
2048 Files can be specified as filenames or filesets.
2048 Files can be specified as filenames or filesets.
2049
2049
2050 If no files are given to match, this command prints the names
2050 If no files are given to match, this command prints the names
2051 of all files under Mercurial control.
2051 of all files under Mercurial control.
2052
2052
2053 .. container:: verbose
2053 .. container:: verbose
2054
2054
2055 Examples:
2055 Examples:
2056
2056
2057 - list all files under the current directory::
2057 - list all files under the current directory::
2058
2058
2059 hg files .
2059 hg files .
2060
2060
2061 - shows sizes and flags for current revision::
2061 - shows sizes and flags for current revision::
2062
2062
2063 hg files -vr .
2063 hg files -vr .
2064
2064
2065 - list all files named README::
2065 - list all files named README::
2066
2066
2067 hg files -I "**/README"
2067 hg files -I "**/README"
2068
2068
2069 - list all binary files::
2069 - list all binary files::
2070
2070
2071 hg files "set:binary()"
2071 hg files "set:binary()"
2072
2072
2073 - find files containing a regular expression::
2073 - find files containing a regular expression::
2074
2074
2075 hg files "set:grep('bob')"
2075 hg files "set:grep('bob')"
2076
2076
2077 - search tracked file contents with xargs and grep::
2077 - search tracked file contents with xargs and grep::
2078
2078
2079 hg files -0 | xargs -0 grep foo
2079 hg files -0 | xargs -0 grep foo
2080
2080
2081 See :hg:`help patterns` and :hg:`help filesets` for more information
2081 See :hg:`help patterns` and :hg:`help filesets` for more information
2082 on specifying file patterns.
2082 on specifying file patterns.
2083
2083
2084 Returns 0 if a match is found, 1 otherwise.
2084 Returns 0 if a match is found, 1 otherwise.
2085
2085
2086 """
2086 """
2087
2087
2088 opts = pycompat.byteskwargs(opts)
2088 opts = pycompat.byteskwargs(opts)
2089 rev = opts.get('rev')
2089 rev = opts.get('rev')
2090 if rev:
2090 if rev:
2091 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2091 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2092 ctx = scmutil.revsingle(repo, rev, None)
2092 ctx = scmutil.revsingle(repo, rev, None)
2093
2093
2094 end = '\n'
2094 end = '\n'
2095 if opts.get('print0'):
2095 if opts.get('print0'):
2096 end = '\0'
2096 end = '\0'
2097 fmt = '%s' + end
2097 fmt = '%s' + end
2098
2098
2099 m = scmutil.match(ctx, pats, opts)
2099 m = scmutil.match(ctx, pats, opts)
2100 ui.pager('files')
2100 ui.pager('files')
2101 with ui.formatter('files', opts) as fm:
2101 with ui.formatter('files', opts) as fm:
2102 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2102 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2103
2103
2104 @command(
2104 @command(
2105 '^forget',
2105 '^forget',
2106 [('i', 'interactive', None, _('use interactive mode')),
2106 [('i', 'interactive', None, _('use interactive mode')),
2107 ] + walkopts + dryrunopts,
2107 ] + walkopts + dryrunopts,
2108 _('[OPTION]... FILE...'), inferrepo=True)
2108 _('[OPTION]... FILE...'), inferrepo=True)
2109 def forget(ui, repo, *pats, **opts):
2109 def forget(ui, repo, *pats, **opts):
2110 """forget the specified files on the next commit
2110 """forget the specified files on the next commit
2111
2111
2112 Mark the specified files so they will no longer be tracked
2112 Mark the specified files so they will no longer be tracked
2113 after the next commit.
2113 after the next commit.
2114
2114
2115 This only removes files from the current branch, not from the
2115 This only removes files from the current branch, not from the
2116 entire project history, and it does not delete them from the
2116 entire project history, and it does not delete them from the
2117 working directory.
2117 working directory.
2118
2118
2119 To delete the file from the working directory, see :hg:`remove`.
2119 To delete the file from the working directory, see :hg:`remove`.
2120
2120
2121 To undo a forget before the next commit, see :hg:`add`.
2121 To undo a forget before the next commit, see :hg:`add`.
2122
2122
2123 .. container:: verbose
2123 .. container:: verbose
2124
2124
2125 Examples:
2125 Examples:
2126
2126
2127 - forget newly-added binary files::
2127 - forget newly-added binary files::
2128
2128
2129 hg forget "set:added() and binary()"
2129 hg forget "set:added() and binary()"
2130
2130
2131 - forget files that would be excluded by .hgignore::
2131 - forget files that would be excluded by .hgignore::
2132
2132
2133 hg forget "set:hgignore()"
2133 hg forget "set:hgignore()"
2134
2134
2135 Returns 0 on success.
2135 Returns 0 on success.
2136 """
2136 """
2137
2137
2138 opts = pycompat.byteskwargs(opts)
2138 opts = pycompat.byteskwargs(opts)
2139 if not pats:
2139 if not pats:
2140 raise error.Abort(_('no files specified'))
2140 raise error.Abort(_('no files specified'))
2141
2141
2142 m = scmutil.match(repo[None], pats, opts)
2142 m = scmutil.match(repo[None], pats, opts)
2143 dryrun, interactive = opts.get('dry_run'), opts.get('interactive')
2143 dryrun, interactive = opts.get('dry_run'), opts.get('interactive')
2144 rejected = cmdutil.forget(ui, repo, m, prefix="",
2144 rejected = cmdutil.forget(ui, repo, m, prefix="",
2145 explicitonly=False, dryrun=dryrun,
2145 explicitonly=False, dryrun=dryrun,
2146 interactive=interactive)[0]
2146 interactive=interactive)[0]
2147 return rejected and 1 or 0
2147 return rejected and 1 or 0
2148
2148
2149 @command(
2149 @command(
2150 'graft',
2150 'graft',
2151 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2151 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2152 ('c', 'continue', False, _('resume interrupted graft')),
2152 ('c', 'continue', False, _('resume interrupted graft')),
2153 ('', 'stop', False, _('stop interrupted graft')),
2153 ('', 'stop', False, _('stop interrupted graft')),
2154 ('', 'abort', False, _('abort interrupted graft')),
2154 ('', 'abort', False, _('abort interrupted graft')),
2155 ('e', 'edit', False, _('invoke editor on commit messages')),
2155 ('e', 'edit', False, _('invoke editor on commit messages')),
2156 ('', 'log', None, _('append graft info to log message')),
2156 ('', 'log', None, _('append graft info to log message')),
2157 ('', 'no-commit', None,
2157 ('', 'no-commit', None,
2158 _("don't commit, just apply the changes in working directory")),
2158 _("don't commit, just apply the changes in working directory")),
2159 ('f', 'force', False, _('force graft')),
2159 ('f', 'force', False, _('force graft')),
2160 ('D', 'currentdate', False,
2160 ('D', 'currentdate', False,
2161 _('record the current date as commit date')),
2161 _('record the current date as commit date')),
2162 ('U', 'currentuser', False,
2162 ('U', 'currentuser', False,
2163 _('record the current user as committer'), _('DATE'))]
2163 _('record the current user as committer'), _('DATE'))]
2164 + commitopts2 + mergetoolopts + dryrunopts,
2164 + commitopts2 + mergetoolopts + dryrunopts,
2165 _('[OPTION]... [-r REV]... REV...'))
2165 _('[OPTION]... [-r REV]... REV...'))
2166 def graft(ui, repo, *revs, **opts):
2166 def graft(ui, repo, *revs, **opts):
2167 '''copy changes from other branches onto the current branch
2167 '''copy changes from other branches onto the current branch
2168
2168
2169 This command uses Mercurial's merge logic to copy individual
2169 This command uses Mercurial's merge logic to copy individual
2170 changes from other branches without merging branches in the
2170 changes from other branches without merging branches in the
2171 history graph. This is sometimes known as 'backporting' or
2171 history graph. This is sometimes known as 'backporting' or
2172 'cherry-picking'. By default, graft will copy user, date, and
2172 'cherry-picking'. By default, graft will copy user, date, and
2173 description from the source changesets.
2173 description from the source changesets.
2174
2174
2175 Changesets that are ancestors of the current revision, that have
2175 Changesets that are ancestors of the current revision, that have
2176 already been grafted, or that are merges will be skipped.
2176 already been grafted, or that are merges will be skipped.
2177
2177
2178 If --log is specified, log messages will have a comment appended
2178 If --log is specified, log messages will have a comment appended
2179 of the form::
2179 of the form::
2180
2180
2181 (grafted from CHANGESETHASH)
2181 (grafted from CHANGESETHASH)
2182
2182
2183 If --force is specified, revisions will be grafted even if they
2183 If --force is specified, revisions will be grafted even if they
2184 are already ancestors of, or have been grafted to, the destination.
2184 are already ancestors of, or have been grafted to, the destination.
2185 This is useful when the revisions have since been backed out.
2185 This is useful when the revisions have since been backed out.
2186
2186
2187 If a graft merge results in conflicts, the graft process is
2187 If a graft merge results in conflicts, the graft process is
2188 interrupted so that the current merge can be manually resolved.
2188 interrupted so that the current merge can be manually resolved.
2189 Once all conflicts are addressed, the graft process can be
2189 Once all conflicts are addressed, the graft process can be
2190 continued with the -c/--continue option.
2190 continued with the -c/--continue option.
2191
2191
2192 The -c/--continue option reapplies all the earlier options.
2192 The -c/--continue option reapplies all the earlier options.
2193
2193
2194 .. container:: verbose
2194 .. container:: verbose
2195
2195
2196 Examples:
2196 Examples:
2197
2197
2198 - copy a single change to the stable branch and edit its description::
2198 - copy a single change to the stable branch and edit its description::
2199
2199
2200 hg update stable
2200 hg update stable
2201 hg graft --edit 9393
2201 hg graft --edit 9393
2202
2202
2203 - graft a range of changesets with one exception, updating dates::
2203 - graft a range of changesets with one exception, updating dates::
2204
2204
2205 hg graft -D "2085::2093 and not 2091"
2205 hg graft -D "2085::2093 and not 2091"
2206
2206
2207 - continue a graft after resolving conflicts::
2207 - continue a graft after resolving conflicts::
2208
2208
2209 hg graft -c
2209 hg graft -c
2210
2210
2211 - show the source of a grafted changeset::
2211 - show the source of a grafted changeset::
2212
2212
2213 hg log --debug -r .
2213 hg log --debug -r .
2214
2214
2215 - show revisions sorted by date::
2215 - show revisions sorted by date::
2216
2216
2217 hg log -r "sort(all(), date)"
2217 hg log -r "sort(all(), date)"
2218
2218
2219 See :hg:`help revisions` for more about specifying revisions.
2219 See :hg:`help revisions` for more about specifying revisions.
2220
2220
2221 Returns 0 on successful completion.
2221 Returns 0 on successful completion.
2222 '''
2222 '''
2223 with repo.wlock():
2223 with repo.wlock():
2224 return _dograft(ui, repo, *revs, **opts)
2224 return _dograft(ui, repo, *revs, **opts)
2225
2225
2226 def _dograft(ui, repo, *revs, **opts):
2226 def _dograft(ui, repo, *revs, **opts):
2227 opts = pycompat.byteskwargs(opts)
2227 opts = pycompat.byteskwargs(opts)
2228 if revs and opts.get('rev'):
2228 if revs and opts.get('rev'):
2229 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2229 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2230 'revision ordering!\n'))
2230 'revision ordering!\n'))
2231
2231
2232 revs = list(revs)
2232 revs = list(revs)
2233 revs.extend(opts.get('rev'))
2233 revs.extend(opts.get('rev'))
2234 # a dict of data to be stored in state file
2234 # a dict of data to be stored in state file
2235 statedata = {}
2235 statedata = {}
2236 # list of new nodes created by ongoing graft
2236 # list of new nodes created by ongoing graft
2237 statedata['newnodes'] = []
2237 statedata['newnodes'] = []
2238
2238
2239 if not opts.get('user') and opts.get('currentuser'):
2239 if not opts.get('user') and opts.get('currentuser'):
2240 opts['user'] = ui.username()
2240 opts['user'] = ui.username()
2241 if not opts.get('date') and opts.get('currentdate'):
2241 if not opts.get('date') and opts.get('currentdate'):
2242 opts['date'] = "%d %d" % dateutil.makedate()
2242 opts['date'] = "%d %d" % dateutil.makedate()
2243
2243
2244 editor = cmdutil.getcommiteditor(editform='graft',
2244 editor = cmdutil.getcommiteditor(editform='graft',
2245 **pycompat.strkwargs(opts))
2245 **pycompat.strkwargs(opts))
2246
2246
2247 cont = False
2247 cont = False
2248 if opts.get('no_commit'):
2248 if opts.get('no_commit'):
2249 if opts.get('edit'):
2249 if opts.get('edit'):
2250 raise error.Abort(_("cannot specify --no-commit and "
2250 raise error.Abort(_("cannot specify --no-commit and "
2251 "--edit together"))
2251 "--edit together"))
2252 if opts.get('currentuser'):
2252 if opts.get('currentuser'):
2253 raise error.Abort(_("cannot specify --no-commit and "
2253 raise error.Abort(_("cannot specify --no-commit and "
2254 "--currentuser together"))
2254 "--currentuser together"))
2255 if opts.get('currentdate'):
2255 if opts.get('currentdate'):
2256 raise error.Abort(_("cannot specify --no-commit and "
2256 raise error.Abort(_("cannot specify --no-commit and "
2257 "--currentdate together"))
2257 "--currentdate together"))
2258 if opts.get('log'):
2258 if opts.get('log'):
2259 raise error.Abort(_("cannot specify --no-commit and "
2259 raise error.Abort(_("cannot specify --no-commit and "
2260 "--log together"))
2260 "--log together"))
2261
2261
2262 graftstate = statemod.cmdstate(repo, 'graftstate')
2262 graftstate = statemod.cmdstate(repo, 'graftstate')
2263
2263
2264 if opts.get('stop'):
2264 if opts.get('stop'):
2265 if opts.get('continue'):
2265 if opts.get('continue'):
2266 raise error.Abort(_("cannot use '--continue' and "
2266 raise error.Abort(_("cannot use '--continue' and "
2267 "'--stop' together"))
2267 "'--stop' together"))
2268 if opts.get('abort'):
2268 if opts.get('abort'):
2269 raise error.Abort(_("cannot use '--abort' and '--stop' together"))
2269 raise error.Abort(_("cannot use '--abort' and '--stop' together"))
2270
2270
2271 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2271 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2272 opts.get('date'), opts.get('currentdate'),
2272 opts.get('date'), opts.get('currentdate'),
2273 opts.get('currentuser'), opts.get('rev'))):
2273 opts.get('currentuser'), opts.get('rev'))):
2274 raise error.Abort(_("cannot specify any other flag with '--stop'"))
2274 raise error.Abort(_("cannot specify any other flag with '--stop'"))
2275 return _stopgraft(ui, repo, graftstate)
2275 return _stopgraft(ui, repo, graftstate)
2276 elif opts.get('abort'):
2276 elif opts.get('abort'):
2277 if opts.get('continue'):
2277 if opts.get('continue'):
2278 raise error.Abort(_("cannot use '--continue' and "
2278 raise error.Abort(_("cannot use '--continue' and "
2279 "'--abort' together"))
2279 "'--abort' together"))
2280 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2280 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2281 opts.get('date'), opts.get('currentdate'),
2281 opts.get('date'), opts.get('currentdate'),
2282 opts.get('currentuser'), opts.get('rev'))):
2282 opts.get('currentuser'), opts.get('rev'))):
2283 raise error.Abort(_("cannot specify any other flag with '--abort'"))
2283 raise error.Abort(_("cannot specify any other flag with '--abort'"))
2284
2284
2285 return _abortgraft(ui, repo, graftstate)
2285 return _abortgraft(ui, repo, graftstate)
2286 elif opts.get('continue'):
2286 elif opts.get('continue'):
2287 cont = True
2287 cont = True
2288 if revs:
2288 if revs:
2289 raise error.Abort(_("can't specify --continue and revisions"))
2289 raise error.Abort(_("can't specify --continue and revisions"))
2290 # read in unfinished revisions
2290 # read in unfinished revisions
2291 if graftstate.exists():
2291 if graftstate.exists():
2292 statedata = _readgraftstate(repo, graftstate)
2292 statedata = _readgraftstate(repo, graftstate)
2293 if statedata.get('date'):
2293 if statedata.get('date'):
2294 opts['date'] = statedata['date']
2294 opts['date'] = statedata['date']
2295 if statedata.get('user'):
2295 if statedata.get('user'):
2296 opts['user'] = statedata['user']
2296 opts['user'] = statedata['user']
2297 if statedata.get('log'):
2297 if statedata.get('log'):
2298 opts['log'] = True
2298 opts['log'] = True
2299 if statedata.get('no_commit'):
2299 if statedata.get('no_commit'):
2300 opts['no_commit'] = statedata.get('no_commit')
2300 opts['no_commit'] = statedata.get('no_commit')
2301 nodes = statedata['nodes']
2301 nodes = statedata['nodes']
2302 revs = [repo[node].rev() for node in nodes]
2302 revs = [repo[node].rev() for node in nodes]
2303 else:
2303 else:
2304 cmdutil.wrongtooltocontinue(repo, _('graft'))
2304 cmdutil.wrongtooltocontinue(repo, _('graft'))
2305 else:
2305 else:
2306 if not revs:
2306 if not revs:
2307 raise error.Abort(_('no revisions specified'))
2307 raise error.Abort(_('no revisions specified'))
2308 cmdutil.checkunfinished(repo)
2308 cmdutil.checkunfinished(repo)
2309 cmdutil.bailifchanged(repo)
2309 cmdutil.bailifchanged(repo)
2310 revs = scmutil.revrange(repo, revs)
2310 revs = scmutil.revrange(repo, revs)
2311
2311
2312 skipped = set()
2312 skipped = set()
2313 # check for merges
2313 # check for merges
2314 for rev in repo.revs('%ld and merge()', revs):
2314 for rev in repo.revs('%ld and merge()', revs):
2315 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2315 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2316 skipped.add(rev)
2316 skipped.add(rev)
2317 revs = [r for r in revs if r not in skipped]
2317 revs = [r for r in revs if r not in skipped]
2318 if not revs:
2318 if not revs:
2319 return -1
2319 return -1
2320
2320
2321 # Don't check in the --continue case, in effect retaining --force across
2321 # Don't check in the --continue case, in effect retaining --force across
2322 # --continues. That's because without --force, any revisions we decided to
2322 # --continues. That's because without --force, any revisions we decided to
2323 # skip would have been filtered out here, so they wouldn't have made their
2323 # skip would have been filtered out here, so they wouldn't have made their
2324 # way to the graftstate. With --force, any revisions we would have otherwise
2324 # way to the graftstate. With --force, any revisions we would have otherwise
2325 # skipped would not have been filtered out, and if they hadn't been applied
2325 # skipped would not have been filtered out, and if they hadn't been applied
2326 # already, they'd have been in the graftstate.
2326 # already, they'd have been in the graftstate.
2327 if not (cont or opts.get('force')):
2327 if not (cont or opts.get('force')):
2328 # check for ancestors of dest branch
2328 # check for ancestors of dest branch
2329 crev = repo['.'].rev()
2329 crev = repo['.'].rev()
2330 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2330 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2331 # XXX make this lazy in the future
2331 # XXX make this lazy in the future
2332 # don't mutate while iterating, create a copy
2332 # don't mutate while iterating, create a copy
2333 for rev in list(revs):
2333 for rev in list(revs):
2334 if rev in ancestors:
2334 if rev in ancestors:
2335 ui.warn(_('skipping ancestor revision %d:%s\n') %
2335 ui.warn(_('skipping ancestor revision %d:%s\n') %
2336 (rev, repo[rev]))
2336 (rev, repo[rev]))
2337 # XXX remove on list is slow
2337 # XXX remove on list is slow
2338 revs.remove(rev)
2338 revs.remove(rev)
2339 if not revs:
2339 if not revs:
2340 return -1
2340 return -1
2341
2341
2342 # analyze revs for earlier grafts
2342 # analyze revs for earlier grafts
2343 ids = {}
2343 ids = {}
2344 for ctx in repo.set("%ld", revs):
2344 for ctx in repo.set("%ld", revs):
2345 ids[ctx.hex()] = ctx.rev()
2345 ids[ctx.hex()] = ctx.rev()
2346 n = ctx.extra().get('source')
2346 n = ctx.extra().get('source')
2347 if n:
2347 if n:
2348 ids[n] = ctx.rev()
2348 ids[n] = ctx.rev()
2349
2349
2350 # check ancestors for earlier grafts
2350 # check ancestors for earlier grafts
2351 ui.debug('scanning for duplicate grafts\n')
2351 ui.debug('scanning for duplicate grafts\n')
2352
2352
2353 # The only changesets we can be sure doesn't contain grafts of any
2353 # The only changesets we can be sure doesn't contain grafts of any
2354 # revs, are the ones that are common ancestors of *all* revs:
2354 # revs, are the ones that are common ancestors of *all* revs:
2355 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2355 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2356 ctx = repo[rev]
2356 ctx = repo[rev]
2357 n = ctx.extra().get('source')
2357 n = ctx.extra().get('source')
2358 if n in ids:
2358 if n in ids:
2359 try:
2359 try:
2360 r = repo[n].rev()
2360 r = repo[n].rev()
2361 except error.RepoLookupError:
2361 except error.RepoLookupError:
2362 r = None
2362 r = None
2363 if r in revs:
2363 if r in revs:
2364 ui.warn(_('skipping revision %d:%s '
2364 ui.warn(_('skipping revision %d:%s '
2365 '(already grafted to %d:%s)\n')
2365 '(already grafted to %d:%s)\n')
2366 % (r, repo[r], rev, ctx))
2366 % (r, repo[r], rev, ctx))
2367 revs.remove(r)
2367 revs.remove(r)
2368 elif ids[n] in revs:
2368 elif ids[n] in revs:
2369 if r is None:
2369 if r is None:
2370 ui.warn(_('skipping already grafted revision %d:%s '
2370 ui.warn(_('skipping already grafted revision %d:%s '
2371 '(%d:%s also has unknown origin %s)\n')
2371 '(%d:%s also has unknown origin %s)\n')
2372 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2372 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2373 else:
2373 else:
2374 ui.warn(_('skipping already grafted revision %d:%s '
2374 ui.warn(_('skipping already grafted revision %d:%s '
2375 '(%d:%s also has origin %d:%s)\n')
2375 '(%d:%s also has origin %d:%s)\n')
2376 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2376 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2377 revs.remove(ids[n])
2377 revs.remove(ids[n])
2378 elif ctx.hex() in ids:
2378 elif ctx.hex() in ids:
2379 r = ids[ctx.hex()]
2379 r = ids[ctx.hex()]
2380 ui.warn(_('skipping already grafted revision %d:%s '
2380 ui.warn(_('skipping already grafted revision %d:%s '
2381 '(was grafted from %d:%s)\n') %
2381 '(was grafted from %d:%s)\n') %
2382 (r, repo[r], rev, ctx))
2382 (r, repo[r], rev, ctx))
2383 revs.remove(r)
2383 revs.remove(r)
2384 if not revs:
2384 if not revs:
2385 return -1
2385 return -1
2386
2386
2387 if opts.get('no_commit'):
2387 if opts.get('no_commit'):
2388 statedata['no_commit'] = True
2388 statedata['no_commit'] = True
2389 for pos, ctx in enumerate(repo.set("%ld", revs)):
2389 for pos, ctx in enumerate(repo.set("%ld", revs)):
2390 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2390 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2391 ctx.description().split('\n', 1)[0])
2391 ctx.description().split('\n', 1)[0])
2392 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2392 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2393 if names:
2393 if names:
2394 desc += ' (%s)' % ' '.join(names)
2394 desc += ' (%s)' % ' '.join(names)
2395 ui.status(_('grafting %s\n') % desc)
2395 ui.status(_('grafting %s\n') % desc)
2396 if opts.get('dry_run'):
2396 if opts.get('dry_run'):
2397 continue
2397 continue
2398
2398
2399 source = ctx.extra().get('source')
2399 source = ctx.extra().get('source')
2400 extra = {}
2400 extra = {}
2401 if source:
2401 if source:
2402 extra['source'] = source
2402 extra['source'] = source
2403 extra['intermediate-source'] = ctx.hex()
2403 extra['intermediate-source'] = ctx.hex()
2404 else:
2404 else:
2405 extra['source'] = ctx.hex()
2405 extra['source'] = ctx.hex()
2406 user = ctx.user()
2406 user = ctx.user()
2407 if opts.get('user'):
2407 if opts.get('user'):
2408 user = opts['user']
2408 user = opts['user']
2409 statedata['user'] = user
2409 statedata['user'] = user
2410 date = ctx.date()
2410 date = ctx.date()
2411 if opts.get('date'):
2411 if opts.get('date'):
2412 date = opts['date']
2412 date = opts['date']
2413 statedata['date'] = date
2413 statedata['date'] = date
2414 message = ctx.description()
2414 message = ctx.description()
2415 if opts.get('log'):
2415 if opts.get('log'):
2416 message += '\n(grafted from %s)' % ctx.hex()
2416 message += '\n(grafted from %s)' % ctx.hex()
2417 statedata['log'] = True
2417 statedata['log'] = True
2418
2418
2419 # we don't merge the first commit when continuing
2419 # we don't merge the first commit when continuing
2420 if not cont:
2420 if not cont:
2421 # perform the graft merge with p1(rev) as 'ancestor'
2421 # perform the graft merge with p1(rev) as 'ancestor'
2422 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
2422 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
2423 with ui.configoverride(overrides, 'graft'):
2423 with ui.configoverride(overrides, 'graft'):
2424 stats = mergemod.graft(repo, ctx, ctx.p1(), ['local', 'graft'])
2424 stats = mergemod.graft(repo, ctx, ctx.p1(), ['local', 'graft'])
2425 # report any conflicts
2425 # report any conflicts
2426 if stats.unresolvedcount > 0:
2426 if stats.unresolvedcount > 0:
2427 # write out state for --continue
2427 # write out state for --continue
2428 nodes = [repo[rev].hex() for rev in revs[pos:]]
2428 nodes = [repo[rev].hex() for rev in revs[pos:]]
2429 statedata['nodes'] = nodes
2429 statedata['nodes'] = nodes
2430 stateversion = 1
2430 stateversion = 1
2431 graftstate.save(stateversion, statedata)
2431 graftstate.save(stateversion, statedata)
2432 hint = _("use 'hg resolve' and 'hg graft --continue'")
2432 hint = _("use 'hg resolve' and 'hg graft --continue'")
2433 raise error.Abort(
2433 raise error.Abort(
2434 _("unresolved conflicts, can't continue"),
2434 _("unresolved conflicts, can't continue"),
2435 hint=hint)
2435 hint=hint)
2436 else:
2436 else:
2437 cont = False
2437 cont = False
2438
2438
2439 # commit if --no-commit is false
2439 # commit if --no-commit is false
2440 if not opts.get('no_commit'):
2440 if not opts.get('no_commit'):
2441 node = repo.commit(text=message, user=user, date=date, extra=extra,
2441 node = repo.commit(text=message, user=user, date=date, extra=extra,
2442 editor=editor)
2442 editor=editor)
2443 if node is None:
2443 if node is None:
2444 ui.warn(
2444 ui.warn(
2445 _('note: graft of %d:%s created no changes to commit\n') %
2445 _('note: graft of %d:%s created no changes to commit\n') %
2446 (ctx.rev(), ctx))
2446 (ctx.rev(), ctx))
2447 # checking that newnodes exist because old state files won't have it
2447 # checking that newnodes exist because old state files won't have it
2448 elif statedata.get('newnodes') is not None:
2448 elif statedata.get('newnodes') is not None:
2449 statedata['newnodes'].append(node)
2449 statedata['newnodes'].append(node)
2450
2450
2451 # remove state when we complete successfully
2451 # remove state when we complete successfully
2452 if not opts.get('dry_run'):
2452 if not opts.get('dry_run'):
2453 graftstate.delete()
2453 graftstate.delete()
2454
2454
2455 return 0
2455 return 0
2456
2456
2457 def _abortgraft(ui, repo, graftstate):
2457 def _abortgraft(ui, repo, graftstate):
2458 """abort the interrupted graft and rollbacks to the state before interrupted
2458 """abort the interrupted graft and rollbacks to the state before interrupted
2459 graft"""
2459 graft"""
2460 if not graftstate.exists():
2460 if not graftstate.exists():
2461 raise error.Abort(_("no interrupted graft to abort"))
2461 raise error.Abort(_("no interrupted graft to abort"))
2462 statedata = _readgraftstate(repo, graftstate)
2462 statedata = _readgraftstate(repo, graftstate)
2463 newnodes = statedata.get('newnodes')
2463 newnodes = statedata.get('newnodes')
2464 if newnodes is None:
2464 if newnodes is None:
2465 # and old graft state which does not have all the data required to abort
2465 # and old graft state which does not have all the data required to abort
2466 # the graft
2466 # the graft
2467 raise error.Abort(_("cannot abort using an old graftstate"))
2467 raise error.Abort(_("cannot abort using an old graftstate"))
2468
2468
2469 # changeset from which graft operation was started
2469 # changeset from which graft operation was started
2470 startctx = None
2470 startctx = None
2471 if len(newnodes) > 0:
2471 if len(newnodes) > 0:
2472 startctx = repo[newnodes[0]].p1()
2472 startctx = repo[newnodes[0]].p1()
2473 else:
2473 else:
2474 startctx = repo['.']
2474 startctx = repo['.']
2475 # whether to strip or not
2475 # whether to strip or not
2476 cleanup = False
2476 cleanup = False
2477 if newnodes:
2477 if newnodes:
2478 newnodes = [repo[r].rev() for r in newnodes]
2478 newnodes = [repo[r].rev() for r in newnodes]
2479 cleanup = True
2479 cleanup = True
2480 # checking that none of the newnodes turned public or is public
2480 # checking that none of the newnodes turned public or is public
2481 immutable = [c for c in newnodes if not repo[c].mutable()]
2481 immutable = [c for c in newnodes if not repo[c].mutable()]
2482 if immutable:
2482 if immutable:
2483 repo.ui.warn(_("cannot clean up public changesets %s\n")
2483 repo.ui.warn(_("cannot clean up public changesets %s\n")
2484 % ', '.join(bytes(repo[r]) for r in immutable),
2484 % ', '.join(bytes(repo[r]) for r in immutable),
2485 hint=_("see 'hg help phases' for details"))
2485 hint=_("see 'hg help phases' for details"))
2486 cleanup = False
2486 cleanup = False
2487
2487
2488 # checking that no new nodes are created on top of grafted revs
2488 # checking that no new nodes are created on top of grafted revs
2489 desc = set(repo.changelog.descendants(newnodes))
2489 desc = set(repo.changelog.descendants(newnodes))
2490 if desc - set(newnodes):
2490 if desc - set(newnodes):
2491 repo.ui.warn(_("new changesets detected on destination "
2491 repo.ui.warn(_("new changesets detected on destination "
2492 "branch, can't strip\n"))
2492 "branch, can't strip\n"))
2493 cleanup = False
2493 cleanup = False
2494
2494
2495 if cleanup:
2495 if cleanup:
2496 with repo.wlock(), repo.lock():
2496 with repo.wlock(), repo.lock():
2497 hg.updaterepo(repo, startctx.node(), overwrite=True)
2497 hg.updaterepo(repo, startctx.node(), overwrite=True)
2498 # stripping the new nodes created
2498 # stripping the new nodes created
2499 strippoints = [c.node() for c in repo.set("roots(%ld)",
2499 strippoints = [c.node() for c in repo.set("roots(%ld)",
2500 newnodes)]
2500 newnodes)]
2501 repair.strip(repo.ui, repo, strippoints, backup=False)
2501 repair.strip(repo.ui, repo, strippoints, backup=False)
2502
2502
2503 if not cleanup:
2503 if not cleanup:
2504 # we don't update to the startnode if we can't strip
2504 # we don't update to the startnode if we can't strip
2505 startctx = repo['.']
2505 startctx = repo['.']
2506 hg.updaterepo(repo, startctx.node(), overwrite=True)
2506 hg.updaterepo(repo, startctx.node(), overwrite=True)
2507
2507
2508 ui.status(_("graft aborted\n"))
2508 ui.status(_("graft aborted\n"))
2509 ui.status(_("working directory is now at %s\n") % startctx.hex()[:12])
2509 ui.status(_("working directory is now at %s\n") % startctx.hex()[:12])
2510 graftstate.delete()
2510 graftstate.delete()
2511 return 0
2511 return 0
2512
2512
2513 def _readgraftstate(repo, graftstate):
2513 def _readgraftstate(repo, graftstate):
2514 """read the graft state file and return a dict of the data stored in it"""
2514 """read the graft state file and return a dict of the data stored in it"""
2515 try:
2515 try:
2516 return graftstate.read()
2516 return graftstate.read()
2517 except error.CorruptedState:
2517 except error.CorruptedState:
2518 nodes = repo.vfs.read('graftstate').splitlines()
2518 nodes = repo.vfs.read('graftstate').splitlines()
2519 return {'nodes': nodes}
2519 return {'nodes': nodes}
2520
2520
2521 def _stopgraft(ui, repo, graftstate):
2521 def _stopgraft(ui, repo, graftstate):
2522 """stop the interrupted graft"""
2522 """stop the interrupted graft"""
2523 if not graftstate.exists():
2523 if not graftstate.exists():
2524 raise error.Abort(_("no interrupted graft found"))
2524 raise error.Abort(_("no interrupted graft found"))
2525 pctx = repo['.']
2525 pctx = repo['.']
2526 hg.updaterepo(repo, pctx.node(), overwrite=True)
2526 hg.updaterepo(repo, pctx.node(), overwrite=True)
2527 graftstate.delete()
2527 graftstate.delete()
2528 ui.status(_("stopped the interrupted graft\n"))
2528 ui.status(_("stopped the interrupted graft\n"))
2529 ui.status(_("working directory is now at %s\n") % pctx.hex()[:12])
2529 ui.status(_("working directory is now at %s\n") % pctx.hex()[:12])
2530 return 0
2530 return 0
2531
2531
2532 @command('grep',
2532 @command('grep',
2533 [('0', 'print0', None, _('end fields with NUL')),
2533 [('0', 'print0', None, _('end fields with NUL')),
2534 ('', 'all', None, _('print all revisions that match (DEPRECATED) ')),
2534 ('', 'all', None, _('print all revisions that match (DEPRECATED) ')),
2535 ('', 'diff', None, _('print all revisions when the term was introduced '
2535 ('', 'diff', None, _('print all revisions when the term was introduced '
2536 'or removed')),
2536 'or removed')),
2537 ('a', 'text', None, _('treat all files as text')),
2537 ('a', 'text', None, _('treat all files as text')),
2538 ('f', 'follow', None,
2538 ('f', 'follow', None,
2539 _('follow changeset history,'
2539 _('follow changeset history,'
2540 ' or file history across copies and renames')),
2540 ' or file history across copies and renames')),
2541 ('i', 'ignore-case', None, _('ignore case when matching')),
2541 ('i', 'ignore-case', None, _('ignore case when matching')),
2542 ('l', 'files-with-matches', None,
2542 ('l', 'files-with-matches', None,
2543 _('print only filenames and revisions that match')),
2543 _('print only filenames and revisions that match')),
2544 ('n', 'line-number', None, _('print matching line numbers')),
2544 ('n', 'line-number', None, _('print matching line numbers')),
2545 ('r', 'rev', [],
2545 ('r', 'rev', [],
2546 _('only search files changed within revision range'), _('REV')),
2546 _('only search files changed within revision range'), _('REV')),
2547 ('', 'all-files', None,
2547 ('', 'all-files', None,
2548 _('include all files in the changeset while grepping (EXPERIMENTAL)')),
2548 _('include all files in the changeset while grepping (EXPERIMENTAL)')),
2549 ('u', 'user', None, _('list the author (long with -v)')),
2549 ('u', 'user', None, _('list the author (long with -v)')),
2550 ('d', 'date', None, _('list the date (short with -q)')),
2550 ('d', 'date', None, _('list the date (short with -q)')),
2551 ] + formatteropts + walkopts,
2551 ] + formatteropts + walkopts,
2552 _('[OPTION]... PATTERN [FILE]...'),
2552 _('[OPTION]... PATTERN [FILE]...'),
2553 inferrepo=True,
2553 inferrepo=True,
2554 intents={INTENT_READONLY})
2554 intents={INTENT_READONLY})
2555 def grep(ui, repo, pattern, *pats, **opts):
2555 def grep(ui, repo, pattern, *pats, **opts):
2556 """search revision history for a pattern in specified files
2556 """search revision history for a pattern in specified files
2557
2557
2558 Search revision history for a regular expression in the specified
2558 Search revision history for a regular expression in the specified
2559 files or the entire project.
2559 files or the entire project.
2560
2560
2561 By default, grep prints the most recent revision number for each
2561 By default, grep prints the most recent revision number for each
2562 file in which it finds a match. To get it to print every revision
2562 file in which it finds a match. To get it to print every revision
2563 that contains a change in match status ("-" for a match that becomes
2563 that contains a change in match status ("-" for a match that becomes
2564 a non-match, or "+" for a non-match that becomes a match), use the
2564 a non-match, or "+" for a non-match that becomes a match), use the
2565 --diff flag.
2565 --diff flag.
2566
2566
2567 PATTERN can be any Python (roughly Perl-compatible) regular
2567 PATTERN can be any Python (roughly Perl-compatible) regular
2568 expression.
2568 expression.
2569
2569
2570 If no FILEs are specified (and -f/--follow isn't set), all files in
2570 If no FILEs are specified (and -f/--follow isn't set), all files in
2571 the repository are searched, including those that don't exist in the
2571 the repository are searched, including those that don't exist in the
2572 current branch or have been deleted in a prior changeset.
2572 current branch or have been deleted in a prior changeset.
2573
2573
2574 Returns 0 if a match is found, 1 otherwise.
2574 Returns 0 if a match is found, 1 otherwise.
2575 """
2575 """
2576 opts = pycompat.byteskwargs(opts)
2576 opts = pycompat.byteskwargs(opts)
2577 diff = opts.get('all') or opts.get('diff')
2577 diff = opts.get('all') or opts.get('diff')
2578 all_files = opts.get('all_files')
2578 all_files = opts.get('all_files')
2579 if diff and opts.get('all_files'):
2579 if diff and opts.get('all_files'):
2580 raise error.Abort(_('--diff and --all-files are mutually exclusive'))
2580 raise error.Abort(_('--diff and --all-files are mutually exclusive'))
2581 # TODO: remove "not opts.get('rev')" if --all-files -rMULTIREV gets working
2581 # TODO: remove "not opts.get('rev')" if --all-files -rMULTIREV gets working
2582 if opts.get('all_files') is None and not opts.get('rev') and not diff:
2582 if opts.get('all_files') is None and not opts.get('rev') and not diff:
2583 # experimental config: commands.grep.all-files
2583 # experimental config: commands.grep.all-files
2584 opts['all_files'] = ui.configbool('commands', 'grep.all-files')
2584 opts['all_files'] = ui.configbool('commands', 'grep.all-files')
2585 plaingrep = opts.get('all_files') and not opts.get('rev')
2585 plaingrep = opts.get('all_files') and not opts.get('rev')
2586 if plaingrep:
2586 if plaingrep:
2587 opts['rev'] = ['wdir()']
2587 opts['rev'] = ['wdir()']
2588
2588
2589 reflags = re.M
2589 reflags = re.M
2590 if opts.get('ignore_case'):
2590 if opts.get('ignore_case'):
2591 reflags |= re.I
2591 reflags |= re.I
2592 try:
2592 try:
2593 regexp = util.re.compile(pattern, reflags)
2593 regexp = util.re.compile(pattern, reflags)
2594 except re.error as inst:
2594 except re.error as inst:
2595 ui.warn(_("grep: invalid match pattern: %s\n") % pycompat.bytestr(inst))
2595 ui.warn(_("grep: invalid match pattern: %s\n") % pycompat.bytestr(inst))
2596 return 1
2596 return 1
2597 sep, eol = ':', '\n'
2597 sep, eol = ':', '\n'
2598 if opts.get('print0'):
2598 if opts.get('print0'):
2599 sep = eol = '\0'
2599 sep = eol = '\0'
2600
2600
2601 getfile = util.lrucachefunc(repo.file)
2601 getfile = util.lrucachefunc(repo.file)
2602
2602
2603 def matchlines(body):
2603 def matchlines(body):
2604 begin = 0
2604 begin = 0
2605 linenum = 0
2605 linenum = 0
2606 while begin < len(body):
2606 while begin < len(body):
2607 match = regexp.search(body, begin)
2607 match = regexp.search(body, begin)
2608 if not match:
2608 if not match:
2609 break
2609 break
2610 mstart, mend = match.span()
2610 mstart, mend = match.span()
2611 linenum += body.count('\n', begin, mstart) + 1
2611 linenum += body.count('\n', begin, mstart) + 1
2612 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2612 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2613 begin = body.find('\n', mend) + 1 or len(body) + 1
2613 begin = body.find('\n', mend) + 1 or len(body) + 1
2614 lend = begin - 1
2614 lend = begin - 1
2615 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2615 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2616
2616
2617 class linestate(object):
2617 class linestate(object):
2618 def __init__(self, line, linenum, colstart, colend):
2618 def __init__(self, line, linenum, colstart, colend):
2619 self.line = line
2619 self.line = line
2620 self.linenum = linenum
2620 self.linenum = linenum
2621 self.colstart = colstart
2621 self.colstart = colstart
2622 self.colend = colend
2622 self.colend = colend
2623
2623
2624 def __hash__(self):
2624 def __hash__(self):
2625 return hash((self.linenum, self.line))
2625 return hash((self.linenum, self.line))
2626
2626
2627 def __eq__(self, other):
2627 def __eq__(self, other):
2628 return self.line == other.line
2628 return self.line == other.line
2629
2629
2630 def findpos(self):
2630 def findpos(self):
2631 """Iterate all (start, end) indices of matches"""
2631 """Iterate all (start, end) indices of matches"""
2632 yield self.colstart, self.colend
2632 yield self.colstart, self.colend
2633 p = self.colend
2633 p = self.colend
2634 while p < len(self.line):
2634 while p < len(self.line):
2635 m = regexp.search(self.line, p)
2635 m = regexp.search(self.line, p)
2636 if not m:
2636 if not m:
2637 break
2637 break
2638 yield m.span()
2638 yield m.span()
2639 p = m.end()
2639 p = m.end()
2640
2640
2641 matches = {}
2641 matches = {}
2642 copies = {}
2642 copies = {}
2643 def grepbody(fn, rev, body):
2643 def grepbody(fn, rev, body):
2644 matches[rev].setdefault(fn, [])
2644 matches[rev].setdefault(fn, [])
2645 m = matches[rev][fn]
2645 m = matches[rev][fn]
2646 for lnum, cstart, cend, line in matchlines(body):
2646 for lnum, cstart, cend, line in matchlines(body):
2647 s = linestate(line, lnum, cstart, cend)
2647 s = linestate(line, lnum, cstart, cend)
2648 m.append(s)
2648 m.append(s)
2649
2649
2650 def difflinestates(a, b):
2650 def difflinestates(a, b):
2651 sm = difflib.SequenceMatcher(None, a, b)
2651 sm = difflib.SequenceMatcher(None, a, b)
2652 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2652 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2653 if tag == r'insert':
2653 if tag == r'insert':
2654 for i in pycompat.xrange(blo, bhi):
2654 for i in pycompat.xrange(blo, bhi):
2655 yield ('+', b[i])
2655 yield ('+', b[i])
2656 elif tag == r'delete':
2656 elif tag == r'delete':
2657 for i in pycompat.xrange(alo, ahi):
2657 for i in pycompat.xrange(alo, ahi):
2658 yield ('-', a[i])
2658 yield ('-', a[i])
2659 elif tag == r'replace':
2659 elif tag == r'replace':
2660 for i in pycompat.xrange(alo, ahi):
2660 for i in pycompat.xrange(alo, ahi):
2661 yield ('-', a[i])
2661 yield ('-', a[i])
2662 for i in pycompat.xrange(blo, bhi):
2662 for i in pycompat.xrange(blo, bhi):
2663 yield ('+', b[i])
2663 yield ('+', b[i])
2664
2664
2665 def display(fm, fn, ctx, pstates, states):
2665 def display(fm, fn, ctx, pstates, states):
2666 rev = scmutil.intrev(ctx)
2666 rev = scmutil.intrev(ctx)
2667 if fm.isplain():
2667 if fm.isplain():
2668 formatuser = ui.shortuser
2668 formatuser = ui.shortuser
2669 else:
2669 else:
2670 formatuser = pycompat.bytestr
2670 formatuser = pycompat.bytestr
2671 if ui.quiet:
2671 if ui.quiet:
2672 datefmt = '%Y-%m-%d'
2672 datefmt = '%Y-%m-%d'
2673 else:
2673 else:
2674 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2674 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2675 found = False
2675 found = False
2676 @util.cachefunc
2676 @util.cachefunc
2677 def binary():
2677 def binary():
2678 flog = getfile(fn)
2678 flog = getfile(fn)
2679 try:
2679 try:
2680 return stringutil.binary(flog.read(ctx.filenode(fn)))
2680 return stringutil.binary(flog.read(ctx.filenode(fn)))
2681 except error.WdirUnsupported:
2681 except error.WdirUnsupported:
2682 return ctx[fn].isbinary()
2682 return ctx[fn].isbinary()
2683
2683
2684 fieldnamemap = {'filename': 'path', 'linenumber': 'line_number'}
2684 fieldnamemap = {'filename': 'path', 'linenumber': 'line_number'}
2685 if diff:
2685 if diff:
2686 iter = difflinestates(pstates, states)
2686 iter = difflinestates(pstates, states)
2687 else:
2687 else:
2688 iter = [('', l) for l in states]
2688 iter = [('', l) for l in states]
2689 for change, l in iter:
2689 for change, l in iter:
2690 fm.startitem()
2690 fm.startitem()
2691 fm.context(ctx=ctx)
2691 fm.context(ctx=ctx)
2692 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)))
2692 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)))
2693
2693
2694 cols = [
2694 cols = [
2695 ('filename', '%s', fn, True),
2695 ('filename', '%s', fn, True),
2696 ('rev', '%d', rev, not plaingrep),
2696 ('rev', '%d', rev, not plaingrep),
2697 ('linenumber', '%d', l.linenum, opts.get('line_number')),
2697 ('linenumber', '%d', l.linenum, opts.get('line_number')),
2698 ]
2698 ]
2699 if diff:
2699 if diff:
2700 cols.append(('change', '%s', change, True))
2700 cols.append(('change', '%s', change, True))
2701 cols.extend([
2701 cols.extend([
2702 ('user', '%s', formatuser(ctx.user()), opts.get('user')),
2702 ('user', '%s', formatuser(ctx.user()), opts.get('user')),
2703 ('date', '%s', fm.formatdate(ctx.date(), datefmt),
2703 ('date', '%s', fm.formatdate(ctx.date(), datefmt),
2704 opts.get('date')),
2704 opts.get('date')),
2705 ])
2705 ])
2706 lastcol = next(
2706 lastcol = next(
2707 name for name, fmt, data, cond in reversed(cols) if cond)
2707 name for name, fmt, data, cond in reversed(cols) if cond)
2708 for name, fmt, data, cond in cols:
2708 for name, fmt, data, cond in cols:
2709 field = fieldnamemap.get(name, name)
2709 field = fieldnamemap.get(name, name)
2710 fm.condwrite(cond, field, fmt, data, label='grep.%s' % name)
2710 fm.condwrite(cond, field, fmt, data, label='grep.%s' % name)
2711 if cond and name != lastcol:
2711 if cond and name != lastcol:
2712 fm.plain(sep, label='grep.sep')
2712 fm.plain(sep, label='grep.sep')
2713 if not opts.get('files_with_matches'):
2713 if not opts.get('files_with_matches'):
2714 fm.plain(sep, label='grep.sep')
2714 fm.plain(sep, label='grep.sep')
2715 if not opts.get('text') and binary():
2715 if not opts.get('text') and binary():
2716 fm.plain(_(" Binary file matches"))
2716 fm.plain(_(" Binary file matches"))
2717 else:
2717 else:
2718 displaymatches(fm.nested('texts', tmpl='{text}'), l)
2718 displaymatches(fm.nested('texts', tmpl='{text}'), l)
2719 fm.plain(eol)
2719 fm.plain(eol)
2720 found = True
2720 found = True
2721 if opts.get('files_with_matches'):
2721 if opts.get('files_with_matches'):
2722 break
2722 break
2723 return found
2723 return found
2724
2724
2725 def displaymatches(fm, l):
2725 def displaymatches(fm, l):
2726 p = 0
2726 p = 0
2727 for s, e in l.findpos():
2727 for s, e in l.findpos():
2728 if p < s:
2728 if p < s:
2729 fm.startitem()
2729 fm.startitem()
2730 fm.write('text', '%s', l.line[p:s])
2730 fm.write('text', '%s', l.line[p:s])
2731 fm.data(matched=False)
2731 fm.data(matched=False)
2732 fm.startitem()
2732 fm.startitem()
2733 fm.write('text', '%s', l.line[s:e], label='grep.match')
2733 fm.write('text', '%s', l.line[s:e], label='grep.match')
2734 fm.data(matched=True)
2734 fm.data(matched=True)
2735 p = e
2735 p = e
2736 if p < len(l.line):
2736 if p < len(l.line):
2737 fm.startitem()
2737 fm.startitem()
2738 fm.write('text', '%s', l.line[p:])
2738 fm.write('text', '%s', l.line[p:])
2739 fm.data(matched=False)
2739 fm.data(matched=False)
2740 fm.end()
2740 fm.end()
2741
2741
2742 skip = {}
2742 skip = {}
2743 revfiles = {}
2743 revfiles = {}
2744 match = scmutil.match(repo[None], pats, opts)
2744 match = scmutil.match(repo[None], pats, opts)
2745 found = False
2745 found = False
2746 follow = opts.get('follow')
2746 follow = opts.get('follow')
2747
2747
2748 def prep(ctx, fns):
2748 def prep(ctx, fns):
2749 rev = ctx.rev()
2749 rev = ctx.rev()
2750 pctx = ctx.p1()
2750 pctx = ctx.p1()
2751 parent = pctx.rev()
2751 parent = pctx.rev()
2752 matches.setdefault(rev, {})
2752 matches.setdefault(rev, {})
2753 matches.setdefault(parent, {})
2753 matches.setdefault(parent, {})
2754 files = revfiles.setdefault(rev, [])
2754 files = revfiles.setdefault(rev, [])
2755 for fn in fns:
2755 for fn in fns:
2756 flog = getfile(fn)
2756 flog = getfile(fn)
2757 try:
2757 try:
2758 fnode = ctx.filenode(fn)
2758 fnode = ctx.filenode(fn)
2759 except error.LookupError:
2759 except error.LookupError:
2760 continue
2760 continue
2761 try:
2761 try:
2762 copied = flog.renamed(fnode)
2762 copied = flog.renamed(fnode)
2763 except error.WdirUnsupported:
2763 except error.WdirUnsupported:
2764 copied = ctx[fn].renamed()
2764 copied = ctx[fn].renamed()
2765 copy = follow and copied and copied[0]
2765 copy = follow and copied and copied[0]
2766 if copy:
2766 if copy:
2767 copies.setdefault(rev, {})[fn] = copy
2767 copies.setdefault(rev, {})[fn] = copy
2768 if fn in skip:
2768 if fn in skip:
2769 if copy:
2769 if copy:
2770 skip[copy] = True
2770 skip[copy] = True
2771 continue
2771 continue
2772 files.append(fn)
2772 files.append(fn)
2773
2773
2774 if fn not in matches[rev]:
2774 if fn not in matches[rev]:
2775 try:
2775 try:
2776 content = flog.read(fnode)
2776 content = flog.read(fnode)
2777 except error.WdirUnsupported:
2777 except error.WdirUnsupported:
2778 content = ctx[fn].data()
2778 content = ctx[fn].data()
2779 grepbody(fn, rev, content)
2779 grepbody(fn, rev, content)
2780
2780
2781 pfn = copy or fn
2781 pfn = copy or fn
2782 if pfn not in matches[parent]:
2782 if pfn not in matches[parent]:
2783 try:
2783 try:
2784 fnode = pctx.filenode(pfn)
2784 fnode = pctx.filenode(pfn)
2785 grepbody(pfn, parent, flog.read(fnode))
2785 grepbody(pfn, parent, flog.read(fnode))
2786 except error.LookupError:
2786 except error.LookupError:
2787 pass
2787 pass
2788
2788
2789 ui.pager('grep')
2789 ui.pager('grep')
2790 fm = ui.formatter('grep', opts)
2790 fm = ui.formatter('grep', opts)
2791 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2791 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2792 rev = ctx.rev()
2792 rev = ctx.rev()
2793 parent = ctx.p1().rev()
2793 parent = ctx.p1().rev()
2794 for fn in sorted(revfiles.get(rev, [])):
2794 for fn in sorted(revfiles.get(rev, [])):
2795 states = matches[rev][fn]
2795 states = matches[rev][fn]
2796 copy = copies.get(rev, {}).get(fn)
2796 copy = copies.get(rev, {}).get(fn)
2797 if fn in skip:
2797 if fn in skip:
2798 if copy:
2798 if copy:
2799 skip[copy] = True
2799 skip[copy] = True
2800 continue
2800 continue
2801 pstates = matches.get(parent, {}).get(copy or fn, [])
2801 pstates = matches.get(parent, {}).get(copy or fn, [])
2802 if pstates or states:
2802 if pstates or states:
2803 r = display(fm, fn, ctx, pstates, states)
2803 r = display(fm, fn, ctx, pstates, states)
2804 found = found or r
2804 found = found or r
2805 if r and not diff and not all_files:
2805 if r and not diff and not all_files:
2806 skip[fn] = True
2806 skip[fn] = True
2807 if copy:
2807 if copy:
2808 skip[copy] = True
2808 skip[copy] = True
2809 del revfiles[rev]
2809 del revfiles[rev]
2810 # We will keep the matches dict for the duration of the window
2810 # We will keep the matches dict for the duration of the window
2811 # clear the matches dict once the window is over
2811 # clear the matches dict once the window is over
2812 if not revfiles:
2812 if not revfiles:
2813 matches.clear()
2813 matches.clear()
2814 fm.end()
2814 fm.end()
2815
2815
2816 return not found
2816 return not found
2817
2817
2818 @command('heads',
2818 @command('heads',
2819 [('r', 'rev', '',
2819 [('r', 'rev', '',
2820 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2820 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2821 ('t', 'topo', False, _('show topological heads only')),
2821 ('t', 'topo', False, _('show topological heads only')),
2822 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2822 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2823 ('c', 'closed', False, _('show normal and closed branch heads')),
2823 ('c', 'closed', False, _('show normal and closed branch heads')),
2824 ] + templateopts,
2824 ] + templateopts,
2825 _('[-ct] [-r STARTREV] [REV]...'),
2825 _('[-ct] [-r STARTREV] [REV]...'),
2826 intents={INTENT_READONLY})
2826 intents={INTENT_READONLY})
2827 def heads(ui, repo, *branchrevs, **opts):
2827 def heads(ui, repo, *branchrevs, **opts):
2828 """show branch heads
2828 """show branch heads
2829
2829
2830 With no arguments, show all open branch heads in the repository.
2830 With no arguments, show all open branch heads in the repository.
2831 Branch heads are changesets that have no descendants on the
2831 Branch heads are changesets that have no descendants on the
2832 same branch. They are where development generally takes place and
2832 same branch. They are where development generally takes place and
2833 are the usual targets for update and merge operations.
2833 are the usual targets for update and merge operations.
2834
2834
2835 If one or more REVs are given, only open branch heads on the
2835 If one or more REVs are given, only open branch heads on the
2836 branches associated with the specified changesets are shown. This
2836 branches associated with the specified changesets are shown. This
2837 means that you can use :hg:`heads .` to see the heads on the
2837 means that you can use :hg:`heads .` to see the heads on the
2838 currently checked-out branch.
2838 currently checked-out branch.
2839
2839
2840 If -c/--closed is specified, also show branch heads marked closed
2840 If -c/--closed is specified, also show branch heads marked closed
2841 (see :hg:`commit --close-branch`).
2841 (see :hg:`commit --close-branch`).
2842
2842
2843 If STARTREV is specified, only those heads that are descendants of
2843 If STARTREV is specified, only those heads that are descendants of
2844 STARTREV will be displayed.
2844 STARTREV will be displayed.
2845
2845
2846 If -t/--topo is specified, named branch mechanics will be ignored and only
2846 If -t/--topo is specified, named branch mechanics will be ignored and only
2847 topological heads (changesets with no children) will be shown.
2847 topological heads (changesets with no children) will be shown.
2848
2848
2849 Returns 0 if matching heads are found, 1 if not.
2849 Returns 0 if matching heads are found, 1 if not.
2850 """
2850 """
2851
2851
2852 opts = pycompat.byteskwargs(opts)
2852 opts = pycompat.byteskwargs(opts)
2853 start = None
2853 start = None
2854 rev = opts.get('rev')
2854 rev = opts.get('rev')
2855 if rev:
2855 if rev:
2856 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2856 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2857 start = scmutil.revsingle(repo, rev, None).node()
2857 start = scmutil.revsingle(repo, rev, None).node()
2858
2858
2859 if opts.get('topo'):
2859 if opts.get('topo'):
2860 heads = [repo[h] for h in repo.heads(start)]
2860 heads = [repo[h] for h in repo.heads(start)]
2861 else:
2861 else:
2862 heads = []
2862 heads = []
2863 for branch in repo.branchmap():
2863 for branch in repo.branchmap():
2864 heads += repo.branchheads(branch, start, opts.get('closed'))
2864 heads += repo.branchheads(branch, start, opts.get('closed'))
2865 heads = [repo[h] for h in heads]
2865 heads = [repo[h] for h in heads]
2866
2866
2867 if branchrevs:
2867 if branchrevs:
2868 branches = set(repo[r].branch()
2868 branches = set(repo[r].branch()
2869 for r in scmutil.revrange(repo, branchrevs))
2869 for r in scmutil.revrange(repo, branchrevs))
2870 heads = [h for h in heads if h.branch() in branches]
2870 heads = [h for h in heads if h.branch() in branches]
2871
2871
2872 if opts.get('active') and branchrevs:
2872 if opts.get('active') and branchrevs:
2873 dagheads = repo.heads(start)
2873 dagheads = repo.heads(start)
2874 heads = [h for h in heads if h.node() in dagheads]
2874 heads = [h for h in heads if h.node() in dagheads]
2875
2875
2876 if branchrevs:
2876 if branchrevs:
2877 haveheads = set(h.branch() for h in heads)
2877 haveheads = set(h.branch() for h in heads)
2878 if branches - haveheads:
2878 if branches - haveheads:
2879 headless = ', '.join(b for b in branches - haveheads)
2879 headless = ', '.join(b for b in branches - haveheads)
2880 msg = _('no open branch heads found on branches %s')
2880 msg = _('no open branch heads found on branches %s')
2881 if opts.get('rev'):
2881 if opts.get('rev'):
2882 msg += _(' (started at %s)') % opts['rev']
2882 msg += _(' (started at %s)') % opts['rev']
2883 ui.warn((msg + '\n') % headless)
2883 ui.warn((msg + '\n') % headless)
2884
2884
2885 if not heads:
2885 if not heads:
2886 return 1
2886 return 1
2887
2887
2888 ui.pager('heads')
2888 ui.pager('heads')
2889 heads = sorted(heads, key=lambda x: -x.rev())
2889 heads = sorted(heads, key=lambda x: -x.rev())
2890 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
2890 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
2891 for ctx in heads:
2891 for ctx in heads:
2892 displayer.show(ctx)
2892 displayer.show(ctx)
2893 displayer.close()
2893 displayer.close()
2894
2894
2895 @command('help',
2895 @command('help',
2896 [('e', 'extension', None, _('show only help for extensions')),
2896 [('e', 'extension', None, _('show only help for extensions')),
2897 ('c', 'command', None, _('show only help for commands')),
2897 ('c', 'command', None, _('show only help for commands')),
2898 ('k', 'keyword', None, _('show topics matching keyword')),
2898 ('k', 'keyword', None, _('show topics matching keyword')),
2899 ('s', 'system', [], _('show help for specific platform(s)')),
2899 ('s', 'system', [], _('show help for specific platform(s)')),
2900 ],
2900 ],
2901 _('[-ecks] [TOPIC]'),
2901 _('[-ecks] [TOPIC]'),
2902 norepo=True,
2902 norepo=True,
2903 intents={INTENT_READONLY})
2903 intents={INTENT_READONLY})
2904 def help_(ui, name=None, **opts):
2904 def help_(ui, name=None, **opts):
2905 """show help for a given topic or a help overview
2905 """show help for a given topic or a help overview
2906
2906
2907 With no arguments, print a list of commands with short help messages.
2907 With no arguments, print a list of commands with short help messages.
2908
2908
2909 Given a topic, extension, or command name, print help for that
2909 Given a topic, extension, or command name, print help for that
2910 topic.
2910 topic.
2911
2911
2912 Returns 0 if successful.
2912 Returns 0 if successful.
2913 """
2913 """
2914
2914
2915 keep = opts.get(r'system') or []
2915 keep = opts.get(r'system') or []
2916 if len(keep) == 0:
2916 if len(keep) == 0:
2917 if pycompat.sysplatform.startswith('win'):
2917 if pycompat.sysplatform.startswith('win'):
2918 keep.append('windows')
2918 keep.append('windows')
2919 elif pycompat.sysplatform == 'OpenVMS':
2919 elif pycompat.sysplatform == 'OpenVMS':
2920 keep.append('vms')
2920 keep.append('vms')
2921 elif pycompat.sysplatform == 'plan9':
2921 elif pycompat.sysplatform == 'plan9':
2922 keep.append('plan9')
2922 keep.append('plan9')
2923 else:
2923 else:
2924 keep.append('unix')
2924 keep.append('unix')
2925 keep.append(pycompat.sysplatform.lower())
2925 keep.append(pycompat.sysplatform.lower())
2926 if ui.verbose:
2926 if ui.verbose:
2927 keep.append('verbose')
2927 keep.append('verbose')
2928
2928
2929 commands = sys.modules[__name__]
2929 commands = sys.modules[__name__]
2930 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
2930 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
2931 ui.pager('help')
2931 ui.pager('help')
2932 ui.write(formatted)
2932 ui.write(formatted)
2933
2933
2934
2934
2935 @command('identify|id',
2935 @command('identify|id',
2936 [('r', 'rev', '',
2936 [('r', 'rev', '',
2937 _('identify the specified revision'), _('REV')),
2937 _('identify the specified revision'), _('REV')),
2938 ('n', 'num', None, _('show local revision number')),
2938 ('n', 'num', None, _('show local revision number')),
2939 ('i', 'id', None, _('show global revision id')),
2939 ('i', 'id', None, _('show global revision id')),
2940 ('b', 'branch', None, _('show branch')),
2940 ('b', 'branch', None, _('show branch')),
2941 ('t', 'tags', None, _('show tags')),
2941 ('t', 'tags', None, _('show tags')),
2942 ('B', 'bookmarks', None, _('show bookmarks')),
2942 ('B', 'bookmarks', None, _('show bookmarks')),
2943 ] + remoteopts + formatteropts,
2943 ] + remoteopts + formatteropts,
2944 _('[-nibtB] [-r REV] [SOURCE]'),
2944 _('[-nibtB] [-r REV] [SOURCE]'),
2945 optionalrepo=True,
2945 optionalrepo=True,
2946 intents={INTENT_READONLY})
2946 intents={INTENT_READONLY})
2947 def identify(ui, repo, source=None, rev=None,
2947 def identify(ui, repo, source=None, rev=None,
2948 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
2948 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
2949 """identify the working directory or specified revision
2949 """identify the working directory or specified revision
2950
2950
2951 Print a summary identifying the repository state at REV using one or
2951 Print a summary identifying the repository state at REV using one or
2952 two parent hash identifiers, followed by a "+" if the working
2952 two parent hash identifiers, followed by a "+" if the working
2953 directory has uncommitted changes, the branch name (if not default),
2953 directory has uncommitted changes, the branch name (if not default),
2954 a list of tags, and a list of bookmarks.
2954 a list of tags, and a list of bookmarks.
2955
2955
2956 When REV is not given, print a summary of the current state of the
2956 When REV is not given, print a summary of the current state of the
2957 repository including the working directory. Specify -r. to get information
2957 repository including the working directory. Specify -r. to get information
2958 of the working directory parent without scanning uncommitted changes.
2958 of the working directory parent without scanning uncommitted changes.
2959
2959
2960 Specifying a path to a repository root or Mercurial bundle will
2960 Specifying a path to a repository root or Mercurial bundle will
2961 cause lookup to operate on that repository/bundle.
2961 cause lookup to operate on that repository/bundle.
2962
2962
2963 .. container:: verbose
2963 .. container:: verbose
2964
2964
2965 Examples:
2965 Examples:
2966
2966
2967 - generate a build identifier for the working directory::
2967 - generate a build identifier for the working directory::
2968
2968
2969 hg id --id > build-id.dat
2969 hg id --id > build-id.dat
2970
2970
2971 - find the revision corresponding to a tag::
2971 - find the revision corresponding to a tag::
2972
2972
2973 hg id -n -r 1.3
2973 hg id -n -r 1.3
2974
2974
2975 - check the most recent revision of a remote repository::
2975 - check the most recent revision of a remote repository::
2976
2976
2977 hg id -r tip https://www.mercurial-scm.org/repo/hg/
2977 hg id -r tip https://www.mercurial-scm.org/repo/hg/
2978
2978
2979 See :hg:`log` for generating more information about specific revisions,
2979 See :hg:`log` for generating more information about specific revisions,
2980 including full hash identifiers.
2980 including full hash identifiers.
2981
2981
2982 Returns 0 if successful.
2982 Returns 0 if successful.
2983 """
2983 """
2984
2984
2985 opts = pycompat.byteskwargs(opts)
2985 opts = pycompat.byteskwargs(opts)
2986 if not repo and not source:
2986 if not repo and not source:
2987 raise error.Abort(_("there is no Mercurial repository here "
2987 raise error.Abort(_("there is no Mercurial repository here "
2988 "(.hg not found)"))
2988 "(.hg not found)"))
2989
2989
2990 default = not (num or id or branch or tags or bookmarks)
2990 default = not (num or id or branch or tags or bookmarks)
2991 output = []
2991 output = []
2992 revs = []
2992 revs = []
2993
2993
2994 if source:
2994 if source:
2995 source, branches = hg.parseurl(ui.expandpath(source))
2995 source, branches = hg.parseurl(ui.expandpath(source))
2996 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
2996 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
2997 repo = peer.local()
2997 repo = peer.local()
2998 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
2998 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
2999
2999
3000 fm = ui.formatter('identify', opts)
3000 fm = ui.formatter('identify', opts)
3001 fm.startitem()
3001 fm.startitem()
3002
3002
3003 if not repo:
3003 if not repo:
3004 if num or branch or tags:
3004 if num or branch or tags:
3005 raise error.Abort(
3005 raise error.Abort(
3006 _("can't query remote revision number, branch, or tags"))
3006 _("can't query remote revision number, branch, or tags"))
3007 if not rev and revs:
3007 if not rev and revs:
3008 rev = revs[0]
3008 rev = revs[0]
3009 if not rev:
3009 if not rev:
3010 rev = "tip"
3010 rev = "tip"
3011
3011
3012 remoterev = peer.lookup(rev)
3012 remoterev = peer.lookup(rev)
3013 hexrev = fm.hexfunc(remoterev)
3013 hexrev = fm.hexfunc(remoterev)
3014 if default or id:
3014 if default or id:
3015 output = [hexrev]
3015 output = [hexrev]
3016 fm.data(id=hexrev)
3016 fm.data(id=hexrev)
3017
3017
3018 def getbms():
3018 def getbms():
3019 bms = []
3019 bms = []
3020
3020
3021 if 'bookmarks' in peer.listkeys('namespaces'):
3021 if 'bookmarks' in peer.listkeys('namespaces'):
3022 hexremoterev = hex(remoterev)
3022 hexremoterev = hex(remoterev)
3023 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3023 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3024 if bmr == hexremoterev]
3024 if bmr == hexremoterev]
3025
3025
3026 return sorted(bms)
3026 return sorted(bms)
3027
3027
3028 bms = getbms()
3028 bms = getbms()
3029 if bookmarks:
3029 if bookmarks:
3030 output.extend(bms)
3030 output.extend(bms)
3031 elif default and not ui.quiet:
3031 elif default and not ui.quiet:
3032 # multiple bookmarks for a single parent separated by '/'
3032 # multiple bookmarks for a single parent separated by '/'
3033 bm = '/'.join(bms)
3033 bm = '/'.join(bms)
3034 if bm:
3034 if bm:
3035 output.append(bm)
3035 output.append(bm)
3036
3036
3037 fm.data(node=hex(remoterev))
3037 fm.data(node=hex(remoterev))
3038 fm.data(bookmarks=fm.formatlist(bms, name='bookmark'))
3038 fm.data(bookmarks=fm.formatlist(bms, name='bookmark'))
3039 else:
3039 else:
3040 if rev:
3040 if rev:
3041 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3041 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3042 ctx = scmutil.revsingle(repo, rev, None)
3042 ctx = scmutil.revsingle(repo, rev, None)
3043
3043
3044 if ctx.rev() is None:
3044 if ctx.rev() is None:
3045 ctx = repo[None]
3045 ctx = repo[None]
3046 parents = ctx.parents()
3046 parents = ctx.parents()
3047 taglist = []
3047 taglist = []
3048 for p in parents:
3048 for p in parents:
3049 taglist.extend(p.tags())
3049 taglist.extend(p.tags())
3050
3050
3051 dirty = ""
3051 dirty = ""
3052 if ctx.dirty(missing=True, merge=False, branch=False):
3052 if ctx.dirty(missing=True, merge=False, branch=False):
3053 dirty = '+'
3053 dirty = '+'
3054 fm.data(dirty=dirty)
3054 fm.data(dirty=dirty)
3055
3055
3056 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3056 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3057 if default or id:
3057 if default or id:
3058 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
3058 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
3059 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
3059 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
3060
3060
3061 if num:
3061 if num:
3062 numoutput = ["%d" % p.rev() for p in parents]
3062 numoutput = ["%d" % p.rev() for p in parents]
3063 output.append("%s%s" % ('+'.join(numoutput), dirty))
3063 output.append("%s%s" % ('+'.join(numoutput), dirty))
3064
3064
3065 fm.data(parents=fm.formatlist([fm.hexfunc(p.node())
3065 fm.data(parents=fm.formatlist([fm.hexfunc(p.node())
3066 for p in parents], name='node'))
3066 for p in parents], name='node'))
3067 else:
3067 else:
3068 hexoutput = fm.hexfunc(ctx.node())
3068 hexoutput = fm.hexfunc(ctx.node())
3069 if default or id:
3069 if default or id:
3070 output = [hexoutput]
3070 output = [hexoutput]
3071 fm.data(id=hexoutput)
3071 fm.data(id=hexoutput)
3072
3072
3073 if num:
3073 if num:
3074 output.append(pycompat.bytestr(ctx.rev()))
3074 output.append(pycompat.bytestr(ctx.rev()))
3075 taglist = ctx.tags()
3075 taglist = ctx.tags()
3076
3076
3077 if default and not ui.quiet:
3077 if default and not ui.quiet:
3078 b = ctx.branch()
3078 b = ctx.branch()
3079 if b != 'default':
3079 if b != 'default':
3080 output.append("(%s)" % b)
3080 output.append("(%s)" % b)
3081
3081
3082 # multiple tags for a single parent separated by '/'
3082 # multiple tags for a single parent separated by '/'
3083 t = '/'.join(taglist)
3083 t = '/'.join(taglist)
3084 if t:
3084 if t:
3085 output.append(t)
3085 output.append(t)
3086
3086
3087 # multiple bookmarks for a single parent separated by '/'
3087 # multiple bookmarks for a single parent separated by '/'
3088 bm = '/'.join(ctx.bookmarks())
3088 bm = '/'.join(ctx.bookmarks())
3089 if bm:
3089 if bm:
3090 output.append(bm)
3090 output.append(bm)
3091 else:
3091 else:
3092 if branch:
3092 if branch:
3093 output.append(ctx.branch())
3093 output.append(ctx.branch())
3094
3094
3095 if tags:
3095 if tags:
3096 output.extend(taglist)
3096 output.extend(taglist)
3097
3097
3098 if bookmarks:
3098 if bookmarks:
3099 output.extend(ctx.bookmarks())
3099 output.extend(ctx.bookmarks())
3100
3100
3101 fm.data(node=ctx.hex())
3101 fm.data(node=ctx.hex())
3102 fm.data(branch=ctx.branch())
3102 fm.data(branch=ctx.branch())
3103 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
3103 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
3104 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
3104 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
3105 fm.context(ctx=ctx)
3105 fm.context(ctx=ctx)
3106
3106
3107 fm.plain("%s\n" % ' '.join(output))
3107 fm.plain("%s\n" % ' '.join(output))
3108 fm.end()
3108 fm.end()
3109
3109
3110 @command('import|patch',
3110 @command('import|patch',
3111 [('p', 'strip', 1,
3111 [('p', 'strip', 1,
3112 _('directory strip option for patch. This has the same '
3112 _('directory strip option for patch. This has the same '
3113 'meaning as the corresponding patch option'), _('NUM')),
3113 'meaning as the corresponding patch option'), _('NUM')),
3114 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3114 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3115 ('e', 'edit', False, _('invoke editor on commit messages')),
3115 ('e', 'edit', False, _('invoke editor on commit messages')),
3116 ('f', 'force', None,
3116 ('f', 'force', None,
3117 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
3117 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
3118 ('', 'no-commit', None,
3118 ('', 'no-commit', None,
3119 _("don't commit, just update the working directory")),
3119 _("don't commit, just update the working directory")),
3120 ('', 'bypass', None,
3120 ('', 'bypass', None,
3121 _("apply patch without touching the working directory")),
3121 _("apply patch without touching the working directory")),
3122 ('', 'partial', None,
3122 ('', 'partial', None,
3123 _('commit even if some hunks fail')),
3123 _('commit even if some hunks fail')),
3124 ('', 'exact', None,
3124 ('', 'exact', None,
3125 _('abort if patch would apply lossily')),
3125 _('abort if patch would apply lossily')),
3126 ('', 'prefix', '',
3126 ('', 'prefix', '',
3127 _('apply patch to subdirectory'), _('DIR')),
3127 _('apply patch to subdirectory'), _('DIR')),
3128 ('', 'import-branch', None,
3128 ('', 'import-branch', None,
3129 _('use any branch information in patch (implied by --exact)'))] +
3129 _('use any branch information in patch (implied by --exact)'))] +
3130 commitopts + commitopts2 + similarityopts,
3130 commitopts + commitopts2 + similarityopts,
3131 _('[OPTION]... PATCH...'))
3131 _('[OPTION]... PATCH...'))
3132 def import_(ui, repo, patch1=None, *patches, **opts):
3132 def import_(ui, repo, patch1=None, *patches, **opts):
3133 """import an ordered set of patches
3133 """import an ordered set of patches
3134
3134
3135 Import a list of patches and commit them individually (unless
3135 Import a list of patches and commit them individually (unless
3136 --no-commit is specified).
3136 --no-commit is specified).
3137
3137
3138 To read a patch from standard input (stdin), use "-" as the patch
3138 To read a patch from standard input (stdin), use "-" as the patch
3139 name. If a URL is specified, the patch will be downloaded from
3139 name. If a URL is specified, the patch will be downloaded from
3140 there.
3140 there.
3141
3141
3142 Import first applies changes to the working directory (unless
3142 Import first applies changes to the working directory (unless
3143 --bypass is specified), import will abort if there are outstanding
3143 --bypass is specified), import will abort if there are outstanding
3144 changes.
3144 changes.
3145
3145
3146 Use --bypass to apply and commit patches directly to the
3146 Use --bypass to apply and commit patches directly to the
3147 repository, without affecting the working directory. Without
3147 repository, without affecting the working directory. Without
3148 --exact, patches will be applied on top of the working directory
3148 --exact, patches will be applied on top of the working directory
3149 parent revision.
3149 parent revision.
3150
3150
3151 You can import a patch straight from a mail message. Even patches
3151 You can import a patch straight from a mail message. Even patches
3152 as attachments work (to use the body part, it must have type
3152 as attachments work (to use the body part, it must have type
3153 text/plain or text/x-patch). From and Subject headers of email
3153 text/plain or text/x-patch). From and Subject headers of email
3154 message are used as default committer and commit message. All
3154 message are used as default committer and commit message. All
3155 text/plain body parts before first diff are added to the commit
3155 text/plain body parts before first diff are added to the commit
3156 message.
3156 message.
3157
3157
3158 If the imported patch was generated by :hg:`export`, user and
3158 If the imported patch was generated by :hg:`export`, user and
3159 description from patch override values from message headers and
3159 description from patch override values from message headers and
3160 body. Values given on command line with -m/--message and -u/--user
3160 body. Values given on command line with -m/--message and -u/--user
3161 override these.
3161 override these.
3162
3162
3163 If --exact is specified, import will set the working directory to
3163 If --exact is specified, import will set the working directory to
3164 the parent of each patch before applying it, and will abort if the
3164 the parent of each patch before applying it, and will abort if the
3165 resulting changeset has a different ID than the one recorded in
3165 resulting changeset has a different ID than the one recorded in
3166 the patch. This will guard against various ways that portable
3166 the patch. This will guard against various ways that portable
3167 patch formats and mail systems might fail to transfer Mercurial
3167 patch formats and mail systems might fail to transfer Mercurial
3168 data or metadata. See :hg:`bundle` for lossless transmission.
3168 data or metadata. See :hg:`bundle` for lossless transmission.
3169
3169
3170 Use --partial to ensure a changeset will be created from the patch
3170 Use --partial to ensure a changeset will be created from the patch
3171 even if some hunks fail to apply. Hunks that fail to apply will be
3171 even if some hunks fail to apply. Hunks that fail to apply will be
3172 written to a <target-file>.rej file. Conflicts can then be resolved
3172 written to a <target-file>.rej file. Conflicts can then be resolved
3173 by hand before :hg:`commit --amend` is run to update the created
3173 by hand before :hg:`commit --amend` is run to update the created
3174 changeset. This flag exists to let people import patches that
3174 changeset. This flag exists to let people import patches that
3175 partially apply without losing the associated metadata (author,
3175 partially apply without losing the associated metadata (author,
3176 date, description, ...).
3176 date, description, ...).
3177
3177
3178 .. note::
3178 .. note::
3179
3179
3180 When no hunks apply cleanly, :hg:`import --partial` will create
3180 When no hunks apply cleanly, :hg:`import --partial` will create
3181 an empty changeset, importing only the patch metadata.
3181 an empty changeset, importing only the patch metadata.
3182
3182
3183 With -s/--similarity, hg will attempt to discover renames and
3183 With -s/--similarity, hg will attempt to discover renames and
3184 copies in the patch in the same way as :hg:`addremove`.
3184 copies in the patch in the same way as :hg:`addremove`.
3185
3185
3186 It is possible to use external patch programs to perform the patch
3186 It is possible to use external patch programs to perform the patch
3187 by setting the ``ui.patch`` configuration option. For the default
3187 by setting the ``ui.patch`` configuration option. For the default
3188 internal tool, the fuzz can also be configured via ``patch.fuzz``.
3188 internal tool, the fuzz can also be configured via ``patch.fuzz``.
3189 See :hg:`help config` for more information about configuration
3189 See :hg:`help config` for more information about configuration
3190 files and how to use these options.
3190 files and how to use these options.
3191
3191
3192 See :hg:`help dates` for a list of formats valid for -d/--date.
3192 See :hg:`help dates` for a list of formats valid for -d/--date.
3193
3193
3194 .. container:: verbose
3194 .. container:: verbose
3195
3195
3196 Examples:
3196 Examples:
3197
3197
3198 - import a traditional patch from a website and detect renames::
3198 - import a traditional patch from a website and detect renames::
3199
3199
3200 hg import -s 80 http://example.com/bugfix.patch
3200 hg import -s 80 http://example.com/bugfix.patch
3201
3201
3202 - import a changeset from an hgweb server::
3202 - import a changeset from an hgweb server::
3203
3203
3204 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3204 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3205
3205
3206 - import all the patches in an Unix-style mbox::
3206 - import all the patches in an Unix-style mbox::
3207
3207
3208 hg import incoming-patches.mbox
3208 hg import incoming-patches.mbox
3209
3209
3210 - import patches from stdin::
3210 - import patches from stdin::
3211
3211
3212 hg import -
3212 hg import -
3213
3213
3214 - attempt to exactly restore an exported changeset (not always
3214 - attempt to exactly restore an exported changeset (not always
3215 possible)::
3215 possible)::
3216
3216
3217 hg import --exact proposed-fix.patch
3217 hg import --exact proposed-fix.patch
3218
3218
3219 - use an external tool to apply a patch which is too fuzzy for
3219 - use an external tool to apply a patch which is too fuzzy for
3220 the default internal tool.
3220 the default internal tool.
3221
3221
3222 hg import --config ui.patch="patch --merge" fuzzy.patch
3222 hg import --config ui.patch="patch --merge" fuzzy.patch
3223
3223
3224 - change the default fuzzing from 2 to a less strict 7
3224 - change the default fuzzing from 2 to a less strict 7
3225
3225
3226 hg import --config ui.fuzz=7 fuzz.patch
3226 hg import --config ui.fuzz=7 fuzz.patch
3227
3227
3228 Returns 0 on success, 1 on partial success (see --partial).
3228 Returns 0 on success, 1 on partial success (see --partial).
3229 """
3229 """
3230
3230
3231 opts = pycompat.byteskwargs(opts)
3231 opts = pycompat.byteskwargs(opts)
3232 if not patch1:
3232 if not patch1:
3233 raise error.Abort(_('need at least one patch to import'))
3233 raise error.Abort(_('need at least one patch to import'))
3234
3234
3235 patches = (patch1,) + patches
3235 patches = (patch1,) + patches
3236
3236
3237 date = opts.get('date')
3237 date = opts.get('date')
3238 if date:
3238 if date:
3239 opts['date'] = dateutil.parsedate(date)
3239 opts['date'] = dateutil.parsedate(date)
3240
3240
3241 exact = opts.get('exact')
3241 exact = opts.get('exact')
3242 update = not opts.get('bypass')
3242 update = not opts.get('bypass')
3243 if not update and opts.get('no_commit'):
3243 if not update and opts.get('no_commit'):
3244 raise error.Abort(_('cannot use --no-commit with --bypass'))
3244 raise error.Abort(_('cannot use --no-commit with --bypass'))
3245 try:
3245 try:
3246 sim = float(opts.get('similarity') or 0)
3246 sim = float(opts.get('similarity') or 0)
3247 except ValueError:
3247 except ValueError:
3248 raise error.Abort(_('similarity must be a number'))
3248 raise error.Abort(_('similarity must be a number'))
3249 if sim < 0 or sim > 100:
3249 if sim < 0 or sim > 100:
3250 raise error.Abort(_('similarity must be between 0 and 100'))
3250 raise error.Abort(_('similarity must be between 0 and 100'))
3251 if sim and not update:
3251 if sim and not update:
3252 raise error.Abort(_('cannot use --similarity with --bypass'))
3252 raise error.Abort(_('cannot use --similarity with --bypass'))
3253 if exact:
3253 if exact:
3254 if opts.get('edit'):
3254 if opts.get('edit'):
3255 raise error.Abort(_('cannot use --exact with --edit'))
3255 raise error.Abort(_('cannot use --exact with --edit'))
3256 if opts.get('prefix'):
3256 if opts.get('prefix'):
3257 raise error.Abort(_('cannot use --exact with --prefix'))
3257 raise error.Abort(_('cannot use --exact with --prefix'))
3258
3258
3259 base = opts["base"]
3259 base = opts["base"]
3260 msgs = []
3260 msgs = []
3261 ret = 0
3261 ret = 0
3262
3262
3263 with repo.wlock():
3263 with repo.wlock():
3264 if update:
3264 if update:
3265 cmdutil.checkunfinished(repo)
3265 cmdutil.checkunfinished(repo)
3266 if (exact or not opts.get('force')):
3266 if (exact or not opts.get('force')):
3267 cmdutil.bailifchanged(repo)
3267 cmdutil.bailifchanged(repo)
3268
3268
3269 if not opts.get('no_commit'):
3269 if not opts.get('no_commit'):
3270 lock = repo.lock
3270 lock = repo.lock
3271 tr = lambda: repo.transaction('import')
3271 tr = lambda: repo.transaction('import')
3272 dsguard = util.nullcontextmanager
3272 dsguard = util.nullcontextmanager
3273 else:
3273 else:
3274 lock = util.nullcontextmanager
3274 lock = util.nullcontextmanager
3275 tr = util.nullcontextmanager
3275 tr = util.nullcontextmanager
3276 dsguard = lambda: dirstateguard.dirstateguard(repo, 'import')
3276 dsguard = lambda: dirstateguard.dirstateguard(repo, 'import')
3277 with lock(), tr(), dsguard():
3277 with lock(), tr(), dsguard():
3278 parents = repo[None].parents()
3278 parents = repo[None].parents()
3279 for patchurl in patches:
3279 for patchurl in patches:
3280 if patchurl == '-':
3280 if patchurl == '-':
3281 ui.status(_('applying patch from stdin\n'))
3281 ui.status(_('applying patch from stdin\n'))
3282 patchfile = ui.fin
3282 patchfile = ui.fin
3283 patchurl = 'stdin' # for error message
3283 patchurl = 'stdin' # for error message
3284 else:
3284 else:
3285 patchurl = os.path.join(base, patchurl)
3285 patchurl = os.path.join(base, patchurl)
3286 ui.status(_('applying %s\n') % patchurl)
3286 ui.status(_('applying %s\n') % patchurl)
3287 patchfile = hg.openpath(ui, patchurl)
3287 patchfile = hg.openpath(ui, patchurl)
3288
3288
3289 haspatch = False
3289 haspatch = False
3290 for hunk in patch.split(patchfile):
3290 for hunk in patch.split(patchfile):
3291 with patch.extract(ui, hunk) as patchdata:
3291 with patch.extract(ui, hunk) as patchdata:
3292 msg, node, rej = cmdutil.tryimportone(ui, repo,
3292 msg, node, rej = cmdutil.tryimportone(ui, repo,
3293 patchdata,
3293 patchdata,
3294 parents, opts,
3294 parents, opts,
3295 msgs, hg.clean)
3295 msgs, hg.clean)
3296 if msg:
3296 if msg:
3297 haspatch = True
3297 haspatch = True
3298 ui.note(msg + '\n')
3298 ui.note(msg + '\n')
3299 if update or exact:
3299 if update or exact:
3300 parents = repo[None].parents()
3300 parents = repo[None].parents()
3301 else:
3301 else:
3302 parents = [repo[node]]
3302 parents = [repo[node]]
3303 if rej:
3303 if rej:
3304 ui.write_err(_("patch applied partially\n"))
3304 ui.write_err(_("patch applied partially\n"))
3305 ui.write_err(_("(fix the .rej files and run "
3305 ui.write_err(_("(fix the .rej files and run "
3306 "`hg commit --amend`)\n"))
3306 "`hg commit --amend`)\n"))
3307 ret = 1
3307 ret = 1
3308 break
3308 break
3309
3309
3310 if not haspatch:
3310 if not haspatch:
3311 raise error.Abort(_('%s: no diffs found') % patchurl)
3311 raise error.Abort(_('%s: no diffs found') % patchurl)
3312
3312
3313 if msgs:
3313 if msgs:
3314 repo.savecommitmessage('\n* * *\n'.join(msgs))
3314 repo.savecommitmessage('\n* * *\n'.join(msgs))
3315 return ret
3315 return ret
3316
3316
3317 @command('incoming|in',
3317 @command('incoming|in',
3318 [('f', 'force', None,
3318 [('f', 'force', None,
3319 _('run even if remote repository is unrelated')),
3319 _('run even if remote repository is unrelated')),
3320 ('n', 'newest-first', None, _('show newest record first')),
3320 ('n', 'newest-first', None, _('show newest record first')),
3321 ('', 'bundle', '',
3321 ('', 'bundle', '',
3322 _('file to store the bundles into'), _('FILE')),
3322 _('file to store the bundles into'), _('FILE')),
3323 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3323 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3324 ('B', 'bookmarks', False, _("compare bookmarks")),
3324 ('B', 'bookmarks', False, _("compare bookmarks")),
3325 ('b', 'branch', [],
3325 ('b', 'branch', [],
3326 _('a specific branch you would like to pull'), _('BRANCH')),
3326 _('a specific branch you would like to pull'), _('BRANCH')),
3327 ] + logopts + remoteopts + subrepoopts,
3327 ] + logopts + remoteopts + subrepoopts,
3328 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3328 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3329 def incoming(ui, repo, source="default", **opts):
3329 def incoming(ui, repo, source="default", **opts):
3330 """show new changesets found in source
3330 """show new changesets found in source
3331
3331
3332 Show new changesets found in the specified path/URL or the default
3332 Show new changesets found in the specified path/URL or the default
3333 pull location. These are the changesets that would have been pulled
3333 pull location. These are the changesets that would have been pulled
3334 by :hg:`pull` at the time you issued this command.
3334 by :hg:`pull` at the time you issued this command.
3335
3335
3336 See pull for valid source format details.
3336 See pull for valid source format details.
3337
3337
3338 .. container:: verbose
3338 .. container:: verbose
3339
3339
3340 With -B/--bookmarks, the result of bookmark comparison between
3340 With -B/--bookmarks, the result of bookmark comparison between
3341 local and remote repositories is displayed. With -v/--verbose,
3341 local and remote repositories is displayed. With -v/--verbose,
3342 status is also displayed for each bookmark like below::
3342 status is also displayed for each bookmark like below::
3343
3343
3344 BM1 01234567890a added
3344 BM1 01234567890a added
3345 BM2 1234567890ab advanced
3345 BM2 1234567890ab advanced
3346 BM3 234567890abc diverged
3346 BM3 234567890abc diverged
3347 BM4 34567890abcd changed
3347 BM4 34567890abcd changed
3348
3348
3349 The action taken locally when pulling depends on the
3349 The action taken locally when pulling depends on the
3350 status of each bookmark:
3350 status of each bookmark:
3351
3351
3352 :``added``: pull will create it
3352 :``added``: pull will create it
3353 :``advanced``: pull will update it
3353 :``advanced``: pull will update it
3354 :``diverged``: pull will create a divergent bookmark
3354 :``diverged``: pull will create a divergent bookmark
3355 :``changed``: result depends on remote changesets
3355 :``changed``: result depends on remote changesets
3356
3356
3357 From the point of view of pulling behavior, bookmark
3357 From the point of view of pulling behavior, bookmark
3358 existing only in the remote repository are treated as ``added``,
3358 existing only in the remote repository are treated as ``added``,
3359 even if it is in fact locally deleted.
3359 even if it is in fact locally deleted.
3360
3360
3361 .. container:: verbose
3361 .. container:: verbose
3362
3362
3363 For remote repository, using --bundle avoids downloading the
3363 For remote repository, using --bundle avoids downloading the
3364 changesets twice if the incoming is followed by a pull.
3364 changesets twice if the incoming is followed by a pull.
3365
3365
3366 Examples:
3366 Examples:
3367
3367
3368 - show incoming changes with patches and full description::
3368 - show incoming changes with patches and full description::
3369
3369
3370 hg incoming -vp
3370 hg incoming -vp
3371
3371
3372 - show incoming changes excluding merges, store a bundle::
3372 - show incoming changes excluding merges, store a bundle::
3373
3373
3374 hg in -vpM --bundle incoming.hg
3374 hg in -vpM --bundle incoming.hg
3375 hg pull incoming.hg
3375 hg pull incoming.hg
3376
3376
3377 - briefly list changes inside a bundle::
3377 - briefly list changes inside a bundle::
3378
3378
3379 hg in changes.hg -T "{desc|firstline}\\n"
3379 hg in changes.hg -T "{desc|firstline}\\n"
3380
3380
3381 Returns 0 if there are incoming changes, 1 otherwise.
3381 Returns 0 if there are incoming changes, 1 otherwise.
3382 """
3382 """
3383 opts = pycompat.byteskwargs(opts)
3383 opts = pycompat.byteskwargs(opts)
3384 if opts.get('graph'):
3384 if opts.get('graph'):
3385 logcmdutil.checkunsupportedgraphflags([], opts)
3385 logcmdutil.checkunsupportedgraphflags([], opts)
3386 def display(other, chlist, displayer):
3386 def display(other, chlist, displayer):
3387 revdag = logcmdutil.graphrevs(other, chlist, opts)
3387 revdag = logcmdutil.graphrevs(other, chlist, opts)
3388 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3388 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3389 graphmod.asciiedges)
3389 graphmod.asciiedges)
3390
3390
3391 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3391 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3392 return 0
3392 return 0
3393
3393
3394 if opts.get('bundle') and opts.get('subrepos'):
3394 if opts.get('bundle') and opts.get('subrepos'):
3395 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3395 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3396
3396
3397 if opts.get('bookmarks'):
3397 if opts.get('bookmarks'):
3398 source, branches = hg.parseurl(ui.expandpath(source),
3398 source, branches = hg.parseurl(ui.expandpath(source),
3399 opts.get('branch'))
3399 opts.get('branch'))
3400 other = hg.peer(repo, opts, source)
3400 other = hg.peer(repo, opts, source)
3401 if 'bookmarks' not in other.listkeys('namespaces'):
3401 if 'bookmarks' not in other.listkeys('namespaces'):
3402 ui.warn(_("remote doesn't support bookmarks\n"))
3402 ui.warn(_("remote doesn't support bookmarks\n"))
3403 return 0
3403 return 0
3404 ui.pager('incoming')
3404 ui.pager('incoming')
3405 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3405 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3406 return bookmarks.incoming(ui, repo, other)
3406 return bookmarks.incoming(ui, repo, other)
3407
3407
3408 repo._subtoppath = ui.expandpath(source)
3408 repo._subtoppath = ui.expandpath(source)
3409 try:
3409 try:
3410 return hg.incoming(ui, repo, source, opts)
3410 return hg.incoming(ui, repo, source, opts)
3411 finally:
3411 finally:
3412 del repo._subtoppath
3412 del repo._subtoppath
3413
3413
3414
3414
3415 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3415 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3416 norepo=True)
3416 norepo=True)
3417 def init(ui, dest=".", **opts):
3417 def init(ui, dest=".", **opts):
3418 """create a new repository in the given directory
3418 """create a new repository in the given directory
3419
3419
3420 Initialize a new repository in the given directory. If the given
3420 Initialize a new repository in the given directory. If the given
3421 directory does not exist, it will be created.
3421 directory does not exist, it will be created.
3422
3422
3423 If no directory is given, the current directory is used.
3423 If no directory is given, the current directory is used.
3424
3424
3425 It is possible to specify an ``ssh://`` URL as the destination.
3425 It is possible to specify an ``ssh://`` URL as the destination.
3426 See :hg:`help urls` for more information.
3426 See :hg:`help urls` for more information.
3427
3427
3428 Returns 0 on success.
3428 Returns 0 on success.
3429 """
3429 """
3430 opts = pycompat.byteskwargs(opts)
3430 opts = pycompat.byteskwargs(opts)
3431 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3431 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3432
3432
3433 @command('locate',
3433 @command('locate',
3434 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3434 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3435 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3435 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3436 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3436 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3437 ] + walkopts,
3437 ] + walkopts,
3438 _('[OPTION]... [PATTERN]...'))
3438 _('[OPTION]... [PATTERN]...'))
3439 def locate(ui, repo, *pats, **opts):
3439 def locate(ui, repo, *pats, **opts):
3440 """locate files matching specific patterns (DEPRECATED)
3440 """locate files matching specific patterns (DEPRECATED)
3441
3441
3442 Print files under Mercurial control in the working directory whose
3442 Print files under Mercurial control in the working directory whose
3443 names match the given patterns.
3443 names match the given patterns.
3444
3444
3445 By default, this command searches all directories in the working
3445 By default, this command searches all directories in the working
3446 directory. To search just the current directory and its
3446 directory. To search just the current directory and its
3447 subdirectories, use "--include .".
3447 subdirectories, use "--include .".
3448
3448
3449 If no patterns are given to match, this command prints the names
3449 If no patterns are given to match, this command prints the names
3450 of all files under Mercurial control in the working directory.
3450 of all files under Mercurial control in the working directory.
3451
3451
3452 If you want to feed the output of this command into the "xargs"
3452 If you want to feed the output of this command into the "xargs"
3453 command, use the -0 option to both this command and "xargs". This
3453 command, use the -0 option to both this command and "xargs". This
3454 will avoid the problem of "xargs" treating single filenames that
3454 will avoid the problem of "xargs" treating single filenames that
3455 contain whitespace as multiple filenames.
3455 contain whitespace as multiple filenames.
3456
3456
3457 See :hg:`help files` for a more versatile command.
3457 See :hg:`help files` for a more versatile command.
3458
3458
3459 Returns 0 if a match is found, 1 otherwise.
3459 Returns 0 if a match is found, 1 otherwise.
3460 """
3460 """
3461 opts = pycompat.byteskwargs(opts)
3461 opts = pycompat.byteskwargs(opts)
3462 if opts.get('print0'):
3462 if opts.get('print0'):
3463 end = '\0'
3463 end = '\0'
3464 else:
3464 else:
3465 end = '\n'
3465 end = '\n'
3466 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3466 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3467
3467
3468 ret = 1
3468 ret = 1
3469 m = scmutil.match(ctx, pats, opts, default='relglob',
3469 m = scmutil.match(ctx, pats, opts, default='relglob',
3470 badfn=lambda x, y: False)
3470 badfn=lambda x, y: False)
3471
3471
3472 ui.pager('locate')
3472 ui.pager('locate')
3473 if ctx.rev() is None:
3473 if ctx.rev() is None:
3474 # When run on the working copy, "locate" includes removed files, so
3474 # When run on the working copy, "locate" includes removed files, so
3475 # we get the list of files from the dirstate.
3475 # we get the list of files from the dirstate.
3476 filesgen = sorted(repo.dirstate.matches(m))
3476 filesgen = sorted(repo.dirstate.matches(m))
3477 else:
3477 else:
3478 filesgen = ctx.matches(m)
3478 filesgen = ctx.matches(m)
3479 for abs in filesgen:
3479 for abs in filesgen:
3480 if opts.get('fullpath'):
3480 if opts.get('fullpath'):
3481 ui.write(repo.wjoin(abs), end)
3481 ui.write(repo.wjoin(abs), end)
3482 else:
3482 else:
3483 ui.write(((pats and m.rel(abs)) or abs), end)
3483 ui.write(((pats and m.rel(abs)) or abs), end)
3484 ret = 0
3484 ret = 0
3485
3485
3486 return ret
3486 return ret
3487
3487
3488 @command('^log|history',
3488 @command('^log|history',
3489 [('f', 'follow', None,
3489 [('f', 'follow', None,
3490 _('follow changeset history, or file history across copies and renames')),
3490 _('follow changeset history, or file history across copies and renames')),
3491 ('', 'follow-first', None,
3491 ('', 'follow-first', None,
3492 _('only follow the first parent of merge changesets (DEPRECATED)')),
3492 _('only follow the first parent of merge changesets (DEPRECATED)')),
3493 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3493 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3494 ('C', 'copies', None, _('show copied files')),
3494 ('C', 'copies', None, _('show copied files')),
3495 ('k', 'keyword', [],
3495 ('k', 'keyword', [],
3496 _('do case-insensitive search for a given text'), _('TEXT')),
3496 _('do case-insensitive search for a given text'), _('TEXT')),
3497 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3497 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3498 ('L', 'line-range', [],
3498 ('L', 'line-range', [],
3499 _('follow line range of specified file (EXPERIMENTAL)'),
3499 _('follow line range of specified file (EXPERIMENTAL)'),
3500 _('FILE,RANGE')),
3500 _('FILE,RANGE')),
3501 ('', 'removed', None, _('include revisions where files were removed')),
3501 ('', 'removed', None, _('include revisions where files were removed')),
3502 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3502 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3503 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3503 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3504 ('', 'only-branch', [],
3504 ('', 'only-branch', [],
3505 _('show only changesets within the given named branch (DEPRECATED)'),
3505 _('show only changesets within the given named branch (DEPRECATED)'),
3506 _('BRANCH')),
3506 _('BRANCH')),
3507 ('b', 'branch', [],
3507 ('b', 'branch', [],
3508 _('show changesets within the given named branch'), _('BRANCH')),
3508 _('show changesets within the given named branch'), _('BRANCH')),
3509 ('P', 'prune', [],
3509 ('P', 'prune', [],
3510 _('do not display revision or any of its ancestors'), _('REV')),
3510 _('do not display revision or any of its ancestors'), _('REV')),
3511 ] + logopts + walkopts,
3511 ] + logopts + walkopts,
3512 _('[OPTION]... [FILE]'),
3512 _('[OPTION]... [FILE]'),
3513 inferrepo=True,
3513 inferrepo=True,
3514 intents={INTENT_READONLY})
3514 intents={INTENT_READONLY})
3515 def log(ui, repo, *pats, **opts):
3515 def log(ui, repo, *pats, **opts):
3516 """show revision history of entire repository or files
3516 """show revision history of entire repository or files
3517
3517
3518 Print the revision history of the specified files or the entire
3518 Print the revision history of the specified files or the entire
3519 project.
3519 project.
3520
3520
3521 If no revision range is specified, the default is ``tip:0`` unless
3521 If no revision range is specified, the default is ``tip:0`` unless
3522 --follow is set, in which case the working directory parent is
3522 --follow is set, in which case the working directory parent is
3523 used as the starting revision.
3523 used as the starting revision.
3524
3524
3525 File history is shown without following rename or copy history of
3525 File history is shown without following rename or copy history of
3526 files. Use -f/--follow with a filename to follow history across
3526 files. Use -f/--follow with a filename to follow history across
3527 renames and copies. --follow without a filename will only show
3527 renames and copies. --follow without a filename will only show
3528 ancestors of the starting revision.
3528 ancestors of the starting revision.
3529
3529
3530 By default this command prints revision number and changeset id,
3530 By default this command prints revision number and changeset id,
3531 tags, non-trivial parents, user, date and time, and a summary for
3531 tags, non-trivial parents, user, date and time, and a summary for
3532 each commit. When the -v/--verbose switch is used, the list of
3532 each commit. When the -v/--verbose switch is used, the list of
3533 changed files and full commit message are shown.
3533 changed files and full commit message are shown.
3534
3534
3535 With --graph the revisions are shown as an ASCII art DAG with the most
3535 With --graph the revisions are shown as an ASCII art DAG with the most
3536 recent changeset at the top.
3536 recent changeset at the top.
3537 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
3537 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
3538 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
3538 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
3539 changeset from the lines below is a parent of the 'o' merge on the same
3539 changeset from the lines below is a parent of the 'o' merge on the same
3540 line.
3540 line.
3541 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3541 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3542 of a '|' indicates one or more revisions in a path are omitted.
3542 of a '|' indicates one or more revisions in a path are omitted.
3543
3543
3544 .. container:: verbose
3544 .. container:: verbose
3545
3545
3546 Use -L/--line-range FILE,M:N options to follow the history of lines
3546 Use -L/--line-range FILE,M:N options to follow the history of lines
3547 from M to N in FILE. With -p/--patch only diff hunks affecting
3547 from M to N in FILE. With -p/--patch only diff hunks affecting
3548 specified line range will be shown. This option requires --follow;
3548 specified line range will be shown. This option requires --follow;
3549 it can be specified multiple times. Currently, this option is not
3549 it can be specified multiple times. Currently, this option is not
3550 compatible with --graph. This option is experimental.
3550 compatible with --graph. This option is experimental.
3551
3551
3552 .. note::
3552 .. note::
3553
3553
3554 :hg:`log --patch` may generate unexpected diff output for merge
3554 :hg:`log --patch` may generate unexpected diff output for merge
3555 changesets, as it will only compare the merge changeset against
3555 changesets, as it will only compare the merge changeset against
3556 its first parent. Also, only files different from BOTH parents
3556 its first parent. Also, only files different from BOTH parents
3557 will appear in files:.
3557 will appear in files:.
3558
3558
3559 .. note::
3559 .. note::
3560
3560
3561 For performance reasons, :hg:`log FILE` may omit duplicate changes
3561 For performance reasons, :hg:`log FILE` may omit duplicate changes
3562 made on branches and will not show removals or mode changes. To
3562 made on branches and will not show removals or mode changes. To
3563 see all such changes, use the --removed switch.
3563 see all such changes, use the --removed switch.
3564
3564
3565 .. container:: verbose
3565 .. container:: verbose
3566
3566
3567 .. note::
3567 .. note::
3568
3568
3569 The history resulting from -L/--line-range options depends on diff
3569 The history resulting from -L/--line-range options depends on diff
3570 options; for instance if white-spaces are ignored, respective changes
3570 options; for instance if white-spaces are ignored, respective changes
3571 with only white-spaces in specified line range will not be listed.
3571 with only white-spaces in specified line range will not be listed.
3572
3572
3573 .. container:: verbose
3573 .. container:: verbose
3574
3574
3575 Some examples:
3575 Some examples:
3576
3576
3577 - changesets with full descriptions and file lists::
3577 - changesets with full descriptions and file lists::
3578
3578
3579 hg log -v
3579 hg log -v
3580
3580
3581 - changesets ancestral to the working directory::
3581 - changesets ancestral to the working directory::
3582
3582
3583 hg log -f
3583 hg log -f
3584
3584
3585 - last 10 commits on the current branch::
3585 - last 10 commits on the current branch::
3586
3586
3587 hg log -l 10 -b .
3587 hg log -l 10 -b .
3588
3588
3589 - changesets showing all modifications of a file, including removals::
3589 - changesets showing all modifications of a file, including removals::
3590
3590
3591 hg log --removed file.c
3591 hg log --removed file.c
3592
3592
3593 - all changesets that touch a directory, with diffs, excluding merges::
3593 - all changesets that touch a directory, with diffs, excluding merges::
3594
3594
3595 hg log -Mp lib/
3595 hg log -Mp lib/
3596
3596
3597 - all revision numbers that match a keyword::
3597 - all revision numbers that match a keyword::
3598
3598
3599 hg log -k bug --template "{rev}\\n"
3599 hg log -k bug --template "{rev}\\n"
3600
3600
3601 - the full hash identifier of the working directory parent::
3601 - the full hash identifier of the working directory parent::
3602
3602
3603 hg log -r . --template "{node}\\n"
3603 hg log -r . --template "{node}\\n"
3604
3604
3605 - list available log templates::
3605 - list available log templates::
3606
3606
3607 hg log -T list
3607 hg log -T list
3608
3608
3609 - check if a given changeset is included in a tagged release::
3609 - check if a given changeset is included in a tagged release::
3610
3610
3611 hg log -r "a21ccf and ancestor(1.9)"
3611 hg log -r "a21ccf and ancestor(1.9)"
3612
3612
3613 - find all changesets by some user in a date range::
3613 - find all changesets by some user in a date range::
3614
3614
3615 hg log -k alice -d "may 2008 to jul 2008"
3615 hg log -k alice -d "may 2008 to jul 2008"
3616
3616
3617 - summary of all changesets after the last tag::
3617 - summary of all changesets after the last tag::
3618
3618
3619 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3619 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3620
3620
3621 - changesets touching lines 13 to 23 for file.c::
3621 - changesets touching lines 13 to 23 for file.c::
3622
3622
3623 hg log -L file.c,13:23
3623 hg log -L file.c,13:23
3624
3624
3625 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
3625 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
3626 main.c with patch::
3626 main.c with patch::
3627
3627
3628 hg log -L file.c,13:23 -L main.c,2:6 -p
3628 hg log -L file.c,13:23 -L main.c,2:6 -p
3629
3629
3630 See :hg:`help dates` for a list of formats valid for -d/--date.
3630 See :hg:`help dates` for a list of formats valid for -d/--date.
3631
3631
3632 See :hg:`help revisions` for more about specifying and ordering
3632 See :hg:`help revisions` for more about specifying and ordering
3633 revisions.
3633 revisions.
3634
3634
3635 See :hg:`help templates` for more about pre-packaged styles and
3635 See :hg:`help templates` for more about pre-packaged styles and
3636 specifying custom templates. The default template used by the log
3636 specifying custom templates. The default template used by the log
3637 command can be customized via the ``ui.logtemplate`` configuration
3637 command can be customized via the ``ui.logtemplate`` configuration
3638 setting.
3638 setting.
3639
3639
3640 Returns 0 on success.
3640 Returns 0 on success.
3641
3641
3642 """
3642 """
3643 opts = pycompat.byteskwargs(opts)
3643 opts = pycompat.byteskwargs(opts)
3644 linerange = opts.get('line_range')
3644 linerange = opts.get('line_range')
3645
3645
3646 if linerange and not opts.get('follow'):
3646 if linerange and not opts.get('follow'):
3647 raise error.Abort(_('--line-range requires --follow'))
3647 raise error.Abort(_('--line-range requires --follow'))
3648
3648
3649 if linerange and pats:
3649 if linerange and pats:
3650 # TODO: take pats as patterns with no line-range filter
3650 # TODO: take pats as patterns with no line-range filter
3651 raise error.Abort(
3651 raise error.Abort(
3652 _('FILE arguments are not compatible with --line-range option')
3652 _('FILE arguments are not compatible with --line-range option')
3653 )
3653 )
3654
3654
3655 repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
3655 repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
3656 revs, differ = logcmdutil.getrevs(repo, pats, opts)
3656 revs, differ = logcmdutil.getrevs(repo, pats, opts)
3657 if linerange:
3657 if linerange:
3658 # TODO: should follow file history from logcmdutil._initialrevs(),
3658 # TODO: should follow file history from logcmdutil._initialrevs(),
3659 # then filter the result by logcmdutil._makerevset() and --limit
3659 # then filter the result by logcmdutil._makerevset() and --limit
3660 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
3660 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
3661
3661
3662 getrenamed = None
3662 getrenamed = None
3663 if opts.get('copies'):
3663 if opts.get('copies'):
3664 endrev = None
3664 endrev = None
3665 if revs:
3665 if revs:
3666 endrev = revs.max() + 1
3666 endrev = revs.max() + 1
3667 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3667 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3668
3668
3669 ui.pager('log')
3669 ui.pager('log')
3670 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, differ,
3670 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, differ,
3671 buffered=True)
3671 buffered=True)
3672 if opts.get('graph'):
3672 if opts.get('graph'):
3673 displayfn = logcmdutil.displaygraphrevs
3673 displayfn = logcmdutil.displaygraphrevs
3674 else:
3674 else:
3675 displayfn = logcmdutil.displayrevs
3675 displayfn = logcmdutil.displayrevs
3676 displayfn(ui, repo, revs, displayer, getrenamed)
3676 displayfn(ui, repo, revs, displayer, getrenamed)
3677
3677
3678 @command('manifest',
3678 @command('manifest',
3679 [('r', 'rev', '', _('revision to display'), _('REV')),
3679 [('r', 'rev', '', _('revision to display'), _('REV')),
3680 ('', 'all', False, _("list files from all revisions"))]
3680 ('', 'all', False, _("list files from all revisions"))]
3681 + formatteropts,
3681 + formatteropts,
3682 _('[-r REV]'),
3682 _('[-r REV]'),
3683 intents={INTENT_READONLY})
3683 intents={INTENT_READONLY})
3684 def manifest(ui, repo, node=None, rev=None, **opts):
3684 def manifest(ui, repo, node=None, rev=None, **opts):
3685 """output the current or given revision of the project manifest
3685 """output the current or given revision of the project manifest
3686
3686
3687 Print a list of version controlled files for the given revision.
3687 Print a list of version controlled files for the given revision.
3688 If no revision is given, the first parent of the working directory
3688 If no revision is given, the first parent of the working directory
3689 is used, or the null revision if no revision is checked out.
3689 is used, or the null revision if no revision is checked out.
3690
3690
3691 With -v, print file permissions, symlink and executable bits.
3691 With -v, print file permissions, symlink and executable bits.
3692 With --debug, print file revision hashes.
3692 With --debug, print file revision hashes.
3693
3693
3694 If option --all is specified, the list of all files from all revisions
3694 If option --all is specified, the list of all files from all revisions
3695 is printed. This includes deleted and renamed files.
3695 is printed. This includes deleted and renamed files.
3696
3696
3697 Returns 0 on success.
3697 Returns 0 on success.
3698 """
3698 """
3699 opts = pycompat.byteskwargs(opts)
3699 opts = pycompat.byteskwargs(opts)
3700 fm = ui.formatter('manifest', opts)
3700 fm = ui.formatter('manifest', opts)
3701
3701
3702 if opts.get('all'):
3702 if opts.get('all'):
3703 if rev or node:
3703 if rev or node:
3704 raise error.Abort(_("can't specify a revision with --all"))
3704 raise error.Abort(_("can't specify a revision with --all"))
3705
3705
3706 res = set()
3706 res = set()
3707 for rev in repo:
3707 for rev in repo:
3708 ctx = repo[rev]
3708 ctx = repo[rev]
3709 res |= set(ctx.files())
3709 res |= set(ctx.files())
3710
3710
3711 ui.pager('manifest')
3711 ui.pager('manifest')
3712 for f in sorted(res):
3712 for f in sorted(res):
3713 fm.startitem()
3713 fm.startitem()
3714 fm.write("path", '%s\n', f)
3714 fm.write("path", '%s\n', f)
3715 fm.end()
3715 fm.end()
3716 return
3716 return
3717
3717
3718 if rev and node:
3718 if rev and node:
3719 raise error.Abort(_("please specify just one revision"))
3719 raise error.Abort(_("please specify just one revision"))
3720
3720
3721 if not node:
3721 if not node:
3722 node = rev
3722 node = rev
3723
3723
3724 char = {'l': '@', 'x': '*', '': '', 't': 'd'}
3724 char = {'l': '@', 'x': '*', '': '', 't': 'd'}
3725 mode = {'l': '644', 'x': '755', '': '644', 't': '755'}
3725 mode = {'l': '644', 'x': '755', '': '644', 't': '755'}
3726 if node:
3726 if node:
3727 repo = scmutil.unhidehashlikerevs(repo, [node], 'nowarn')
3727 repo = scmutil.unhidehashlikerevs(repo, [node], 'nowarn')
3728 ctx = scmutil.revsingle(repo, node)
3728 ctx = scmutil.revsingle(repo, node)
3729 mf = ctx.manifest()
3729 mf = ctx.manifest()
3730 ui.pager('manifest')
3730 ui.pager('manifest')
3731 for f in ctx:
3731 for f in ctx:
3732 fm.startitem()
3732 fm.startitem()
3733 fm.context(ctx=ctx)
3733 fm.context(ctx=ctx)
3734 fl = ctx[f].flags()
3734 fl = ctx[f].flags()
3735 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3735 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3736 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3736 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3737 fm.write('path', '%s\n', f)
3737 fm.write('path', '%s\n', f)
3738 fm.end()
3738 fm.end()
3739
3739
3740 @command('^merge',
3740 @command('^merge',
3741 [('f', 'force', None,
3741 [('f', 'force', None,
3742 _('force a merge including outstanding changes (DEPRECATED)')),
3742 _('force a merge including outstanding changes (DEPRECATED)')),
3743 ('r', 'rev', '', _('revision to merge'), _('REV')),
3743 ('r', 'rev', '', _('revision to merge'), _('REV')),
3744 ('P', 'preview', None,
3744 ('P', 'preview', None,
3745 _('review revisions to merge (no merge is performed)')),
3745 _('review revisions to merge (no merge is performed)')),
3746 ('', 'abort', None, _('abort the ongoing merge')),
3746 ('', 'abort', None, _('abort the ongoing merge')),
3747 ] + mergetoolopts,
3747 ] + mergetoolopts,
3748 _('[-P] [[-r] REV]'))
3748 _('[-P] [[-r] REV]'))
3749 def merge(ui, repo, node=None, **opts):
3749 def merge(ui, repo, node=None, **opts):
3750 """merge another revision into working directory
3750 """merge another revision into working directory
3751
3751
3752 The current working directory is updated with all changes made in
3752 The current working directory is updated with all changes made in
3753 the requested revision since the last common predecessor revision.
3753 the requested revision since the last common predecessor revision.
3754
3754
3755 Files that changed between either parent are marked as changed for
3755 Files that changed between either parent are marked as changed for
3756 the next commit and a commit must be performed before any further
3756 the next commit and a commit must be performed before any further
3757 updates to the repository are allowed. The next commit will have
3757 updates to the repository are allowed. The next commit will have
3758 two parents.
3758 two parents.
3759
3759
3760 ``--tool`` can be used to specify the merge tool used for file
3760 ``--tool`` can be used to specify the merge tool used for file
3761 merges. It overrides the HGMERGE environment variable and your
3761 merges. It overrides the HGMERGE environment variable and your
3762 configuration files. See :hg:`help merge-tools` for options.
3762 configuration files. See :hg:`help merge-tools` for options.
3763
3763
3764 If no revision is specified, the working directory's parent is a
3764 If no revision is specified, the working directory's parent is a
3765 head revision, and the current branch contains exactly one other
3765 head revision, and the current branch contains exactly one other
3766 head, the other head is merged with by default. Otherwise, an
3766 head, the other head is merged with by default. Otherwise, an
3767 explicit revision with which to merge with must be provided.
3767 explicit revision with which to merge with must be provided.
3768
3768
3769 See :hg:`help resolve` for information on handling file conflicts.
3769 See :hg:`help resolve` for information on handling file conflicts.
3770
3770
3771 To undo an uncommitted merge, use :hg:`merge --abort` which
3771 To undo an uncommitted merge, use :hg:`merge --abort` which
3772 will check out a clean copy of the original merge parent, losing
3772 will check out a clean copy of the original merge parent, losing
3773 all changes.
3773 all changes.
3774
3774
3775 Returns 0 on success, 1 if there are unresolved files.
3775 Returns 0 on success, 1 if there are unresolved files.
3776 """
3776 """
3777
3777
3778 opts = pycompat.byteskwargs(opts)
3778 opts = pycompat.byteskwargs(opts)
3779 abort = opts.get('abort')
3779 abort = opts.get('abort')
3780 if abort and repo.dirstate.p2() == nullid:
3780 if abort and repo.dirstate.p2() == nullid:
3781 cmdutil.wrongtooltocontinue(repo, _('merge'))
3781 cmdutil.wrongtooltocontinue(repo, _('merge'))
3782 if abort:
3782 if abort:
3783 if node:
3783 if node:
3784 raise error.Abort(_("cannot specify a node with --abort"))
3784 raise error.Abort(_("cannot specify a node with --abort"))
3785 if opts.get('rev'):
3785 if opts.get('rev'):
3786 raise error.Abort(_("cannot specify both --rev and --abort"))
3786 raise error.Abort(_("cannot specify both --rev and --abort"))
3787 if opts.get('preview'):
3787 if opts.get('preview'):
3788 raise error.Abort(_("cannot specify --preview with --abort"))
3788 raise error.Abort(_("cannot specify --preview with --abort"))
3789 if opts.get('rev') and node:
3789 if opts.get('rev') and node:
3790 raise error.Abort(_("please specify just one revision"))
3790 raise error.Abort(_("please specify just one revision"))
3791 if not node:
3791 if not node:
3792 node = opts.get('rev')
3792 node = opts.get('rev')
3793
3793
3794 if node:
3794 if node:
3795 node = scmutil.revsingle(repo, node).node()
3795 node = scmutil.revsingle(repo, node).node()
3796
3796
3797 if not node and not abort:
3797 if not node and not abort:
3798 node = repo[destutil.destmerge(repo)].node()
3798 node = repo[destutil.destmerge(repo)].node()
3799
3799
3800 if opts.get('preview'):
3800 if opts.get('preview'):
3801 # find nodes that are ancestors of p2 but not of p1
3801 # find nodes that are ancestors of p2 but not of p1
3802 p1 = repo.lookup('.')
3802 p1 = repo.lookup('.')
3803 p2 = node
3803 p2 = node
3804 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3804 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3805
3805
3806 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3806 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3807 for node in nodes:
3807 for node in nodes:
3808 displayer.show(repo[node])
3808 displayer.show(repo[node])
3809 displayer.close()
3809 displayer.close()
3810 return 0
3810 return 0
3811
3811
3812 # ui.forcemerge is an internal variable, do not document
3812 # ui.forcemerge is an internal variable, do not document
3813 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
3813 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
3814 with ui.configoverride(overrides, 'merge'):
3814 with ui.configoverride(overrides, 'merge'):
3815 force = opts.get('force')
3815 force = opts.get('force')
3816 labels = ['working copy', 'merge rev']
3816 labels = ['working copy', 'merge rev']
3817 return hg.merge(repo, node, force=force, mergeforce=force,
3817 return hg.merge(repo, node, force=force, mergeforce=force,
3818 labels=labels, abort=abort)
3818 labels=labels, abort=abort)
3819
3819
3820 @command('outgoing|out',
3820 @command('outgoing|out',
3821 [('f', 'force', None, _('run even when the destination is unrelated')),
3821 [('f', 'force', None, _('run even when the destination is unrelated')),
3822 ('r', 'rev', [],
3822 ('r', 'rev', [],
3823 _('a changeset intended to be included in the destination'), _('REV')),
3823 _('a changeset intended to be included in the destination'), _('REV')),
3824 ('n', 'newest-first', None, _('show newest record first')),
3824 ('n', 'newest-first', None, _('show newest record first')),
3825 ('B', 'bookmarks', False, _('compare bookmarks')),
3825 ('B', 'bookmarks', False, _('compare bookmarks')),
3826 ('b', 'branch', [], _('a specific branch you would like to push'),
3826 ('b', 'branch', [], _('a specific branch you would like to push'),
3827 _('BRANCH')),
3827 _('BRANCH')),
3828 ] + logopts + remoteopts + subrepoopts,
3828 ] + logopts + remoteopts + subrepoopts,
3829 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3829 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3830 def outgoing(ui, repo, dest=None, **opts):
3830 def outgoing(ui, repo, dest=None, **opts):
3831 """show changesets not found in the destination
3831 """show changesets not found in the destination
3832
3832
3833 Show changesets not found in the specified destination repository
3833 Show changesets not found in the specified destination repository
3834 or the default push location. These are the changesets that would
3834 or the default push location. These are the changesets that would
3835 be pushed if a push was requested.
3835 be pushed if a push was requested.
3836
3836
3837 See pull for details of valid destination formats.
3837 See pull for details of valid destination formats.
3838
3838
3839 .. container:: verbose
3839 .. container:: verbose
3840
3840
3841 With -B/--bookmarks, the result of bookmark comparison between
3841 With -B/--bookmarks, the result of bookmark comparison between
3842 local and remote repositories is displayed. With -v/--verbose,
3842 local and remote repositories is displayed. With -v/--verbose,
3843 status is also displayed for each bookmark like below::
3843 status is also displayed for each bookmark like below::
3844
3844
3845 BM1 01234567890a added
3845 BM1 01234567890a added
3846 BM2 deleted
3846 BM2 deleted
3847 BM3 234567890abc advanced
3847 BM3 234567890abc advanced
3848 BM4 34567890abcd diverged
3848 BM4 34567890abcd diverged
3849 BM5 4567890abcde changed
3849 BM5 4567890abcde changed
3850
3850
3851 The action taken when pushing depends on the
3851 The action taken when pushing depends on the
3852 status of each bookmark:
3852 status of each bookmark:
3853
3853
3854 :``added``: push with ``-B`` will create it
3854 :``added``: push with ``-B`` will create it
3855 :``deleted``: push with ``-B`` will delete it
3855 :``deleted``: push with ``-B`` will delete it
3856 :``advanced``: push will update it
3856 :``advanced``: push will update it
3857 :``diverged``: push with ``-B`` will update it
3857 :``diverged``: push with ``-B`` will update it
3858 :``changed``: push with ``-B`` will update it
3858 :``changed``: push with ``-B`` will update it
3859
3859
3860 From the point of view of pushing behavior, bookmarks
3860 From the point of view of pushing behavior, bookmarks
3861 existing only in the remote repository are treated as
3861 existing only in the remote repository are treated as
3862 ``deleted``, even if it is in fact added remotely.
3862 ``deleted``, even if it is in fact added remotely.
3863
3863
3864 Returns 0 if there are outgoing changes, 1 otherwise.
3864 Returns 0 if there are outgoing changes, 1 otherwise.
3865 """
3865 """
3866 # hg._outgoing() needs to re-resolve the path in order to handle #branch
3866 # hg._outgoing() needs to re-resolve the path in order to handle #branch
3867 # style URLs, so don't overwrite dest.
3867 # style URLs, so don't overwrite dest.
3868 path = ui.paths.getpath(dest, default=('default-push', 'default'))
3868 path = ui.paths.getpath(dest, default=('default-push', 'default'))
3869 if not path:
3869 if not path:
3870 raise error.Abort(_('default repository not configured!'),
3870 raise error.Abort(_('default repository not configured!'),
3871 hint=_("see 'hg help config.paths'"))
3871 hint=_("see 'hg help config.paths'"))
3872
3872
3873 opts = pycompat.byteskwargs(opts)
3873 opts = pycompat.byteskwargs(opts)
3874 if opts.get('graph'):
3874 if opts.get('graph'):
3875 logcmdutil.checkunsupportedgraphflags([], opts)
3875 logcmdutil.checkunsupportedgraphflags([], opts)
3876 o, other = hg._outgoing(ui, repo, dest, opts)
3876 o, other = hg._outgoing(ui, repo, dest, opts)
3877 if not o:
3877 if not o:
3878 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3878 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3879 return
3879 return
3880
3880
3881 revdag = logcmdutil.graphrevs(repo, o, opts)
3881 revdag = logcmdutil.graphrevs(repo, o, opts)
3882 ui.pager('outgoing')
3882 ui.pager('outgoing')
3883 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
3883 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
3884 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3884 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3885 graphmod.asciiedges)
3885 graphmod.asciiedges)
3886 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3886 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3887 return 0
3887 return 0
3888
3888
3889 if opts.get('bookmarks'):
3889 if opts.get('bookmarks'):
3890 dest = path.pushloc or path.loc
3890 dest = path.pushloc or path.loc
3891 other = hg.peer(repo, opts, dest)
3891 other = hg.peer(repo, opts, dest)
3892 if 'bookmarks' not in other.listkeys('namespaces'):
3892 if 'bookmarks' not in other.listkeys('namespaces'):
3893 ui.warn(_("remote doesn't support bookmarks\n"))
3893 ui.warn(_("remote doesn't support bookmarks\n"))
3894 return 0
3894 return 0
3895 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3895 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3896 ui.pager('outgoing')
3896 ui.pager('outgoing')
3897 return bookmarks.outgoing(ui, repo, other)
3897 return bookmarks.outgoing(ui, repo, other)
3898
3898
3899 repo._subtoppath = path.pushloc or path.loc
3899 repo._subtoppath = path.pushloc or path.loc
3900 try:
3900 try:
3901 return hg.outgoing(ui, repo, dest, opts)
3901 return hg.outgoing(ui, repo, dest, opts)
3902 finally:
3902 finally:
3903 del repo._subtoppath
3903 del repo._subtoppath
3904
3904
3905 @command('parents',
3905 @command('parents',
3906 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3906 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3907 ] + templateopts,
3907 ] + templateopts,
3908 _('[-r REV] [FILE]'),
3908 _('[-r REV] [FILE]'),
3909 inferrepo=True)
3909 inferrepo=True)
3910 def parents(ui, repo, file_=None, **opts):
3910 def parents(ui, repo, file_=None, **opts):
3911 """show the parents of the working directory or revision (DEPRECATED)
3911 """show the parents of the working directory or revision (DEPRECATED)
3912
3912
3913 Print the working directory's parent revisions. If a revision is
3913 Print the working directory's parent revisions. If a revision is
3914 given via -r/--rev, the parent of that revision will be printed.
3914 given via -r/--rev, the parent of that revision will be printed.
3915 If a file argument is given, the revision in which the file was
3915 If a file argument is given, the revision in which the file was
3916 last changed (before the working directory revision or the
3916 last changed (before the working directory revision or the
3917 argument to --rev if given) is printed.
3917 argument to --rev if given) is printed.
3918
3918
3919 This command is equivalent to::
3919 This command is equivalent to::
3920
3920
3921 hg log -r "p1()+p2()" or
3921 hg log -r "p1()+p2()" or
3922 hg log -r "p1(REV)+p2(REV)" or
3922 hg log -r "p1(REV)+p2(REV)" or
3923 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
3923 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
3924 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
3924 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
3925
3925
3926 See :hg:`summary` and :hg:`help revsets` for related information.
3926 See :hg:`summary` and :hg:`help revsets` for related information.
3927
3927
3928 Returns 0 on success.
3928 Returns 0 on success.
3929 """
3929 """
3930
3930
3931 opts = pycompat.byteskwargs(opts)
3931 opts = pycompat.byteskwargs(opts)
3932 rev = opts.get('rev')
3932 rev = opts.get('rev')
3933 if rev:
3933 if rev:
3934 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3934 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3935 ctx = scmutil.revsingle(repo, rev, None)
3935 ctx = scmutil.revsingle(repo, rev, None)
3936
3936
3937 if file_:
3937 if file_:
3938 m = scmutil.match(ctx, (file_,), opts)
3938 m = scmutil.match(ctx, (file_,), opts)
3939 if m.anypats() or len(m.files()) != 1:
3939 if m.anypats() or len(m.files()) != 1:
3940 raise error.Abort(_('can only specify an explicit filename'))
3940 raise error.Abort(_('can only specify an explicit filename'))
3941 file_ = m.files()[0]
3941 file_ = m.files()[0]
3942 filenodes = []
3942 filenodes = []
3943 for cp in ctx.parents():
3943 for cp in ctx.parents():
3944 if not cp:
3944 if not cp:
3945 continue
3945 continue
3946 try:
3946 try:
3947 filenodes.append(cp.filenode(file_))
3947 filenodes.append(cp.filenode(file_))
3948 except error.LookupError:
3948 except error.LookupError:
3949 pass
3949 pass
3950 if not filenodes:
3950 if not filenodes:
3951 raise error.Abort(_("'%s' not found in manifest!") % file_)
3951 raise error.Abort(_("'%s' not found in manifest!") % file_)
3952 p = []
3952 p = []
3953 for fn in filenodes:
3953 for fn in filenodes:
3954 fctx = repo.filectx(file_, fileid=fn)
3954 fctx = repo.filectx(file_, fileid=fn)
3955 p.append(fctx.node())
3955 p.append(fctx.node())
3956 else:
3956 else:
3957 p = [cp.node() for cp in ctx.parents()]
3957 p = [cp.node() for cp in ctx.parents()]
3958
3958
3959 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3959 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3960 for n in p:
3960 for n in p:
3961 if n != nullid:
3961 if n != nullid:
3962 displayer.show(repo[n])
3962 displayer.show(repo[n])
3963 displayer.close()
3963 displayer.close()
3964
3964
3965 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True,
3965 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True,
3966 intents={INTENT_READONLY})
3966 intents={INTENT_READONLY})
3967 def paths(ui, repo, search=None, **opts):
3967 def paths(ui, repo, search=None, **opts):
3968 """show aliases for remote repositories
3968 """show aliases for remote repositories
3969
3969
3970 Show definition of symbolic path name NAME. If no name is given,
3970 Show definition of symbolic path name NAME. If no name is given,
3971 show definition of all available names.
3971 show definition of all available names.
3972
3972
3973 Option -q/--quiet suppresses all output when searching for NAME
3973 Option -q/--quiet suppresses all output when searching for NAME
3974 and shows only the path names when listing all definitions.
3974 and shows only the path names when listing all definitions.
3975
3975
3976 Path names are defined in the [paths] section of your
3976 Path names are defined in the [paths] section of your
3977 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3977 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3978 repository, ``.hg/hgrc`` is used, too.
3978 repository, ``.hg/hgrc`` is used, too.
3979
3979
3980 The path names ``default`` and ``default-push`` have a special
3980 The path names ``default`` and ``default-push`` have a special
3981 meaning. When performing a push or pull operation, they are used
3981 meaning. When performing a push or pull operation, they are used
3982 as fallbacks if no location is specified on the command-line.
3982 as fallbacks if no location is specified on the command-line.
3983 When ``default-push`` is set, it will be used for push and
3983 When ``default-push`` is set, it will be used for push and
3984 ``default`` will be used for pull; otherwise ``default`` is used
3984 ``default`` will be used for pull; otherwise ``default`` is used
3985 as the fallback for both. When cloning a repository, the clone
3985 as the fallback for both. When cloning a repository, the clone
3986 source is written as ``default`` in ``.hg/hgrc``.
3986 source is written as ``default`` in ``.hg/hgrc``.
3987
3987
3988 .. note::
3988 .. note::
3989
3989
3990 ``default`` and ``default-push`` apply to all inbound (e.g.
3990 ``default`` and ``default-push`` apply to all inbound (e.g.
3991 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
3991 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
3992 and :hg:`bundle`) operations.
3992 and :hg:`bundle`) operations.
3993
3993
3994 See :hg:`help urls` for more information.
3994 See :hg:`help urls` for more information.
3995
3995
3996 Returns 0 on success.
3996 Returns 0 on success.
3997 """
3997 """
3998
3998
3999 opts = pycompat.byteskwargs(opts)
3999 opts = pycompat.byteskwargs(opts)
4000 ui.pager('paths')
4000 ui.pager('paths')
4001 if search:
4001 if search:
4002 pathitems = [(name, path) for name, path in ui.paths.iteritems()
4002 pathitems = [(name, path) for name, path in ui.paths.iteritems()
4003 if name == search]
4003 if name == search]
4004 else:
4004 else:
4005 pathitems = sorted(ui.paths.iteritems())
4005 pathitems = sorted(ui.paths.iteritems())
4006
4006
4007 fm = ui.formatter('paths', opts)
4007 fm = ui.formatter('paths', opts)
4008 if fm.isplain():
4008 if fm.isplain():
4009 hidepassword = util.hidepassword
4009 hidepassword = util.hidepassword
4010 else:
4010 else:
4011 hidepassword = bytes
4011 hidepassword = bytes
4012 if ui.quiet:
4012 if ui.quiet:
4013 namefmt = '%s\n'
4013 namefmt = '%s\n'
4014 else:
4014 else:
4015 namefmt = '%s = '
4015 namefmt = '%s = '
4016 showsubopts = not search and not ui.quiet
4016 showsubopts = not search and not ui.quiet
4017
4017
4018 for name, path in pathitems:
4018 for name, path in pathitems:
4019 fm.startitem()
4019 fm.startitem()
4020 fm.condwrite(not search, 'name', namefmt, name)
4020 fm.condwrite(not search, 'name', namefmt, name)
4021 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
4021 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
4022 for subopt, value in sorted(path.suboptions.items()):
4022 for subopt, value in sorted(path.suboptions.items()):
4023 assert subopt not in ('name', 'url')
4023 assert subopt not in ('name', 'url')
4024 if showsubopts:
4024 if showsubopts:
4025 fm.plain('%s:%s = ' % (name, subopt))
4025 fm.plain('%s:%s = ' % (name, subopt))
4026 fm.condwrite(showsubopts, subopt, '%s\n', value)
4026 fm.condwrite(showsubopts, subopt, '%s\n', value)
4027
4027
4028 fm.end()
4028 fm.end()
4029
4029
4030 if search and not pathitems:
4030 if search and not pathitems:
4031 if not ui.quiet:
4031 if not ui.quiet:
4032 ui.warn(_("not found!\n"))
4032 ui.warn(_("not found!\n"))
4033 return 1
4033 return 1
4034 else:
4034 else:
4035 return 0
4035 return 0
4036
4036
4037 @command('phase',
4037 @command('phase',
4038 [('p', 'public', False, _('set changeset phase to public')),
4038 [('p', 'public', False, _('set changeset phase to public')),
4039 ('d', 'draft', False, _('set changeset phase to draft')),
4039 ('d', 'draft', False, _('set changeset phase to draft')),
4040 ('s', 'secret', False, _('set changeset phase to secret')),
4040 ('s', 'secret', False, _('set changeset phase to secret')),
4041 ('f', 'force', False, _('allow to move boundary backward')),
4041 ('f', 'force', False, _('allow to move boundary backward')),
4042 ('r', 'rev', [], _('target revision'), _('REV')),
4042 ('r', 'rev', [], _('target revision'), _('REV')),
4043 ],
4043 ],
4044 _('[-p|-d|-s] [-f] [-r] [REV...]'))
4044 _('[-p|-d|-s] [-f] [-r] [REV...]'))
4045 def phase(ui, repo, *revs, **opts):
4045 def phase(ui, repo, *revs, **opts):
4046 """set or show the current phase name
4046 """set or show the current phase name
4047
4047
4048 With no argument, show the phase name of the current revision(s).
4048 With no argument, show the phase name of the current revision(s).
4049
4049
4050 With one of -p/--public, -d/--draft or -s/--secret, change the
4050 With one of -p/--public, -d/--draft or -s/--secret, change the
4051 phase value of the specified revisions.
4051 phase value of the specified revisions.
4052
4052
4053 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
4053 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
4054 lower phase to a higher phase. Phases are ordered as follows::
4054 lower phase to a higher phase. Phases are ordered as follows::
4055
4055
4056 public < draft < secret
4056 public < draft < secret
4057
4057
4058 Returns 0 on success, 1 if some phases could not be changed.
4058 Returns 0 on success, 1 if some phases could not be changed.
4059
4059
4060 (For more information about the phases concept, see :hg:`help phases`.)
4060 (For more information about the phases concept, see :hg:`help phases`.)
4061 """
4061 """
4062 opts = pycompat.byteskwargs(opts)
4062 opts = pycompat.byteskwargs(opts)
4063 # search for a unique phase argument
4063 # search for a unique phase argument
4064 targetphase = None
4064 targetphase = None
4065 for idx, name in enumerate(phases.phasenames):
4065 for idx, name in enumerate(phases.phasenames):
4066 if opts.get(name, False):
4066 if opts.get(name, False):
4067 if targetphase is not None:
4067 if targetphase is not None:
4068 raise error.Abort(_('only one phase can be specified'))
4068 raise error.Abort(_('only one phase can be specified'))
4069 targetphase = idx
4069 targetphase = idx
4070
4070
4071 # look for specified revision
4071 # look for specified revision
4072 revs = list(revs)
4072 revs = list(revs)
4073 revs.extend(opts['rev'])
4073 revs.extend(opts['rev'])
4074 if not revs:
4074 if not revs:
4075 # display both parents as the second parent phase can influence
4075 # display both parents as the second parent phase can influence
4076 # the phase of a merge commit
4076 # the phase of a merge commit
4077 revs = [c.rev() for c in repo[None].parents()]
4077 revs = [c.rev() for c in repo[None].parents()]
4078
4078
4079 revs = scmutil.revrange(repo, revs)
4079 revs = scmutil.revrange(repo, revs)
4080
4080
4081 ret = 0
4081 ret = 0
4082 if targetphase is None:
4082 if targetphase is None:
4083 # display
4083 # display
4084 for r in revs:
4084 for r in revs:
4085 ctx = repo[r]
4085 ctx = repo[r]
4086 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4086 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4087 else:
4087 else:
4088 with repo.lock(), repo.transaction("phase") as tr:
4088 with repo.lock(), repo.transaction("phase") as tr:
4089 # set phase
4089 # set phase
4090 if not revs:
4090 if not revs:
4091 raise error.Abort(_('empty revision set'))
4091 raise error.Abort(_('empty revision set'))
4092 nodes = [repo[r].node() for r in revs]
4092 nodes = [repo[r].node() for r in revs]
4093 # moving revision from public to draft may hide them
4093 # moving revision from public to draft may hide them
4094 # We have to check result on an unfiltered repository
4094 # We have to check result on an unfiltered repository
4095 unfi = repo.unfiltered()
4095 unfi = repo.unfiltered()
4096 getphase = unfi._phasecache.phase
4096 getphase = unfi._phasecache.phase
4097 olddata = [getphase(unfi, r) for r in unfi]
4097 olddata = [getphase(unfi, r) for r in unfi]
4098 phases.advanceboundary(repo, tr, targetphase, nodes)
4098 phases.advanceboundary(repo, tr, targetphase, nodes)
4099 if opts['force']:
4099 if opts['force']:
4100 phases.retractboundary(repo, tr, targetphase, nodes)
4100 phases.retractboundary(repo, tr, targetphase, nodes)
4101 getphase = unfi._phasecache.phase
4101 getphase = unfi._phasecache.phase
4102 newdata = [getphase(unfi, r) for r in unfi]
4102 newdata = [getphase(unfi, r) for r in unfi]
4103 changes = sum(newdata[r] != olddata[r] for r in unfi)
4103 changes = sum(newdata[r] != olddata[r] for r in unfi)
4104 cl = unfi.changelog
4104 cl = unfi.changelog
4105 rejected = [n for n in nodes
4105 rejected = [n for n in nodes
4106 if newdata[cl.rev(n)] < targetphase]
4106 if newdata[cl.rev(n)] < targetphase]
4107 if rejected:
4107 if rejected:
4108 ui.warn(_('cannot move %i changesets to a higher '
4108 ui.warn(_('cannot move %i changesets to a higher '
4109 'phase, use --force\n') % len(rejected))
4109 'phase, use --force\n') % len(rejected))
4110 ret = 1
4110 ret = 1
4111 if changes:
4111 if changes:
4112 msg = _('phase changed for %i changesets\n') % changes
4112 msg = _('phase changed for %i changesets\n') % changes
4113 if ret:
4113 if ret:
4114 ui.status(msg)
4114 ui.status(msg)
4115 else:
4115 else:
4116 ui.note(msg)
4116 ui.note(msg)
4117 else:
4117 else:
4118 ui.warn(_('no phases changed\n'))
4118 ui.warn(_('no phases changed\n'))
4119 return ret
4119 return ret
4120
4120
4121 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
4121 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
4122 """Run after a changegroup has been added via pull/unbundle
4122 """Run after a changegroup has been added via pull/unbundle
4123
4123
4124 This takes arguments below:
4124 This takes arguments below:
4125
4125
4126 :modheads: change of heads by pull/unbundle
4126 :modheads: change of heads by pull/unbundle
4127 :optupdate: updating working directory is needed or not
4127 :optupdate: updating working directory is needed or not
4128 :checkout: update destination revision (or None to default destination)
4128 :checkout: update destination revision (or None to default destination)
4129 :brev: a name, which might be a bookmark to be activated after updating
4129 :brev: a name, which might be a bookmark to be activated after updating
4130 """
4130 """
4131 if modheads == 0:
4131 if modheads == 0:
4132 return
4132 return
4133 if optupdate:
4133 if optupdate:
4134 try:
4134 try:
4135 return hg.updatetotally(ui, repo, checkout, brev)
4135 return hg.updatetotally(ui, repo, checkout, brev)
4136 except error.UpdateAbort as inst:
4136 except error.UpdateAbort as inst:
4137 msg = _("not updating: %s") % stringutil.forcebytestr(inst)
4137 msg = _("not updating: %s") % stringutil.forcebytestr(inst)
4138 hint = inst.hint
4138 hint = inst.hint
4139 raise error.UpdateAbort(msg, hint=hint)
4139 raise error.UpdateAbort(msg, hint=hint)
4140 if modheads > 1:
4140 if modheads > 1:
4141 currentbranchheads = len(repo.branchheads())
4141 currentbranchheads = len(repo.branchheads())
4142 if currentbranchheads == modheads:
4142 if currentbranchheads == modheads:
4143 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4143 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4144 elif currentbranchheads > 1:
4144 elif currentbranchheads > 1:
4145 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4145 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4146 "merge)\n"))
4146 "merge)\n"))
4147 else:
4147 else:
4148 ui.status(_("(run 'hg heads' to see heads)\n"))
4148 ui.status(_("(run 'hg heads' to see heads)\n"))
4149 elif not ui.configbool('commands', 'update.requiredest'):
4149 elif not ui.configbool('commands', 'update.requiredest'):
4150 ui.status(_("(run 'hg update' to get a working copy)\n"))
4150 ui.status(_("(run 'hg update' to get a working copy)\n"))
4151
4151
4152 @command('^pull',
4152 @command('^pull',
4153 [('u', 'update', None,
4153 [('u', 'update', None,
4154 _('update to new branch head if new descendants were pulled')),
4154 _('update to new branch head if new descendants were pulled')),
4155 ('f', 'force', None, _('run even when remote repository is unrelated')),
4155 ('f', 'force', None, _('run even when remote repository is unrelated')),
4156 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4156 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4157 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4157 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4158 ('b', 'branch', [], _('a specific branch you would like to pull'),
4158 ('b', 'branch', [], _('a specific branch you would like to pull'),
4159 _('BRANCH')),
4159 _('BRANCH')),
4160 ] + remoteopts,
4160 ] + remoteopts,
4161 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4161 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4162 def pull(ui, repo, source="default", **opts):
4162 def pull(ui, repo, source="default", **opts):
4163 """pull changes from the specified source
4163 """pull changes from the specified source
4164
4164
4165 Pull changes from a remote repository to a local one.
4165 Pull changes from a remote repository to a local one.
4166
4166
4167 This finds all changes from the repository at the specified path
4167 This finds all changes from the repository at the specified path
4168 or URL and adds them to a local repository (the current one unless
4168 or URL and adds them to a local repository (the current one unless
4169 -R is specified). By default, this does not update the copy of the
4169 -R is specified). By default, this does not update the copy of the
4170 project in the working directory.
4170 project in the working directory.
4171
4171
4172 When cloning from servers that support it, Mercurial may fetch
4172 When cloning from servers that support it, Mercurial may fetch
4173 pre-generated data. When this is done, hooks operating on incoming
4173 pre-generated data. When this is done, hooks operating on incoming
4174 changesets and changegroups may fire more than once, once for each
4174 changesets and changegroups may fire more than once, once for each
4175 pre-generated bundle and as well as for any additional remaining
4175 pre-generated bundle and as well as for any additional remaining
4176 data. See :hg:`help -e clonebundles` for more.
4176 data. See :hg:`help -e clonebundles` for more.
4177
4177
4178 Use :hg:`incoming` if you want to see what would have been added
4178 Use :hg:`incoming` if you want to see what would have been added
4179 by a pull at the time you issued this command. If you then decide
4179 by a pull at the time you issued this command. If you then decide
4180 to add those changes to the repository, you should use :hg:`pull
4180 to add those changes to the repository, you should use :hg:`pull
4181 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4181 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4182
4182
4183 If SOURCE is omitted, the 'default' path will be used.
4183 If SOURCE is omitted, the 'default' path will be used.
4184 See :hg:`help urls` for more information.
4184 See :hg:`help urls` for more information.
4185
4185
4186 Specifying bookmark as ``.`` is equivalent to specifying the active
4186 Specifying bookmark as ``.`` is equivalent to specifying the active
4187 bookmark's name.
4187 bookmark's name.
4188
4188
4189 Returns 0 on success, 1 if an update had unresolved files.
4189 Returns 0 on success, 1 if an update had unresolved files.
4190 """
4190 """
4191
4191
4192 opts = pycompat.byteskwargs(opts)
4192 opts = pycompat.byteskwargs(opts)
4193 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
4193 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
4194 msg = _('update destination required by configuration')
4194 msg = _('update destination required by configuration')
4195 hint = _('use hg pull followed by hg update DEST')
4195 hint = _('use hg pull followed by hg update DEST')
4196 raise error.Abort(msg, hint=hint)
4196 raise error.Abort(msg, hint=hint)
4197
4197
4198 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4198 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4199 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4199 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4200 other = hg.peer(repo, opts, source)
4200 other = hg.peer(repo, opts, source)
4201 try:
4201 try:
4202 revs, checkout = hg.addbranchrevs(repo, other, branches,
4202 revs, checkout = hg.addbranchrevs(repo, other, branches,
4203 opts.get('rev'))
4203 opts.get('rev'))
4204
4204
4205
4205
4206 pullopargs = {}
4206 pullopargs = {}
4207 if opts.get('bookmark'):
4207 if opts.get('bookmark'):
4208 if not revs:
4208 if not revs:
4209 revs = []
4209 revs = []
4210 # The list of bookmark used here is not the one used to actually
4210 # The list of bookmark used here is not the one used to actually
4211 # update the bookmark name. This can result in the revision pulled
4211 # update the bookmark name. This can result in the revision pulled
4212 # not ending up with the name of the bookmark because of a race
4212 # not ending up with the name of the bookmark because of a race
4213 # condition on the server. (See issue 4689 for details)
4213 # condition on the server. (See issue 4689 for details)
4214 remotebookmarks = other.listkeys('bookmarks')
4214 remotebookmarks = other.listkeys('bookmarks')
4215 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
4215 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
4216 pullopargs['remotebookmarks'] = remotebookmarks
4216 pullopargs['remotebookmarks'] = remotebookmarks
4217 for b in opts['bookmark']:
4217 for b in opts['bookmark']:
4218 b = repo._bookmarks.expandname(b)
4218 b = repo._bookmarks.expandname(b)
4219 if b not in remotebookmarks:
4219 if b not in remotebookmarks:
4220 raise error.Abort(_('remote bookmark %s not found!') % b)
4220 raise error.Abort(_('remote bookmark %s not found!') % b)
4221 revs.append(hex(remotebookmarks[b]))
4221 revs.append(hex(remotebookmarks[b]))
4222
4222
4223 if revs:
4223 if revs:
4224 try:
4224 try:
4225 # When 'rev' is a bookmark name, we cannot guarantee that it
4225 # When 'rev' is a bookmark name, we cannot guarantee that it
4226 # will be updated with that name because of a race condition
4226 # will be updated with that name because of a race condition
4227 # server side. (See issue 4689 for details)
4227 # server side. (See issue 4689 for details)
4228 oldrevs = revs
4228 oldrevs = revs
4229 revs = [] # actually, nodes
4229 revs = [] # actually, nodes
4230 for r in oldrevs:
4230 for r in oldrevs:
4231 with other.commandexecutor() as e:
4231 with other.commandexecutor() as e:
4232 node = e.callcommand('lookup', {'key': r}).result()
4232 node = e.callcommand('lookup', {'key': r}).result()
4233
4233
4234 revs.append(node)
4234 revs.append(node)
4235 if r == checkout:
4235 if r == checkout:
4236 checkout = node
4236 checkout = node
4237 except error.CapabilityError:
4237 except error.CapabilityError:
4238 err = _("other repository doesn't support revision lookup, "
4238 err = _("other repository doesn't support revision lookup, "
4239 "so a rev cannot be specified.")
4239 "so a rev cannot be specified.")
4240 raise error.Abort(err)
4240 raise error.Abort(err)
4241
4241
4242 wlock = util.nullcontextmanager()
4242 wlock = util.nullcontextmanager()
4243 if opts.get('update'):
4243 if opts.get('update'):
4244 wlock = repo.wlock()
4244 wlock = repo.wlock()
4245 with wlock:
4245 with wlock:
4246 pullopargs.update(opts.get('opargs', {}))
4246 pullopargs.update(opts.get('opargs', {}))
4247 modheads = exchange.pull(repo, other, heads=revs,
4247 modheads = exchange.pull(repo, other, heads=revs,
4248 force=opts.get('force'),
4248 force=opts.get('force'),
4249 bookmarks=opts.get('bookmark', ()),
4249 bookmarks=opts.get('bookmark', ()),
4250 opargs=pullopargs).cgresult
4250 opargs=pullopargs).cgresult
4251
4251
4252 # brev is a name, which might be a bookmark to be activated at
4252 # brev is a name, which might be a bookmark to be activated at
4253 # the end of the update. In other words, it is an explicit
4253 # the end of the update. In other words, it is an explicit
4254 # destination of the update
4254 # destination of the update
4255 brev = None
4255 brev = None
4256
4256
4257 if checkout:
4257 if checkout:
4258 checkout = repo.changelog.rev(checkout)
4258 checkout = repo.changelog.rev(checkout)
4259
4259
4260 # order below depends on implementation of
4260 # order below depends on implementation of
4261 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4261 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4262 # because 'checkout' is determined without it.
4262 # because 'checkout' is determined without it.
4263 if opts.get('rev'):
4263 if opts.get('rev'):
4264 brev = opts['rev'][0]
4264 brev = opts['rev'][0]
4265 elif opts.get('branch'):
4265 elif opts.get('branch'):
4266 brev = opts['branch'][0]
4266 brev = opts['branch'][0]
4267 else:
4267 else:
4268 brev = branches[0]
4268 brev = branches[0]
4269 repo._subtoppath = source
4269 repo._subtoppath = source
4270 try:
4270 try:
4271 ret = postincoming(ui, repo, modheads, opts.get('update'),
4271 ret = postincoming(ui, repo, modheads, opts.get('update'),
4272 checkout, brev)
4272 checkout, brev)
4273
4273
4274 finally:
4274 finally:
4275 del repo._subtoppath
4275 del repo._subtoppath
4276
4276
4277 finally:
4277 finally:
4278 other.close()
4278 other.close()
4279 return ret
4279 return ret
4280
4280
4281 @command('^push',
4281 @command('^push',
4282 [('f', 'force', None, _('force push')),
4282 [('f', 'force', None, _('force push')),
4283 ('r', 'rev', [],
4283 ('r', 'rev', [],
4284 _('a changeset intended to be included in the destination'),
4284 _('a changeset intended to be included in the destination'),
4285 _('REV')),
4285 _('REV')),
4286 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4286 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4287 ('b', 'branch', [],
4287 ('b', 'branch', [],
4288 _('a specific branch you would like to push'), _('BRANCH')),
4288 _('a specific branch you would like to push'), _('BRANCH')),
4289 ('', 'new-branch', False, _('allow pushing a new branch')),
4289 ('', 'new-branch', False, _('allow pushing a new branch')),
4290 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4290 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4291 ] + remoteopts,
4291 ] + remoteopts,
4292 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4292 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4293 def push(ui, repo, dest=None, **opts):
4293 def push(ui, repo, dest=None, **opts):
4294 """push changes to the specified destination
4294 """push changes to the specified destination
4295
4295
4296 Push changesets from the local repository to the specified
4296 Push changesets from the local repository to the specified
4297 destination.
4297 destination.
4298
4298
4299 This operation is symmetrical to pull: it is identical to a pull
4299 This operation is symmetrical to pull: it is identical to a pull
4300 in the destination repository from the current one.
4300 in the destination repository from the current one.
4301
4301
4302 By default, push will not allow creation of new heads at the
4302 By default, push will not allow creation of new heads at the
4303 destination, since multiple heads would make it unclear which head
4303 destination, since multiple heads would make it unclear which head
4304 to use. In this situation, it is recommended to pull and merge
4304 to use. In this situation, it is recommended to pull and merge
4305 before pushing.
4305 before pushing.
4306
4306
4307 Use --new-branch if you want to allow push to create a new named
4307 Use --new-branch if you want to allow push to create a new named
4308 branch that is not present at the destination. This allows you to
4308 branch that is not present at the destination. This allows you to
4309 only create a new branch without forcing other changes.
4309 only create a new branch without forcing other changes.
4310
4310
4311 .. note::
4311 .. note::
4312
4312
4313 Extra care should be taken with the -f/--force option,
4313 Extra care should be taken with the -f/--force option,
4314 which will push all new heads on all branches, an action which will
4314 which will push all new heads on all branches, an action which will
4315 almost always cause confusion for collaborators.
4315 almost always cause confusion for collaborators.
4316
4316
4317 If -r/--rev is used, the specified revision and all its ancestors
4317 If -r/--rev is used, the specified revision and all its ancestors
4318 will be pushed to the remote repository.
4318 will be pushed to the remote repository.
4319
4319
4320 If -B/--bookmark is used, the specified bookmarked revision, its
4320 If -B/--bookmark is used, the specified bookmarked revision, its
4321 ancestors, and the bookmark will be pushed to the remote
4321 ancestors, and the bookmark will be pushed to the remote
4322 repository. Specifying ``.`` is equivalent to specifying the active
4322 repository. Specifying ``.`` is equivalent to specifying the active
4323 bookmark's name.
4323 bookmark's name.
4324
4324
4325 Please see :hg:`help urls` for important details about ``ssh://``
4325 Please see :hg:`help urls` for important details about ``ssh://``
4326 URLs. If DESTINATION is omitted, a default path will be used.
4326 URLs. If DESTINATION is omitted, a default path will be used.
4327
4327
4328 .. container:: verbose
4328 .. container:: verbose
4329
4329
4330 The --pushvars option sends strings to the server that become
4330 The --pushvars option sends strings to the server that become
4331 environment variables prepended with ``HG_USERVAR_``. For example,
4331 environment variables prepended with ``HG_USERVAR_``. For example,
4332 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4332 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4333 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4333 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4334
4334
4335 pushvars can provide for user-overridable hooks as well as set debug
4335 pushvars can provide for user-overridable hooks as well as set debug
4336 levels. One example is having a hook that blocks commits containing
4336 levels. One example is having a hook that blocks commits containing
4337 conflict markers, but enables the user to override the hook if the file
4337 conflict markers, but enables the user to override the hook if the file
4338 is using conflict markers for testing purposes or the file format has
4338 is using conflict markers for testing purposes or the file format has
4339 strings that look like conflict markers.
4339 strings that look like conflict markers.
4340
4340
4341 By default, servers will ignore `--pushvars`. To enable it add the
4341 By default, servers will ignore `--pushvars`. To enable it add the
4342 following to your configuration file::
4342 following to your configuration file::
4343
4343
4344 [push]
4344 [push]
4345 pushvars.server = true
4345 pushvars.server = true
4346
4346
4347 Returns 0 if push was successful, 1 if nothing to push.
4347 Returns 0 if push was successful, 1 if nothing to push.
4348 """
4348 """
4349
4349
4350 opts = pycompat.byteskwargs(opts)
4350 opts = pycompat.byteskwargs(opts)
4351 if opts.get('bookmark'):
4351 if opts.get('bookmark'):
4352 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4352 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4353 for b in opts['bookmark']:
4353 for b in opts['bookmark']:
4354 # translate -B options to -r so changesets get pushed
4354 # translate -B options to -r so changesets get pushed
4355 b = repo._bookmarks.expandname(b)
4355 b = repo._bookmarks.expandname(b)
4356 if b in repo._bookmarks:
4356 if b in repo._bookmarks:
4357 opts.setdefault('rev', []).append(b)
4357 opts.setdefault('rev', []).append(b)
4358 else:
4358 else:
4359 # if we try to push a deleted bookmark, translate it to null
4359 # if we try to push a deleted bookmark, translate it to null
4360 # this lets simultaneous -r, -b options continue working
4360 # this lets simultaneous -r, -b options continue working
4361 opts.setdefault('rev', []).append("null")
4361 opts.setdefault('rev', []).append("null")
4362
4362
4363 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4363 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4364 if not path:
4364 if not path:
4365 raise error.Abort(_('default repository not configured!'),
4365 raise error.Abort(_('default repository not configured!'),
4366 hint=_("see 'hg help config.paths'"))
4366 hint=_("see 'hg help config.paths'"))
4367 dest = path.pushloc or path.loc
4367 dest = path.pushloc or path.loc
4368 branches = (path.branch, opts.get('branch') or [])
4368 branches = (path.branch, opts.get('branch') or [])
4369 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4369 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4370 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4370 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4371 other = hg.peer(repo, opts, dest)
4371 other = hg.peer(repo, opts, dest)
4372
4372
4373 if revs:
4373 if revs:
4374 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
4374 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
4375 if not revs:
4375 if not revs:
4376 raise error.Abort(_("specified revisions evaluate to an empty set"),
4376 raise error.Abort(_("specified revisions evaluate to an empty set"),
4377 hint=_("use different revision arguments"))
4377 hint=_("use different revision arguments"))
4378 elif path.pushrev:
4378 elif path.pushrev:
4379 # It doesn't make any sense to specify ancestor revisions. So limit
4379 # It doesn't make any sense to specify ancestor revisions. So limit
4380 # to DAG heads to make discovery simpler.
4380 # to DAG heads to make discovery simpler.
4381 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4381 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4382 revs = scmutil.revrange(repo, [expr])
4382 revs = scmutil.revrange(repo, [expr])
4383 revs = [repo[rev].node() for rev in revs]
4383 revs = [repo[rev].node() for rev in revs]
4384 if not revs:
4384 if not revs:
4385 raise error.Abort(_('default push revset for path evaluates to an '
4385 raise error.Abort(_('default push revset for path evaluates to an '
4386 'empty set'))
4386 'empty set'))
4387
4387
4388 repo._subtoppath = dest
4388 repo._subtoppath = dest
4389 try:
4389 try:
4390 # push subrepos depth-first for coherent ordering
4390 # push subrepos depth-first for coherent ordering
4391 c = repo['.']
4391 c = repo['.']
4392 subs = c.substate # only repos that are committed
4392 subs = c.substate # only repos that are committed
4393 for s in sorted(subs):
4393 for s in sorted(subs):
4394 result = c.sub(s).push(opts)
4394 result = c.sub(s).push(opts)
4395 if result == 0:
4395 if result == 0:
4396 return not result
4396 return not result
4397 finally:
4397 finally:
4398 del repo._subtoppath
4398 del repo._subtoppath
4399
4399
4400 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4400 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4401 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4401 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4402
4402
4403 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4403 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4404 newbranch=opts.get('new_branch'),
4404 newbranch=opts.get('new_branch'),
4405 bookmarks=opts.get('bookmark', ()),
4405 bookmarks=opts.get('bookmark', ()),
4406 opargs=opargs)
4406 opargs=opargs)
4407
4407
4408 result = not pushop.cgresult
4408 result = not pushop.cgresult
4409
4409
4410 if pushop.bkresult is not None:
4410 if pushop.bkresult is not None:
4411 if pushop.bkresult == 2:
4411 if pushop.bkresult == 2:
4412 result = 2
4412 result = 2
4413 elif not result and pushop.bkresult:
4413 elif not result and pushop.bkresult:
4414 result = 2
4414 result = 2
4415
4415
4416 return result
4416 return result
4417
4417
4418 @command('recover', [])
4418 @command('recover', [])
4419 def recover(ui, repo):
4419 def recover(ui, repo):
4420 """roll back an interrupted transaction
4420 """roll back an interrupted transaction
4421
4421
4422 Recover from an interrupted commit or pull.
4422 Recover from an interrupted commit or pull.
4423
4423
4424 This command tries to fix the repository status after an
4424 This command tries to fix the repository status after an
4425 interrupted operation. It should only be necessary when Mercurial
4425 interrupted operation. It should only be necessary when Mercurial
4426 suggests it.
4426 suggests it.
4427
4427
4428 Returns 0 if successful, 1 if nothing to recover or verify fails.
4428 Returns 0 if successful, 1 if nothing to recover or verify fails.
4429 """
4429 """
4430 if repo.recover():
4430 if repo.recover():
4431 return hg.verify(repo)
4431 return hg.verify(repo)
4432 return 1
4432 return 1
4433
4433
4434 @command('^remove|rm',
4434 @command('^remove|rm',
4435 [('A', 'after', None, _('record delete for missing files')),
4435 [('A', 'after', None, _('record delete for missing files')),
4436 ('f', 'force', None,
4436 ('f', 'force', None,
4437 _('forget added files, delete modified files')),
4437 _('forget added files, delete modified files')),
4438 ] + subrepoopts + walkopts + dryrunopts,
4438 ] + subrepoopts + walkopts + dryrunopts,
4439 _('[OPTION]... FILE...'),
4439 _('[OPTION]... FILE...'),
4440 inferrepo=True)
4440 inferrepo=True)
4441 def remove(ui, repo, *pats, **opts):
4441 def remove(ui, repo, *pats, **opts):
4442 """remove the specified files on the next commit
4442 """remove the specified files on the next commit
4443
4443
4444 Schedule the indicated files for removal from the current branch.
4444 Schedule the indicated files for removal from the current branch.
4445
4445
4446 This command schedules the files to be removed at the next commit.
4446 This command schedules the files to be removed at the next commit.
4447 To undo a remove before that, see :hg:`revert`. To undo added
4447 To undo a remove before that, see :hg:`revert`. To undo added
4448 files, see :hg:`forget`.
4448 files, see :hg:`forget`.
4449
4449
4450 .. container:: verbose
4450 .. container:: verbose
4451
4451
4452 -A/--after can be used to remove only files that have already
4452 -A/--after can be used to remove only files that have already
4453 been deleted, -f/--force can be used to force deletion, and -Af
4453 been deleted, -f/--force can be used to force deletion, and -Af
4454 can be used to remove files from the next revision without
4454 can be used to remove files from the next revision without
4455 deleting them from the working directory.
4455 deleting them from the working directory.
4456
4456
4457 The following table details the behavior of remove for different
4457 The following table details the behavior of remove for different
4458 file states (columns) and option combinations (rows). The file
4458 file states (columns) and option combinations (rows). The file
4459 states are Added [A], Clean [C], Modified [M] and Missing [!]
4459 states are Added [A], Clean [C], Modified [M] and Missing [!]
4460 (as reported by :hg:`status`). The actions are Warn, Remove
4460 (as reported by :hg:`status`). The actions are Warn, Remove
4461 (from branch) and Delete (from disk):
4461 (from branch) and Delete (from disk):
4462
4462
4463 ========= == == == ==
4463 ========= == == == ==
4464 opt/state A C M !
4464 opt/state A C M !
4465 ========= == == == ==
4465 ========= == == == ==
4466 none W RD W R
4466 none W RD W R
4467 -f R RD RD R
4467 -f R RD RD R
4468 -A W W W R
4468 -A W W W R
4469 -Af R R R R
4469 -Af R R R R
4470 ========= == == == ==
4470 ========= == == == ==
4471
4471
4472 .. note::
4472 .. note::
4473
4473
4474 :hg:`remove` never deletes files in Added [A] state from the
4474 :hg:`remove` never deletes files in Added [A] state from the
4475 working directory, not even if ``--force`` is specified.
4475 working directory, not even if ``--force`` is specified.
4476
4476
4477 Returns 0 on success, 1 if any warnings encountered.
4477 Returns 0 on success, 1 if any warnings encountered.
4478 """
4478 """
4479
4479
4480 opts = pycompat.byteskwargs(opts)
4480 opts = pycompat.byteskwargs(opts)
4481 after, force = opts.get('after'), opts.get('force')
4481 after, force = opts.get('after'), opts.get('force')
4482 dryrun = opts.get('dry_run')
4482 dryrun = opts.get('dry_run')
4483 if not pats and not after:
4483 if not pats and not after:
4484 raise error.Abort(_('no files specified'))
4484 raise error.Abort(_('no files specified'))
4485
4485
4486 m = scmutil.match(repo[None], pats, opts)
4486 m = scmutil.match(repo[None], pats, opts)
4487 subrepos = opts.get('subrepos')
4487 subrepos = opts.get('subrepos')
4488 return cmdutil.remove(ui, repo, m, "", after, force, subrepos,
4488 return cmdutil.remove(ui, repo, m, "", after, force, subrepos,
4489 dryrun=dryrun)
4489 dryrun=dryrun)
4490
4490
4491 @command('rename|move|mv',
4491 @command('rename|move|mv',
4492 [('A', 'after', None, _('record a rename that has already occurred')),
4492 [('A', 'after', None, _('record a rename that has already occurred')),
4493 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4493 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4494 ] + walkopts + dryrunopts,
4494 ] + walkopts + dryrunopts,
4495 _('[OPTION]... SOURCE... DEST'))
4495 _('[OPTION]... SOURCE... DEST'))
4496 def rename(ui, repo, *pats, **opts):
4496 def rename(ui, repo, *pats, **opts):
4497 """rename files; equivalent of copy + remove
4497 """rename files; equivalent of copy + remove
4498
4498
4499 Mark dest as copies of sources; mark sources for deletion. If dest
4499 Mark dest as copies of sources; mark sources for deletion. If dest
4500 is a directory, copies are put in that directory. If dest is a
4500 is a directory, copies are put in that directory. If dest is a
4501 file, there can only be one source.
4501 file, there can only be one source.
4502
4502
4503 By default, this command copies the contents of files as they
4503 By default, this command copies the contents of files as they
4504 exist in the working directory. If invoked with -A/--after, the
4504 exist in the working directory. If invoked with -A/--after, the
4505 operation is recorded, but no copying is performed.
4505 operation is recorded, but no copying is performed.
4506
4506
4507 This command takes effect at the next commit. To undo a rename
4507 This command takes effect at the next commit. To undo a rename
4508 before that, see :hg:`revert`.
4508 before that, see :hg:`revert`.
4509
4509
4510 Returns 0 on success, 1 if errors are encountered.
4510 Returns 0 on success, 1 if errors are encountered.
4511 """
4511 """
4512 opts = pycompat.byteskwargs(opts)
4512 opts = pycompat.byteskwargs(opts)
4513 with repo.wlock(False):
4513 with repo.wlock(False):
4514 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4514 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4515
4515
4516 @command('resolve',
4516 @command('resolve',
4517 [('a', 'all', None, _('select all unresolved files')),
4517 [('a', 'all', None, _('select all unresolved files')),
4518 ('l', 'list', None, _('list state of files needing merge')),
4518 ('l', 'list', None, _('list state of files needing merge')),
4519 ('m', 'mark', None, _('mark files as resolved')),
4519 ('m', 'mark', None, _('mark files as resolved')),
4520 ('u', 'unmark', None, _('mark files as unresolved')),
4520 ('u', 'unmark', None, _('mark files as unresolved')),
4521 ('n', 'no-status', None, _('hide status prefix')),
4521 ('n', 'no-status', None, _('hide status prefix')),
4522 ('', 're-merge', None, _('re-merge files'))]
4522 ('', 're-merge', None, _('re-merge files'))]
4523 + mergetoolopts + walkopts + formatteropts,
4523 + mergetoolopts + walkopts + formatteropts,
4524 _('[OPTION]... [FILE]...'),
4524 _('[OPTION]... [FILE]...'),
4525 inferrepo=True)
4525 inferrepo=True)
4526 def resolve(ui, repo, *pats, **opts):
4526 def resolve(ui, repo, *pats, **opts):
4527 """redo merges or set/view the merge status of files
4527 """redo merges or set/view the merge status of files
4528
4528
4529 Merges with unresolved conflicts are often the result of
4529 Merges with unresolved conflicts are often the result of
4530 non-interactive merging using the ``internal:merge`` configuration
4530 non-interactive merging using the ``internal:merge`` configuration
4531 setting, or a command-line merge tool like ``diff3``. The resolve
4531 setting, or a command-line merge tool like ``diff3``. The resolve
4532 command is used to manage the files involved in a merge, after
4532 command is used to manage the files involved in a merge, after
4533 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4533 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4534 working directory must have two parents). See :hg:`help
4534 working directory must have two parents). See :hg:`help
4535 merge-tools` for information on configuring merge tools.
4535 merge-tools` for information on configuring merge tools.
4536
4536
4537 The resolve command can be used in the following ways:
4537 The resolve command can be used in the following ways:
4538
4538
4539 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
4539 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
4540 the specified files, discarding any previous merge attempts. Re-merging
4540 the specified files, discarding any previous merge attempts. Re-merging
4541 is not performed for files already marked as resolved. Use ``--all/-a``
4541 is not performed for files already marked as resolved. Use ``--all/-a``
4542 to select all unresolved files. ``--tool`` can be used to specify
4542 to select all unresolved files. ``--tool`` can be used to specify
4543 the merge tool used for the given files. It overrides the HGMERGE
4543 the merge tool used for the given files. It overrides the HGMERGE
4544 environment variable and your configuration files. Previous file
4544 environment variable and your configuration files. Previous file
4545 contents are saved with a ``.orig`` suffix.
4545 contents are saved with a ``.orig`` suffix.
4546
4546
4547 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4547 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4548 (e.g. after having manually fixed-up the files). The default is
4548 (e.g. after having manually fixed-up the files). The default is
4549 to mark all unresolved files.
4549 to mark all unresolved files.
4550
4550
4551 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4551 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4552 default is to mark all resolved files.
4552 default is to mark all resolved files.
4553
4553
4554 - :hg:`resolve -l`: list files which had or still have conflicts.
4554 - :hg:`resolve -l`: list files which had or still have conflicts.
4555 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4555 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4556 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4556 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4557 the list. See :hg:`help filesets` for details.
4557 the list. See :hg:`help filesets` for details.
4558
4558
4559 .. note::
4559 .. note::
4560
4560
4561 Mercurial will not let you commit files with unresolved merge
4561 Mercurial will not let you commit files with unresolved merge
4562 conflicts. You must use :hg:`resolve -m ...` before you can
4562 conflicts. You must use :hg:`resolve -m ...` before you can
4563 commit after a conflicting merge.
4563 commit after a conflicting merge.
4564
4564
4565 Returns 0 on success, 1 if any files fail a resolve attempt.
4565 Returns 0 on success, 1 if any files fail a resolve attempt.
4566 """
4566 """
4567
4567
4568 opts = pycompat.byteskwargs(opts)
4568 opts = pycompat.byteskwargs(opts)
4569 confirm = ui.configbool('commands', 'resolve.confirm')
4569 confirm = ui.configbool('commands', 'resolve.confirm')
4570 flaglist = 'all mark unmark list no_status re_merge'.split()
4570 flaglist = 'all mark unmark list no_status re_merge'.split()
4571 all, mark, unmark, show, nostatus, remerge = \
4571 all, mark, unmark, show, nostatus, remerge = \
4572 [opts.get(o) for o in flaglist]
4572 [opts.get(o) for o in flaglist]
4573
4573
4574 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
4574 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
4575 if actioncount > 1:
4575 if actioncount > 1:
4576 raise error.Abort(_("too many actions specified"))
4576 raise error.Abort(_("too many actions specified"))
4577 elif (actioncount == 0
4577 elif (actioncount == 0
4578 and ui.configbool('commands', 'resolve.explicit-re-merge')):
4578 and ui.configbool('commands', 'resolve.explicit-re-merge')):
4579 hint = _('use --mark, --unmark, --list or --re-merge')
4579 hint = _('use --mark, --unmark, --list or --re-merge')
4580 raise error.Abort(_('no action specified'), hint=hint)
4580 raise error.Abort(_('no action specified'), hint=hint)
4581 if pats and all:
4581 if pats and all:
4582 raise error.Abort(_("can't specify --all and patterns"))
4582 raise error.Abort(_("can't specify --all and patterns"))
4583 if not (all or pats or show or mark or unmark):
4583 if not (all or pats or show or mark or unmark):
4584 raise error.Abort(_('no files or directories specified'),
4584 raise error.Abort(_('no files or directories specified'),
4585 hint=('use --all to re-merge all unresolved files'))
4585 hint=('use --all to re-merge all unresolved files'))
4586
4586
4587 if confirm:
4587 if confirm:
4588 if all:
4588 if all:
4589 if ui.promptchoice(_(b're-merge all unresolved files (yn)?'
4589 if ui.promptchoice(_(b're-merge all unresolved files (yn)?'
4590 b'$$ &Yes $$ &No')):
4590 b'$$ &Yes $$ &No')):
4591 raise error.Abort(_('user quit'))
4591 raise error.Abort(_('user quit'))
4592 if mark and not pats:
4592 if mark and not pats:
4593 if ui.promptchoice(_(b'mark all unresolved files as resolved (yn)?'
4593 if ui.promptchoice(_(b'mark all unresolved files as resolved (yn)?'
4594 b'$$ &Yes $$ &No')):
4594 b'$$ &Yes $$ &No')):
4595 raise error.Abort(_('user quit'))
4595 raise error.Abort(_('user quit'))
4596 if unmark and not pats:
4596 if unmark and not pats:
4597 if ui.promptchoice(_(b'mark all resolved files as unresolved (yn)?'
4597 if ui.promptchoice(_(b'mark all resolved files as unresolved (yn)?'
4598 b'$$ &Yes $$ &No')):
4598 b'$$ &Yes $$ &No')):
4599 raise error.Abort(_('user quit'))
4599 raise error.Abort(_('user quit'))
4600
4600
4601 if show:
4601 if show:
4602 ui.pager('resolve')
4602 ui.pager('resolve')
4603 fm = ui.formatter('resolve', opts)
4603 fm = ui.formatter('resolve', opts)
4604 ms = mergemod.mergestate.read(repo)
4604 ms = mergemod.mergestate.read(repo)
4605 wctx = repo[None]
4605 wctx = repo[None]
4606 m = scmutil.match(wctx, pats, opts)
4606 m = scmutil.match(wctx, pats, opts)
4607
4607
4608 # Labels and keys based on merge state. Unresolved path conflicts show
4608 # Labels and keys based on merge state. Unresolved path conflicts show
4609 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4609 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4610 # resolved conflicts.
4610 # resolved conflicts.
4611 mergestateinfo = {
4611 mergestateinfo = {
4612 mergemod.MERGE_RECORD_UNRESOLVED: ('resolve.unresolved', 'U'),
4612 mergemod.MERGE_RECORD_UNRESOLVED: ('resolve.unresolved', 'U'),
4613 mergemod.MERGE_RECORD_RESOLVED: ('resolve.resolved', 'R'),
4613 mergemod.MERGE_RECORD_RESOLVED: ('resolve.resolved', 'R'),
4614 mergemod.MERGE_RECORD_UNRESOLVED_PATH: ('resolve.unresolved', 'P'),
4614 mergemod.MERGE_RECORD_UNRESOLVED_PATH: ('resolve.unresolved', 'P'),
4615 mergemod.MERGE_RECORD_RESOLVED_PATH: ('resolve.resolved', 'R'),
4615 mergemod.MERGE_RECORD_RESOLVED_PATH: ('resolve.resolved', 'R'),
4616 mergemod.MERGE_RECORD_DRIVER_RESOLVED: ('resolve.driverresolved',
4616 mergemod.MERGE_RECORD_DRIVER_RESOLVED: ('resolve.driverresolved',
4617 'D'),
4617 'D'),
4618 }
4618 }
4619
4619
4620 for f in ms:
4620 for f in ms:
4621 if not m(f):
4621 if not m(f):
4622 continue
4622 continue
4623
4623
4624 label, key = mergestateinfo[ms[f]]
4624 label, key = mergestateinfo[ms[f]]
4625 fm.startitem()
4625 fm.startitem()
4626 fm.context(ctx=wctx)
4626 fm.context(ctx=wctx)
4627 fm.condwrite(not nostatus, 'status', '%s ', key, label=label)
4627 fm.condwrite(not nostatus, 'status', '%s ', key, label=label)
4628 fm.write('path', '%s\n', f, label=label)
4628 fm.write('path', '%s\n', f, label=label)
4629 fm.end()
4629 fm.end()
4630 return 0
4630 return 0
4631
4631
4632 with repo.wlock():
4632 with repo.wlock():
4633 ms = mergemod.mergestate.read(repo)
4633 ms = mergemod.mergestate.read(repo)
4634
4634
4635 if not (ms.active() or repo.dirstate.p2() != nullid):
4635 if not (ms.active() or repo.dirstate.p2() != nullid):
4636 raise error.Abort(
4636 raise error.Abort(
4637 _('resolve command not applicable when not merging'))
4637 _('resolve command not applicable when not merging'))
4638
4638
4639 wctx = repo[None]
4639 wctx = repo[None]
4640
4640
4641 if (ms.mergedriver
4641 if (ms.mergedriver
4642 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED):
4642 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED):
4643 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4643 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4644 ms.commit()
4644 ms.commit()
4645 # allow mark and unmark to go through
4645 # allow mark and unmark to go through
4646 if not mark and not unmark and not proceed:
4646 if not mark and not unmark and not proceed:
4647 return 1
4647 return 1
4648
4648
4649 m = scmutil.match(wctx, pats, opts)
4649 m = scmutil.match(wctx, pats, opts)
4650 ret = 0
4650 ret = 0
4651 didwork = False
4651 didwork = False
4652 runconclude = False
4652 runconclude = False
4653
4653
4654 tocomplete = []
4654 tocomplete = []
4655 hasconflictmarkers = []
4655 hasconflictmarkers = []
4656 if mark:
4656 if mark:
4657 markcheck = ui.config('commands', 'resolve.mark-check')
4657 markcheck = ui.config('commands', 'resolve.mark-check')
4658 if markcheck not in ['warn', 'abort']:
4658 if markcheck not in ['warn', 'abort']:
4659 # Treat all invalid / unrecognized values as 'none'.
4659 # Treat all invalid / unrecognized values as 'none'.
4660 markcheck = False
4660 markcheck = False
4661 for f in ms:
4661 for f in ms:
4662 if not m(f):
4662 if not m(f):
4663 continue
4663 continue
4664
4664
4665 didwork = True
4665 didwork = True
4666
4666
4667 # don't let driver-resolved files be marked, and run the conclude
4667 # don't let driver-resolved files be marked, and run the conclude
4668 # step if asked to resolve
4668 # step if asked to resolve
4669 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
4669 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
4670 exact = m.exact(f)
4670 exact = m.exact(f)
4671 if mark:
4671 if mark:
4672 if exact:
4672 if exact:
4673 ui.warn(_('not marking %s as it is driver-resolved\n')
4673 ui.warn(_('not marking %s as it is driver-resolved\n')
4674 % f)
4674 % f)
4675 elif unmark:
4675 elif unmark:
4676 if exact:
4676 if exact:
4677 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4677 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4678 % f)
4678 % f)
4679 else:
4679 else:
4680 runconclude = True
4680 runconclude = True
4681 continue
4681 continue
4682
4682
4683 # path conflicts must be resolved manually
4683 # path conflicts must be resolved manually
4684 if ms[f] in (mergemod.MERGE_RECORD_UNRESOLVED_PATH,
4684 if ms[f] in (mergemod.MERGE_RECORD_UNRESOLVED_PATH,
4685 mergemod.MERGE_RECORD_RESOLVED_PATH):
4685 mergemod.MERGE_RECORD_RESOLVED_PATH):
4686 if mark:
4686 if mark:
4687 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
4687 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
4688 elif unmark:
4688 elif unmark:
4689 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
4689 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
4690 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
4690 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
4691 ui.warn(_('%s: path conflict must be resolved manually\n')
4691 ui.warn(_('%s: path conflict must be resolved manually\n')
4692 % f)
4692 % f)
4693 continue
4693 continue
4694
4694
4695 if mark:
4695 if mark:
4696 if markcheck:
4696 if markcheck:
4697 with repo.wvfs(f) as fobj:
4697 with repo.wvfs(f) as fobj:
4698 fdata = fobj.read()
4698 fdata = fobj.read()
4699 if filemerge.hasconflictmarkers(fdata) and \
4699 if filemerge.hasconflictmarkers(fdata) and \
4700 ms[f] != mergemod.MERGE_RECORD_RESOLVED:
4700 ms[f] != mergemod.MERGE_RECORD_RESOLVED:
4701 hasconflictmarkers.append(f)
4701 hasconflictmarkers.append(f)
4702 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
4702 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
4703 elif unmark:
4703 elif unmark:
4704 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
4704 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
4705 else:
4705 else:
4706 # backup pre-resolve (merge uses .orig for its own purposes)
4706 # backup pre-resolve (merge uses .orig for its own purposes)
4707 a = repo.wjoin(f)
4707 a = repo.wjoin(f)
4708 try:
4708 try:
4709 util.copyfile(a, a + ".resolve")
4709 util.copyfile(a, a + ".resolve")
4710 except (IOError, OSError) as inst:
4710 except (IOError, OSError) as inst:
4711 if inst.errno != errno.ENOENT:
4711 if inst.errno != errno.ENOENT:
4712 raise
4712 raise
4713
4713
4714 try:
4714 try:
4715 # preresolve file
4715 # preresolve file
4716 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4716 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4717 with ui.configoverride(overrides, 'resolve'):
4717 with ui.configoverride(overrides, 'resolve'):
4718 complete, r = ms.preresolve(f, wctx)
4718 complete, r = ms.preresolve(f, wctx)
4719 if not complete:
4719 if not complete:
4720 tocomplete.append(f)
4720 tocomplete.append(f)
4721 elif r:
4721 elif r:
4722 ret = 1
4722 ret = 1
4723 finally:
4723 finally:
4724 ms.commit()
4724 ms.commit()
4725
4725
4726 # replace filemerge's .orig file with our resolve file, but only
4726 # replace filemerge's .orig file with our resolve file, but only
4727 # for merges that are complete
4727 # for merges that are complete
4728 if complete:
4728 if complete:
4729 try:
4729 try:
4730 util.rename(a + ".resolve",
4730 util.rename(a + ".resolve",
4731 scmutil.origpath(ui, repo, a))
4731 scmutil.origpath(ui, repo, a))
4732 except OSError as inst:
4732 except OSError as inst:
4733 if inst.errno != errno.ENOENT:
4733 if inst.errno != errno.ENOENT:
4734 raise
4734 raise
4735
4735
4736 if hasconflictmarkers:
4736 if hasconflictmarkers:
4737 ui.warn(_('warning: the following files still have conflict '
4737 ui.warn(_('warning: the following files still have conflict '
4738 'markers:\n ') + '\n '.join(hasconflictmarkers) + '\n')
4738 'markers:\n ') + '\n '.join(hasconflictmarkers) + '\n')
4739 if markcheck == 'abort' and not all:
4739 if markcheck == 'abort' and not all:
4740 raise error.Abort(_('conflict markers detected'),
4740 raise error.Abort(_('conflict markers detected'),
4741 hint=_('use --all to mark anyway'))
4741 hint=_('use --all to mark anyway'))
4742
4742
4743 for f in tocomplete:
4743 for f in tocomplete:
4744 try:
4744 try:
4745 # resolve file
4745 # resolve file
4746 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4746 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4747 with ui.configoverride(overrides, 'resolve'):
4747 with ui.configoverride(overrides, 'resolve'):
4748 r = ms.resolve(f, wctx)
4748 r = ms.resolve(f, wctx)
4749 if r:
4749 if r:
4750 ret = 1
4750 ret = 1
4751 finally:
4751 finally:
4752 ms.commit()
4752 ms.commit()
4753
4753
4754 # replace filemerge's .orig file with our resolve file
4754 # replace filemerge's .orig file with our resolve file
4755 a = repo.wjoin(f)
4755 a = repo.wjoin(f)
4756 try:
4756 try:
4757 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4757 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4758 except OSError as inst:
4758 except OSError as inst:
4759 if inst.errno != errno.ENOENT:
4759 if inst.errno != errno.ENOENT:
4760 raise
4760 raise
4761
4761
4762 ms.commit()
4762 ms.commit()
4763 ms.recordactions()
4763 ms.recordactions()
4764
4764
4765 if not didwork and pats:
4765 if not didwork and pats:
4766 hint = None
4766 hint = None
4767 if not any([p for p in pats if p.find(':') >= 0]):
4767 if not any([p for p in pats if p.find(':') >= 0]):
4768 pats = ['path:%s' % p for p in pats]
4768 pats = ['path:%s' % p for p in pats]
4769 m = scmutil.match(wctx, pats, opts)
4769 m = scmutil.match(wctx, pats, opts)
4770 for f in ms:
4770 for f in ms:
4771 if not m(f):
4771 if not m(f):
4772 continue
4772 continue
4773 def flag(o):
4773 def flag(o):
4774 if o == 're_merge':
4774 if o == 're_merge':
4775 return '--re-merge '
4775 return '--re-merge '
4776 return '-%s ' % o[0:1]
4776 return '-%s ' % o[0:1]
4777 flags = ''.join([flag(o) for o in flaglist if opts.get(o)])
4777 flags = ''.join([flag(o) for o in flaglist if opts.get(o)])
4778 hint = _("(try: hg resolve %s%s)\n") % (
4778 hint = _("(try: hg resolve %s%s)\n") % (
4779 flags,
4779 flags,
4780 ' '.join(pats))
4780 ' '.join(pats))
4781 break
4781 break
4782 ui.warn(_("arguments do not match paths that need resolving\n"))
4782 ui.warn(_("arguments do not match paths that need resolving\n"))
4783 if hint:
4783 if hint:
4784 ui.warn(hint)
4784 ui.warn(hint)
4785 elif ms.mergedriver and ms.mdstate() != 's':
4785 elif ms.mergedriver and ms.mdstate() != 's':
4786 # run conclude step when either a driver-resolved file is requested
4786 # run conclude step when either a driver-resolved file is requested
4787 # or there are no driver-resolved files
4787 # or there are no driver-resolved files
4788 # we can't use 'ret' to determine whether any files are unresolved
4788 # we can't use 'ret' to determine whether any files are unresolved
4789 # because we might not have tried to resolve some
4789 # because we might not have tried to resolve some
4790 if ((runconclude or not list(ms.driverresolved()))
4790 if ((runconclude or not list(ms.driverresolved()))
4791 and not list(ms.unresolved())):
4791 and not list(ms.unresolved())):
4792 proceed = mergemod.driverconclude(repo, ms, wctx)
4792 proceed = mergemod.driverconclude(repo, ms, wctx)
4793 ms.commit()
4793 ms.commit()
4794 if not proceed:
4794 if not proceed:
4795 return 1
4795 return 1
4796
4796
4797 # Nudge users into finishing an unfinished operation
4797 # Nudge users into finishing an unfinished operation
4798 unresolvedf = list(ms.unresolved())
4798 unresolvedf = list(ms.unresolved())
4799 driverresolvedf = list(ms.driverresolved())
4799 driverresolvedf = list(ms.driverresolved())
4800 if not unresolvedf and not driverresolvedf:
4800 if not unresolvedf and not driverresolvedf:
4801 ui.status(_('(no more unresolved files)\n'))
4801 ui.status(_('(no more unresolved files)\n'))
4802 cmdutil.checkafterresolved(repo)
4802 cmdutil.checkafterresolved(repo)
4803 elif not unresolvedf:
4803 elif not unresolvedf:
4804 ui.status(_('(no more unresolved files -- '
4804 ui.status(_('(no more unresolved files -- '
4805 'run "hg resolve --all" to conclude)\n'))
4805 'run "hg resolve --all" to conclude)\n'))
4806
4806
4807 return ret
4807 return ret
4808
4808
4809 @command('revert',
4809 @command('revert',
4810 [('a', 'all', None, _('revert all changes when no arguments given')),
4810 [('a', 'all', None, _('revert all changes when no arguments given')),
4811 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4811 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4812 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4812 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4813 ('C', 'no-backup', None, _('do not save backup copies of files')),
4813 ('C', 'no-backup', None, _('do not save backup copies of files')),
4814 ('i', 'interactive', None, _('interactively select the changes')),
4814 ('i', 'interactive', None, _('interactively select the changes')),
4815 ] + walkopts + dryrunopts,
4815 ] + walkopts + dryrunopts,
4816 _('[OPTION]... [-r REV] [NAME]...'))
4816 _('[OPTION]... [-r REV] [NAME]...'))
4817 def revert(ui, repo, *pats, **opts):
4817 def revert(ui, repo, *pats, **opts):
4818 """restore files to their checkout state
4818 """restore files to their checkout state
4819
4819
4820 .. note::
4820 .. note::
4821
4821
4822 To check out earlier revisions, you should use :hg:`update REV`.
4822 To check out earlier revisions, you should use :hg:`update REV`.
4823 To cancel an uncommitted merge (and lose your changes),
4823 To cancel an uncommitted merge (and lose your changes),
4824 use :hg:`merge --abort`.
4824 use :hg:`merge --abort`.
4825
4825
4826 With no revision specified, revert the specified files or directories
4826 With no revision specified, revert the specified files or directories
4827 to the contents they had in the parent of the working directory.
4827 to the contents they had in the parent of the working directory.
4828 This restores the contents of files to an unmodified
4828 This restores the contents of files to an unmodified
4829 state and unschedules adds, removes, copies, and renames. If the
4829 state and unschedules adds, removes, copies, and renames. If the
4830 working directory has two parents, you must explicitly specify a
4830 working directory has two parents, you must explicitly specify a
4831 revision.
4831 revision.
4832
4832
4833 Using the -r/--rev or -d/--date options, revert the given files or
4833 Using the -r/--rev or -d/--date options, revert the given files or
4834 directories to their states as of a specific revision. Because
4834 directories to their states as of a specific revision. Because
4835 revert does not change the working directory parents, this will
4835 revert does not change the working directory parents, this will
4836 cause these files to appear modified. This can be helpful to "back
4836 cause these files to appear modified. This can be helpful to "back
4837 out" some or all of an earlier change. See :hg:`backout` for a
4837 out" some or all of an earlier change. See :hg:`backout` for a
4838 related method.
4838 related method.
4839
4839
4840 Modified files are saved with a .orig suffix before reverting.
4840 Modified files are saved with a .orig suffix before reverting.
4841 To disable these backups, use --no-backup. It is possible to store
4841 To disable these backups, use --no-backup. It is possible to store
4842 the backup files in a custom directory relative to the root of the
4842 the backup files in a custom directory relative to the root of the
4843 repository by setting the ``ui.origbackuppath`` configuration
4843 repository by setting the ``ui.origbackuppath`` configuration
4844 option.
4844 option.
4845
4845
4846 See :hg:`help dates` for a list of formats valid for -d/--date.
4846 See :hg:`help dates` for a list of formats valid for -d/--date.
4847
4847
4848 See :hg:`help backout` for a way to reverse the effect of an
4848 See :hg:`help backout` for a way to reverse the effect of an
4849 earlier changeset.
4849 earlier changeset.
4850
4850
4851 Returns 0 on success.
4851 Returns 0 on success.
4852 """
4852 """
4853
4853
4854 opts = pycompat.byteskwargs(opts)
4854 opts = pycompat.byteskwargs(opts)
4855 if opts.get("date"):
4855 if opts.get("date"):
4856 if opts.get("rev"):
4856 if opts.get("rev"):
4857 raise error.Abort(_("you can't specify a revision and a date"))
4857 raise error.Abort(_("you can't specify a revision and a date"))
4858 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4858 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4859
4859
4860 parent, p2 = repo.dirstate.parents()
4860 parent, p2 = repo.dirstate.parents()
4861 if not opts.get('rev') and p2 != nullid:
4861 if not opts.get('rev') and p2 != nullid:
4862 # revert after merge is a trap for new users (issue2915)
4862 # revert after merge is a trap for new users (issue2915)
4863 raise error.Abort(_('uncommitted merge with no revision specified'),
4863 raise error.Abort(_('uncommitted merge with no revision specified'),
4864 hint=_("use 'hg update' or see 'hg help revert'"))
4864 hint=_("use 'hg update' or see 'hg help revert'"))
4865
4865
4866 rev = opts.get('rev')
4866 rev = opts.get('rev')
4867 if rev:
4867 if rev:
4868 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
4868 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
4869 ctx = scmutil.revsingle(repo, rev)
4869 ctx = scmutil.revsingle(repo, rev)
4870
4870
4871 if (not (pats or opts.get('include') or opts.get('exclude') or
4871 if (not (pats or opts.get('include') or opts.get('exclude') or
4872 opts.get('all') or opts.get('interactive'))):
4872 opts.get('all') or opts.get('interactive'))):
4873 msg = _("no files or directories specified")
4873 msg = _("no files or directories specified")
4874 if p2 != nullid:
4874 if p2 != nullid:
4875 hint = _("uncommitted merge, use --all to discard all changes,"
4875 hint = _("uncommitted merge, use --all to discard all changes,"
4876 " or 'hg update -C .' to abort the merge")
4876 " or 'hg update -C .' to abort the merge")
4877 raise error.Abort(msg, hint=hint)
4877 raise error.Abort(msg, hint=hint)
4878 dirty = any(repo.status())
4878 dirty = any(repo.status())
4879 node = ctx.node()
4879 node = ctx.node()
4880 if node != parent:
4880 if node != parent:
4881 if dirty:
4881 if dirty:
4882 hint = _("uncommitted changes, use --all to discard all"
4882 hint = _("uncommitted changes, use --all to discard all"
4883 " changes, or 'hg update %d' to update") % ctx.rev()
4883 " changes, or 'hg update %d' to update") % ctx.rev()
4884 else:
4884 else:
4885 hint = _("use --all to revert all files,"
4885 hint = _("use --all to revert all files,"
4886 " or 'hg update %d' to update") % ctx.rev()
4886 " or 'hg update %d' to update") % ctx.rev()
4887 elif dirty:
4887 elif dirty:
4888 hint = _("uncommitted changes, use --all to discard all changes")
4888 hint = _("uncommitted changes, use --all to discard all changes")
4889 else:
4889 else:
4890 hint = _("use --all to revert all files")
4890 hint = _("use --all to revert all files")
4891 raise error.Abort(msg, hint=hint)
4891 raise error.Abort(msg, hint=hint)
4892
4892
4893 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats,
4893 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats,
4894 **pycompat.strkwargs(opts))
4894 **pycompat.strkwargs(opts))
4895
4895
4896 @command('rollback', dryrunopts +
4896 @command('rollback', dryrunopts +
4897 [('f', 'force', False, _('ignore safety measures'))])
4897 [('f', 'force', False, _('ignore safety measures'))])
4898 def rollback(ui, repo, **opts):
4898 def rollback(ui, repo, **opts):
4899 """roll back the last transaction (DANGEROUS) (DEPRECATED)
4899 """roll back the last transaction (DANGEROUS) (DEPRECATED)
4900
4900
4901 Please use :hg:`commit --amend` instead of rollback to correct
4901 Please use :hg:`commit --amend` instead of rollback to correct
4902 mistakes in the last commit.
4902 mistakes in the last commit.
4903
4903
4904 This command should be used with care. There is only one level of
4904 This command should be used with care. There is only one level of
4905 rollback, and there is no way to undo a rollback. It will also
4905 rollback, and there is no way to undo a rollback. It will also
4906 restore the dirstate at the time of the last transaction, losing
4906 restore the dirstate at the time of the last transaction, losing
4907 any dirstate changes since that time. This command does not alter
4907 any dirstate changes since that time. This command does not alter
4908 the working directory.
4908 the working directory.
4909
4909
4910 Transactions are used to encapsulate the effects of all commands
4910 Transactions are used to encapsulate the effects of all commands
4911 that create new changesets or propagate existing changesets into a
4911 that create new changesets or propagate existing changesets into a
4912 repository.
4912 repository.
4913
4913
4914 .. container:: verbose
4914 .. container:: verbose
4915
4915
4916 For example, the following commands are transactional, and their
4916 For example, the following commands are transactional, and their
4917 effects can be rolled back:
4917 effects can be rolled back:
4918
4918
4919 - commit
4919 - commit
4920 - import
4920 - import
4921 - pull
4921 - pull
4922 - push (with this repository as the destination)
4922 - push (with this repository as the destination)
4923 - unbundle
4923 - unbundle
4924
4924
4925 To avoid permanent data loss, rollback will refuse to rollback a
4925 To avoid permanent data loss, rollback will refuse to rollback a
4926 commit transaction if it isn't checked out. Use --force to
4926 commit transaction if it isn't checked out. Use --force to
4927 override this protection.
4927 override this protection.
4928
4928
4929 The rollback command can be entirely disabled by setting the
4929 The rollback command can be entirely disabled by setting the
4930 ``ui.rollback`` configuration setting to false. If you're here
4930 ``ui.rollback`` configuration setting to false. If you're here
4931 because you want to use rollback and it's disabled, you can
4931 because you want to use rollback and it's disabled, you can
4932 re-enable the command by setting ``ui.rollback`` to true.
4932 re-enable the command by setting ``ui.rollback`` to true.
4933
4933
4934 This command is not intended for use on public repositories. Once
4934 This command is not intended for use on public repositories. Once
4935 changes are visible for pull by other users, rolling a transaction
4935 changes are visible for pull by other users, rolling a transaction
4936 back locally is ineffective (someone else may already have pulled
4936 back locally is ineffective (someone else may already have pulled
4937 the changes). Furthermore, a race is possible with readers of the
4937 the changes). Furthermore, a race is possible with readers of the
4938 repository; for example an in-progress pull from the repository
4938 repository; for example an in-progress pull from the repository
4939 may fail if a rollback is performed.
4939 may fail if a rollback is performed.
4940
4940
4941 Returns 0 on success, 1 if no rollback data is available.
4941 Returns 0 on success, 1 if no rollback data is available.
4942 """
4942 """
4943 if not ui.configbool('ui', 'rollback'):
4943 if not ui.configbool('ui', 'rollback'):
4944 raise error.Abort(_('rollback is disabled because it is unsafe'),
4944 raise error.Abort(_('rollback is disabled because it is unsafe'),
4945 hint=('see `hg help -v rollback` for information'))
4945 hint=('see `hg help -v rollback` for information'))
4946 return repo.rollback(dryrun=opts.get(r'dry_run'),
4946 return repo.rollback(dryrun=opts.get(r'dry_run'),
4947 force=opts.get(r'force'))
4947 force=opts.get(r'force'))
4948
4948
4949 @command('root', [], intents={INTENT_READONLY})
4949 @command('root', [], intents={INTENT_READONLY})
4950 def root(ui, repo):
4950 def root(ui, repo):
4951 """print the root (top) of the current working directory
4951 """print the root (top) of the current working directory
4952
4952
4953 Print the root directory of the current repository.
4953 Print the root directory of the current repository.
4954
4954
4955 Returns 0 on success.
4955 Returns 0 on success.
4956 """
4956 """
4957 ui.write(repo.root + "\n")
4957 ui.write(repo.root + "\n")
4958
4958
4959 @command('^serve',
4959 @command('^serve',
4960 [('A', 'accesslog', '', _('name of access log file to write to'),
4960 [('A', 'accesslog', '', _('name of access log file to write to'),
4961 _('FILE')),
4961 _('FILE')),
4962 ('d', 'daemon', None, _('run server in background')),
4962 ('d', 'daemon', None, _('run server in background')),
4963 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
4963 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
4964 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4964 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4965 # use string type, then we can check if something was passed
4965 # use string type, then we can check if something was passed
4966 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4966 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4967 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4967 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4968 _('ADDR')),
4968 _('ADDR')),
4969 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4969 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4970 _('PREFIX')),
4970 _('PREFIX')),
4971 ('n', 'name', '',
4971 ('n', 'name', '',
4972 _('name to show in web pages (default: working directory)'), _('NAME')),
4972 _('name to show in web pages (default: working directory)'), _('NAME')),
4973 ('', 'web-conf', '',
4973 ('', 'web-conf', '',
4974 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
4974 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
4975 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4975 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4976 _('FILE')),
4976 _('FILE')),
4977 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4977 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4978 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
4978 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
4979 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
4979 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
4980 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4980 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4981 ('', 'style', '', _('template style to use'), _('STYLE')),
4981 ('', 'style', '', _('template style to use'), _('STYLE')),
4982 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4982 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4983 ('', 'certificate', '', _('SSL certificate file'), _('FILE')),
4983 ('', 'certificate', '', _('SSL certificate file'), _('FILE')),
4984 ('', 'print-url', None, _('start and print only the URL'))]
4984 ('', 'print-url', None, _('start and print only the URL'))]
4985 + subrepoopts,
4985 + subrepoopts,
4986 _('[OPTION]...'),
4986 _('[OPTION]...'),
4987 optionalrepo=True)
4987 optionalrepo=True)
4988 def serve(ui, repo, **opts):
4988 def serve(ui, repo, **opts):
4989 """start stand-alone webserver
4989 """start stand-alone webserver
4990
4990
4991 Start a local HTTP repository browser and pull server. You can use
4991 Start a local HTTP repository browser and pull server. You can use
4992 this for ad-hoc sharing and browsing of repositories. It is
4992 this for ad-hoc sharing and browsing of repositories. It is
4993 recommended to use a real web server to serve a repository for
4993 recommended to use a real web server to serve a repository for
4994 longer periods of time.
4994 longer periods of time.
4995
4995
4996 Please note that the server does not implement access control.
4996 Please note that the server does not implement access control.
4997 This means that, by default, anybody can read from the server and
4997 This means that, by default, anybody can read from the server and
4998 nobody can write to it by default. Set the ``web.allow-push``
4998 nobody can write to it by default. Set the ``web.allow-push``
4999 option to ``*`` to allow everybody to push to the server. You
4999 option to ``*`` to allow everybody to push to the server. You
5000 should use a real web server if you need to authenticate users.
5000 should use a real web server if you need to authenticate users.
5001
5001
5002 By default, the server logs accesses to stdout and errors to
5002 By default, the server logs accesses to stdout and errors to
5003 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5003 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5004 files.
5004 files.
5005
5005
5006 To have the server choose a free port number to listen on, specify
5006 To have the server choose a free port number to listen on, specify
5007 a port number of 0; in this case, the server will print the port
5007 a port number of 0; in this case, the server will print the port
5008 number it uses.
5008 number it uses.
5009
5009
5010 Returns 0 on success.
5010 Returns 0 on success.
5011 """
5011 """
5012
5012
5013 opts = pycompat.byteskwargs(opts)
5013 opts = pycompat.byteskwargs(opts)
5014 if opts["stdio"] and opts["cmdserver"]:
5014 if opts["stdio"] and opts["cmdserver"]:
5015 raise error.Abort(_("cannot use --stdio with --cmdserver"))
5015 raise error.Abort(_("cannot use --stdio with --cmdserver"))
5016 if opts["print_url"] and ui.verbose:
5016 if opts["print_url"] and ui.verbose:
5017 raise error.Abort(_("cannot use --print-url with --verbose"))
5017 raise error.Abort(_("cannot use --print-url with --verbose"))
5018
5018
5019 if opts["stdio"]:
5019 if opts["stdio"]:
5020 if repo is None:
5020 if repo is None:
5021 raise error.RepoError(_("there is no Mercurial repository here"
5021 raise error.RepoError(_("there is no Mercurial repository here"
5022 " (.hg not found)"))
5022 " (.hg not found)"))
5023 s = wireprotoserver.sshserver(ui, repo)
5023 s = wireprotoserver.sshserver(ui, repo)
5024 s.serve_forever()
5024 s.serve_forever()
5025
5025
5026 service = server.createservice(ui, repo, opts)
5026 service = server.createservice(ui, repo, opts)
5027 return server.runservice(opts, initfn=service.init, runfn=service.run)
5027 return server.runservice(opts, initfn=service.init, runfn=service.run)
5028
5028
5029 _NOTTERSE = 'nothing'
5029 _NOTTERSE = 'nothing'
5030
5030
5031 @command('^status|st',
5031 @command('^status|st',
5032 [('A', 'all', None, _('show status of all files')),
5032 [('A', 'all', None, _('show status of all files')),
5033 ('m', 'modified', None, _('show only modified files')),
5033 ('m', 'modified', None, _('show only modified files')),
5034 ('a', 'added', None, _('show only added files')),
5034 ('a', 'added', None, _('show only added files')),
5035 ('r', 'removed', None, _('show only removed files')),
5035 ('r', 'removed', None, _('show only removed files')),
5036 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5036 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5037 ('c', 'clean', None, _('show only files without changes')),
5037 ('c', 'clean', None, _('show only files without changes')),
5038 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5038 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5039 ('i', 'ignored', None, _('show only ignored files')),
5039 ('i', 'ignored', None, _('show only ignored files')),
5040 ('n', 'no-status', None, _('hide status prefix')),
5040 ('n', 'no-status', None, _('hide status prefix')),
5041 ('t', 'terse', _NOTTERSE, _('show the terse output (EXPERIMENTAL)')),
5041 ('t', 'terse', _NOTTERSE, _('show the terse output (EXPERIMENTAL)')),
5042 ('C', 'copies', None, _('show source of copied files')),
5042 ('C', 'copies', None, _('show source of copied files')),
5043 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5043 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5044 ('', 'rev', [], _('show difference from revision'), _('REV')),
5044 ('', 'rev', [], _('show difference from revision'), _('REV')),
5045 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5045 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5046 ] + walkopts + subrepoopts + formatteropts,
5046 ] + walkopts + subrepoopts + formatteropts,
5047 _('[OPTION]... [FILE]...'),
5047 _('[OPTION]... [FILE]...'),
5048 inferrepo=True,
5048 inferrepo=True,
5049 intents={INTENT_READONLY})
5049 intents={INTENT_READONLY})
5050 def status(ui, repo, *pats, **opts):
5050 def status(ui, repo, *pats, **opts):
5051 """show changed files in the working directory
5051 """show changed files in the working directory
5052
5052
5053 Show status of files in the repository. If names are given, only
5053 Show status of files in the repository. If names are given, only
5054 files that match are shown. Files that are clean or ignored or
5054 files that match are shown. Files that are clean or ignored or
5055 the source of a copy/move operation, are not listed unless
5055 the source of a copy/move operation, are not listed unless
5056 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5056 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5057 Unless options described with "show only ..." are given, the
5057 Unless options described with "show only ..." are given, the
5058 options -mardu are used.
5058 options -mardu are used.
5059
5059
5060 Option -q/--quiet hides untracked (unknown and ignored) files
5060 Option -q/--quiet hides untracked (unknown and ignored) files
5061 unless explicitly requested with -u/--unknown or -i/--ignored.
5061 unless explicitly requested with -u/--unknown or -i/--ignored.
5062
5062
5063 .. note::
5063 .. note::
5064
5064
5065 :hg:`status` may appear to disagree with diff if permissions have
5065 :hg:`status` may appear to disagree with diff if permissions have
5066 changed or a merge has occurred. The standard diff format does
5066 changed or a merge has occurred. The standard diff format does
5067 not report permission changes and diff only reports changes
5067 not report permission changes and diff only reports changes
5068 relative to one merge parent.
5068 relative to one merge parent.
5069
5069
5070 If one revision is given, it is used as the base revision.
5070 If one revision is given, it is used as the base revision.
5071 If two revisions are given, the differences between them are
5071 If two revisions are given, the differences between them are
5072 shown. The --change option can also be used as a shortcut to list
5072 shown. The --change option can also be used as a shortcut to list
5073 the changed files of a revision from its first parent.
5073 the changed files of a revision from its first parent.
5074
5074
5075 The codes used to show the status of files are::
5075 The codes used to show the status of files are::
5076
5076
5077 M = modified
5077 M = modified
5078 A = added
5078 A = added
5079 R = removed
5079 R = removed
5080 C = clean
5080 C = clean
5081 ! = missing (deleted by non-hg command, but still tracked)
5081 ! = missing (deleted by non-hg command, but still tracked)
5082 ? = not tracked
5082 ? = not tracked
5083 I = ignored
5083 I = ignored
5084 = origin of the previous file (with --copies)
5084 = origin of the previous file (with --copies)
5085
5085
5086 .. container:: verbose
5086 .. container:: verbose
5087
5087
5088 The -t/--terse option abbreviates the output by showing only the directory
5088 The -t/--terse option abbreviates the output by showing only the directory
5089 name if all the files in it share the same status. The option takes an
5089 name if all the files in it share the same status. The option takes an
5090 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
5090 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
5091 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
5091 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
5092 for 'ignored' and 'c' for clean.
5092 for 'ignored' and 'c' for clean.
5093
5093
5094 It abbreviates only those statuses which are passed. Note that clean and
5094 It abbreviates only those statuses which are passed. Note that clean and
5095 ignored files are not displayed with '--terse ic' unless the -c/--clean
5095 ignored files are not displayed with '--terse ic' unless the -c/--clean
5096 and -i/--ignored options are also used.
5096 and -i/--ignored options are also used.
5097
5097
5098 The -v/--verbose option shows information when the repository is in an
5098 The -v/--verbose option shows information when the repository is in an
5099 unfinished merge, shelve, rebase state etc. You can have this behavior
5099 unfinished merge, shelve, rebase state etc. You can have this behavior
5100 turned on by default by enabling the ``commands.status.verbose`` option.
5100 turned on by default by enabling the ``commands.status.verbose`` option.
5101
5101
5102 You can skip displaying some of these states by setting
5102 You can skip displaying some of these states by setting
5103 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
5103 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
5104 'histedit', 'merge', 'rebase', or 'unshelve'.
5104 'histedit', 'merge', 'rebase', or 'unshelve'.
5105
5105
5106 Examples:
5106 Examples:
5107
5107
5108 - show changes in the working directory relative to a
5108 - show changes in the working directory relative to a
5109 changeset::
5109 changeset::
5110
5110
5111 hg status --rev 9353
5111 hg status --rev 9353
5112
5112
5113 - show changes in the working directory relative to the
5113 - show changes in the working directory relative to the
5114 current directory (see :hg:`help patterns` for more information)::
5114 current directory (see :hg:`help patterns` for more information)::
5115
5115
5116 hg status re:
5116 hg status re:
5117
5117
5118 - show all changes including copies in an existing changeset::
5118 - show all changes including copies in an existing changeset::
5119
5119
5120 hg status --copies --change 9353
5120 hg status --copies --change 9353
5121
5121
5122 - get a NUL separated list of added files, suitable for xargs::
5122 - get a NUL separated list of added files, suitable for xargs::
5123
5123
5124 hg status -an0
5124 hg status -an0
5125
5125
5126 - show more information about the repository status, abbreviating
5126 - show more information about the repository status, abbreviating
5127 added, removed, modified, deleted, and untracked paths::
5127 added, removed, modified, deleted, and untracked paths::
5128
5128
5129 hg status -v -t mardu
5129 hg status -v -t mardu
5130
5130
5131 Returns 0 on success.
5131 Returns 0 on success.
5132
5132
5133 """
5133 """
5134
5134
5135 opts = pycompat.byteskwargs(opts)
5135 opts = pycompat.byteskwargs(opts)
5136 revs = opts.get('rev')
5136 revs = opts.get('rev')
5137 change = opts.get('change')
5137 change = opts.get('change')
5138 terse = opts.get('terse')
5138 terse = opts.get('terse')
5139 if terse is _NOTTERSE:
5139 if terse is _NOTTERSE:
5140 if revs:
5140 if revs:
5141 terse = ''
5141 terse = ''
5142 else:
5142 else:
5143 terse = ui.config('commands', 'status.terse')
5143 terse = ui.config('commands', 'status.terse')
5144
5144
5145 if revs and change:
5145 if revs and change:
5146 msg = _('cannot specify --rev and --change at the same time')
5146 msg = _('cannot specify --rev and --change at the same time')
5147 raise error.Abort(msg)
5147 raise error.Abort(msg)
5148 elif revs and terse:
5148 elif revs and terse:
5149 msg = _('cannot use --terse with --rev')
5149 msg = _('cannot use --terse with --rev')
5150 raise error.Abort(msg)
5150 raise error.Abort(msg)
5151 elif change:
5151 elif change:
5152 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
5152 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
5153 ctx2 = scmutil.revsingle(repo, change, None)
5153 ctx2 = scmutil.revsingle(repo, change, None)
5154 ctx1 = ctx2.p1()
5154 ctx1 = ctx2.p1()
5155 else:
5155 else:
5156 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
5156 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
5157 ctx1, ctx2 = scmutil.revpair(repo, revs)
5157 ctx1, ctx2 = scmutil.revpair(repo, revs)
5158
5158
5159 if pats or ui.configbool('commands', 'status.relative'):
5159 if pats or ui.configbool('commands', 'status.relative'):
5160 cwd = repo.getcwd()
5160 cwd = repo.getcwd()
5161 else:
5161 else:
5162 cwd = ''
5162 cwd = ''
5163
5163
5164 if opts.get('print0'):
5164 if opts.get('print0'):
5165 end = '\0'
5165 end = '\0'
5166 else:
5166 else:
5167 end = '\n'
5167 end = '\n'
5168 copy = {}
5168 copy = {}
5169 states = 'modified added removed deleted unknown ignored clean'.split()
5169 states = 'modified added removed deleted unknown ignored clean'.split()
5170 show = [k for k in states if opts.get(k)]
5170 show = [k for k in states if opts.get(k)]
5171 if opts.get('all'):
5171 if opts.get('all'):
5172 show += ui.quiet and (states[:4] + ['clean']) or states
5172 show += ui.quiet and (states[:4] + ['clean']) or states
5173
5173
5174 if not show:
5174 if not show:
5175 if ui.quiet:
5175 if ui.quiet:
5176 show = states[:4]
5176 show = states[:4]
5177 else:
5177 else:
5178 show = states[:5]
5178 show = states[:5]
5179
5179
5180 m = scmutil.match(ctx2, pats, opts)
5180 m = scmutil.match(ctx2, pats, opts)
5181 if terse:
5181 if terse:
5182 # we need to compute clean and unknown to terse
5182 # we need to compute clean and unknown to terse
5183 stat = repo.status(ctx1.node(), ctx2.node(), m,
5183 stat = repo.status(ctx1.node(), ctx2.node(), m,
5184 'ignored' in show or 'i' in terse,
5184 'ignored' in show or 'i' in terse,
5185 clean=True, unknown=True,
5185 clean=True, unknown=True,
5186 listsubrepos=opts.get('subrepos'))
5186 listsubrepos=opts.get('subrepos'))
5187
5187
5188 stat = cmdutil.tersedir(stat, terse)
5188 stat = cmdutil.tersedir(stat, terse)
5189 else:
5189 else:
5190 stat = repo.status(ctx1.node(), ctx2.node(), m,
5190 stat = repo.status(ctx1.node(), ctx2.node(), m,
5191 'ignored' in show, 'clean' in show,
5191 'ignored' in show, 'clean' in show,
5192 'unknown' in show, opts.get('subrepos'))
5192 'unknown' in show, opts.get('subrepos'))
5193
5193
5194 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
5194 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
5195
5195
5196 if (opts.get('all') or opts.get('copies')
5196 if (opts.get('all') or opts.get('copies')
5197 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5197 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5198 copy = copies.pathcopies(ctx1, ctx2, m)
5198 copy = copies.pathcopies(ctx1, ctx2, m)
5199
5199
5200 ui.pager('status')
5200 ui.pager('status')
5201 fm = ui.formatter('status', opts)
5201 fm = ui.formatter('status', opts)
5202 fmt = '%s' + end
5202 fmt = '%s' + end
5203 showchar = not opts.get('no_status')
5203 showchar = not opts.get('no_status')
5204
5204
5205 for state, char, files in changestates:
5205 for state, char, files in changestates:
5206 if state in show:
5206 if state in show:
5207 label = 'status.' + state
5207 label = 'status.' + state
5208 for f in files:
5208 for f in files:
5209 fm.startitem()
5209 fm.startitem()
5210 fm.context(ctx=ctx2)
5210 fm.context(ctx=ctx2)
5211 fm.data(path=f)
5211 fm.data(path=f)
5212 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5212 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5213 fm.plain(fmt % repo.pathto(f, cwd), label=label)
5213 fm.plain(fmt % repo.pathto(f, cwd), label=label)
5214 if f in copy:
5214 if f in copy:
5215 fm.data(source=copy[f])
5215 fm.data(source=copy[f])
5216 fm.plain((' %s' + end) % repo.pathto(copy[f], cwd),
5216 fm.plain((' %s' + end) % repo.pathto(copy[f], cwd),
5217 label='status.copied')
5217 label='status.copied')
5218
5218
5219 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
5219 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
5220 and not ui.plain()):
5220 and not ui.plain()):
5221 cmdutil.morestatus(repo, fm)
5221 cmdutil.morestatus(repo, fm)
5222 fm.end()
5222 fm.end()
5223
5223
5224 @command('^summary|sum',
5224 @command('^summary|sum',
5225 [('', 'remote', None, _('check for push and pull'))],
5225 [('', 'remote', None, _('check for push and pull'))],
5226 '[--remote]',
5226 '[--remote]',
5227 intents={INTENT_READONLY})
5227 intents={INTENT_READONLY})
5228 def summary(ui, repo, **opts):
5228 def summary(ui, repo, **opts):
5229 """summarize working directory state
5229 """summarize working directory state
5230
5230
5231 This generates a brief summary of the working directory state,
5231 This generates a brief summary of the working directory state,
5232 including parents, branch, commit status, phase and available updates.
5232 including parents, branch, commit status, phase and available updates.
5233
5233
5234 With the --remote option, this will check the default paths for
5234 With the --remote option, this will check the default paths for
5235 incoming and outgoing changes. This can be time-consuming.
5235 incoming and outgoing changes. This can be time-consuming.
5236
5236
5237 Returns 0 on success.
5237 Returns 0 on success.
5238 """
5238 """
5239
5239
5240 opts = pycompat.byteskwargs(opts)
5240 opts = pycompat.byteskwargs(opts)
5241 ui.pager('summary')
5241 ui.pager('summary')
5242 ctx = repo[None]
5242 ctx = repo[None]
5243 parents = ctx.parents()
5243 parents = ctx.parents()
5244 pnode = parents[0].node()
5244 pnode = parents[0].node()
5245 marks = []
5245 marks = []
5246
5246
5247 ms = None
5247 ms = None
5248 try:
5248 try:
5249 ms = mergemod.mergestate.read(repo)
5249 ms = mergemod.mergestate.read(repo)
5250 except error.UnsupportedMergeRecords as e:
5250 except error.UnsupportedMergeRecords as e:
5251 s = ' '.join(e.recordtypes)
5251 s = ' '.join(e.recordtypes)
5252 ui.warn(
5252 ui.warn(
5253 _('warning: merge state has unsupported record types: %s\n') % s)
5253 _('warning: merge state has unsupported record types: %s\n') % s)
5254 unresolved = []
5254 unresolved = []
5255 else:
5255 else:
5256 unresolved = list(ms.unresolved())
5256 unresolved = list(ms.unresolved())
5257
5257
5258 for p in parents:
5258 for p in parents:
5259 # label with log.changeset (instead of log.parent) since this
5259 # label with log.changeset (instead of log.parent) since this
5260 # shows a working directory parent *changeset*:
5260 # shows a working directory parent *changeset*:
5261 # i18n: column positioning for "hg summary"
5261 # i18n: column positioning for "hg summary"
5262 ui.write(_('parent: %d:%s ') % (p.rev(), p),
5262 ui.write(_('parent: %d:%s ') % (p.rev(), p),
5263 label=logcmdutil.changesetlabels(p))
5263 label=logcmdutil.changesetlabels(p))
5264 ui.write(' '.join(p.tags()), label='log.tag')
5264 ui.write(' '.join(p.tags()), label='log.tag')
5265 if p.bookmarks():
5265 if p.bookmarks():
5266 marks.extend(p.bookmarks())
5266 marks.extend(p.bookmarks())
5267 if p.rev() == -1:
5267 if p.rev() == -1:
5268 if not len(repo):
5268 if not len(repo):
5269 ui.write(_(' (empty repository)'))
5269 ui.write(_(' (empty repository)'))
5270 else:
5270 else:
5271 ui.write(_(' (no revision checked out)'))
5271 ui.write(_(' (no revision checked out)'))
5272 if p.obsolete():
5272 if p.obsolete():
5273 ui.write(_(' (obsolete)'))
5273 ui.write(_(' (obsolete)'))
5274 if p.isunstable():
5274 if p.isunstable():
5275 instabilities = (ui.label(instability, 'trouble.%s' % instability)
5275 instabilities = (ui.label(instability, 'trouble.%s' % instability)
5276 for instability in p.instabilities())
5276 for instability in p.instabilities())
5277 ui.write(' ('
5277 ui.write(' ('
5278 + ', '.join(instabilities)
5278 + ', '.join(instabilities)
5279 + ')')
5279 + ')')
5280 ui.write('\n')
5280 ui.write('\n')
5281 if p.description():
5281 if p.description():
5282 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5282 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5283 label='log.summary')
5283 label='log.summary')
5284
5284
5285 branch = ctx.branch()
5285 branch = ctx.branch()
5286 bheads = repo.branchheads(branch)
5286 bheads = repo.branchheads(branch)
5287 # i18n: column positioning for "hg summary"
5287 # i18n: column positioning for "hg summary"
5288 m = _('branch: %s\n') % branch
5288 m = _('branch: %s\n') % branch
5289 if branch != 'default':
5289 if branch != 'default':
5290 ui.write(m, label='log.branch')
5290 ui.write(m, label='log.branch')
5291 else:
5291 else:
5292 ui.status(m, label='log.branch')
5292 ui.status(m, label='log.branch')
5293
5293
5294 if marks:
5294 if marks:
5295 active = repo._activebookmark
5295 active = repo._activebookmark
5296 # i18n: column positioning for "hg summary"
5296 # i18n: column positioning for "hg summary"
5297 ui.write(_('bookmarks:'), label='log.bookmark')
5297 ui.write(_('bookmarks:'), label='log.bookmark')
5298 if active is not None:
5298 if active is not None:
5299 if active in marks:
5299 if active in marks:
5300 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
5300 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
5301 marks.remove(active)
5301 marks.remove(active)
5302 else:
5302 else:
5303 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
5303 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
5304 for m in marks:
5304 for m in marks:
5305 ui.write(' ' + m, label='log.bookmark')
5305 ui.write(' ' + m, label='log.bookmark')
5306 ui.write('\n', label='log.bookmark')
5306 ui.write('\n', label='log.bookmark')
5307
5307
5308 status = repo.status(unknown=True)
5308 status = repo.status(unknown=True)
5309
5309
5310 c = repo.dirstate.copies()
5310 c = repo.dirstate.copies()
5311 copied, renamed = [], []
5311 copied, renamed = [], []
5312 for d, s in c.iteritems():
5312 for d, s in c.iteritems():
5313 if s in status.removed:
5313 if s in status.removed:
5314 status.removed.remove(s)
5314 status.removed.remove(s)
5315 renamed.append(d)
5315 renamed.append(d)
5316 else:
5316 else:
5317 copied.append(d)
5317 copied.append(d)
5318 if d in status.added:
5318 if d in status.added:
5319 status.added.remove(d)
5319 status.added.remove(d)
5320
5320
5321 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5321 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5322
5322
5323 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5323 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5324 (ui.label(_('%d added'), 'status.added'), status.added),
5324 (ui.label(_('%d added'), 'status.added'), status.added),
5325 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5325 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5326 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5326 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5327 (ui.label(_('%d copied'), 'status.copied'), copied),
5327 (ui.label(_('%d copied'), 'status.copied'), copied),
5328 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5328 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5329 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5329 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5330 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5330 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5331 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5331 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5332 t = []
5332 t = []
5333 for l, s in labels:
5333 for l, s in labels:
5334 if s:
5334 if s:
5335 t.append(l % len(s))
5335 t.append(l % len(s))
5336
5336
5337 t = ', '.join(t)
5337 t = ', '.join(t)
5338 cleanworkdir = False
5338 cleanworkdir = False
5339
5339
5340 if repo.vfs.exists('graftstate'):
5340 if repo.vfs.exists('graftstate'):
5341 t += _(' (graft in progress)')
5341 t += _(' (graft in progress)')
5342 if repo.vfs.exists('updatestate'):
5342 if repo.vfs.exists('updatestate'):
5343 t += _(' (interrupted update)')
5343 t += _(' (interrupted update)')
5344 elif len(parents) > 1:
5344 elif len(parents) > 1:
5345 t += _(' (merge)')
5345 t += _(' (merge)')
5346 elif branch != parents[0].branch():
5346 elif branch != parents[0].branch():
5347 t += _(' (new branch)')
5347 t += _(' (new branch)')
5348 elif (parents[0].closesbranch() and
5348 elif (parents[0].closesbranch() and
5349 pnode in repo.branchheads(branch, closed=True)):
5349 pnode in repo.branchheads(branch, closed=True)):
5350 t += _(' (head closed)')
5350 t += _(' (head closed)')
5351 elif not (status.modified or status.added or status.removed or renamed or
5351 elif not (status.modified or status.added or status.removed or renamed or
5352 copied or subs):
5352 copied or subs):
5353 t += _(' (clean)')
5353 t += _(' (clean)')
5354 cleanworkdir = True
5354 cleanworkdir = True
5355 elif pnode not in bheads:
5355 elif pnode not in bheads:
5356 t += _(' (new branch head)')
5356 t += _(' (new branch head)')
5357
5357
5358 if parents:
5358 if parents:
5359 pendingphase = max(p.phase() for p in parents)
5359 pendingphase = max(p.phase() for p in parents)
5360 else:
5360 else:
5361 pendingphase = phases.public
5361 pendingphase = phases.public
5362
5362
5363 if pendingphase > phases.newcommitphase(ui):
5363 if pendingphase > phases.newcommitphase(ui):
5364 t += ' (%s)' % phases.phasenames[pendingphase]
5364 t += ' (%s)' % phases.phasenames[pendingphase]
5365
5365
5366 if cleanworkdir:
5366 if cleanworkdir:
5367 # i18n: column positioning for "hg summary"
5367 # i18n: column positioning for "hg summary"
5368 ui.status(_('commit: %s\n') % t.strip())
5368 ui.status(_('commit: %s\n') % t.strip())
5369 else:
5369 else:
5370 # i18n: column positioning for "hg summary"
5370 # i18n: column positioning for "hg summary"
5371 ui.write(_('commit: %s\n') % t.strip())
5371 ui.write(_('commit: %s\n') % t.strip())
5372
5372
5373 # all ancestors of branch heads - all ancestors of parent = new csets
5373 # all ancestors of branch heads - all ancestors of parent = new csets
5374 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5374 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5375 bheads))
5375 bheads))
5376
5376
5377 if new == 0:
5377 if new == 0:
5378 # i18n: column positioning for "hg summary"
5378 # i18n: column positioning for "hg summary"
5379 ui.status(_('update: (current)\n'))
5379 ui.status(_('update: (current)\n'))
5380 elif pnode not in bheads:
5380 elif pnode not in bheads:
5381 # i18n: column positioning for "hg summary"
5381 # i18n: column positioning for "hg summary"
5382 ui.write(_('update: %d new changesets (update)\n') % new)
5382 ui.write(_('update: %d new changesets (update)\n') % new)
5383 else:
5383 else:
5384 # i18n: column positioning for "hg summary"
5384 # i18n: column positioning for "hg summary"
5385 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5385 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5386 (new, len(bheads)))
5386 (new, len(bheads)))
5387
5387
5388 t = []
5388 t = []
5389 draft = len(repo.revs('draft()'))
5389 draft = len(repo.revs('draft()'))
5390 if draft:
5390 if draft:
5391 t.append(_('%d draft') % draft)
5391 t.append(_('%d draft') % draft)
5392 secret = len(repo.revs('secret()'))
5392 secret = len(repo.revs('secret()'))
5393 if secret:
5393 if secret:
5394 t.append(_('%d secret') % secret)
5394 t.append(_('%d secret') % secret)
5395
5395
5396 if draft or secret:
5396 if draft or secret:
5397 ui.status(_('phases: %s\n') % ', '.join(t))
5397 ui.status(_('phases: %s\n') % ', '.join(t))
5398
5398
5399 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5399 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5400 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5400 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5401 numtrouble = len(repo.revs(trouble + "()"))
5401 numtrouble = len(repo.revs(trouble + "()"))
5402 # We write all the possibilities to ease translation
5402 # We write all the possibilities to ease translation
5403 troublemsg = {
5403 troublemsg = {
5404 "orphan": _("orphan: %d changesets"),
5404 "orphan": _("orphan: %d changesets"),
5405 "contentdivergent": _("content-divergent: %d changesets"),
5405 "contentdivergent": _("content-divergent: %d changesets"),
5406 "phasedivergent": _("phase-divergent: %d changesets"),
5406 "phasedivergent": _("phase-divergent: %d changesets"),
5407 }
5407 }
5408 if numtrouble > 0:
5408 if numtrouble > 0:
5409 ui.status(troublemsg[trouble] % numtrouble + "\n")
5409 ui.status(troublemsg[trouble] % numtrouble + "\n")
5410
5410
5411 cmdutil.summaryhooks(ui, repo)
5411 cmdutil.summaryhooks(ui, repo)
5412
5412
5413 if opts.get('remote'):
5413 if opts.get('remote'):
5414 needsincoming, needsoutgoing = True, True
5414 needsincoming, needsoutgoing = True, True
5415 else:
5415 else:
5416 needsincoming, needsoutgoing = False, False
5416 needsincoming, needsoutgoing = False, False
5417 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5417 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5418 if i:
5418 if i:
5419 needsincoming = True
5419 needsincoming = True
5420 if o:
5420 if o:
5421 needsoutgoing = True
5421 needsoutgoing = True
5422 if not needsincoming and not needsoutgoing:
5422 if not needsincoming and not needsoutgoing:
5423 return
5423 return
5424
5424
5425 def getincoming():
5425 def getincoming():
5426 source, branches = hg.parseurl(ui.expandpath('default'))
5426 source, branches = hg.parseurl(ui.expandpath('default'))
5427 sbranch = branches[0]
5427 sbranch = branches[0]
5428 try:
5428 try:
5429 other = hg.peer(repo, {}, source)
5429 other = hg.peer(repo, {}, source)
5430 except error.RepoError:
5430 except error.RepoError:
5431 if opts.get('remote'):
5431 if opts.get('remote'):
5432 raise
5432 raise
5433 return source, sbranch, None, None, None
5433 return source, sbranch, None, None, None
5434 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5434 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5435 if revs:
5435 if revs:
5436 revs = [other.lookup(rev) for rev in revs]
5436 revs = [other.lookup(rev) for rev in revs]
5437 ui.debug('comparing with %s\n' % util.hidepassword(source))
5437 ui.debug('comparing with %s\n' % util.hidepassword(source))
5438 repo.ui.pushbuffer()
5438 repo.ui.pushbuffer()
5439 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5439 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5440 repo.ui.popbuffer()
5440 repo.ui.popbuffer()
5441 return source, sbranch, other, commoninc, commoninc[1]
5441 return source, sbranch, other, commoninc, commoninc[1]
5442
5442
5443 if needsincoming:
5443 if needsincoming:
5444 source, sbranch, sother, commoninc, incoming = getincoming()
5444 source, sbranch, sother, commoninc, incoming = getincoming()
5445 else:
5445 else:
5446 source = sbranch = sother = commoninc = incoming = None
5446 source = sbranch = sother = commoninc = incoming = None
5447
5447
5448 def getoutgoing():
5448 def getoutgoing():
5449 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5449 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5450 dbranch = branches[0]
5450 dbranch = branches[0]
5451 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5451 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5452 if source != dest:
5452 if source != dest:
5453 try:
5453 try:
5454 dother = hg.peer(repo, {}, dest)
5454 dother = hg.peer(repo, {}, dest)
5455 except error.RepoError:
5455 except error.RepoError:
5456 if opts.get('remote'):
5456 if opts.get('remote'):
5457 raise
5457 raise
5458 return dest, dbranch, None, None
5458 return dest, dbranch, None, None
5459 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5459 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5460 elif sother is None:
5460 elif sother is None:
5461 # there is no explicit destination peer, but source one is invalid
5461 # there is no explicit destination peer, but source one is invalid
5462 return dest, dbranch, None, None
5462 return dest, dbranch, None, None
5463 else:
5463 else:
5464 dother = sother
5464 dother = sother
5465 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5465 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5466 common = None
5466 common = None
5467 else:
5467 else:
5468 common = commoninc
5468 common = commoninc
5469 if revs:
5469 if revs:
5470 revs = [repo.lookup(rev) for rev in revs]
5470 revs = [repo.lookup(rev) for rev in revs]
5471 repo.ui.pushbuffer()
5471 repo.ui.pushbuffer()
5472 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5472 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5473 commoninc=common)
5473 commoninc=common)
5474 repo.ui.popbuffer()
5474 repo.ui.popbuffer()
5475 return dest, dbranch, dother, outgoing
5475 return dest, dbranch, dother, outgoing
5476
5476
5477 if needsoutgoing:
5477 if needsoutgoing:
5478 dest, dbranch, dother, outgoing = getoutgoing()
5478 dest, dbranch, dother, outgoing = getoutgoing()
5479 else:
5479 else:
5480 dest = dbranch = dother = outgoing = None
5480 dest = dbranch = dother = outgoing = None
5481
5481
5482 if opts.get('remote'):
5482 if opts.get('remote'):
5483 t = []
5483 t = []
5484 if incoming:
5484 if incoming:
5485 t.append(_('1 or more incoming'))
5485 t.append(_('1 or more incoming'))
5486 o = outgoing.missing
5486 o = outgoing.missing
5487 if o:
5487 if o:
5488 t.append(_('%d outgoing') % len(o))
5488 t.append(_('%d outgoing') % len(o))
5489 other = dother or sother
5489 other = dother or sother
5490 if 'bookmarks' in other.listkeys('namespaces'):
5490 if 'bookmarks' in other.listkeys('namespaces'):
5491 counts = bookmarks.summary(repo, other)
5491 counts = bookmarks.summary(repo, other)
5492 if counts[0] > 0:
5492 if counts[0] > 0:
5493 t.append(_('%d incoming bookmarks') % counts[0])
5493 t.append(_('%d incoming bookmarks') % counts[0])
5494 if counts[1] > 0:
5494 if counts[1] > 0:
5495 t.append(_('%d outgoing bookmarks') % counts[1])
5495 t.append(_('%d outgoing bookmarks') % counts[1])
5496
5496
5497 if t:
5497 if t:
5498 # i18n: column positioning for "hg summary"
5498 # i18n: column positioning for "hg summary"
5499 ui.write(_('remote: %s\n') % (', '.join(t)))
5499 ui.write(_('remote: %s\n') % (', '.join(t)))
5500 else:
5500 else:
5501 # i18n: column positioning for "hg summary"
5501 # i18n: column positioning for "hg summary"
5502 ui.status(_('remote: (synced)\n'))
5502 ui.status(_('remote: (synced)\n'))
5503
5503
5504 cmdutil.summaryremotehooks(ui, repo, opts,
5504 cmdutil.summaryremotehooks(ui, repo, opts,
5505 ((source, sbranch, sother, commoninc),
5505 ((source, sbranch, sother, commoninc),
5506 (dest, dbranch, dother, outgoing)))
5506 (dest, dbranch, dother, outgoing)))
5507
5507
5508 @command('tag',
5508 @command('tag',
5509 [('f', 'force', None, _('force tag')),
5509 [('f', 'force', None, _('force tag')),
5510 ('l', 'local', None, _('make the tag local')),
5510 ('l', 'local', None, _('make the tag local')),
5511 ('r', 'rev', '', _('revision to tag'), _('REV')),
5511 ('r', 'rev', '', _('revision to tag'), _('REV')),
5512 ('', 'remove', None, _('remove a tag')),
5512 ('', 'remove', None, _('remove a tag')),
5513 # -l/--local is already there, commitopts cannot be used
5513 # -l/--local is already there, commitopts cannot be used
5514 ('e', 'edit', None, _('invoke editor on commit messages')),
5514 ('e', 'edit', None, _('invoke editor on commit messages')),
5515 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5515 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5516 ] + commitopts2,
5516 ] + commitopts2,
5517 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5517 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5518 def tag(ui, repo, name1, *names, **opts):
5518 def tag(ui, repo, name1, *names, **opts):
5519 """add one or more tags for the current or given revision
5519 """add one or more tags for the current or given revision
5520
5520
5521 Name a particular revision using <name>.
5521 Name a particular revision using <name>.
5522
5522
5523 Tags are used to name particular revisions of the repository and are
5523 Tags are used to name particular revisions of the repository and are
5524 very useful to compare different revisions, to go back to significant
5524 very useful to compare different revisions, to go back to significant
5525 earlier versions or to mark branch points as releases, etc. Changing
5525 earlier versions or to mark branch points as releases, etc. Changing
5526 an existing tag is normally disallowed; use -f/--force to override.
5526 an existing tag is normally disallowed; use -f/--force to override.
5527
5527
5528 If no revision is given, the parent of the working directory is
5528 If no revision is given, the parent of the working directory is
5529 used.
5529 used.
5530
5530
5531 To facilitate version control, distribution, and merging of tags,
5531 To facilitate version control, distribution, and merging of tags,
5532 they are stored as a file named ".hgtags" which is managed similarly
5532 they are stored as a file named ".hgtags" which is managed similarly
5533 to other project files and can be hand-edited if necessary. This
5533 to other project files and can be hand-edited if necessary. This
5534 also means that tagging creates a new commit. The file
5534 also means that tagging creates a new commit. The file
5535 ".hg/localtags" is used for local tags (not shared among
5535 ".hg/localtags" is used for local tags (not shared among
5536 repositories).
5536 repositories).
5537
5537
5538 Tag commits are usually made at the head of a branch. If the parent
5538 Tag commits are usually made at the head of a branch. If the parent
5539 of the working directory is not a branch head, :hg:`tag` aborts; use
5539 of the working directory is not a branch head, :hg:`tag` aborts; use
5540 -f/--force to force the tag commit to be based on a non-head
5540 -f/--force to force the tag commit to be based on a non-head
5541 changeset.
5541 changeset.
5542
5542
5543 See :hg:`help dates` for a list of formats valid for -d/--date.
5543 See :hg:`help dates` for a list of formats valid for -d/--date.
5544
5544
5545 Since tag names have priority over branch names during revision
5545 Since tag names have priority over branch names during revision
5546 lookup, using an existing branch name as a tag name is discouraged.
5546 lookup, using an existing branch name as a tag name is discouraged.
5547
5547
5548 Returns 0 on success.
5548 Returns 0 on success.
5549 """
5549 """
5550 opts = pycompat.byteskwargs(opts)
5550 opts = pycompat.byteskwargs(opts)
5551 with repo.wlock(), repo.lock():
5551 with repo.wlock(), repo.lock():
5552 rev_ = "."
5552 rev_ = "."
5553 names = [t.strip() for t in (name1,) + names]
5553 names = [t.strip() for t in (name1,) + names]
5554 if len(names) != len(set(names)):
5554 if len(names) != len(set(names)):
5555 raise error.Abort(_('tag names must be unique'))
5555 raise error.Abort(_('tag names must be unique'))
5556 for n in names:
5556 for n in names:
5557 scmutil.checknewlabel(repo, n, 'tag')
5557 scmutil.checknewlabel(repo, n, 'tag')
5558 if not n:
5558 if not n:
5559 raise error.Abort(_('tag names cannot consist entirely of '
5559 raise error.Abort(_('tag names cannot consist entirely of '
5560 'whitespace'))
5560 'whitespace'))
5561 if opts.get('rev') and opts.get('remove'):
5561 if opts.get('rev') and opts.get('remove'):
5562 raise error.Abort(_("--rev and --remove are incompatible"))
5562 raise error.Abort(_("--rev and --remove are incompatible"))
5563 if opts.get('rev'):
5563 if opts.get('rev'):
5564 rev_ = opts['rev']
5564 rev_ = opts['rev']
5565 message = opts.get('message')
5565 message = opts.get('message')
5566 if opts.get('remove'):
5566 if opts.get('remove'):
5567 if opts.get('local'):
5567 if opts.get('local'):
5568 expectedtype = 'local'
5568 expectedtype = 'local'
5569 else:
5569 else:
5570 expectedtype = 'global'
5570 expectedtype = 'global'
5571
5571
5572 for n in names:
5572 for n in names:
5573 if not repo.tagtype(n):
5573 if not repo.tagtype(n):
5574 raise error.Abort(_("tag '%s' does not exist") % n)
5574 raise error.Abort(_("tag '%s' does not exist") % n)
5575 if repo.tagtype(n) != expectedtype:
5575 if repo.tagtype(n) != expectedtype:
5576 if expectedtype == 'global':
5576 if expectedtype == 'global':
5577 raise error.Abort(_("tag '%s' is not a global tag") % n)
5577 raise error.Abort(_("tag '%s' is not a global tag") % n)
5578 else:
5578 else:
5579 raise error.Abort(_("tag '%s' is not a local tag") % n)
5579 raise error.Abort(_("tag '%s' is not a local tag") % n)
5580 rev_ = 'null'
5580 rev_ = 'null'
5581 if not message:
5581 if not message:
5582 # we don't translate commit messages
5582 # we don't translate commit messages
5583 message = 'Removed tag %s' % ', '.join(names)
5583 message = 'Removed tag %s' % ', '.join(names)
5584 elif not opts.get('force'):
5584 elif not opts.get('force'):
5585 for n in names:
5585 for n in names:
5586 if n in repo.tags():
5586 if n in repo.tags():
5587 raise error.Abort(_("tag '%s' already exists "
5587 raise error.Abort(_("tag '%s' already exists "
5588 "(use -f to force)") % n)
5588 "(use -f to force)") % n)
5589 if not opts.get('local'):
5589 if not opts.get('local'):
5590 p1, p2 = repo.dirstate.parents()
5590 p1, p2 = repo.dirstate.parents()
5591 if p2 != nullid:
5591 if p2 != nullid:
5592 raise error.Abort(_('uncommitted merge'))
5592 raise error.Abort(_('uncommitted merge'))
5593 bheads = repo.branchheads()
5593 bheads = repo.branchheads()
5594 if not opts.get('force') and bheads and p1 not in bheads:
5594 if not opts.get('force') and bheads and p1 not in bheads:
5595 raise error.Abort(_('working directory is not at a branch head '
5595 raise error.Abort(_('working directory is not at a branch head '
5596 '(use -f to force)'))
5596 '(use -f to force)'))
5597 node = scmutil.revsingle(repo, rev_).node()
5597 node = scmutil.revsingle(repo, rev_).node()
5598
5598
5599 if not message:
5599 if not message:
5600 # we don't translate commit messages
5600 # we don't translate commit messages
5601 message = ('Added tag %s for changeset %s' %
5601 message = ('Added tag %s for changeset %s' %
5602 (', '.join(names), short(node)))
5602 (', '.join(names), short(node)))
5603
5603
5604 date = opts.get('date')
5604 date = opts.get('date')
5605 if date:
5605 if date:
5606 date = dateutil.parsedate(date)
5606 date = dateutil.parsedate(date)
5607
5607
5608 if opts.get('remove'):
5608 if opts.get('remove'):
5609 editform = 'tag.remove'
5609 editform = 'tag.remove'
5610 else:
5610 else:
5611 editform = 'tag.add'
5611 editform = 'tag.add'
5612 editor = cmdutil.getcommiteditor(editform=editform,
5612 editor = cmdutil.getcommiteditor(editform=editform,
5613 **pycompat.strkwargs(opts))
5613 **pycompat.strkwargs(opts))
5614
5614
5615 # don't allow tagging the null rev
5615 # don't allow tagging the null rev
5616 if (not opts.get('remove') and
5616 if (not opts.get('remove') and
5617 scmutil.revsingle(repo, rev_).rev() == nullrev):
5617 scmutil.revsingle(repo, rev_).rev() == nullrev):
5618 raise error.Abort(_("cannot tag null revision"))
5618 raise error.Abort(_("cannot tag null revision"))
5619
5619
5620 tagsmod.tag(repo, names, node, message, opts.get('local'),
5620 tagsmod.tag(repo, names, node, message, opts.get('local'),
5621 opts.get('user'), date, editor=editor)
5621 opts.get('user'), date, editor=editor)
5622
5622
5623 @command('tags', formatteropts, '', intents={INTENT_READONLY})
5623 @command('tags', formatteropts, '', intents={INTENT_READONLY})
5624 def tags(ui, repo, **opts):
5624 def tags(ui, repo, **opts):
5625 """list repository tags
5625 """list repository tags
5626
5626
5627 This lists both regular and local tags. When the -v/--verbose
5627 This lists both regular and local tags. When the -v/--verbose
5628 switch is used, a third column "local" is printed for local tags.
5628 switch is used, a third column "local" is printed for local tags.
5629 When the -q/--quiet switch is used, only the tag name is printed.
5629 When the -q/--quiet switch is used, only the tag name is printed.
5630
5630
5631 Returns 0 on success.
5631 Returns 0 on success.
5632 """
5632 """
5633
5633
5634 opts = pycompat.byteskwargs(opts)
5634 opts = pycompat.byteskwargs(opts)
5635 ui.pager('tags')
5635 ui.pager('tags')
5636 fm = ui.formatter('tags', opts)
5636 fm = ui.formatter('tags', opts)
5637 hexfunc = fm.hexfunc
5637 hexfunc = fm.hexfunc
5638 tagtype = ""
5638 tagtype = ""
5639
5639
5640 for t, n in reversed(repo.tagslist()):
5640 for t, n in reversed(repo.tagslist()):
5641 hn = hexfunc(n)
5641 hn = hexfunc(n)
5642 label = 'tags.normal'
5642 label = 'tags.normal'
5643 tagtype = ''
5643 tagtype = ''
5644 if repo.tagtype(t) == 'local':
5644 if repo.tagtype(t) == 'local':
5645 label = 'tags.local'
5645 label = 'tags.local'
5646 tagtype = 'local'
5646 tagtype = 'local'
5647
5647
5648 fm.startitem()
5648 fm.startitem()
5649 fm.context(repo=repo)
5649 fm.context(repo=repo)
5650 fm.write('tag', '%s', t, label=label)
5650 fm.write('tag', '%s', t, label=label)
5651 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5651 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5652 fm.condwrite(not ui.quiet, 'rev node', fmt,
5652 fm.condwrite(not ui.quiet, 'rev node', fmt,
5653 repo.changelog.rev(n), hn, label=label)
5653 repo.changelog.rev(n), hn, label=label)
5654 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5654 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5655 tagtype, label=label)
5655 tagtype, label=label)
5656 fm.plain('\n')
5656 fm.plain('\n')
5657 fm.end()
5657 fm.end()
5658
5658
5659 @command('tip',
5659 @command('tip',
5660 [('p', 'patch', None, _('show patch')),
5660 [('p', 'patch', None, _('show patch')),
5661 ('g', 'git', None, _('use git extended diff format')),
5661 ('g', 'git', None, _('use git extended diff format')),
5662 ] + templateopts,
5662 ] + templateopts,
5663 _('[-p] [-g]'))
5663 _('[-p] [-g]'))
5664 def tip(ui, repo, **opts):
5664 def tip(ui, repo, **opts):
5665 """show the tip revision (DEPRECATED)
5665 """show the tip revision (DEPRECATED)
5666
5666
5667 The tip revision (usually just called the tip) is the changeset
5667 The tip revision (usually just called the tip) is the changeset
5668 most recently added to the repository (and therefore the most
5668 most recently added to the repository (and therefore the most
5669 recently changed head).
5669 recently changed head).
5670
5670
5671 If you have just made a commit, that commit will be the tip. If
5671 If you have just made a commit, that commit will be the tip. If
5672 you have just pulled changes from another repository, the tip of
5672 you have just pulled changes from another repository, the tip of
5673 that repository becomes the current tip. The "tip" tag is special
5673 that repository becomes the current tip. The "tip" tag is special
5674 and cannot be renamed or assigned to a different changeset.
5674 and cannot be renamed or assigned to a different changeset.
5675
5675
5676 This command is deprecated, please use :hg:`heads` instead.
5676 This command is deprecated, please use :hg:`heads` instead.
5677
5677
5678 Returns 0 on success.
5678 Returns 0 on success.
5679 """
5679 """
5680 opts = pycompat.byteskwargs(opts)
5680 opts = pycompat.byteskwargs(opts)
5681 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5681 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5682 displayer.show(repo['tip'])
5682 displayer.show(repo['tip'])
5683 displayer.close()
5683 displayer.close()
5684
5684
5685 @command('unbundle',
5685 @command('unbundle',
5686 [('u', 'update', None,
5686 [('u', 'update', None,
5687 _('update to new branch head if changesets were unbundled'))],
5687 _('update to new branch head if changesets were unbundled'))],
5688 _('[-u] FILE...'))
5688 _('[-u] FILE...'))
5689 def unbundle(ui, repo, fname1, *fnames, **opts):
5689 def unbundle(ui, repo, fname1, *fnames, **opts):
5690 """apply one or more bundle files
5690 """apply one or more bundle files
5691
5691
5692 Apply one or more bundle files generated by :hg:`bundle`.
5692 Apply one or more bundle files generated by :hg:`bundle`.
5693
5693
5694 Returns 0 on success, 1 if an update has unresolved files.
5694 Returns 0 on success, 1 if an update has unresolved files.
5695 """
5695 """
5696 fnames = (fname1,) + fnames
5696 fnames = (fname1,) + fnames
5697
5697
5698 with repo.lock():
5698 with repo.lock():
5699 for fname in fnames:
5699 for fname in fnames:
5700 f = hg.openpath(ui, fname)
5700 f = hg.openpath(ui, fname)
5701 gen = exchange.readbundle(ui, f, fname)
5701 gen = exchange.readbundle(ui, f, fname)
5702 if isinstance(gen, streamclone.streamcloneapplier):
5702 if isinstance(gen, streamclone.streamcloneapplier):
5703 raise error.Abort(
5703 raise error.Abort(
5704 _('packed bundles cannot be applied with '
5704 _('packed bundles cannot be applied with '
5705 '"hg unbundle"'),
5705 '"hg unbundle"'),
5706 hint=_('use "hg debugapplystreamclonebundle"'))
5706 hint=_('use "hg debugapplystreamclonebundle"'))
5707 url = 'bundle:' + fname
5707 url = 'bundle:' + fname
5708 try:
5708 try:
5709 txnname = 'unbundle'
5709 txnname = 'unbundle'
5710 if not isinstance(gen, bundle2.unbundle20):
5710 if not isinstance(gen, bundle2.unbundle20):
5711 txnname = 'unbundle\n%s' % util.hidepassword(url)
5711 txnname = 'unbundle\n%s' % util.hidepassword(url)
5712 with repo.transaction(txnname) as tr:
5712 with repo.transaction(txnname) as tr:
5713 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5713 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5714 url=url)
5714 url=url)
5715 except error.BundleUnknownFeatureError as exc:
5715 except error.BundleUnknownFeatureError as exc:
5716 raise error.Abort(
5716 raise error.Abort(
5717 _('%s: unknown bundle feature, %s') % (fname, exc),
5717 _('%s: unknown bundle feature, %s') % (fname, exc),
5718 hint=_("see https://mercurial-scm.org/"
5718 hint=_("see https://mercurial-scm.org/"
5719 "wiki/BundleFeature for more "
5719 "wiki/BundleFeature for more "
5720 "information"))
5720 "information"))
5721 modheads = bundle2.combinechangegroupresults(op)
5721 modheads = bundle2.combinechangegroupresults(op)
5722
5722
5723 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5723 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5724
5724
5725 @command('^update|up|checkout|co',
5725 @command('^update|up|checkout|co',
5726 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5726 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5727 ('c', 'check', None, _('require clean working directory')),
5727 ('c', 'check', None, _('require clean working directory')),
5728 ('m', 'merge', None, _('merge uncommitted changes')),
5728 ('m', 'merge', None, _('merge uncommitted changes')),
5729 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5729 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5730 ('r', 'rev', '', _('revision'), _('REV'))
5730 ('r', 'rev', '', _('revision'), _('REV'))
5731 ] + mergetoolopts,
5731 ] + mergetoolopts,
5732 _('[-C|-c|-m] [-d DATE] [[-r] REV]'))
5732 _('[-C|-c|-m] [-d DATE] [[-r] REV]'))
5733 def update(ui, repo, node=None, **opts):
5733 def update(ui, repo, node=None, **opts):
5734 """update working directory (or switch revisions)
5734 """update working directory (or switch revisions)
5735
5735
5736 Update the repository's working directory to the specified
5736 Update the repository's working directory to the specified
5737 changeset. If no changeset is specified, update to the tip of the
5737 changeset. If no changeset is specified, update to the tip of the
5738 current named branch and move the active bookmark (see :hg:`help
5738 current named branch and move the active bookmark (see :hg:`help
5739 bookmarks`).
5739 bookmarks`).
5740
5740
5741 Update sets the working directory's parent revision to the specified
5741 Update sets the working directory's parent revision to the specified
5742 changeset (see :hg:`help parents`).
5742 changeset (see :hg:`help parents`).
5743
5743
5744 If the changeset is not a descendant or ancestor of the working
5744 If the changeset is not a descendant or ancestor of the working
5745 directory's parent and there are uncommitted changes, the update is
5745 directory's parent and there are uncommitted changes, the update is
5746 aborted. With the -c/--check option, the working directory is checked
5746 aborted. With the -c/--check option, the working directory is checked
5747 for uncommitted changes; if none are found, the working directory is
5747 for uncommitted changes; if none are found, the working directory is
5748 updated to the specified changeset.
5748 updated to the specified changeset.
5749
5749
5750 .. container:: verbose
5750 .. container:: verbose
5751
5751
5752 The -C/--clean, -c/--check, and -m/--merge options control what
5752 The -C/--clean, -c/--check, and -m/--merge options control what
5753 happens if the working directory contains uncommitted changes.
5753 happens if the working directory contains uncommitted changes.
5754 At most of one of them can be specified.
5754 At most of one of them can be specified.
5755
5755
5756 1. If no option is specified, and if
5756 1. If no option is specified, and if
5757 the requested changeset is an ancestor or descendant of
5757 the requested changeset is an ancestor or descendant of
5758 the working directory's parent, the uncommitted changes
5758 the working directory's parent, the uncommitted changes
5759 are merged into the requested changeset and the merged
5759 are merged into the requested changeset and the merged
5760 result is left uncommitted. If the requested changeset is
5760 result is left uncommitted. If the requested changeset is
5761 not an ancestor or descendant (that is, it is on another
5761 not an ancestor or descendant (that is, it is on another
5762 branch), the update is aborted and the uncommitted changes
5762 branch), the update is aborted and the uncommitted changes
5763 are preserved.
5763 are preserved.
5764
5764
5765 2. With the -m/--merge option, the update is allowed even if the
5765 2. With the -m/--merge option, the update is allowed even if the
5766 requested changeset is not an ancestor or descendant of
5766 requested changeset is not an ancestor or descendant of
5767 the working directory's parent.
5767 the working directory's parent.
5768
5768
5769 3. With the -c/--check option, the update is aborted and the
5769 3. With the -c/--check option, the update is aborted and the
5770 uncommitted changes are preserved.
5770 uncommitted changes are preserved.
5771
5771
5772 4. With the -C/--clean option, uncommitted changes are discarded and
5772 4. With the -C/--clean option, uncommitted changes are discarded and
5773 the working directory is updated to the requested changeset.
5773 the working directory is updated to the requested changeset.
5774
5774
5775 To cancel an uncommitted merge (and lose your changes), use
5775 To cancel an uncommitted merge (and lose your changes), use
5776 :hg:`merge --abort`.
5776 :hg:`merge --abort`.
5777
5777
5778 Use null as the changeset to remove the working directory (like
5778 Use null as the changeset to remove the working directory (like
5779 :hg:`clone -U`).
5779 :hg:`clone -U`).
5780
5780
5781 If you want to revert just one file to an older revision, use
5781 If you want to revert just one file to an older revision, use
5782 :hg:`revert [-r REV] NAME`.
5782 :hg:`revert [-r REV] NAME`.
5783
5783
5784 See :hg:`help dates` for a list of formats valid for -d/--date.
5784 See :hg:`help dates` for a list of formats valid for -d/--date.
5785
5785
5786 Returns 0 on success, 1 if there are unresolved files.
5786 Returns 0 on success, 1 if there are unresolved files.
5787 """
5787 """
5788 rev = opts.get(r'rev')
5788 rev = opts.get(r'rev')
5789 date = opts.get(r'date')
5789 date = opts.get(r'date')
5790 clean = opts.get(r'clean')
5790 clean = opts.get(r'clean')
5791 check = opts.get(r'check')
5791 check = opts.get(r'check')
5792 merge = opts.get(r'merge')
5792 merge = opts.get(r'merge')
5793 if rev and node:
5793 if rev and node:
5794 raise error.Abort(_("please specify just one revision"))
5794 raise error.Abort(_("please specify just one revision"))
5795
5795
5796 if ui.configbool('commands', 'update.requiredest'):
5796 if ui.configbool('commands', 'update.requiredest'):
5797 if not node and not rev and not date:
5797 if not node and not rev and not date:
5798 raise error.Abort(_('you must specify a destination'),
5798 raise error.Abort(_('you must specify a destination'),
5799 hint=_('for example: hg update ".::"'))
5799 hint=_('for example: hg update ".::"'))
5800
5800
5801 if rev is None or rev == '':
5801 if rev is None or rev == '':
5802 rev = node
5802 rev = node
5803
5803
5804 if date and rev is not None:
5804 if date and rev is not None:
5805 raise error.Abort(_("you can't specify a revision and a date"))
5805 raise error.Abort(_("you can't specify a revision and a date"))
5806
5806
5807 if len([x for x in (clean, check, merge) if x]) > 1:
5807 if len([x for x in (clean, check, merge) if x]) > 1:
5808 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
5808 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
5809 "or -m/--merge"))
5809 "or -m/--merge"))
5810
5810
5811 updatecheck = None
5811 updatecheck = None
5812 if check:
5812 if check:
5813 updatecheck = 'abort'
5813 updatecheck = 'abort'
5814 elif merge:
5814 elif merge:
5815 updatecheck = 'none'
5815 updatecheck = 'none'
5816
5816
5817 with repo.wlock():
5817 with repo.wlock():
5818 cmdutil.clearunfinished(repo)
5818 cmdutil.clearunfinished(repo)
5819
5819
5820 if date:
5820 if date:
5821 rev = cmdutil.finddate(ui, repo, date)
5821 rev = cmdutil.finddate(ui, repo, date)
5822
5822
5823 # if we defined a bookmark, we have to remember the original name
5823 # if we defined a bookmark, we have to remember the original name
5824 brev = rev
5824 brev = rev
5825 if rev:
5825 if rev:
5826 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
5826 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
5827 ctx = scmutil.revsingle(repo, rev, rev)
5827 ctx = scmutil.revsingle(repo, rev, rev)
5828 rev = ctx.rev()
5828 rev = ctx.rev()
5829 hidden = ctx.hidden()
5829 hidden = ctx.hidden()
5830 overrides = {('ui', 'forcemerge'): opts.get(r'tool', '')}
5830 overrides = {('ui', 'forcemerge'): opts.get(r'tool', '')}
5831 with ui.configoverride(overrides, 'update'):
5831 with ui.configoverride(overrides, 'update'):
5832 ret = hg.updatetotally(ui, repo, rev, brev, clean=clean,
5832 ret = hg.updatetotally(ui, repo, rev, brev, clean=clean,
5833 updatecheck=updatecheck)
5833 updatecheck=updatecheck)
5834 if hidden:
5834 if hidden:
5835 ctxstr = ctx.hex()[:12]
5835 ctxstr = ctx.hex()[:12]
5836 ui.warn(_("updated to hidden changeset %s\n") % ctxstr)
5836 ui.warn(_("updated to hidden changeset %s\n") % ctxstr)
5837
5837
5838 if ctx.obsolete():
5838 if ctx.obsolete():
5839 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
5839 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
5840 ui.warn("(%s)\n" % obsfatemsg)
5840 ui.warn("(%s)\n" % obsfatemsg)
5841 return ret
5841 return ret
5842
5842
5843 @command('verify', [])
5843 @command('verify', [])
5844 def verify(ui, repo):
5844 def verify(ui, repo):
5845 """verify the integrity of the repository
5845 """verify the integrity of the repository
5846
5846
5847 Verify the integrity of the current repository.
5847 Verify the integrity of the current repository.
5848
5848
5849 This will perform an extensive check of the repository's
5849 This will perform an extensive check of the repository's
5850 integrity, validating the hashes and checksums of each entry in
5850 integrity, validating the hashes and checksums of each entry in
5851 the changelog, manifest, and tracked files, as well as the
5851 the changelog, manifest, and tracked files, as well as the
5852 integrity of their crosslinks and indices.
5852 integrity of their crosslinks and indices.
5853
5853
5854 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
5854 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
5855 for more information about recovery from corruption of the
5855 for more information about recovery from corruption of the
5856 repository.
5856 repository.
5857
5857
5858 Returns 0 on success, 1 if errors are encountered.
5858 Returns 0 on success, 1 if errors are encountered.
5859 """
5859 """
5860 return hg.verify(repo)
5860 return hg.verify(repo)
5861
5861
5862 @command('version', [] + formatteropts, norepo=True,
5862 @command('version', [] + formatteropts, norepo=True,
5863 intents={INTENT_READONLY})
5863 intents={INTENT_READONLY})
5864 def version_(ui, **opts):
5864 def version_(ui, **opts):
5865 """output version and copyright information"""
5865 """output version and copyright information"""
5866 opts = pycompat.byteskwargs(opts)
5866 opts = pycompat.byteskwargs(opts)
5867 if ui.verbose:
5867 if ui.verbose:
5868 ui.pager('version')
5868 ui.pager('version')
5869 fm = ui.formatter("version", opts)
5869 fm = ui.formatter("version", opts)
5870 fm.startitem()
5870 fm.startitem()
5871 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
5871 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
5872 util.version())
5872 util.version())
5873 license = _(
5873 license = _(
5874 "(see https://mercurial-scm.org for more information)\n"
5874 "(see https://mercurial-scm.org for more information)\n"
5875 "\nCopyright (C) 2005-2018 Matt Mackall and others\n"
5875 "\nCopyright (C) 2005-2018 Matt Mackall and others\n"
5876 "This is free software; see the source for copying conditions. "
5876 "This is free software; see the source for copying conditions. "
5877 "There is NO\nwarranty; "
5877 "There is NO\nwarranty; "
5878 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5878 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5879 )
5879 )
5880 if not ui.quiet:
5880 if not ui.quiet:
5881 fm.plain(license)
5881 fm.plain(license)
5882
5882
5883 if ui.verbose:
5883 if ui.verbose:
5884 fm.plain(_("\nEnabled extensions:\n\n"))
5884 fm.plain(_("\nEnabled extensions:\n\n"))
5885 # format names and versions into columns
5885 # format names and versions into columns
5886 names = []
5886 names = []
5887 vers = []
5887 vers = []
5888 isinternals = []
5888 isinternals = []
5889 for name, module in extensions.extensions():
5889 for name, module in extensions.extensions():
5890 names.append(name)
5890 names.append(name)
5891 vers.append(extensions.moduleversion(module) or None)
5891 vers.append(extensions.moduleversion(module) or None)
5892 isinternals.append(extensions.ismoduleinternal(module))
5892 isinternals.append(extensions.ismoduleinternal(module))
5893 fn = fm.nested("extensions", tmpl='{name}\n')
5893 fn = fm.nested("extensions", tmpl='{name}\n')
5894 if names:
5894 if names:
5895 namefmt = " %%-%ds " % max(len(n) for n in names)
5895 namefmt = " %%-%ds " % max(len(n) for n in names)
5896 places = [_("external"), _("internal")]
5896 places = [_("external"), _("internal")]
5897 for n, v, p in zip(names, vers, isinternals):
5897 for n, v, p in zip(names, vers, isinternals):
5898 fn.startitem()
5898 fn.startitem()
5899 fn.condwrite(ui.verbose, "name", namefmt, n)
5899 fn.condwrite(ui.verbose, "name", namefmt, n)
5900 if ui.verbose:
5900 if ui.verbose:
5901 fn.plain("%s " % places[p])
5901 fn.plain("%s " % places[p])
5902 fn.data(bundled=p)
5902 fn.data(bundled=p)
5903 fn.condwrite(ui.verbose and v, "ver", "%s", v)
5903 fn.condwrite(ui.verbose and v, "ver", "%s", v)
5904 if ui.verbose:
5904 if ui.verbose:
5905 fn.plain("\n")
5905 fn.plain("\n")
5906 fn.end()
5906 fn.end()
5907 fm.end()
5907 fm.end()
5908
5908
5909 def loadcmdtable(ui, name, cmdtable):
5909 def loadcmdtable(ui, name, cmdtable):
5910 """Load command functions from specified cmdtable
5910 """Load command functions from specified cmdtable
5911 """
5911 """
5912 overrides = [cmd for cmd in cmdtable if cmd in table]
5912 overrides = [cmd for cmd in cmdtable if cmd in table]
5913 if overrides:
5913 if overrides:
5914 ui.warn(_("extension '%s' overrides commands: %s\n")
5914 ui.warn(_("extension '%s' overrides commands: %s\n")
5915 % (name, " ".join(overrides)))
5915 % (name, " ".join(overrides)))
5916 table.update(cmdtable)
5916 table.update(cmdtable)
@@ -1,1213 +1,1219 b''
1
1
2 $ hg init repo
2 $ hg init repo
3 $ cd repo
3 $ cd repo
4
4
5 $ cat > $TESTTMP/hook.sh <<'EOF'
5 $ cat > $TESTTMP/hook.sh <<'EOF'
6 > echo "test-hook-bookmark: $HG_BOOKMARK: $HG_OLDNODE -> $HG_NODE"
6 > echo "test-hook-bookmark: $HG_BOOKMARK: $HG_OLDNODE -> $HG_NODE"
7 > EOF
7 > EOF
8 $ TESTHOOK="hooks.txnclose-bookmark.test=sh $TESTTMP/hook.sh"
8 $ TESTHOOK="hooks.txnclose-bookmark.test=sh $TESTTMP/hook.sh"
9
9
10 no bookmarks
10 no bookmarks
11
11
12 $ hg bookmarks
12 $ hg bookmarks
13 no bookmarks set
13 no bookmarks set
14
14
15 $ hg bookmarks -Tjson
15 $ hg bookmarks -Tjson
16 [
16 [
17 ]
17 ]
18
18
19 bookmark rev -1
19 bookmark rev -1
20
20
21 $ hg bookmark X --config "$TESTHOOK"
21 $ hg bookmark X --config "$TESTHOOK"
22 test-hook-bookmark: X: -> 0000000000000000000000000000000000000000
22 test-hook-bookmark: X: -> 0000000000000000000000000000000000000000
23
23
24 list bookmarks
24 list bookmarks
25
25
26 $ hg bookmarks
26 $ hg bookmarks
27 * X -1:000000000000
27 * X -1:000000000000
28
28
29 list bookmarks with color
29 list bookmarks with color
30
30
31 $ hg --config extensions.color= --config color.mode=ansi \
31 $ hg --config extensions.color= --config color.mode=ansi \
32 > bookmarks --color=always
32 > bookmarks --color=always
33 \x1b[0;32m * \x1b[0m\x1b[0;32mX\x1b[0m\x1b[0;32m -1:000000000000\x1b[0m (esc)
33 \x1b[0;32m * \x1b[0m\x1b[0;32mX\x1b[0m\x1b[0;32m -1:000000000000\x1b[0m (esc)
34
34
35 $ echo a > a
35 $ echo a > a
36 $ hg add a
36 $ hg add a
37 $ hg commit -m 0 --config "$TESTHOOK"
37 $ hg commit -m 0 --config "$TESTHOOK"
38 test-hook-bookmark: X: 0000000000000000000000000000000000000000 -> f7b1eb17ad24730a1651fccd46c43826d1bbc2ac
38 test-hook-bookmark: X: 0000000000000000000000000000000000000000 -> f7b1eb17ad24730a1651fccd46c43826d1bbc2ac
39
39
40 bookmark X moved to rev 0
40 bookmark X moved to rev 0
41
41
42 $ hg bookmarks
42 $ hg bookmarks
43 * X 0:f7b1eb17ad24
43 * X 0:f7b1eb17ad24
44
44
45 look up bookmark
45 look up bookmark
46
46
47 $ hg log -r X
47 $ hg log -r X
48 changeset: 0:f7b1eb17ad24
48 changeset: 0:f7b1eb17ad24
49 bookmark: X
49 bookmark: X
50 tag: tip
50 tag: tip
51 user: test
51 user: test
52 date: Thu Jan 01 00:00:00 1970 +0000
52 date: Thu Jan 01 00:00:00 1970 +0000
53 summary: 0
53 summary: 0
54
54
55
55
56 second bookmark for rev 0, command should work even with ui.strict on
56 second bookmark for rev 0, command should work even with ui.strict on
57
57
58 $ hg --config ui.strict=1 bookmark X2 --config "$TESTHOOK"
58 $ hg --config ui.strict=1 bookmark X2 --config "$TESTHOOK"
59 test-hook-bookmark: X2: -> f7b1eb17ad24730a1651fccd46c43826d1bbc2ac
59 test-hook-bookmark: X2: -> f7b1eb17ad24730a1651fccd46c43826d1bbc2ac
60
60
61 bookmark rev -1 again
61 bookmark rev -1 again
62
62
63 $ hg bookmark -r null Y
63 $ hg bookmark -r null Y
64
64
65 list bookmarks
65 list bookmarks
66
66
67 $ hg bookmarks
67 $ hg bookmarks
68 X 0:f7b1eb17ad24
68 X 0:f7b1eb17ad24
69 * X2 0:f7b1eb17ad24
69 * X2 0:f7b1eb17ad24
70 Y -1:000000000000
70 Y -1:000000000000
71 $ hg log -T '{bookmarks % "{rev} {bookmark}\n"}'
71 $ hg log -T '{bookmarks % "{rev} {bookmark}\n"}'
72 0 X
72 0 X
73 0 X2
73 0 X2
74
74
75 $ echo b > b
75 $ echo b > b
76 $ hg add b
76 $ hg add b
77 $ hg commit -m 1 --config "$TESTHOOK"
77 $ hg commit -m 1 --config "$TESTHOOK"
78 test-hook-bookmark: X2: f7b1eb17ad24730a1651fccd46c43826d1bbc2ac -> 925d80f479bb026b0fb3deb27503780b13f74123
78 test-hook-bookmark: X2: f7b1eb17ad24730a1651fccd46c43826d1bbc2ac -> 925d80f479bb026b0fb3deb27503780b13f74123
79
79
80 $ hg bookmarks -T '{rev}:{node|shortest} {bookmark} {desc|firstline}\n'
80 $ hg bookmarks -T '{rev}:{node|shortest} {bookmark} {desc|firstline}\n'
81 0:f7b1 X 0
81 0:f7b1 X 0
82 1:925d X2 1
82 1:925d X2 1
83 -1:0000 Y
83 -1:0000 Y
84
84
85 $ hg bookmarks -Tjson
85 $ hg bookmarks -Tjson
86 [
86 [
87 {
87 {
88 "active": false,
88 "active": false,
89 "bookmark": "X",
89 "bookmark": "X",
90 "node": "f7b1eb17ad24730a1651fccd46c43826d1bbc2ac",
90 "node": "f7b1eb17ad24730a1651fccd46c43826d1bbc2ac",
91 "rev": 0
91 "rev": 0
92 },
92 },
93 {
93 {
94 "active": true,
94 "active": true,
95 "bookmark": "X2",
95 "bookmark": "X2",
96 "node": "925d80f479bb026b0fb3deb27503780b13f74123",
96 "node": "925d80f479bb026b0fb3deb27503780b13f74123",
97 "rev": 1
97 "rev": 1
98 },
98 },
99 {
99 {
100 "active": false,
100 "active": false,
101 "bookmark": "Y",
101 "bookmark": "Y",
102 "node": "0000000000000000000000000000000000000000",
102 "node": "0000000000000000000000000000000000000000",
103 "rev": -1
103 "rev": -1
104 }
104 }
105 ]
105 ]
106
106
107 bookmarks revset
107 bookmarks revset
108
108
109 $ hg log -r 'bookmark()'
109 $ hg log -r 'bookmark()'
110 changeset: 0:f7b1eb17ad24
110 changeset: 0:f7b1eb17ad24
111 bookmark: X
111 bookmark: X
112 user: test
112 user: test
113 date: Thu Jan 01 00:00:00 1970 +0000
113 date: Thu Jan 01 00:00:00 1970 +0000
114 summary: 0
114 summary: 0
115
115
116 changeset: 1:925d80f479bb
116 changeset: 1:925d80f479bb
117 bookmark: X2
117 bookmark: X2
118 tag: tip
118 tag: tip
119 user: test
119 user: test
120 date: Thu Jan 01 00:00:00 1970 +0000
120 date: Thu Jan 01 00:00:00 1970 +0000
121 summary: 1
121 summary: 1
122
122
123 $ hg log -r 'bookmark(Y)'
123 $ hg log -r 'bookmark(Y)'
124 $ hg log -r 'bookmark(X2)'
124 $ hg log -r 'bookmark(X2)'
125 changeset: 1:925d80f479bb
125 changeset: 1:925d80f479bb
126 bookmark: X2
126 bookmark: X2
127 tag: tip
127 tag: tip
128 user: test
128 user: test
129 date: Thu Jan 01 00:00:00 1970 +0000
129 date: Thu Jan 01 00:00:00 1970 +0000
130 summary: 1
130 summary: 1
131
131
132 $ hg log -r 'bookmark("re:X")'
132 $ hg log -r 'bookmark("re:X")'
133 changeset: 0:f7b1eb17ad24
133 changeset: 0:f7b1eb17ad24
134 bookmark: X
134 bookmark: X
135 user: test
135 user: test
136 date: Thu Jan 01 00:00:00 1970 +0000
136 date: Thu Jan 01 00:00:00 1970 +0000
137 summary: 0
137 summary: 0
138
138
139 changeset: 1:925d80f479bb
139 changeset: 1:925d80f479bb
140 bookmark: X2
140 bookmark: X2
141 tag: tip
141 tag: tip
142 user: test
142 user: test
143 date: Thu Jan 01 00:00:00 1970 +0000
143 date: Thu Jan 01 00:00:00 1970 +0000
144 summary: 1
144 summary: 1
145
145
146 $ hg log -r 'bookmark("literal:X")'
146 $ hg log -r 'bookmark("literal:X")'
147 changeset: 0:f7b1eb17ad24
147 changeset: 0:f7b1eb17ad24
148 bookmark: X
148 bookmark: X
149 user: test
149 user: test
150 date: Thu Jan 01 00:00:00 1970 +0000
150 date: Thu Jan 01 00:00:00 1970 +0000
151 summary: 0
151 summary: 0
152
152
153
153
154 "." is expanded to the active bookmark:
154 "." is expanded to the active bookmark:
155
155
156 $ hg log -r 'bookmark(.)'
156 $ hg log -r 'bookmark(.)'
157 changeset: 1:925d80f479bb
157 changeset: 1:925d80f479bb
158 bookmark: X2
158 bookmark: X2
159 tag: tip
159 tag: tip
160 user: test
160 user: test
161 date: Thu Jan 01 00:00:00 1970 +0000
161 date: Thu Jan 01 00:00:00 1970 +0000
162 summary: 1
162 summary: 1
163
163
164
164
165 but "literal:." is not since "." seems not a literal bookmark:
165 but "literal:." is not since "." seems not a literal bookmark:
166
166
167 $ hg log -r 'bookmark("literal:.")'
167 $ hg log -r 'bookmark("literal:.")'
168 abort: bookmark '.' does not exist!
168 abort: bookmark '.' does not exist!
169 [255]
169 [255]
170
170
171 "." should fail if there's no active bookmark:
171 "." should fail if there's no active bookmark:
172
172
173 $ hg bookmark --inactive
173 $ hg bookmark --inactive
174 $ hg log -r 'bookmark(.)'
174 $ hg log -r 'bookmark(.)'
175 abort: no active bookmark!
175 abort: no active bookmark!
176 [255]
176 [255]
177 $ hg log -r 'present(bookmark(.))'
177 $ hg log -r 'present(bookmark(.))'
178
178
179 $ hg log -r 'bookmark(unknown)'
179 $ hg log -r 'bookmark(unknown)'
180 abort: bookmark 'unknown' does not exist!
180 abort: bookmark 'unknown' does not exist!
181 [255]
181 [255]
182 $ hg log -r 'bookmark("literal:unknown")'
182 $ hg log -r 'bookmark("literal:unknown")'
183 abort: bookmark 'unknown' does not exist!
183 abort: bookmark 'unknown' does not exist!
184 [255]
184 [255]
185 $ hg log -r 'bookmark("re:unknown")'
185 $ hg log -r 'bookmark("re:unknown")'
186 abort: no bookmarks exist that match 'unknown'!
186 abort: no bookmarks exist that match 'unknown'!
187 [255]
187 [255]
188 $ hg log -r 'present(bookmark("literal:unknown"))'
188 $ hg log -r 'present(bookmark("literal:unknown"))'
189 $ hg log -r 'present(bookmark("re:unknown"))'
189 $ hg log -r 'present(bookmark("re:unknown"))'
190
190
191 $ hg help revsets | grep 'bookmark('
191 $ hg help revsets | grep 'bookmark('
192 "bookmark([name])"
192 "bookmark([name])"
193
193
194 reactivate "X2"
194 reactivate "X2"
195
195
196 $ hg update X2
196 $ hg update X2
197 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
197 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
198 (activating bookmark X2)
198 (activating bookmark X2)
199
199
200 bookmarks X and X2 moved to rev 1, Y at rev -1
200 bookmarks X and X2 moved to rev 1, Y at rev -1
201
201
202 $ hg bookmarks
202 $ hg bookmarks
203 X 0:f7b1eb17ad24
203 X 0:f7b1eb17ad24
204 * X2 1:925d80f479bb
204 * X2 1:925d80f479bb
205 Y -1:000000000000
205 Y -1:000000000000
206
206
207 bookmark rev 0 again
207 bookmark rev 0 again
208
208
209 $ hg bookmark -r 0 Z
209 $ hg bookmark -r 0 Z
210
210
211 $ hg update X
211 $ hg update X
212 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
212 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
213 (activating bookmark X)
213 (activating bookmark X)
214 $ echo c > c
214 $ echo c > c
215 $ hg add c
215 $ hg add c
216 $ hg commit -m 2
216 $ hg commit -m 2
217 created new head
217 created new head
218
218
219 bookmarks X moved to rev 2, Y at rev -1, Z at rev 0
219 bookmarks X moved to rev 2, Y at rev -1, Z at rev 0
220
220
221 $ hg bookmarks
221 $ hg bookmarks
222 * X 2:db815d6d32e6
222 * X 2:db815d6d32e6
223 X2 1:925d80f479bb
223 X2 1:925d80f479bb
224 Y -1:000000000000
224 Y -1:000000000000
225 Z 0:f7b1eb17ad24
225 Z 0:f7b1eb17ad24
226
226
227 rename nonexistent bookmark
227 rename nonexistent bookmark
228
228
229 $ hg bookmark -m A B
229 $ hg bookmark -m A B
230 abort: bookmark 'A' does not exist
230 abort: bookmark 'A' does not exist
231 [255]
231 [255]
232
232
233 rename to existent bookmark
233 rename to existent bookmark
234
234
235 $ hg bookmark -m X Y
235 $ hg bookmark -m X Y
236 abort: bookmark 'Y' already exists (use -f to force)
236 abort: bookmark 'Y' already exists (use -f to force)
237 [255]
237 [255]
238
238
239 force rename to existent bookmark
239 force rename to existent bookmark
240
240
241 $ hg bookmark -f -m X Y
241 $ hg bookmark -f -m X Y
242
242
243 rename bookmark using .
243 rename bookmark using .
244
244
245 $ hg book rename-me
245 $ hg book rename-me
246 $ hg book -m . renamed --config "$TESTHOOK"
246 $ hg book -m . renamed --config "$TESTHOOK"
247 test-hook-bookmark: rename-me: db815d6d32e69058eadefc8cffbad37675707975 ->
247 test-hook-bookmark: rename-me: db815d6d32e69058eadefc8cffbad37675707975 ->
248 test-hook-bookmark: renamed: -> db815d6d32e69058eadefc8cffbad37675707975
248 test-hook-bookmark: renamed: -> db815d6d32e69058eadefc8cffbad37675707975
249 $ hg bookmark
249 $ hg bookmark
250 X2 1:925d80f479bb
250 X2 1:925d80f479bb
251 Y 2:db815d6d32e6
251 Y 2:db815d6d32e6
252 Z 0:f7b1eb17ad24
252 Z 0:f7b1eb17ad24
253 * renamed 2:db815d6d32e6
253 * renamed 2:db815d6d32e6
254 $ hg up -q Y
254 $ hg up -q Y
255 $ hg book -d renamed --config "$TESTHOOK"
255 $ hg book -d renamed --config "$TESTHOOK"
256 test-hook-bookmark: renamed: db815d6d32e69058eadefc8cffbad37675707975 ->
256 test-hook-bookmark: renamed: db815d6d32e69058eadefc8cffbad37675707975 ->
257
257
258 rename bookmark using . with no active bookmark
258 rename bookmark using . with no active bookmark
259
259
260 $ hg book rename-me
260 $ hg book rename-me
261 $ hg book -i rename-me
261 $ hg book -i rename-me
262 $ hg book -m . renamed
262 $ hg book -m . renamed
263 abort: no active bookmark!
263 abort: no active bookmark!
264 [255]
264 [255]
265 $ hg up -q Y
265 $ hg up -q Y
266 $ hg book -d rename-me
266 $ hg book -d rename-me
267
267
268 delete bookmark using .
268 delete bookmark using .
269
269
270 $ hg book delete-me
270 $ hg book delete-me
271 $ hg book -d .
271 $ hg book -d .
272 $ hg bookmark
272 $ hg bookmark
273 X2 1:925d80f479bb
273 X2 1:925d80f479bb
274 Y 2:db815d6d32e6
274 Y 2:db815d6d32e6
275 Z 0:f7b1eb17ad24
275 Z 0:f7b1eb17ad24
276 $ hg up -q Y
276 $ hg up -q Y
277
277
278 delete bookmark using . with no active bookmark
278 delete bookmark using . with no active bookmark
279
279
280 $ hg book delete-me
280 $ hg book delete-me
281 $ hg book -i delete-me
281 $ hg book -i delete-me
282 $ hg book -d .
282 $ hg book -d .
283 abort: no active bookmark!
283 abort: no active bookmark!
284 [255]
284 [255]
285 $ hg up -q Y
285 $ hg up -q Y
286 $ hg book -d delete-me
286 $ hg book -d delete-me
287
287
288 list bookmarks
288 list bookmarks
289
289
290 $ hg bookmark
290 $ hg bookmark
291 X2 1:925d80f479bb
291 X2 1:925d80f479bb
292 * Y 2:db815d6d32e6
292 * Y 2:db815d6d32e6
293 Z 0:f7b1eb17ad24
293 Z 0:f7b1eb17ad24
294
294
295 bookmarks from a revset
295 bookmarks from a revset
296 $ hg bookmark -r '.^1' REVSET
296 $ hg bookmark -r '.^1' REVSET
297 $ hg bookmark -r ':tip' TIP
297 $ hg bookmark -r ':tip' TIP
298 $ hg up -q TIP
298 $ hg up -q TIP
299 $ hg bookmarks
299 $ hg bookmarks
300 REVSET 0:f7b1eb17ad24
300 REVSET 0:f7b1eb17ad24
301 * TIP 2:db815d6d32e6
301 * TIP 2:db815d6d32e6
302 X2 1:925d80f479bb
302 X2 1:925d80f479bb
303 Y 2:db815d6d32e6
303 Y 2:db815d6d32e6
304 Z 0:f7b1eb17ad24
304 Z 0:f7b1eb17ad24
305
305
306 $ hg bookmark -d REVSET
306 $ hg bookmark -d REVSET
307 $ hg bookmark -d TIP
307 $ hg bookmark -d TIP
308
308
309 rename without new name or multiple names
309 rename without new name or multiple names
310
310
311 $ hg bookmark -m Y
311 $ hg bookmark -m Y
312 abort: new bookmark name required
312 abort: new bookmark name required
313 [255]
313 [255]
314 $ hg bookmark -m Y Y2 Y3
314 $ hg bookmark -m Y Y2 Y3
315 abort: only one new bookmark name allowed
315 abort: only one new bookmark name allowed
316 [255]
316 [255]
317
317
318 delete without name
318 delete without name
319
319
320 $ hg bookmark -d
320 $ hg bookmark -d
321 abort: bookmark name required
321 abort: bookmark name required
322 [255]
322 [255]
323
323
324 delete nonexistent bookmark
324 delete nonexistent bookmark
325
325
326 $ hg bookmark -d A
326 $ hg bookmark -d A
327 abort: bookmark 'A' does not exist
327 abort: bookmark 'A' does not exist
328 [255]
328 [255]
329
329
330 delete with --inactive
331
332 $ hg bookmark -d --inactive Y
333 abort: --inactive is incompatible with --delete
334 [255]
335
330 bookmark name with spaces should be stripped
336 bookmark name with spaces should be stripped
331
337
332 $ hg bookmark ' x y '
338 $ hg bookmark ' x y '
333
339
334 list bookmarks
340 list bookmarks
335
341
336 $ hg bookmarks
342 $ hg bookmarks
337 X2 1:925d80f479bb
343 X2 1:925d80f479bb
338 Y 2:db815d6d32e6
344 Y 2:db815d6d32e6
339 Z 0:f7b1eb17ad24
345 Z 0:f7b1eb17ad24
340 * x y 2:db815d6d32e6
346 * x y 2:db815d6d32e6
341 $ hg log -T '{bookmarks % "{rev} {bookmark}\n"}'
347 $ hg log -T '{bookmarks % "{rev} {bookmark}\n"}'
342 2 Y
348 2 Y
343 2 x y
349 2 x y
344 1 X2
350 1 X2
345 0 Z
351 0 Z
346
352
347 look up stripped bookmark name
353 look up stripped bookmark name
348
354
349 $ hg log -r '"x y"'
355 $ hg log -r '"x y"'
350 changeset: 2:db815d6d32e6
356 changeset: 2:db815d6d32e6
351 bookmark: Y
357 bookmark: Y
352 bookmark: x y
358 bookmark: x y
353 tag: tip
359 tag: tip
354 parent: 0:f7b1eb17ad24
360 parent: 0:f7b1eb17ad24
355 user: test
361 user: test
356 date: Thu Jan 01 00:00:00 1970 +0000
362 date: Thu Jan 01 00:00:00 1970 +0000
357 summary: 2
363 summary: 2
358
364
359
365
360 reject bookmark name with newline
366 reject bookmark name with newline
361
367
362 $ hg bookmark '
368 $ hg bookmark '
363 > '
369 > '
364 abort: bookmark names cannot consist entirely of whitespace
370 abort: bookmark names cannot consist entirely of whitespace
365 [255]
371 [255]
366
372
367 $ hg bookmark -m Z '
373 $ hg bookmark -m Z '
368 > '
374 > '
369 abort: bookmark names cannot consist entirely of whitespace
375 abort: bookmark names cannot consist entirely of whitespace
370 [255]
376 [255]
371
377
372 bookmark with reserved name
378 bookmark with reserved name
373
379
374 $ hg bookmark tip
380 $ hg bookmark tip
375 abort: the name 'tip' is reserved
381 abort: the name 'tip' is reserved
376 [255]
382 [255]
377
383
378 $ hg bookmark .
384 $ hg bookmark .
379 abort: the name '.' is reserved
385 abort: the name '.' is reserved
380 [255]
386 [255]
381
387
382 $ hg bookmark null
388 $ hg bookmark null
383 abort: the name 'null' is reserved
389 abort: the name 'null' is reserved
384 [255]
390 [255]
385
391
386
392
387 bookmark with existing name
393 bookmark with existing name
388
394
389 $ hg bookmark X2
395 $ hg bookmark X2
390 abort: bookmark 'X2' already exists (use -f to force)
396 abort: bookmark 'X2' already exists (use -f to force)
391 [255]
397 [255]
392
398
393 $ hg bookmark -m Y Z
399 $ hg bookmark -m Y Z
394 abort: bookmark 'Z' already exists (use -f to force)
400 abort: bookmark 'Z' already exists (use -f to force)
395 [255]
401 [255]
396
402
397 bookmark with name of branch
403 bookmark with name of branch
398
404
399 $ hg bookmark default
405 $ hg bookmark default
400 abort: a bookmark cannot have the name of an existing branch
406 abort: a bookmark cannot have the name of an existing branch
401 [255]
407 [255]
402
408
403 $ hg bookmark -m Y default
409 $ hg bookmark -m Y default
404 abort: a bookmark cannot have the name of an existing branch
410 abort: a bookmark cannot have the name of an existing branch
405 [255]
411 [255]
406
412
407 bookmark with integer name
413 bookmark with integer name
408
414
409 $ hg bookmark 10
415 $ hg bookmark 10
410 abort: cannot use an integer as a name
416 abort: cannot use an integer as a name
411 [255]
417 [255]
412
418
413 bookmark with a name that matches a node id
419 bookmark with a name that matches a node id
414 $ hg bookmark 925d80f479bb db815d6d32e6 --config "$TESTHOOK"
420 $ hg bookmark 925d80f479bb db815d6d32e6 --config "$TESTHOOK"
415 bookmark 925d80f479bb matches a changeset hash
421 bookmark 925d80f479bb matches a changeset hash
416 (did you leave a -r out of an 'hg bookmark' command?)
422 (did you leave a -r out of an 'hg bookmark' command?)
417 bookmark db815d6d32e6 matches a changeset hash
423 bookmark db815d6d32e6 matches a changeset hash
418 (did you leave a -r out of an 'hg bookmark' command?)
424 (did you leave a -r out of an 'hg bookmark' command?)
419 test-hook-bookmark: 925d80f479bb: -> db815d6d32e69058eadefc8cffbad37675707975
425 test-hook-bookmark: 925d80f479bb: -> db815d6d32e69058eadefc8cffbad37675707975
420 test-hook-bookmark: db815d6d32e6: -> db815d6d32e69058eadefc8cffbad37675707975
426 test-hook-bookmark: db815d6d32e6: -> db815d6d32e69058eadefc8cffbad37675707975
421 $ hg bookmark -d 925d80f479bb
427 $ hg bookmark -d 925d80f479bb
422 $ hg bookmark -d db815d6d32e6
428 $ hg bookmark -d db815d6d32e6
423
429
424 $ cd ..
430 $ cd ..
425
431
426 bookmark with a name that matches an ambiguous node id
432 bookmark with a name that matches an ambiguous node id
427
433
428 $ hg init ambiguous
434 $ hg init ambiguous
429 $ cd ambiguous
435 $ cd ambiguous
430 $ echo 0 > a
436 $ echo 0 > a
431 $ hg ci -qAm 0
437 $ hg ci -qAm 0
432 $ for i in 1057 2857 4025; do
438 $ for i in 1057 2857 4025; do
433 > hg up -q 0
439 > hg up -q 0
434 > echo $i > a
440 > echo $i > a
435 > hg ci -qm $i
441 > hg ci -qm $i
436 > done
442 > done
437 $ hg up -q null
443 $ hg up -q null
438 $ hg log -r0: -T '{rev}:{node}\n'
444 $ hg log -r0: -T '{rev}:{node}\n'
439 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a
445 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a
440 1:c56256a09cd28e5764f32e8e2810d0f01e2e357a
446 1:c56256a09cd28e5764f32e8e2810d0f01e2e357a
441 2:c5623987d205cd6d9d8389bfc40fff9dbb670b48
447 2:c5623987d205cd6d9d8389bfc40fff9dbb670b48
442 3:c562ddd9c94164376c20b86b0b4991636a3bf84f
448 3:c562ddd9c94164376c20b86b0b4991636a3bf84f
443
449
444 $ hg bookmark -r0 c562
450 $ hg bookmark -r0 c562
445 $ hg bookmarks
451 $ hg bookmarks
446 c562 0:b4e73ffab476
452 c562 0:b4e73ffab476
447
453
448 $ cd ..
454 $ cd ..
449
455
450 incompatible options
456 incompatible options
451
457
452 $ cd repo
458 $ cd repo
453
459
454 $ hg bookmark -m Y -d Z
460 $ hg bookmark -m Y -d Z
455 abort: --delete and --rename are incompatible
461 abort: --delete and --rename are incompatible
456 [255]
462 [255]
457
463
458 $ hg bookmark -r 1 -d Z
464 $ hg bookmark -r 1 -d Z
459 abort: --rev is incompatible with --delete
465 abort: --rev is incompatible with --delete
460 [255]
466 [255]
461
467
462 $ hg bookmark -r 1 -m Z Y
468 $ hg bookmark -r 1 -m Z Y
463 abort: --rev is incompatible with --rename
469 abort: --rev is incompatible with --rename
464 [255]
470 [255]
465
471
466 force bookmark with existing name
472 force bookmark with existing name
467
473
468 $ hg bookmark -f X2 --config "$TESTHOOK"
474 $ hg bookmark -f X2 --config "$TESTHOOK"
469 test-hook-bookmark: X2: 925d80f479bb026b0fb3deb27503780b13f74123 -> db815d6d32e69058eadefc8cffbad37675707975
475 test-hook-bookmark: X2: 925d80f479bb026b0fb3deb27503780b13f74123 -> db815d6d32e69058eadefc8cffbad37675707975
470
476
471 force bookmark back to where it was, should deactivate it
477 force bookmark back to where it was, should deactivate it
472
478
473 $ hg bookmark -fr1 X2
479 $ hg bookmark -fr1 X2
474 $ hg bookmarks
480 $ hg bookmarks
475 X2 1:925d80f479bb
481 X2 1:925d80f479bb
476 Y 2:db815d6d32e6
482 Y 2:db815d6d32e6
477 Z 0:f7b1eb17ad24
483 Z 0:f7b1eb17ad24
478 x y 2:db815d6d32e6
484 x y 2:db815d6d32e6
479
485
480 forward bookmark to descendant without --force
486 forward bookmark to descendant without --force
481
487
482 $ hg bookmark Z
488 $ hg bookmark Z
483 moving bookmark 'Z' forward from f7b1eb17ad24
489 moving bookmark 'Z' forward from f7b1eb17ad24
484
490
485 list bookmarks
491 list bookmarks
486
492
487 $ hg bookmark
493 $ hg bookmark
488 X2 1:925d80f479bb
494 X2 1:925d80f479bb
489 Y 2:db815d6d32e6
495 Y 2:db815d6d32e6
490 * Z 2:db815d6d32e6
496 * Z 2:db815d6d32e6
491 x y 2:db815d6d32e6
497 x y 2:db815d6d32e6
492 $ hg log -T '{bookmarks % "{rev} {bookmark}\n"}'
498 $ hg log -T '{bookmarks % "{rev} {bookmark}\n"}'
493 2 Y
499 2 Y
494 2 Z
500 2 Z
495 2 x y
501 2 x y
496 1 X2
502 1 X2
497
503
498 revision but no bookmark name
504 revision but no bookmark name
499
505
500 $ hg bookmark -r .
506 $ hg bookmark -r .
501 abort: bookmark name required
507 abort: bookmark name required
502 [255]
508 [255]
503
509
504 bookmark name with whitespace only
510 bookmark name with whitespace only
505
511
506 $ hg bookmark ' '
512 $ hg bookmark ' '
507 abort: bookmark names cannot consist entirely of whitespace
513 abort: bookmark names cannot consist entirely of whitespace
508 [255]
514 [255]
509
515
510 $ hg bookmark -m Y ' '
516 $ hg bookmark -m Y ' '
511 abort: bookmark names cannot consist entirely of whitespace
517 abort: bookmark names cannot consist entirely of whitespace
512 [255]
518 [255]
513
519
514 invalid bookmark
520 invalid bookmark
515
521
516 $ hg bookmark 'foo:bar'
522 $ hg bookmark 'foo:bar'
517 abort: ':' cannot be used in a name
523 abort: ':' cannot be used in a name
518 [255]
524 [255]
519
525
520 $ hg bookmark 'foo
526 $ hg bookmark 'foo
521 > bar'
527 > bar'
522 abort: '\n' cannot be used in a name
528 abort: '\n' cannot be used in a name
523 [255]
529 [255]
524
530
525 the bookmark extension should be ignored now that it is part of core
531 the bookmark extension should be ignored now that it is part of core
526
532
527 $ echo "[extensions]" >> $HGRCPATH
533 $ echo "[extensions]" >> $HGRCPATH
528 $ echo "bookmarks=" >> $HGRCPATH
534 $ echo "bookmarks=" >> $HGRCPATH
529 $ hg bookmarks
535 $ hg bookmarks
530 X2 1:925d80f479bb
536 X2 1:925d80f479bb
531 Y 2:db815d6d32e6
537 Y 2:db815d6d32e6
532 * Z 2:db815d6d32e6
538 * Z 2:db815d6d32e6
533 x y 2:db815d6d32e6
539 x y 2:db815d6d32e6
534
540
535 test summary
541 test summary
536
542
537 $ hg summary
543 $ hg summary
538 parent: 2:db815d6d32e6 tip
544 parent: 2:db815d6d32e6 tip
539 2
545 2
540 branch: default
546 branch: default
541 bookmarks: *Z Y x y
547 bookmarks: *Z Y x y
542 commit: (clean)
548 commit: (clean)
543 update: 1 new changesets, 2 branch heads (merge)
549 update: 1 new changesets, 2 branch heads (merge)
544 phases: 3 draft
550 phases: 3 draft
545
551
546 test id
552 test id
547
553
548 $ hg id
554 $ hg id
549 db815d6d32e6 tip Y/Z/x y
555 db815d6d32e6 tip Y/Z/x y
550
556
551 test rollback
557 test rollback
552
558
553 $ echo foo > f1
559 $ echo foo > f1
554 $ hg bookmark tmp-rollback
560 $ hg bookmark tmp-rollback
555 $ hg ci -Amr
561 $ hg ci -Amr
556 adding f1
562 adding f1
557 $ hg bookmarks
563 $ hg bookmarks
558 X2 1:925d80f479bb
564 X2 1:925d80f479bb
559 Y 2:db815d6d32e6
565 Y 2:db815d6d32e6
560 Z 2:db815d6d32e6
566 Z 2:db815d6d32e6
561 * tmp-rollback 3:2bf5cfec5864
567 * tmp-rollback 3:2bf5cfec5864
562 x y 2:db815d6d32e6
568 x y 2:db815d6d32e6
563 $ hg rollback
569 $ hg rollback
564 repository tip rolled back to revision 2 (undo commit)
570 repository tip rolled back to revision 2 (undo commit)
565 working directory now based on revision 2
571 working directory now based on revision 2
566 $ hg bookmarks
572 $ hg bookmarks
567 X2 1:925d80f479bb
573 X2 1:925d80f479bb
568 Y 2:db815d6d32e6
574 Y 2:db815d6d32e6
569 Z 2:db815d6d32e6
575 Z 2:db815d6d32e6
570 * tmp-rollback 2:db815d6d32e6
576 * tmp-rollback 2:db815d6d32e6
571 x y 2:db815d6d32e6
577 x y 2:db815d6d32e6
572 $ hg bookmark -f Z -r 1
578 $ hg bookmark -f Z -r 1
573 $ hg rollback
579 $ hg rollback
574 repository tip rolled back to revision 2 (undo bookmark)
580 repository tip rolled back to revision 2 (undo bookmark)
575 $ hg bookmarks
581 $ hg bookmarks
576 X2 1:925d80f479bb
582 X2 1:925d80f479bb
577 Y 2:db815d6d32e6
583 Y 2:db815d6d32e6
578 Z 2:db815d6d32e6
584 Z 2:db815d6d32e6
579 * tmp-rollback 2:db815d6d32e6
585 * tmp-rollback 2:db815d6d32e6
580 x y 2:db815d6d32e6
586 x y 2:db815d6d32e6
581 $ hg bookmark -d tmp-rollback
587 $ hg bookmark -d tmp-rollback
582
588
583 activate bookmark on working dir parent without --force
589 activate bookmark on working dir parent without --force
584
590
585 $ hg bookmark --inactive Z
591 $ hg bookmark --inactive Z
586 $ hg bookmark Z
592 $ hg bookmark Z
587
593
588 test clone
594 test clone
589
595
590 $ hg bookmark -r 2 -i @
596 $ hg bookmark -r 2 -i @
591 $ hg bookmark -r 2 -i a@
597 $ hg bookmark -r 2 -i a@
592 $ hg bookmarks
598 $ hg bookmarks
593 @ 2:db815d6d32e6
599 @ 2:db815d6d32e6
594 X2 1:925d80f479bb
600 X2 1:925d80f479bb
595 Y 2:db815d6d32e6
601 Y 2:db815d6d32e6
596 * Z 2:db815d6d32e6
602 * Z 2:db815d6d32e6
597 a@ 2:db815d6d32e6
603 a@ 2:db815d6d32e6
598 x y 2:db815d6d32e6
604 x y 2:db815d6d32e6
599 $ hg clone . cloned-bookmarks
605 $ hg clone . cloned-bookmarks
600 updating to bookmark @
606 updating to bookmark @
601 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
607 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
602 $ hg -R cloned-bookmarks bookmarks
608 $ hg -R cloned-bookmarks bookmarks
603 * @ 2:db815d6d32e6
609 * @ 2:db815d6d32e6
604 X2 1:925d80f479bb
610 X2 1:925d80f479bb
605 Y 2:db815d6d32e6
611 Y 2:db815d6d32e6
606 Z 2:db815d6d32e6
612 Z 2:db815d6d32e6
607 a@ 2:db815d6d32e6
613 a@ 2:db815d6d32e6
608 x y 2:db815d6d32e6
614 x y 2:db815d6d32e6
609
615
610 test clone with pull protocol
616 test clone with pull protocol
611
617
612 $ hg clone --pull . cloned-bookmarks-pull
618 $ hg clone --pull . cloned-bookmarks-pull
613 requesting all changes
619 requesting all changes
614 adding changesets
620 adding changesets
615 adding manifests
621 adding manifests
616 adding file changes
622 adding file changes
617 added 3 changesets with 3 changes to 3 files (+1 heads)
623 added 3 changesets with 3 changes to 3 files (+1 heads)
618 new changesets f7b1eb17ad24:db815d6d32e6
624 new changesets f7b1eb17ad24:db815d6d32e6
619 updating to bookmark @
625 updating to bookmark @
620 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
626 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
621 $ hg -R cloned-bookmarks-pull bookmarks
627 $ hg -R cloned-bookmarks-pull bookmarks
622 * @ 2:db815d6d32e6
628 * @ 2:db815d6d32e6
623 X2 1:925d80f479bb
629 X2 1:925d80f479bb
624 Y 2:db815d6d32e6
630 Y 2:db815d6d32e6
625 Z 2:db815d6d32e6
631 Z 2:db815d6d32e6
626 a@ 2:db815d6d32e6
632 a@ 2:db815d6d32e6
627 x y 2:db815d6d32e6
633 x y 2:db815d6d32e6
628
634
629 delete multiple bookmarks at once
635 delete multiple bookmarks at once
630
636
631 $ hg bookmark -d @ a@
637 $ hg bookmark -d @ a@
632
638
633 test clone with a bookmark named "default" (issue3677)
639 test clone with a bookmark named "default" (issue3677)
634
640
635 $ hg bookmark -r 1 -f -i default
641 $ hg bookmark -r 1 -f -i default
636 $ hg clone . cloned-bookmark-default
642 $ hg clone . cloned-bookmark-default
637 updating to branch default
643 updating to branch default
638 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
644 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
639 $ hg -R cloned-bookmark-default bookmarks
645 $ hg -R cloned-bookmark-default bookmarks
640 X2 1:925d80f479bb
646 X2 1:925d80f479bb
641 Y 2:db815d6d32e6
647 Y 2:db815d6d32e6
642 Z 2:db815d6d32e6
648 Z 2:db815d6d32e6
643 default 1:925d80f479bb
649 default 1:925d80f479bb
644 x y 2:db815d6d32e6
650 x y 2:db815d6d32e6
645 $ hg -R cloned-bookmark-default parents -q
651 $ hg -R cloned-bookmark-default parents -q
646 2:db815d6d32e6
652 2:db815d6d32e6
647 $ hg bookmark -d default
653 $ hg bookmark -d default
648
654
649 test clone with a specific revision
655 test clone with a specific revision
650
656
651 $ hg clone -r 925d80 . cloned-bookmarks-rev
657 $ hg clone -r 925d80 . cloned-bookmarks-rev
652 adding changesets
658 adding changesets
653 adding manifests
659 adding manifests
654 adding file changes
660 adding file changes
655 added 2 changesets with 2 changes to 2 files
661 added 2 changesets with 2 changes to 2 files
656 new changesets f7b1eb17ad24:925d80f479bb
662 new changesets f7b1eb17ad24:925d80f479bb
657 updating to branch default
663 updating to branch default
658 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
664 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
659 $ hg -R cloned-bookmarks-rev bookmarks
665 $ hg -R cloned-bookmarks-rev bookmarks
660 X2 1:925d80f479bb
666 X2 1:925d80f479bb
661
667
662 test clone with update to a bookmark
668 test clone with update to a bookmark
663
669
664 $ hg clone -u Z . ../cloned-bookmarks-update
670 $ hg clone -u Z . ../cloned-bookmarks-update
665 updating to branch default
671 updating to branch default
666 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
672 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
667 $ hg -R ../cloned-bookmarks-update bookmarks
673 $ hg -R ../cloned-bookmarks-update bookmarks
668 X2 1:925d80f479bb
674 X2 1:925d80f479bb
669 Y 2:db815d6d32e6
675 Y 2:db815d6d32e6
670 * Z 2:db815d6d32e6
676 * Z 2:db815d6d32e6
671 x y 2:db815d6d32e6
677 x y 2:db815d6d32e6
672
678
673 create bundle with two heads
679 create bundle with two heads
674
680
675 $ hg clone . tobundle
681 $ hg clone . tobundle
676 updating to branch default
682 updating to branch default
677 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
683 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
678 $ echo x > tobundle/x
684 $ echo x > tobundle/x
679 $ hg -R tobundle add tobundle/x
685 $ hg -R tobundle add tobundle/x
680 $ hg -R tobundle commit -m'x'
686 $ hg -R tobundle commit -m'x'
681 $ hg -R tobundle update -r -2
687 $ hg -R tobundle update -r -2
682 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
688 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
683 $ echo y > tobundle/y
689 $ echo y > tobundle/y
684 $ hg -R tobundle branch test
690 $ hg -R tobundle branch test
685 marked working directory as branch test
691 marked working directory as branch test
686 (branches are permanent and global, did you want a bookmark?)
692 (branches are permanent and global, did you want a bookmark?)
687 $ hg -R tobundle add tobundle/y
693 $ hg -R tobundle add tobundle/y
688 $ hg -R tobundle commit -m'y'
694 $ hg -R tobundle commit -m'y'
689 $ hg -R tobundle bundle tobundle.hg
695 $ hg -R tobundle bundle tobundle.hg
690 searching for changes
696 searching for changes
691 2 changesets found
697 2 changesets found
692 $ hg unbundle tobundle.hg
698 $ hg unbundle tobundle.hg
693 adding changesets
699 adding changesets
694 adding manifests
700 adding manifests
695 adding file changes
701 adding file changes
696 added 2 changesets with 2 changes to 2 files (+1 heads)
702 added 2 changesets with 2 changes to 2 files (+1 heads)
697 new changesets 125c9a1d6df6:9ba5f110a0b3 (2 drafts)
703 new changesets 125c9a1d6df6:9ba5f110a0b3 (2 drafts)
698 (run 'hg heads' to see heads, 'hg merge' to merge)
704 (run 'hg heads' to see heads, 'hg merge' to merge)
699
705
700 update to active bookmark if it's not the parent
706 update to active bookmark if it's not the parent
701
707
702 (it is known issue that fsmonitor can't handle nested repositories. In
708 (it is known issue that fsmonitor can't handle nested repositories. In
703 this test scenario, cloned-bookmark-default and tobundle exist in the
709 this test scenario, cloned-bookmark-default and tobundle exist in the
704 working directory of current repository)
710 working directory of current repository)
705
711
706 $ hg summary
712 $ hg summary
707 parent: 2:db815d6d32e6
713 parent: 2:db815d6d32e6
708 2
714 2
709 branch: default
715 branch: default
710 bookmarks: *Z Y x y
716 bookmarks: *Z Y x y
711 commit: 1 added, 1 unknown (new branch head) (no-fsmonitor !)
717 commit: 1 added, 1 unknown (new branch head) (no-fsmonitor !)
712 commit: 1 added, * unknown (new branch head) (glob) (fsmonitor !)
718 commit: 1 added, * unknown (new branch head) (glob) (fsmonitor !)
713 update: 2 new changesets (update)
719 update: 2 new changesets (update)
714 phases: 5 draft
720 phases: 5 draft
715 $ hg update
721 $ hg update
716 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
722 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
717 updating bookmark Z
723 updating bookmark Z
718 $ hg bookmarks
724 $ hg bookmarks
719 X2 1:925d80f479bb
725 X2 1:925d80f479bb
720 Y 2:db815d6d32e6
726 Y 2:db815d6d32e6
721 * Z 3:125c9a1d6df6
727 * Z 3:125c9a1d6df6
722 x y 2:db815d6d32e6
728 x y 2:db815d6d32e6
723
729
724 pull --update works the same as pull && update
730 pull --update works the same as pull && update
725
731
726 $ hg bookmark -r3 Y
732 $ hg bookmark -r3 Y
727 moving bookmark 'Y' forward from db815d6d32e6
733 moving bookmark 'Y' forward from db815d6d32e6
728 $ cp -R ../cloned-bookmarks-update ../cloned-bookmarks-manual-update
734 $ cp -R ../cloned-bookmarks-update ../cloned-bookmarks-manual-update
729 $ cp -R ../cloned-bookmarks-update ../cloned-bookmarks-manual-update-with-divergence
735 $ cp -R ../cloned-bookmarks-update ../cloned-bookmarks-manual-update-with-divergence
730
736
731 (manual version)
737 (manual version)
732
738
733 $ hg -R ../cloned-bookmarks-manual-update update Y
739 $ hg -R ../cloned-bookmarks-manual-update update Y
734 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
740 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
735 (activating bookmark Y)
741 (activating bookmark Y)
736 $ hg -R ../cloned-bookmarks-manual-update pull .
742 $ hg -R ../cloned-bookmarks-manual-update pull .
737 pulling from .
743 pulling from .
738 searching for changes
744 searching for changes
739 adding changesets
745 adding changesets
740 adding manifests
746 adding manifests
741 adding file changes
747 adding file changes
742 added 2 changesets with 2 changes to 2 files (+1 heads)
748 added 2 changesets with 2 changes to 2 files (+1 heads)
743 updating bookmark Y
749 updating bookmark Y
744 updating bookmark Z
750 updating bookmark Z
745 new changesets 125c9a1d6df6:9ba5f110a0b3
751 new changesets 125c9a1d6df6:9ba5f110a0b3
746 (run 'hg heads' to see heads, 'hg merge' to merge)
752 (run 'hg heads' to see heads, 'hg merge' to merge)
747
753
748 (# tests strange but with --date crashing when bookmark have to move)
754 (# tests strange but with --date crashing when bookmark have to move)
749
755
750 $ hg -R ../cloned-bookmarks-manual-update update -d 1986
756 $ hg -R ../cloned-bookmarks-manual-update update -d 1986
751 abort: revision matching date not found
757 abort: revision matching date not found
752 [255]
758 [255]
753 $ hg -R ../cloned-bookmarks-manual-update update
759 $ hg -R ../cloned-bookmarks-manual-update update
754 updating to active bookmark Y
760 updating to active bookmark Y
755 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
761 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
756
762
757 (all in one version)
763 (all in one version)
758
764
759 $ hg -R ../cloned-bookmarks-update update Y
765 $ hg -R ../cloned-bookmarks-update update Y
760 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
766 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
761 (activating bookmark Y)
767 (activating bookmark Y)
762 $ hg -R ../cloned-bookmarks-update pull --update .
768 $ hg -R ../cloned-bookmarks-update pull --update .
763 pulling from .
769 pulling from .
764 searching for changes
770 searching for changes
765 adding changesets
771 adding changesets
766 adding manifests
772 adding manifests
767 adding file changes
773 adding file changes
768 added 2 changesets with 2 changes to 2 files (+1 heads)
774 added 2 changesets with 2 changes to 2 files (+1 heads)
769 updating bookmark Y
775 updating bookmark Y
770 updating bookmark Z
776 updating bookmark Z
771 new changesets 125c9a1d6df6:9ba5f110a0b3
777 new changesets 125c9a1d6df6:9ba5f110a0b3
772 updating to active bookmark Y
778 updating to active bookmark Y
773 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
779 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
774
780
775 We warn about divergent during bare update to the active bookmark
781 We warn about divergent during bare update to the active bookmark
776
782
777 $ hg -R ../cloned-bookmarks-manual-update-with-divergence update Y
783 $ hg -R ../cloned-bookmarks-manual-update-with-divergence update Y
778 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
784 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
779 (activating bookmark Y)
785 (activating bookmark Y)
780 $ hg -R ../cloned-bookmarks-manual-update-with-divergence bookmarks -r X2 Y@1
786 $ hg -R ../cloned-bookmarks-manual-update-with-divergence bookmarks -r X2 Y@1
781 $ hg -R ../cloned-bookmarks-manual-update-with-divergence bookmarks
787 $ hg -R ../cloned-bookmarks-manual-update-with-divergence bookmarks
782 X2 1:925d80f479bb
788 X2 1:925d80f479bb
783 * Y 2:db815d6d32e6
789 * Y 2:db815d6d32e6
784 Y@1 1:925d80f479bb
790 Y@1 1:925d80f479bb
785 Z 2:db815d6d32e6
791 Z 2:db815d6d32e6
786 x y 2:db815d6d32e6
792 x y 2:db815d6d32e6
787 $ hg -R ../cloned-bookmarks-manual-update-with-divergence pull
793 $ hg -R ../cloned-bookmarks-manual-update-with-divergence pull
788 pulling from $TESTTMP/repo
794 pulling from $TESTTMP/repo
789 searching for changes
795 searching for changes
790 adding changesets
796 adding changesets
791 adding manifests
797 adding manifests
792 adding file changes
798 adding file changes
793 added 2 changesets with 2 changes to 2 files (+1 heads)
799 added 2 changesets with 2 changes to 2 files (+1 heads)
794 updating bookmark Y
800 updating bookmark Y
795 updating bookmark Z
801 updating bookmark Z
796 new changesets 125c9a1d6df6:9ba5f110a0b3
802 new changesets 125c9a1d6df6:9ba5f110a0b3
797 (run 'hg heads' to see heads, 'hg merge' to merge)
803 (run 'hg heads' to see heads, 'hg merge' to merge)
798 $ hg -R ../cloned-bookmarks-manual-update-with-divergence update
804 $ hg -R ../cloned-bookmarks-manual-update-with-divergence update
799 updating to active bookmark Y
805 updating to active bookmark Y
800 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
806 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
801 1 other divergent bookmarks for "Y"
807 1 other divergent bookmarks for "Y"
802
808
803 test wrongly formated bookmark
809 test wrongly formated bookmark
804
810
805 $ echo '' >> .hg/bookmarks
811 $ echo '' >> .hg/bookmarks
806 $ hg bookmarks
812 $ hg bookmarks
807 X2 1:925d80f479bb
813 X2 1:925d80f479bb
808 Y 3:125c9a1d6df6
814 Y 3:125c9a1d6df6
809 * Z 3:125c9a1d6df6
815 * Z 3:125c9a1d6df6
810 x y 2:db815d6d32e6
816 x y 2:db815d6d32e6
811 $ echo "Ican'thasformatedlines" >> .hg/bookmarks
817 $ echo "Ican'thasformatedlines" >> .hg/bookmarks
812 $ hg bookmarks
818 $ hg bookmarks
813 malformed line in .hg/bookmarks: "Ican'thasformatedlines"
819 malformed line in .hg/bookmarks: "Ican'thasformatedlines"
814 X2 1:925d80f479bb
820 X2 1:925d80f479bb
815 Y 3:125c9a1d6df6
821 Y 3:125c9a1d6df6
816 * Z 3:125c9a1d6df6
822 * Z 3:125c9a1d6df6
817 x y 2:db815d6d32e6
823 x y 2:db815d6d32e6
818
824
819 test missing revisions
825 test missing revisions
820
826
821 $ echo "925d80f479b925d80f479bc925d80f479bccabab z" > .hg/bookmarks
827 $ echo "925d80f479b925d80f479bc925d80f479bccabab z" > .hg/bookmarks
822 $ hg book
828 $ hg book
823 no bookmarks set
829 no bookmarks set
824
830
825 test stripping a non-checked-out but bookmarked revision
831 test stripping a non-checked-out but bookmarked revision
826
832
827 $ hg log --graph
833 $ hg log --graph
828 o changeset: 4:9ba5f110a0b3
834 o changeset: 4:9ba5f110a0b3
829 | branch: test
835 | branch: test
830 | tag: tip
836 | tag: tip
831 | parent: 2:db815d6d32e6
837 | parent: 2:db815d6d32e6
832 | user: test
838 | user: test
833 | date: Thu Jan 01 00:00:00 1970 +0000
839 | date: Thu Jan 01 00:00:00 1970 +0000
834 | summary: y
840 | summary: y
835 |
841 |
836 | @ changeset: 3:125c9a1d6df6
842 | @ changeset: 3:125c9a1d6df6
837 |/ user: test
843 |/ user: test
838 | date: Thu Jan 01 00:00:00 1970 +0000
844 | date: Thu Jan 01 00:00:00 1970 +0000
839 | summary: x
845 | summary: x
840 |
846 |
841 o changeset: 2:db815d6d32e6
847 o changeset: 2:db815d6d32e6
842 | parent: 0:f7b1eb17ad24
848 | parent: 0:f7b1eb17ad24
843 | user: test
849 | user: test
844 | date: Thu Jan 01 00:00:00 1970 +0000
850 | date: Thu Jan 01 00:00:00 1970 +0000
845 | summary: 2
851 | summary: 2
846 |
852 |
847 | o changeset: 1:925d80f479bb
853 | o changeset: 1:925d80f479bb
848 |/ user: test
854 |/ user: test
849 | date: Thu Jan 01 00:00:00 1970 +0000
855 | date: Thu Jan 01 00:00:00 1970 +0000
850 | summary: 1
856 | summary: 1
851 |
857 |
852 o changeset: 0:f7b1eb17ad24
858 o changeset: 0:f7b1eb17ad24
853 user: test
859 user: test
854 date: Thu Jan 01 00:00:00 1970 +0000
860 date: Thu Jan 01 00:00:00 1970 +0000
855 summary: 0
861 summary: 0
856
862
857 $ hg book should-end-on-two
863 $ hg book should-end-on-two
858 $ hg co --clean 4
864 $ hg co --clean 4
859 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
865 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
860 (leaving bookmark should-end-on-two)
866 (leaving bookmark should-end-on-two)
861 $ hg book four
867 $ hg book four
862 $ hg --config extensions.mq= strip 3
868 $ hg --config extensions.mq= strip 3
863 saved backup bundle to * (glob)
869 saved backup bundle to * (glob)
864 should-end-on-two should end up pointing to revision 2, as that's the
870 should-end-on-two should end up pointing to revision 2, as that's the
865 tipmost surviving ancestor of the stripped revision.
871 tipmost surviving ancestor of the stripped revision.
866 $ hg log --graph
872 $ hg log --graph
867 @ changeset: 3:9ba5f110a0b3
873 @ changeset: 3:9ba5f110a0b3
868 | branch: test
874 | branch: test
869 | bookmark: four
875 | bookmark: four
870 | tag: tip
876 | tag: tip
871 | user: test
877 | user: test
872 | date: Thu Jan 01 00:00:00 1970 +0000
878 | date: Thu Jan 01 00:00:00 1970 +0000
873 | summary: y
879 | summary: y
874 |
880 |
875 o changeset: 2:db815d6d32e6
881 o changeset: 2:db815d6d32e6
876 | bookmark: should-end-on-two
882 | bookmark: should-end-on-two
877 | parent: 0:f7b1eb17ad24
883 | parent: 0:f7b1eb17ad24
878 | user: test
884 | user: test
879 | date: Thu Jan 01 00:00:00 1970 +0000
885 | date: Thu Jan 01 00:00:00 1970 +0000
880 | summary: 2
886 | summary: 2
881 |
887 |
882 | o changeset: 1:925d80f479bb
888 | o changeset: 1:925d80f479bb
883 |/ user: test
889 |/ user: test
884 | date: Thu Jan 01 00:00:00 1970 +0000
890 | date: Thu Jan 01 00:00:00 1970 +0000
885 | summary: 1
891 | summary: 1
886 |
892 |
887 o changeset: 0:f7b1eb17ad24
893 o changeset: 0:f7b1eb17ad24
888 user: test
894 user: test
889 date: Thu Jan 01 00:00:00 1970 +0000
895 date: Thu Jan 01 00:00:00 1970 +0000
890 summary: 0
896 summary: 0
891
897
892
898
893 no-op update doesn't deactivate bookmarks
899 no-op update doesn't deactivate bookmarks
894
900
895 (it is known issue that fsmonitor can't handle nested repositories. In
901 (it is known issue that fsmonitor can't handle nested repositories. In
896 this test scenario, cloned-bookmark-default and tobundle exist in the
902 this test scenario, cloned-bookmark-default and tobundle exist in the
897 working directory of current repository)
903 working directory of current repository)
898
904
899 $ hg bookmarks
905 $ hg bookmarks
900 * four 3:9ba5f110a0b3
906 * four 3:9ba5f110a0b3
901 should-end-on-two 2:db815d6d32e6
907 should-end-on-two 2:db815d6d32e6
902 $ hg up four
908 $ hg up four
903 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
909 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
904 $ hg up
910 $ hg up
905 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
911 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
906 $ hg sum
912 $ hg sum
907 parent: 3:9ba5f110a0b3 tip
913 parent: 3:9ba5f110a0b3 tip
908 y
914 y
909 branch: test
915 branch: test
910 bookmarks: *four
916 bookmarks: *four
911 commit: 2 unknown (clean) (no-fsmonitor !)
917 commit: 2 unknown (clean) (no-fsmonitor !)
912 commit: * unknown (clean) (glob) (fsmonitor !)
918 commit: * unknown (clean) (glob) (fsmonitor !)
913 update: (current)
919 update: (current)
914 phases: 4 draft
920 phases: 4 draft
915
921
916 test clearing divergent bookmarks of linear ancestors
922 test clearing divergent bookmarks of linear ancestors
917
923
918 $ hg bookmark Z -r 0
924 $ hg bookmark Z -r 0
919 $ hg bookmark Z@1 -r 1
925 $ hg bookmark Z@1 -r 1
920 $ hg bookmark Z@2 -r 2
926 $ hg bookmark Z@2 -r 2
921 $ hg bookmark Z@3 -r 3
927 $ hg bookmark Z@3 -r 3
922 $ hg book
928 $ hg book
923 Z 0:f7b1eb17ad24
929 Z 0:f7b1eb17ad24
924 Z@1 1:925d80f479bb
930 Z@1 1:925d80f479bb
925 Z@2 2:db815d6d32e6
931 Z@2 2:db815d6d32e6
926 Z@3 3:9ba5f110a0b3
932 Z@3 3:9ba5f110a0b3
927 * four 3:9ba5f110a0b3
933 * four 3:9ba5f110a0b3
928 should-end-on-two 2:db815d6d32e6
934 should-end-on-two 2:db815d6d32e6
929 $ hg bookmark Z
935 $ hg bookmark Z
930 moving bookmark 'Z' forward from f7b1eb17ad24
936 moving bookmark 'Z' forward from f7b1eb17ad24
931 $ hg book
937 $ hg book
932 * Z 3:9ba5f110a0b3
938 * Z 3:9ba5f110a0b3
933 Z@1 1:925d80f479bb
939 Z@1 1:925d80f479bb
934 four 3:9ba5f110a0b3
940 four 3:9ba5f110a0b3
935 should-end-on-two 2:db815d6d32e6
941 should-end-on-two 2:db815d6d32e6
936
942
937 test clearing only a single divergent bookmark across branches
943 test clearing only a single divergent bookmark across branches
938
944
939 $ hg book foo -r 1
945 $ hg book foo -r 1
940 $ hg book foo@1 -r 0
946 $ hg book foo@1 -r 0
941 $ hg book foo@2 -r 2
947 $ hg book foo@2 -r 2
942 $ hg book foo@3 -r 3
948 $ hg book foo@3 -r 3
943 $ hg book foo -r foo@3
949 $ hg book foo -r foo@3
944 $ hg book
950 $ hg book
945 * Z 3:9ba5f110a0b3
951 * Z 3:9ba5f110a0b3
946 Z@1 1:925d80f479bb
952 Z@1 1:925d80f479bb
947 foo 3:9ba5f110a0b3
953 foo 3:9ba5f110a0b3
948 foo@1 0:f7b1eb17ad24
954 foo@1 0:f7b1eb17ad24
949 foo@2 2:db815d6d32e6
955 foo@2 2:db815d6d32e6
950 four 3:9ba5f110a0b3
956 four 3:9ba5f110a0b3
951 should-end-on-two 2:db815d6d32e6
957 should-end-on-two 2:db815d6d32e6
952
958
953 pull --update works the same as pull && update (case #2)
959 pull --update works the same as pull && update (case #2)
954
960
955 It is assumed that "hg pull" itself doesn't update current active
961 It is assumed that "hg pull" itself doesn't update current active
956 bookmark ('Y' in tests below).
962 bookmark ('Y' in tests below).
957
963
958 $ hg pull -q ../cloned-bookmarks-update
964 $ hg pull -q ../cloned-bookmarks-update
959 divergent bookmark Z stored as Z@2
965 divergent bookmark Z stored as Z@2
960
966
961 (pulling revision on another named branch with --update updates
967 (pulling revision on another named branch with --update updates
962 neither the working directory nor current active bookmark: "no-op"
968 neither the working directory nor current active bookmark: "no-op"
963 case)
969 case)
964
970
965 $ echo yy >> y
971 $ echo yy >> y
966 $ hg commit -m yy
972 $ hg commit -m yy
967
973
968 $ hg -R ../cloned-bookmarks-update bookmarks | grep ' Y '
974 $ hg -R ../cloned-bookmarks-update bookmarks | grep ' Y '
969 * Y 3:125c9a1d6df6
975 * Y 3:125c9a1d6df6
970 $ hg -R ../cloned-bookmarks-update pull . --update
976 $ hg -R ../cloned-bookmarks-update pull . --update
971 pulling from .
977 pulling from .
972 searching for changes
978 searching for changes
973 adding changesets
979 adding changesets
974 adding manifests
980 adding manifests
975 adding file changes
981 adding file changes
976 added 1 changesets with 1 changes to 1 files
982 added 1 changesets with 1 changes to 1 files
977 divergent bookmark Z stored as Z@default
983 divergent bookmark Z stored as Z@default
978 adding remote bookmark foo
984 adding remote bookmark foo
979 adding remote bookmark four
985 adding remote bookmark four
980 adding remote bookmark should-end-on-two
986 adding remote bookmark should-end-on-two
981 new changesets 5fb12f0f2d51
987 new changesets 5fb12f0f2d51
982 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
988 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
983 $ hg -R ../cloned-bookmarks-update parents -T "{rev}:{node|short}\n"
989 $ hg -R ../cloned-bookmarks-update parents -T "{rev}:{node|short}\n"
984 3:125c9a1d6df6
990 3:125c9a1d6df6
985 $ hg -R ../cloned-bookmarks-update bookmarks | grep ' Y '
991 $ hg -R ../cloned-bookmarks-update bookmarks | grep ' Y '
986 * Y 3:125c9a1d6df6
992 * Y 3:125c9a1d6df6
987
993
988 (pulling revision on current named/topological branch with --update
994 (pulling revision on current named/topological branch with --update
989 updates the working directory and current active bookmark)
995 updates the working directory and current active bookmark)
990
996
991 $ hg update -C -q 125c9a1d6df6
997 $ hg update -C -q 125c9a1d6df6
992 $ echo xx >> x
998 $ echo xx >> x
993 $ hg commit -m xx
999 $ hg commit -m xx
994
1000
995 $ hg -R ../cloned-bookmarks-update bookmarks | grep ' Y '
1001 $ hg -R ../cloned-bookmarks-update bookmarks | grep ' Y '
996 * Y 3:125c9a1d6df6
1002 * Y 3:125c9a1d6df6
997 $ hg -R ../cloned-bookmarks-update pull . --update
1003 $ hg -R ../cloned-bookmarks-update pull . --update
998 pulling from .
1004 pulling from .
999 searching for changes
1005 searching for changes
1000 adding changesets
1006 adding changesets
1001 adding manifests
1007 adding manifests
1002 adding file changes
1008 adding file changes
1003 added 1 changesets with 1 changes to 1 files
1009 added 1 changesets with 1 changes to 1 files
1004 divergent bookmark Z stored as Z@default
1010 divergent bookmark Z stored as Z@default
1005 new changesets 81dcce76aa0b
1011 new changesets 81dcce76aa0b
1006 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1012 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1007 updating bookmark Y
1013 updating bookmark Y
1008 $ hg -R ../cloned-bookmarks-update parents -T "{rev}:{node|short}\n"
1014 $ hg -R ../cloned-bookmarks-update parents -T "{rev}:{node|short}\n"
1009 6:81dcce76aa0b
1015 6:81dcce76aa0b
1010 $ hg -R ../cloned-bookmarks-update bookmarks | grep ' Y '
1016 $ hg -R ../cloned-bookmarks-update bookmarks | grep ' Y '
1011 * Y 6:81dcce76aa0b
1017 * Y 6:81dcce76aa0b
1012
1018
1013 $ cd ..
1019 $ cd ..
1014
1020
1015 ensure changelog is written before bookmarks
1021 ensure changelog is written before bookmarks
1016 $ hg init orderrepo
1022 $ hg init orderrepo
1017 $ cd orderrepo
1023 $ cd orderrepo
1018 $ touch a
1024 $ touch a
1019 $ hg commit -Aqm one
1025 $ hg commit -Aqm one
1020 $ hg book mybook
1026 $ hg book mybook
1021 $ echo a > a
1027 $ echo a > a
1022
1028
1023 $ cat > $TESTTMP/pausefinalize.py <<EOF
1029 $ cat > $TESTTMP/pausefinalize.py <<EOF
1024 > from __future__ import absolute_import
1030 > from __future__ import absolute_import
1025 > import os
1031 > import os
1026 > import time
1032 > import time
1027 > from mercurial import extensions, localrepo
1033 > from mercurial import extensions, localrepo
1028 > def transaction(orig, self, desc, report=None):
1034 > def transaction(orig, self, desc, report=None):
1029 > tr = orig(self, desc, report)
1035 > tr = orig(self, desc, report)
1030 > def sleep(*args, **kwargs):
1036 > def sleep(*args, **kwargs):
1031 > retry = 20
1037 > retry = 20
1032 > while retry > 0 and not os.path.exists(b"$TESTTMP/unpause"):
1038 > while retry > 0 and not os.path.exists(b"$TESTTMP/unpause"):
1033 > retry -= 1
1039 > retry -= 1
1034 > time.sleep(0.5)
1040 > time.sleep(0.5)
1035 > if os.path.exists(b"$TESTTMP/unpause"):
1041 > if os.path.exists(b"$TESTTMP/unpause"):
1036 > os.remove(b"$TESTTMP/unpause")
1042 > os.remove(b"$TESTTMP/unpause")
1037 > # It is important that this finalizer start with 'a', so it runs before
1043 > # It is important that this finalizer start with 'a', so it runs before
1038 > # the changelog finalizer appends to the changelog.
1044 > # the changelog finalizer appends to the changelog.
1039 > tr.addfinalize(b'a-sleep', sleep)
1045 > tr.addfinalize(b'a-sleep', sleep)
1040 > return tr
1046 > return tr
1041 >
1047 >
1042 > def extsetup(ui):
1048 > def extsetup(ui):
1043 > # This extension inserts an artifical pause during the transaction
1049 > # This extension inserts an artifical pause during the transaction
1044 > # finalizer, so we can run commands mid-transaction-close.
1050 > # finalizer, so we can run commands mid-transaction-close.
1045 > extensions.wrapfunction(localrepo.localrepository, 'transaction',
1051 > extensions.wrapfunction(localrepo.localrepository, 'transaction',
1046 > transaction)
1052 > transaction)
1047 > EOF
1053 > EOF
1048 $ hg commit -qm two --config extensions.pausefinalize=$TESTTMP/pausefinalize.py &
1054 $ hg commit -qm two --config extensions.pausefinalize=$TESTTMP/pausefinalize.py &
1049 $ sleep 2
1055 $ sleep 2
1050 $ hg log -r .
1056 $ hg log -r .
1051 changeset: 0:867bc5792c8c
1057 changeset: 0:867bc5792c8c
1052 bookmark: mybook
1058 bookmark: mybook
1053 tag: tip
1059 tag: tip
1054 user: test
1060 user: test
1055 date: Thu Jan 01 00:00:00 1970 +0000
1061 date: Thu Jan 01 00:00:00 1970 +0000
1056 summary: one
1062 summary: one
1057
1063
1058 $ hg bookmarks
1064 $ hg bookmarks
1059 * mybook 0:867bc5792c8c
1065 * mybook 0:867bc5792c8c
1060 $ touch $TESTTMP/unpause
1066 $ touch $TESTTMP/unpause
1061
1067
1062 $ cd ..
1068 $ cd ..
1063
1069
1064 check whether HG_PENDING makes pending changes only in related
1070 check whether HG_PENDING makes pending changes only in related
1065 repositories visible to an external hook.
1071 repositories visible to an external hook.
1066
1072
1067 (emulate a transaction running concurrently by copied
1073 (emulate a transaction running concurrently by copied
1068 .hg/bookmarks.pending in subsequent test)
1074 .hg/bookmarks.pending in subsequent test)
1069
1075
1070 $ cat > $TESTTMP/savepending.sh <<EOF
1076 $ cat > $TESTTMP/savepending.sh <<EOF
1071 > cp .hg/bookmarks.pending .hg/bookmarks.pending.saved
1077 > cp .hg/bookmarks.pending .hg/bookmarks.pending.saved
1072 > exit 1 # to avoid adding new bookmark for subsequent tests
1078 > exit 1 # to avoid adding new bookmark for subsequent tests
1073 > EOF
1079 > EOF
1074
1080
1075 $ hg init unrelated
1081 $ hg init unrelated
1076 $ cd unrelated
1082 $ cd unrelated
1077 $ echo a > a
1083 $ echo a > a
1078 $ hg add a
1084 $ hg add a
1079 $ hg commit -m '#0'
1085 $ hg commit -m '#0'
1080 $ hg --config hooks.pretxnclose="sh $TESTTMP/savepending.sh" bookmarks INVISIBLE
1086 $ hg --config hooks.pretxnclose="sh $TESTTMP/savepending.sh" bookmarks INVISIBLE
1081 transaction abort!
1087 transaction abort!
1082 rollback completed
1088 rollback completed
1083 abort: pretxnclose hook exited with status 1
1089 abort: pretxnclose hook exited with status 1
1084 [255]
1090 [255]
1085 $ cp .hg/bookmarks.pending.saved .hg/bookmarks.pending
1091 $ cp .hg/bookmarks.pending.saved .hg/bookmarks.pending
1086
1092
1087 (check visible bookmarks while transaction running in repo)
1093 (check visible bookmarks while transaction running in repo)
1088
1094
1089 $ cat > $TESTTMP/checkpending.sh <<EOF
1095 $ cat > $TESTTMP/checkpending.sh <<EOF
1090 > echo "@repo"
1096 > echo "@repo"
1091 > hg -R "$TESTTMP/repo" bookmarks
1097 > hg -R "$TESTTMP/repo" bookmarks
1092 > echo "@unrelated"
1098 > echo "@unrelated"
1093 > hg -R "$TESTTMP/unrelated" bookmarks
1099 > hg -R "$TESTTMP/unrelated" bookmarks
1094 > exit 1 # to avoid adding new bookmark for subsequent tests
1100 > exit 1 # to avoid adding new bookmark for subsequent tests
1095 > EOF
1101 > EOF
1096
1102
1097 $ cd ../repo
1103 $ cd ../repo
1098 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkpending.sh" bookmarks NEW
1104 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkpending.sh" bookmarks NEW
1099 @repo
1105 @repo
1100 * NEW 6:81dcce76aa0b
1106 * NEW 6:81dcce76aa0b
1101 X2 1:925d80f479bb
1107 X2 1:925d80f479bb
1102 Y 4:125c9a1d6df6
1108 Y 4:125c9a1d6df6
1103 Z 5:5fb12f0f2d51
1109 Z 5:5fb12f0f2d51
1104 Z@1 1:925d80f479bb
1110 Z@1 1:925d80f479bb
1105 Z@2 4:125c9a1d6df6
1111 Z@2 4:125c9a1d6df6
1106 foo 3:9ba5f110a0b3
1112 foo 3:9ba5f110a0b3
1107 foo@1 0:f7b1eb17ad24
1113 foo@1 0:f7b1eb17ad24
1108 foo@2 2:db815d6d32e6
1114 foo@2 2:db815d6d32e6
1109 four 3:9ba5f110a0b3
1115 four 3:9ba5f110a0b3
1110 should-end-on-two 2:db815d6d32e6
1116 should-end-on-two 2:db815d6d32e6
1111 x y 2:db815d6d32e6
1117 x y 2:db815d6d32e6
1112 @unrelated
1118 @unrelated
1113 no bookmarks set
1119 no bookmarks set
1114 transaction abort!
1120 transaction abort!
1115 rollback completed
1121 rollback completed
1116 abort: pretxnclose hook exited with status 1
1122 abort: pretxnclose hook exited with status 1
1117 [255]
1123 [255]
1118
1124
1119 Check pretxnclose-bookmark can abort a transaction
1125 Check pretxnclose-bookmark can abort a transaction
1120 --------------------------------------------------
1126 --------------------------------------------------
1121
1127
1122 add hooks:
1128 add hooks:
1123
1129
1124 * to prevent NEW bookmark on a non-public changeset
1130 * to prevent NEW bookmark on a non-public changeset
1125 * to prevent non-forward move of NEW bookmark
1131 * to prevent non-forward move of NEW bookmark
1126
1132
1127 $ cat << EOF >> .hg/hgrc
1133 $ cat << EOF >> .hg/hgrc
1128 > [hooks]
1134 > [hooks]
1129 > pretxnclose-bookmark.force-public = sh -c "(echo \$HG_BOOKMARK| grep -v NEW > /dev/null) || [ -z \"\$HG_NODE\" ] || (hg log -r \"\$HG_NODE\" -T '{phase}' | grep public > /dev/null)"
1135 > pretxnclose-bookmark.force-public = sh -c "(echo \$HG_BOOKMARK| grep -v NEW > /dev/null) || [ -z \"\$HG_NODE\" ] || (hg log -r \"\$HG_NODE\" -T '{phase}' | grep public > /dev/null)"
1130 > pretxnclose-bookmark.force-forward = sh -c "(echo \$HG_BOOKMARK| grep -v NEW > /dev/null) || [ -z \"\$HG_NODE\" ] || (hg log -r \"max(\$HG_OLDNODE::\$HG_NODE)\" -T 'MATCH' | grep MATCH > /dev/null)"
1136 > pretxnclose-bookmark.force-forward = sh -c "(echo \$HG_BOOKMARK| grep -v NEW > /dev/null) || [ -z \"\$HG_NODE\" ] || (hg log -r \"max(\$HG_OLDNODE::\$HG_NODE)\" -T 'MATCH' | grep MATCH > /dev/null)"
1131 > EOF
1137 > EOF
1132
1138
1133 $ hg log -G -T phases
1139 $ hg log -G -T phases
1134 @ changeset: 6:81dcce76aa0b
1140 @ changeset: 6:81dcce76aa0b
1135 | tag: tip
1141 | tag: tip
1136 | phase: draft
1142 | phase: draft
1137 | parent: 4:125c9a1d6df6
1143 | parent: 4:125c9a1d6df6
1138 | user: test
1144 | user: test
1139 | date: Thu Jan 01 00:00:00 1970 +0000
1145 | date: Thu Jan 01 00:00:00 1970 +0000
1140 | summary: xx
1146 | summary: xx
1141 |
1147 |
1142 | o changeset: 5:5fb12f0f2d51
1148 | o changeset: 5:5fb12f0f2d51
1143 | | branch: test
1149 | | branch: test
1144 | | bookmark: Z
1150 | | bookmark: Z
1145 | | phase: draft
1151 | | phase: draft
1146 | | parent: 3:9ba5f110a0b3
1152 | | parent: 3:9ba5f110a0b3
1147 | | user: test
1153 | | user: test
1148 | | date: Thu Jan 01 00:00:00 1970 +0000
1154 | | date: Thu Jan 01 00:00:00 1970 +0000
1149 | | summary: yy
1155 | | summary: yy
1150 | |
1156 | |
1151 o | changeset: 4:125c9a1d6df6
1157 o | changeset: 4:125c9a1d6df6
1152 | | bookmark: Y
1158 | | bookmark: Y
1153 | | bookmark: Z@2
1159 | | bookmark: Z@2
1154 | | phase: public
1160 | | phase: public
1155 | | parent: 2:db815d6d32e6
1161 | | parent: 2:db815d6d32e6
1156 | | user: test
1162 | | user: test
1157 | | date: Thu Jan 01 00:00:00 1970 +0000
1163 | | date: Thu Jan 01 00:00:00 1970 +0000
1158 | | summary: x
1164 | | summary: x
1159 | |
1165 | |
1160 | o changeset: 3:9ba5f110a0b3
1166 | o changeset: 3:9ba5f110a0b3
1161 |/ branch: test
1167 |/ branch: test
1162 | bookmark: foo
1168 | bookmark: foo
1163 | bookmark: four
1169 | bookmark: four
1164 | phase: public
1170 | phase: public
1165 | user: test
1171 | user: test
1166 | date: Thu Jan 01 00:00:00 1970 +0000
1172 | date: Thu Jan 01 00:00:00 1970 +0000
1167 | summary: y
1173 | summary: y
1168 |
1174 |
1169 o changeset: 2:db815d6d32e6
1175 o changeset: 2:db815d6d32e6
1170 | bookmark: foo@2
1176 | bookmark: foo@2
1171 | bookmark: should-end-on-two
1177 | bookmark: should-end-on-two
1172 | bookmark: x y
1178 | bookmark: x y
1173 | phase: public
1179 | phase: public
1174 | parent: 0:f7b1eb17ad24
1180 | parent: 0:f7b1eb17ad24
1175 | user: test
1181 | user: test
1176 | date: Thu Jan 01 00:00:00 1970 +0000
1182 | date: Thu Jan 01 00:00:00 1970 +0000
1177 | summary: 2
1183 | summary: 2
1178 |
1184 |
1179 | o changeset: 1:925d80f479bb
1185 | o changeset: 1:925d80f479bb
1180 |/ bookmark: X2
1186 |/ bookmark: X2
1181 | bookmark: Z@1
1187 | bookmark: Z@1
1182 | phase: public
1188 | phase: public
1183 | user: test
1189 | user: test
1184 | date: Thu Jan 01 00:00:00 1970 +0000
1190 | date: Thu Jan 01 00:00:00 1970 +0000
1185 | summary: 1
1191 | summary: 1
1186 |
1192 |
1187 o changeset: 0:f7b1eb17ad24
1193 o changeset: 0:f7b1eb17ad24
1188 bookmark: foo@1
1194 bookmark: foo@1
1189 phase: public
1195 phase: public
1190 user: test
1196 user: test
1191 date: Thu Jan 01 00:00:00 1970 +0000
1197 date: Thu Jan 01 00:00:00 1970 +0000
1192 summary: 0
1198 summary: 0
1193
1199
1194
1200
1195 attempt to create on a default changeset
1201 attempt to create on a default changeset
1196
1202
1197 $ hg bookmark -r 81dcce76aa0b NEW
1203 $ hg bookmark -r 81dcce76aa0b NEW
1198 transaction abort!
1204 transaction abort!
1199 rollback completed
1205 rollback completed
1200 abort: pretxnclose-bookmark.force-public hook exited with status 1
1206 abort: pretxnclose-bookmark.force-public hook exited with status 1
1201 [255]
1207 [255]
1202
1208
1203 create on a public changeset
1209 create on a public changeset
1204
1210
1205 $ hg bookmark -r 9ba5f110a0b3 NEW
1211 $ hg bookmark -r 9ba5f110a0b3 NEW
1206
1212
1207 move to the other branch
1213 move to the other branch
1208
1214
1209 $ hg bookmark -f -r 125c9a1d6df6 NEW
1215 $ hg bookmark -f -r 125c9a1d6df6 NEW
1210 transaction abort!
1216 transaction abort!
1211 rollback completed
1217 rollback completed
1212 abort: pretxnclose-bookmark.force-forward hook exited with status 1
1218 abort: pretxnclose-bookmark.force-forward hook exited with status 1
1213 [255]
1219 [255]
General Comments 0
You need to be logged in to leave comments. Login now