##// END OF EJS Templates
bundle: transpose transaction scope with bundle type switch...
Martin von Zweigbergk -
r33042:dbc2ee17 default
parent child Browse files
Show More
@@ -1,5401 +1,5402 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 formatter,
38 formatter,
39 graphmod,
39 graphmod,
40 hbisect,
40 hbisect,
41 help,
41 help,
42 hg,
42 hg,
43 lock as lockmod,
43 lock as lockmod,
44 merge as mergemod,
44 merge as mergemod,
45 obsolete,
45 obsolete,
46 patch,
46 patch,
47 phases,
47 phases,
48 pycompat,
48 pycompat,
49 rcutil,
49 rcutil,
50 registrar,
50 registrar,
51 revsetlang,
51 revsetlang,
52 scmutil,
52 scmutil,
53 server,
53 server,
54 sshserver,
54 sshserver,
55 streamclone,
55 streamclone,
56 tags as tagsmod,
56 tags as tagsmod,
57 templatekw,
57 templatekw,
58 ui as uimod,
58 ui as uimod,
59 util,
59 util,
60 )
60 )
61
61
62 release = lockmod.release
62 release = lockmod.release
63
63
64 table = {}
64 table = {}
65 table.update(debugcommandsmod.command._table)
65 table.update(debugcommandsmod.command._table)
66
66
67 command = registrar.command(table)
67 command = registrar.command(table)
68
68
69 # common command options
69 # common command options
70
70
71 globalopts = [
71 globalopts = [
72 ('R', 'repository', '',
72 ('R', 'repository', '',
73 _('repository root directory or name of overlay bundle file'),
73 _('repository root directory or name of overlay bundle file'),
74 _('REPO')),
74 _('REPO')),
75 ('', 'cwd', '',
75 ('', 'cwd', '',
76 _('change working directory'), _('DIR')),
76 _('change working directory'), _('DIR')),
77 ('y', 'noninteractive', None,
77 ('y', 'noninteractive', None,
78 _('do not prompt, automatically pick the first choice for all prompts')),
78 _('do not prompt, automatically pick the first choice for all prompts')),
79 ('q', 'quiet', None, _('suppress output')),
79 ('q', 'quiet', None, _('suppress output')),
80 ('v', 'verbose', None, _('enable additional output')),
80 ('v', 'verbose', None, _('enable additional output')),
81 ('', 'color', '',
81 ('', 'color', '',
82 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
82 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
83 # and should not be translated
83 # and should not be translated
84 _("when to colorize (boolean, always, auto, never, or debug)"),
84 _("when to colorize (boolean, always, auto, never, or debug)"),
85 _('TYPE')),
85 _('TYPE')),
86 ('', 'config', [],
86 ('', 'config', [],
87 _('set/override config option (use \'section.name=value\')'),
87 _('set/override config option (use \'section.name=value\')'),
88 _('CONFIG')),
88 _('CONFIG')),
89 ('', 'debug', None, _('enable debugging output')),
89 ('', 'debug', None, _('enable debugging output')),
90 ('', 'debugger', None, _('start debugger')),
90 ('', 'debugger', None, _('start debugger')),
91 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
91 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
92 _('ENCODE')),
92 _('ENCODE')),
93 ('', 'encodingmode', encoding.encodingmode,
93 ('', 'encodingmode', encoding.encodingmode,
94 _('set the charset encoding mode'), _('MODE')),
94 _('set the charset encoding mode'), _('MODE')),
95 ('', 'traceback', None, _('always print a traceback on exception')),
95 ('', 'traceback', None, _('always print a traceback on exception')),
96 ('', 'time', None, _('time how long the command takes')),
96 ('', 'time', None, _('time how long the command takes')),
97 ('', 'profile', None, _('print command execution profile')),
97 ('', 'profile', None, _('print command execution profile')),
98 ('', 'version', None, _('output version information and exit')),
98 ('', 'version', None, _('output version information and exit')),
99 ('h', 'help', None, _('display help and exit')),
99 ('h', 'help', None, _('display help and exit')),
100 ('', 'hidden', False, _('consider hidden changesets')),
100 ('', 'hidden', False, _('consider hidden changesets')),
101 ('', 'pager', 'auto',
101 ('', 'pager', 'auto',
102 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
102 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
103 ]
103 ]
104
104
105 dryrunopts = cmdutil.dryrunopts
105 dryrunopts = cmdutil.dryrunopts
106 remoteopts = cmdutil.remoteopts
106 remoteopts = cmdutil.remoteopts
107 walkopts = cmdutil.walkopts
107 walkopts = cmdutil.walkopts
108 commitopts = cmdutil.commitopts
108 commitopts = cmdutil.commitopts
109 commitopts2 = cmdutil.commitopts2
109 commitopts2 = cmdutil.commitopts2
110 formatteropts = cmdutil.formatteropts
110 formatteropts = cmdutil.formatteropts
111 templateopts = cmdutil.templateopts
111 templateopts = cmdutil.templateopts
112 logopts = cmdutil.logopts
112 logopts = cmdutil.logopts
113 diffopts = cmdutil.diffopts
113 diffopts = cmdutil.diffopts
114 diffwsopts = cmdutil.diffwsopts
114 diffwsopts = cmdutil.diffwsopts
115 diffopts2 = cmdutil.diffopts2
115 diffopts2 = cmdutil.diffopts2
116 mergetoolopts = cmdutil.mergetoolopts
116 mergetoolopts = cmdutil.mergetoolopts
117 similarityopts = cmdutil.similarityopts
117 similarityopts = cmdutil.similarityopts
118 subrepoopts = cmdutil.subrepoopts
118 subrepoopts = cmdutil.subrepoopts
119 debugrevlogopts = cmdutil.debugrevlogopts
119 debugrevlogopts = cmdutil.debugrevlogopts
120
120
121 # Commands start here, listed alphabetically
121 # Commands start here, listed alphabetically
122
122
123 @command('^add',
123 @command('^add',
124 walkopts + subrepoopts + dryrunopts,
124 walkopts + subrepoopts + dryrunopts,
125 _('[OPTION]... [FILE]...'),
125 _('[OPTION]... [FILE]...'),
126 inferrepo=True)
126 inferrepo=True)
127 def add(ui, repo, *pats, **opts):
127 def add(ui, repo, *pats, **opts):
128 """add the specified files on the next commit
128 """add the specified files on the next commit
129
129
130 Schedule files to be version controlled and added to the
130 Schedule files to be version controlled and added to the
131 repository.
131 repository.
132
132
133 The files will be added to the repository at the next commit. To
133 The files will be added to the repository at the next commit. To
134 undo an add before that, see :hg:`forget`.
134 undo an add before that, see :hg:`forget`.
135
135
136 If no names are given, add all files to the repository (except
136 If no names are given, add all files to the repository (except
137 files matching ``.hgignore``).
137 files matching ``.hgignore``).
138
138
139 .. container:: verbose
139 .. container:: verbose
140
140
141 Examples:
141 Examples:
142
142
143 - New (unknown) files are added
143 - New (unknown) files are added
144 automatically by :hg:`add`::
144 automatically by :hg:`add`::
145
145
146 $ ls
146 $ ls
147 foo.c
147 foo.c
148 $ hg status
148 $ hg status
149 ? foo.c
149 ? foo.c
150 $ hg add
150 $ hg add
151 adding foo.c
151 adding foo.c
152 $ hg status
152 $ hg status
153 A foo.c
153 A foo.c
154
154
155 - Specific files to be added can be specified::
155 - Specific files to be added can be specified::
156
156
157 $ ls
157 $ ls
158 bar.c foo.c
158 bar.c foo.c
159 $ hg status
159 $ hg status
160 ? bar.c
160 ? bar.c
161 ? foo.c
161 ? foo.c
162 $ hg add bar.c
162 $ hg add bar.c
163 $ hg status
163 $ hg status
164 A bar.c
164 A bar.c
165 ? foo.c
165 ? foo.c
166
166
167 Returns 0 if all files are successfully added.
167 Returns 0 if all files are successfully added.
168 """
168 """
169
169
170 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
170 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
171 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
171 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
172 return rejected and 1 or 0
172 return rejected and 1 or 0
173
173
174 @command('addremove',
174 @command('addremove',
175 similarityopts + subrepoopts + walkopts + dryrunopts,
175 similarityopts + subrepoopts + walkopts + dryrunopts,
176 _('[OPTION]... [FILE]...'),
176 _('[OPTION]... [FILE]...'),
177 inferrepo=True)
177 inferrepo=True)
178 def addremove(ui, repo, *pats, **opts):
178 def addremove(ui, repo, *pats, **opts):
179 """add all new files, delete all missing files
179 """add all new files, delete all missing files
180
180
181 Add all new files and remove all missing files from the
181 Add all new files and remove all missing files from the
182 repository.
182 repository.
183
183
184 Unless names are given, new files are ignored if they match any of
184 Unless names are given, new files are ignored if they match any of
185 the patterns in ``.hgignore``. As with add, these changes take
185 the patterns in ``.hgignore``. As with add, these changes take
186 effect at the next commit.
186 effect at the next commit.
187
187
188 Use the -s/--similarity option to detect renamed files. This
188 Use the -s/--similarity option to detect renamed files. This
189 option takes a percentage between 0 (disabled) and 100 (files must
189 option takes a percentage between 0 (disabled) and 100 (files must
190 be identical) as its parameter. With a parameter greater than 0,
190 be identical) as its parameter. With a parameter greater than 0,
191 this compares every removed file with every added file and records
191 this compares every removed file with every added file and records
192 those similar enough as renames. Detecting renamed files this way
192 those similar enough as renames. Detecting renamed files this way
193 can be expensive. After using this option, :hg:`status -C` can be
193 can be expensive. After using this option, :hg:`status -C` can be
194 used to check which files were identified as moved or renamed. If
194 used to check which files were identified as moved or renamed. If
195 not specified, -s/--similarity defaults to 100 and only renames of
195 not specified, -s/--similarity defaults to 100 and only renames of
196 identical files are detected.
196 identical files are detected.
197
197
198 .. container:: verbose
198 .. container:: verbose
199
199
200 Examples:
200 Examples:
201
201
202 - A number of files (bar.c and foo.c) are new,
202 - A number of files (bar.c and foo.c) are new,
203 while foobar.c has been removed (without using :hg:`remove`)
203 while foobar.c has been removed (without using :hg:`remove`)
204 from the repository::
204 from the repository::
205
205
206 $ ls
206 $ ls
207 bar.c foo.c
207 bar.c foo.c
208 $ hg status
208 $ hg status
209 ! foobar.c
209 ! foobar.c
210 ? bar.c
210 ? bar.c
211 ? foo.c
211 ? foo.c
212 $ hg addremove
212 $ hg addremove
213 adding bar.c
213 adding bar.c
214 adding foo.c
214 adding foo.c
215 removing foobar.c
215 removing foobar.c
216 $ hg status
216 $ hg status
217 A bar.c
217 A bar.c
218 A foo.c
218 A foo.c
219 R foobar.c
219 R foobar.c
220
220
221 - A file foobar.c was moved to foo.c without using :hg:`rename`.
221 - A file foobar.c was moved to foo.c without using :hg:`rename`.
222 Afterwards, it was edited slightly::
222 Afterwards, it was edited slightly::
223
223
224 $ ls
224 $ ls
225 foo.c
225 foo.c
226 $ hg status
226 $ hg status
227 ! foobar.c
227 ! foobar.c
228 ? foo.c
228 ? foo.c
229 $ hg addremove --similarity 90
229 $ hg addremove --similarity 90
230 removing foobar.c
230 removing foobar.c
231 adding foo.c
231 adding foo.c
232 recording removal of foobar.c as rename to foo.c (94% similar)
232 recording removal of foobar.c as rename to foo.c (94% similar)
233 $ hg status -C
233 $ hg status -C
234 A foo.c
234 A foo.c
235 foobar.c
235 foobar.c
236 R foobar.c
236 R foobar.c
237
237
238 Returns 0 if all files are successfully added.
238 Returns 0 if all files are successfully added.
239 """
239 """
240 opts = pycompat.byteskwargs(opts)
240 opts = pycompat.byteskwargs(opts)
241 try:
241 try:
242 sim = float(opts.get('similarity') or 100)
242 sim = float(opts.get('similarity') or 100)
243 except ValueError:
243 except ValueError:
244 raise error.Abort(_('similarity must be a number'))
244 raise error.Abort(_('similarity must be a number'))
245 if sim < 0 or sim > 100:
245 if sim < 0 or sim > 100:
246 raise error.Abort(_('similarity must be between 0 and 100'))
246 raise error.Abort(_('similarity must be between 0 and 100'))
247 matcher = scmutil.match(repo[None], pats, opts)
247 matcher = scmutil.match(repo[None], pats, opts)
248 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
248 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
249
249
250 @command('^annotate|blame',
250 @command('^annotate|blame',
251 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
251 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
252 ('', 'follow', None,
252 ('', 'follow', None,
253 _('follow copies/renames and list the filename (DEPRECATED)')),
253 _('follow copies/renames and list the filename (DEPRECATED)')),
254 ('', 'no-follow', None, _("don't follow copies and renames")),
254 ('', 'no-follow', None, _("don't follow copies and renames")),
255 ('a', 'text', None, _('treat all files as text')),
255 ('a', 'text', None, _('treat all files as text')),
256 ('u', 'user', None, _('list the author (long with -v)')),
256 ('u', 'user', None, _('list the author (long with -v)')),
257 ('f', 'file', None, _('list the filename')),
257 ('f', 'file', None, _('list the filename')),
258 ('d', 'date', None, _('list the date (short with -q)')),
258 ('d', 'date', None, _('list the date (short with -q)')),
259 ('n', 'number', None, _('list the revision number (default)')),
259 ('n', 'number', None, _('list the revision number (default)')),
260 ('c', 'changeset', None, _('list the changeset')),
260 ('c', 'changeset', None, _('list the changeset')),
261 ('l', 'line-number', None, _('show line number at the first appearance')),
261 ('l', 'line-number', None, _('show line number at the first appearance')),
262 ('', 'skip', [], _('revision to not display (EXPERIMENTAL)'), _('REV')),
262 ('', 'skip', [], _('revision to not display (EXPERIMENTAL)'), _('REV')),
263 ] + diffwsopts + walkopts + formatteropts,
263 ] + diffwsopts + walkopts + formatteropts,
264 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
264 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
265 inferrepo=True)
265 inferrepo=True)
266 def annotate(ui, repo, *pats, **opts):
266 def annotate(ui, repo, *pats, **opts):
267 """show changeset information by line for each file
267 """show changeset information by line for each file
268
268
269 List changes in files, showing the revision id responsible for
269 List changes in files, showing the revision id responsible for
270 each line.
270 each line.
271
271
272 This command is useful for discovering when a change was made and
272 This command is useful for discovering when a change was made and
273 by whom.
273 by whom.
274
274
275 If you include --file, --user, or --date, the revision number is
275 If you include --file, --user, or --date, the revision number is
276 suppressed unless you also include --number.
276 suppressed unless you also include --number.
277
277
278 Without the -a/--text option, annotate will avoid processing files
278 Without the -a/--text option, annotate will avoid processing files
279 it detects as binary. With -a, annotate will annotate the file
279 it detects as binary. With -a, annotate will annotate the file
280 anyway, although the results will probably be neither useful
280 anyway, although the results will probably be neither useful
281 nor desirable.
281 nor desirable.
282
282
283 Returns 0 on success.
283 Returns 0 on success.
284 """
284 """
285 opts = pycompat.byteskwargs(opts)
285 opts = pycompat.byteskwargs(opts)
286 if not pats:
286 if not pats:
287 raise error.Abort(_('at least one filename or pattern is required'))
287 raise error.Abort(_('at least one filename or pattern is required'))
288
288
289 if opts.get('follow'):
289 if opts.get('follow'):
290 # --follow is deprecated and now just an alias for -f/--file
290 # --follow is deprecated and now just an alias for -f/--file
291 # to mimic the behavior of Mercurial before version 1.5
291 # to mimic the behavior of Mercurial before version 1.5
292 opts['file'] = True
292 opts['file'] = True
293
293
294 ctx = scmutil.revsingle(repo, opts.get('rev'))
294 ctx = scmutil.revsingle(repo, opts.get('rev'))
295
295
296 rootfm = ui.formatter('annotate', opts)
296 rootfm = ui.formatter('annotate', opts)
297 if ui.quiet:
297 if ui.quiet:
298 datefunc = util.shortdate
298 datefunc = util.shortdate
299 else:
299 else:
300 datefunc = util.datestr
300 datefunc = util.datestr
301 if ctx.rev() is None:
301 if ctx.rev() is None:
302 def hexfn(node):
302 def hexfn(node):
303 if node is None:
303 if node is None:
304 return None
304 return None
305 else:
305 else:
306 return rootfm.hexfunc(node)
306 return rootfm.hexfunc(node)
307 if opts.get('changeset'):
307 if opts.get('changeset'):
308 # omit "+" suffix which is appended to node hex
308 # omit "+" suffix which is appended to node hex
309 def formatrev(rev):
309 def formatrev(rev):
310 if rev is None:
310 if rev is None:
311 return '%d' % ctx.p1().rev()
311 return '%d' % ctx.p1().rev()
312 else:
312 else:
313 return '%d' % rev
313 return '%d' % rev
314 else:
314 else:
315 def formatrev(rev):
315 def formatrev(rev):
316 if rev is None:
316 if rev is None:
317 return '%d+' % ctx.p1().rev()
317 return '%d+' % ctx.p1().rev()
318 else:
318 else:
319 return '%d ' % rev
319 return '%d ' % rev
320 def formathex(hex):
320 def formathex(hex):
321 if hex is None:
321 if hex is None:
322 return '%s+' % rootfm.hexfunc(ctx.p1().node())
322 return '%s+' % rootfm.hexfunc(ctx.p1().node())
323 else:
323 else:
324 return '%s ' % hex
324 return '%s ' % hex
325 else:
325 else:
326 hexfn = rootfm.hexfunc
326 hexfn = rootfm.hexfunc
327 formatrev = formathex = str
327 formatrev = formathex = str
328
328
329 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
329 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
330 ('number', ' ', lambda x: x[0].rev(), formatrev),
330 ('number', ' ', lambda x: x[0].rev(), formatrev),
331 ('changeset', ' ', lambda x: hexfn(x[0].node()), formathex),
331 ('changeset', ' ', lambda x: hexfn(x[0].node()), formathex),
332 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
332 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
333 ('file', ' ', lambda x: x[0].path(), str),
333 ('file', ' ', lambda x: x[0].path(), str),
334 ('line_number', ':', lambda x: x[1], str),
334 ('line_number', ':', lambda x: x[1], str),
335 ]
335 ]
336 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
336 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
337
337
338 if (not opts.get('user') and not opts.get('changeset')
338 if (not opts.get('user') and not opts.get('changeset')
339 and not opts.get('date') and not opts.get('file')):
339 and not opts.get('date') and not opts.get('file')):
340 opts['number'] = True
340 opts['number'] = True
341
341
342 linenumber = opts.get('line_number') is not None
342 linenumber = opts.get('line_number') is not None
343 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
343 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
344 raise error.Abort(_('at least one of -n/-c is required for -l'))
344 raise error.Abort(_('at least one of -n/-c is required for -l'))
345
345
346 ui.pager('annotate')
346 ui.pager('annotate')
347
347
348 if rootfm.isplain():
348 if rootfm.isplain():
349 def makefunc(get, fmt):
349 def makefunc(get, fmt):
350 return lambda x: fmt(get(x))
350 return lambda x: fmt(get(x))
351 else:
351 else:
352 def makefunc(get, fmt):
352 def makefunc(get, fmt):
353 return get
353 return get
354 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
354 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
355 if opts.get(op)]
355 if opts.get(op)]
356 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
356 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
357 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
357 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
358 if opts.get(op))
358 if opts.get(op))
359
359
360 def bad(x, y):
360 def bad(x, y):
361 raise error.Abort("%s: %s" % (x, y))
361 raise error.Abort("%s: %s" % (x, y))
362
362
363 m = scmutil.match(ctx, pats, opts, badfn=bad)
363 m = scmutil.match(ctx, pats, opts, badfn=bad)
364
364
365 follow = not opts.get('no_follow')
365 follow = not opts.get('no_follow')
366 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
366 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
367 whitespace=True)
367 whitespace=True)
368 skiprevs = opts.get('skip')
368 skiprevs = opts.get('skip')
369 if skiprevs:
369 if skiprevs:
370 skiprevs = scmutil.revrange(repo, skiprevs)
370 skiprevs = scmutil.revrange(repo, skiprevs)
371
371
372 for abs in ctx.walk(m):
372 for abs in ctx.walk(m):
373 fctx = ctx[abs]
373 fctx = ctx[abs]
374 rootfm.startitem()
374 rootfm.startitem()
375 rootfm.data(abspath=abs, path=m.rel(abs))
375 rootfm.data(abspath=abs, path=m.rel(abs))
376 if not opts.get('text') and fctx.isbinary():
376 if not opts.get('text') and fctx.isbinary():
377 rootfm.plain(_("%s: binary file\n")
377 rootfm.plain(_("%s: binary file\n")
378 % ((pats and m.rel(abs)) or abs))
378 % ((pats and m.rel(abs)) or abs))
379 continue
379 continue
380
380
381 fm = rootfm.nested('lines')
381 fm = rootfm.nested('lines')
382 lines = fctx.annotate(follow=follow, linenumber=linenumber,
382 lines = fctx.annotate(follow=follow, linenumber=linenumber,
383 skiprevs=skiprevs, diffopts=diffopts)
383 skiprevs=skiprevs, diffopts=diffopts)
384 if not lines:
384 if not lines:
385 fm.end()
385 fm.end()
386 continue
386 continue
387 formats = []
387 formats = []
388 pieces = []
388 pieces = []
389
389
390 for f, sep in funcmap:
390 for f, sep in funcmap:
391 l = [f(n) for n, dummy in lines]
391 l = [f(n) for n, dummy in lines]
392 if fm.isplain():
392 if fm.isplain():
393 sizes = [encoding.colwidth(x) for x in l]
393 sizes = [encoding.colwidth(x) for x in l]
394 ml = max(sizes)
394 ml = max(sizes)
395 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
395 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
396 else:
396 else:
397 formats.append(['%s' for x in l])
397 formats.append(['%s' for x in l])
398 pieces.append(l)
398 pieces.append(l)
399
399
400 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
400 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
401 fm.startitem()
401 fm.startitem()
402 fm.write(fields, "".join(f), *p)
402 fm.write(fields, "".join(f), *p)
403 fm.write('line', ": %s", l[1])
403 fm.write('line', ": %s", l[1])
404
404
405 if not lines[-1][1].endswith('\n'):
405 if not lines[-1][1].endswith('\n'):
406 fm.plain('\n')
406 fm.plain('\n')
407 fm.end()
407 fm.end()
408
408
409 rootfm.end()
409 rootfm.end()
410
410
411 @command('archive',
411 @command('archive',
412 [('', 'no-decode', None, _('do not pass files through decoders')),
412 [('', 'no-decode', None, _('do not pass files through decoders')),
413 ('p', 'prefix', '', _('directory prefix for files in archive'),
413 ('p', 'prefix', '', _('directory prefix for files in archive'),
414 _('PREFIX')),
414 _('PREFIX')),
415 ('r', 'rev', '', _('revision to distribute'), _('REV')),
415 ('r', 'rev', '', _('revision to distribute'), _('REV')),
416 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
416 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
417 ] + subrepoopts + walkopts,
417 ] + subrepoopts + walkopts,
418 _('[OPTION]... DEST'))
418 _('[OPTION]... DEST'))
419 def archive(ui, repo, dest, **opts):
419 def archive(ui, repo, dest, **opts):
420 '''create an unversioned archive of a repository revision
420 '''create an unversioned archive of a repository revision
421
421
422 By default, the revision used is the parent of the working
422 By default, the revision used is the parent of the working
423 directory; use -r/--rev to specify a different revision.
423 directory; use -r/--rev to specify a different revision.
424
424
425 The archive type is automatically detected based on file
425 The archive type is automatically detected based on file
426 extension (to override, use -t/--type).
426 extension (to override, use -t/--type).
427
427
428 .. container:: verbose
428 .. container:: verbose
429
429
430 Examples:
430 Examples:
431
431
432 - create a zip file containing the 1.0 release::
432 - create a zip file containing the 1.0 release::
433
433
434 hg archive -r 1.0 project-1.0.zip
434 hg archive -r 1.0 project-1.0.zip
435
435
436 - create a tarball excluding .hg files::
436 - create a tarball excluding .hg files::
437
437
438 hg archive project.tar.gz -X ".hg*"
438 hg archive project.tar.gz -X ".hg*"
439
439
440 Valid types are:
440 Valid types are:
441
441
442 :``files``: a directory full of files (default)
442 :``files``: a directory full of files (default)
443 :``tar``: tar archive, uncompressed
443 :``tar``: tar archive, uncompressed
444 :``tbz2``: tar archive, compressed using bzip2
444 :``tbz2``: tar archive, compressed using bzip2
445 :``tgz``: tar archive, compressed using gzip
445 :``tgz``: tar archive, compressed using gzip
446 :``uzip``: zip archive, uncompressed
446 :``uzip``: zip archive, uncompressed
447 :``zip``: zip archive, compressed using deflate
447 :``zip``: zip archive, compressed using deflate
448
448
449 The exact name of the destination archive or directory is given
449 The exact name of the destination archive or directory is given
450 using a format string; see :hg:`help export` for details.
450 using a format string; see :hg:`help export` for details.
451
451
452 Each member added to an archive file has a directory prefix
452 Each member added to an archive file has a directory prefix
453 prepended. Use -p/--prefix to specify a format string for the
453 prepended. Use -p/--prefix to specify a format string for the
454 prefix. The default is the basename of the archive, with suffixes
454 prefix. The default is the basename of the archive, with suffixes
455 removed.
455 removed.
456
456
457 Returns 0 on success.
457 Returns 0 on success.
458 '''
458 '''
459
459
460 opts = pycompat.byteskwargs(opts)
460 opts = pycompat.byteskwargs(opts)
461 ctx = scmutil.revsingle(repo, opts.get('rev'))
461 ctx = scmutil.revsingle(repo, opts.get('rev'))
462 if not ctx:
462 if not ctx:
463 raise error.Abort(_('no working directory: please specify a revision'))
463 raise error.Abort(_('no working directory: please specify a revision'))
464 node = ctx.node()
464 node = ctx.node()
465 dest = cmdutil.makefilename(repo, dest, node)
465 dest = cmdutil.makefilename(repo, dest, node)
466 if os.path.realpath(dest) == repo.root:
466 if os.path.realpath(dest) == repo.root:
467 raise error.Abort(_('repository root cannot be destination'))
467 raise error.Abort(_('repository root cannot be destination'))
468
468
469 kind = opts.get('type') or archival.guesskind(dest) or 'files'
469 kind = opts.get('type') or archival.guesskind(dest) or 'files'
470 prefix = opts.get('prefix')
470 prefix = opts.get('prefix')
471
471
472 if dest == '-':
472 if dest == '-':
473 if kind == 'files':
473 if kind == 'files':
474 raise error.Abort(_('cannot archive plain files to stdout'))
474 raise error.Abort(_('cannot archive plain files to stdout'))
475 dest = cmdutil.makefileobj(repo, dest)
475 dest = cmdutil.makefileobj(repo, dest)
476 if not prefix:
476 if not prefix:
477 prefix = os.path.basename(repo.root) + '-%h'
477 prefix = os.path.basename(repo.root) + '-%h'
478
478
479 prefix = cmdutil.makefilename(repo, prefix, node)
479 prefix = cmdutil.makefilename(repo, prefix, node)
480 matchfn = scmutil.match(ctx, [], opts)
480 matchfn = scmutil.match(ctx, [], opts)
481 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
481 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
482 matchfn, prefix, subrepos=opts.get('subrepos'))
482 matchfn, prefix, subrepos=opts.get('subrepos'))
483
483
484 @command('backout',
484 @command('backout',
485 [('', 'merge', None, _('merge with old dirstate parent after backout')),
485 [('', 'merge', None, _('merge with old dirstate parent after backout')),
486 ('', 'commit', None,
486 ('', 'commit', None,
487 _('commit if no conflicts were encountered (DEPRECATED)')),
487 _('commit if no conflicts were encountered (DEPRECATED)')),
488 ('', 'no-commit', None, _('do not commit')),
488 ('', 'no-commit', None, _('do not commit')),
489 ('', 'parent', '',
489 ('', 'parent', '',
490 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
490 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
491 ('r', 'rev', '', _('revision to backout'), _('REV')),
491 ('r', 'rev', '', _('revision to backout'), _('REV')),
492 ('e', 'edit', False, _('invoke editor on commit messages')),
492 ('e', 'edit', False, _('invoke editor on commit messages')),
493 ] + mergetoolopts + walkopts + commitopts + commitopts2,
493 ] + mergetoolopts + walkopts + commitopts + commitopts2,
494 _('[OPTION]... [-r] REV'))
494 _('[OPTION]... [-r] REV'))
495 def backout(ui, repo, node=None, rev=None, **opts):
495 def backout(ui, repo, node=None, rev=None, **opts):
496 '''reverse effect of earlier changeset
496 '''reverse effect of earlier changeset
497
497
498 Prepare a new changeset with the effect of REV undone in the
498 Prepare a new changeset with the effect of REV undone in the
499 current working directory. If no conflicts were encountered,
499 current working directory. If no conflicts were encountered,
500 it will be committed immediately.
500 it will be committed immediately.
501
501
502 If REV is the parent of the working directory, then this new changeset
502 If REV is the parent of the working directory, then this new changeset
503 is committed automatically (unless --no-commit is specified).
503 is committed automatically (unless --no-commit is specified).
504
504
505 .. note::
505 .. note::
506
506
507 :hg:`backout` cannot be used to fix either an unwanted or
507 :hg:`backout` cannot be used to fix either an unwanted or
508 incorrect merge.
508 incorrect merge.
509
509
510 .. container:: verbose
510 .. container:: verbose
511
511
512 Examples:
512 Examples:
513
513
514 - Reverse the effect of the parent of the working directory.
514 - Reverse the effect of the parent of the working directory.
515 This backout will be committed immediately::
515 This backout will be committed immediately::
516
516
517 hg backout -r .
517 hg backout -r .
518
518
519 - Reverse the effect of previous bad revision 23::
519 - Reverse the effect of previous bad revision 23::
520
520
521 hg backout -r 23
521 hg backout -r 23
522
522
523 - Reverse the effect of previous bad revision 23 and
523 - Reverse the effect of previous bad revision 23 and
524 leave changes uncommitted::
524 leave changes uncommitted::
525
525
526 hg backout -r 23 --no-commit
526 hg backout -r 23 --no-commit
527 hg commit -m "Backout revision 23"
527 hg commit -m "Backout revision 23"
528
528
529 By default, the pending changeset will have one parent,
529 By default, the pending changeset will have one parent,
530 maintaining a linear history. With --merge, the pending
530 maintaining a linear history. With --merge, the pending
531 changeset will instead have two parents: the old parent of the
531 changeset will instead have two parents: the old parent of the
532 working directory and a new child of REV that simply undoes REV.
532 working directory and a new child of REV that simply undoes REV.
533
533
534 Before version 1.7, the behavior without --merge was equivalent
534 Before version 1.7, the behavior without --merge was equivalent
535 to specifying --merge followed by :hg:`update --clean .` to
535 to specifying --merge followed by :hg:`update --clean .` to
536 cancel the merge and leave the child of REV as a head to be
536 cancel the merge and leave the child of REV as a head to be
537 merged separately.
537 merged separately.
538
538
539 See :hg:`help dates` for a list of formats valid for -d/--date.
539 See :hg:`help dates` for a list of formats valid for -d/--date.
540
540
541 See :hg:`help revert` for a way to restore files to the state
541 See :hg:`help revert` for a way to restore files to the state
542 of another revision.
542 of another revision.
543
543
544 Returns 0 on success, 1 if nothing to backout or there are unresolved
544 Returns 0 on success, 1 if nothing to backout or there are unresolved
545 files.
545 files.
546 '''
546 '''
547 wlock = lock = None
547 wlock = lock = None
548 try:
548 try:
549 wlock = repo.wlock()
549 wlock = repo.wlock()
550 lock = repo.lock()
550 lock = repo.lock()
551 return _dobackout(ui, repo, node, rev, **opts)
551 return _dobackout(ui, repo, node, rev, **opts)
552 finally:
552 finally:
553 release(lock, wlock)
553 release(lock, wlock)
554
554
555 def _dobackout(ui, repo, node=None, rev=None, **opts):
555 def _dobackout(ui, repo, node=None, rev=None, **opts):
556 opts = pycompat.byteskwargs(opts)
556 opts = pycompat.byteskwargs(opts)
557 if opts.get('commit') and opts.get('no_commit'):
557 if opts.get('commit') and opts.get('no_commit'):
558 raise error.Abort(_("cannot use --commit with --no-commit"))
558 raise error.Abort(_("cannot use --commit with --no-commit"))
559 if opts.get('merge') and opts.get('no_commit'):
559 if opts.get('merge') and opts.get('no_commit'):
560 raise error.Abort(_("cannot use --merge with --no-commit"))
560 raise error.Abort(_("cannot use --merge with --no-commit"))
561
561
562 if rev and node:
562 if rev and node:
563 raise error.Abort(_("please specify just one revision"))
563 raise error.Abort(_("please specify just one revision"))
564
564
565 if not rev:
565 if not rev:
566 rev = node
566 rev = node
567
567
568 if not rev:
568 if not rev:
569 raise error.Abort(_("please specify a revision to backout"))
569 raise error.Abort(_("please specify a revision to backout"))
570
570
571 date = opts.get('date')
571 date = opts.get('date')
572 if date:
572 if date:
573 opts['date'] = util.parsedate(date)
573 opts['date'] = util.parsedate(date)
574
574
575 cmdutil.checkunfinished(repo)
575 cmdutil.checkunfinished(repo)
576 cmdutil.bailifchanged(repo)
576 cmdutil.bailifchanged(repo)
577 node = scmutil.revsingle(repo, rev).node()
577 node = scmutil.revsingle(repo, rev).node()
578
578
579 op1, op2 = repo.dirstate.parents()
579 op1, op2 = repo.dirstate.parents()
580 if not repo.changelog.isancestor(node, op1):
580 if not repo.changelog.isancestor(node, op1):
581 raise error.Abort(_('cannot backout change that is not an ancestor'))
581 raise error.Abort(_('cannot backout change that is not an ancestor'))
582
582
583 p1, p2 = repo.changelog.parents(node)
583 p1, p2 = repo.changelog.parents(node)
584 if p1 == nullid:
584 if p1 == nullid:
585 raise error.Abort(_('cannot backout a change with no parents'))
585 raise error.Abort(_('cannot backout a change with no parents'))
586 if p2 != nullid:
586 if p2 != nullid:
587 if not opts.get('parent'):
587 if not opts.get('parent'):
588 raise error.Abort(_('cannot backout a merge changeset'))
588 raise error.Abort(_('cannot backout a merge changeset'))
589 p = repo.lookup(opts['parent'])
589 p = repo.lookup(opts['parent'])
590 if p not in (p1, p2):
590 if p not in (p1, p2):
591 raise error.Abort(_('%s is not a parent of %s') %
591 raise error.Abort(_('%s is not a parent of %s') %
592 (short(p), short(node)))
592 (short(p), short(node)))
593 parent = p
593 parent = p
594 else:
594 else:
595 if opts.get('parent'):
595 if opts.get('parent'):
596 raise error.Abort(_('cannot use --parent on non-merge changeset'))
596 raise error.Abort(_('cannot use --parent on non-merge changeset'))
597 parent = p1
597 parent = p1
598
598
599 # the backout should appear on the same branch
599 # the backout should appear on the same branch
600 branch = repo.dirstate.branch()
600 branch = repo.dirstate.branch()
601 bheads = repo.branchheads(branch)
601 bheads = repo.branchheads(branch)
602 rctx = scmutil.revsingle(repo, hex(parent))
602 rctx = scmutil.revsingle(repo, hex(parent))
603 if not opts.get('merge') and op1 != node:
603 if not opts.get('merge') and op1 != node:
604 dsguard = dirstateguard.dirstateguard(repo, 'backout')
604 dsguard = dirstateguard.dirstateguard(repo, 'backout')
605 try:
605 try:
606 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
606 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
607 'backout')
607 'backout')
608 stats = mergemod.update(repo, parent, True, True, node, False)
608 stats = mergemod.update(repo, parent, True, True, node, False)
609 repo.setparents(op1, op2)
609 repo.setparents(op1, op2)
610 dsguard.close()
610 dsguard.close()
611 hg._showstats(repo, stats)
611 hg._showstats(repo, stats)
612 if stats[3]:
612 if stats[3]:
613 repo.ui.status(_("use 'hg resolve' to retry unresolved "
613 repo.ui.status(_("use 'hg resolve' to retry unresolved "
614 "file merges\n"))
614 "file merges\n"))
615 return 1
615 return 1
616 finally:
616 finally:
617 ui.setconfig('ui', 'forcemerge', '', '')
617 ui.setconfig('ui', 'forcemerge', '', '')
618 lockmod.release(dsguard)
618 lockmod.release(dsguard)
619 else:
619 else:
620 hg.clean(repo, node, show_stats=False)
620 hg.clean(repo, node, show_stats=False)
621 repo.dirstate.setbranch(branch)
621 repo.dirstate.setbranch(branch)
622 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
622 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
623
623
624 if opts.get('no_commit'):
624 if opts.get('no_commit'):
625 msg = _("changeset %s backed out, "
625 msg = _("changeset %s backed out, "
626 "don't forget to commit.\n")
626 "don't forget to commit.\n")
627 ui.status(msg % short(node))
627 ui.status(msg % short(node))
628 return 0
628 return 0
629
629
630 def commitfunc(ui, repo, message, match, opts):
630 def commitfunc(ui, repo, message, match, opts):
631 editform = 'backout'
631 editform = 'backout'
632 e = cmdutil.getcommiteditor(editform=editform,
632 e = cmdutil.getcommiteditor(editform=editform,
633 **pycompat.strkwargs(opts))
633 **pycompat.strkwargs(opts))
634 if not message:
634 if not message:
635 # we don't translate commit messages
635 # we don't translate commit messages
636 message = "Backed out changeset %s" % short(node)
636 message = "Backed out changeset %s" % short(node)
637 e = cmdutil.getcommiteditor(edit=True, editform=editform)
637 e = cmdutil.getcommiteditor(edit=True, editform=editform)
638 return repo.commit(message, opts.get('user'), opts.get('date'),
638 return repo.commit(message, opts.get('user'), opts.get('date'),
639 match, editor=e)
639 match, editor=e)
640 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
640 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
641 if not newnode:
641 if not newnode:
642 ui.status(_("nothing changed\n"))
642 ui.status(_("nothing changed\n"))
643 return 1
643 return 1
644 cmdutil.commitstatus(repo, newnode, branch, bheads)
644 cmdutil.commitstatus(repo, newnode, branch, bheads)
645
645
646 def nice(node):
646 def nice(node):
647 return '%d:%s' % (repo.changelog.rev(node), short(node))
647 return '%d:%s' % (repo.changelog.rev(node), short(node))
648 ui.status(_('changeset %s backs out changeset %s\n') %
648 ui.status(_('changeset %s backs out changeset %s\n') %
649 (nice(repo.changelog.tip()), nice(node)))
649 (nice(repo.changelog.tip()), nice(node)))
650 if opts.get('merge') and op1 != node:
650 if opts.get('merge') and op1 != node:
651 hg.clean(repo, op1, show_stats=False)
651 hg.clean(repo, op1, show_stats=False)
652 ui.status(_('merging with changeset %s\n')
652 ui.status(_('merging with changeset %s\n')
653 % nice(repo.changelog.tip()))
653 % nice(repo.changelog.tip()))
654 try:
654 try:
655 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
655 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
656 'backout')
656 'backout')
657 return hg.merge(repo, hex(repo.changelog.tip()))
657 return hg.merge(repo, hex(repo.changelog.tip()))
658 finally:
658 finally:
659 ui.setconfig('ui', 'forcemerge', '', '')
659 ui.setconfig('ui', 'forcemerge', '', '')
660 return 0
660 return 0
661
661
662 @command('bisect',
662 @command('bisect',
663 [('r', 'reset', False, _('reset bisect state')),
663 [('r', 'reset', False, _('reset bisect state')),
664 ('g', 'good', False, _('mark changeset good')),
664 ('g', 'good', False, _('mark changeset good')),
665 ('b', 'bad', False, _('mark changeset bad')),
665 ('b', 'bad', False, _('mark changeset bad')),
666 ('s', 'skip', False, _('skip testing changeset')),
666 ('s', 'skip', False, _('skip testing changeset')),
667 ('e', 'extend', False, _('extend the bisect range')),
667 ('e', 'extend', False, _('extend the bisect range')),
668 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
668 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
669 ('U', 'noupdate', False, _('do not update to target'))],
669 ('U', 'noupdate', False, _('do not update to target'))],
670 _("[-gbsr] [-U] [-c CMD] [REV]"))
670 _("[-gbsr] [-U] [-c CMD] [REV]"))
671 def bisect(ui, repo, rev=None, extra=None, command=None,
671 def bisect(ui, repo, rev=None, extra=None, command=None,
672 reset=None, good=None, bad=None, skip=None, extend=None,
672 reset=None, good=None, bad=None, skip=None, extend=None,
673 noupdate=None):
673 noupdate=None):
674 """subdivision search of changesets
674 """subdivision search of changesets
675
675
676 This command helps to find changesets which introduce problems. To
676 This command helps to find changesets which introduce problems. To
677 use, mark the earliest changeset you know exhibits the problem as
677 use, mark the earliest changeset you know exhibits the problem as
678 bad, then mark the latest changeset which is free from the problem
678 bad, then mark the latest changeset which is free from the problem
679 as good. Bisect will update your working directory to a revision
679 as good. Bisect will update your working directory to a revision
680 for testing (unless the -U/--noupdate option is specified). Once
680 for testing (unless the -U/--noupdate option is specified). Once
681 you have performed tests, mark the working directory as good or
681 you have performed tests, mark the working directory as good or
682 bad, and bisect will either update to another candidate changeset
682 bad, and bisect will either update to another candidate changeset
683 or announce that it has found the bad revision.
683 or announce that it has found the bad revision.
684
684
685 As a shortcut, you can also use the revision argument to mark a
685 As a shortcut, you can also use the revision argument to mark a
686 revision as good or bad without checking it out first.
686 revision as good or bad without checking it out first.
687
687
688 If you supply a command, it will be used for automatic bisection.
688 If you supply a command, it will be used for automatic bisection.
689 The environment variable HG_NODE will contain the ID of the
689 The environment variable HG_NODE will contain the ID of the
690 changeset being tested. The exit status of the command will be
690 changeset being tested. The exit status of the command will be
691 used to mark revisions as good or bad: status 0 means good, 125
691 used to mark revisions as good or bad: status 0 means good, 125
692 means to skip the revision, 127 (command not found) will abort the
692 means to skip the revision, 127 (command not found) will abort the
693 bisection, and any other non-zero exit status means the revision
693 bisection, and any other non-zero exit status means the revision
694 is bad.
694 is bad.
695
695
696 .. container:: verbose
696 .. container:: verbose
697
697
698 Some examples:
698 Some examples:
699
699
700 - start a bisection with known bad revision 34, and good revision 12::
700 - start a bisection with known bad revision 34, and good revision 12::
701
701
702 hg bisect --bad 34
702 hg bisect --bad 34
703 hg bisect --good 12
703 hg bisect --good 12
704
704
705 - advance the current bisection by marking current revision as good or
705 - advance the current bisection by marking current revision as good or
706 bad::
706 bad::
707
707
708 hg bisect --good
708 hg bisect --good
709 hg bisect --bad
709 hg bisect --bad
710
710
711 - mark the current revision, or a known revision, to be skipped (e.g. if
711 - mark the current revision, or a known revision, to be skipped (e.g. if
712 that revision is not usable because of another issue)::
712 that revision is not usable because of another issue)::
713
713
714 hg bisect --skip
714 hg bisect --skip
715 hg bisect --skip 23
715 hg bisect --skip 23
716
716
717 - skip all revisions that do not touch directories ``foo`` or ``bar``::
717 - skip all revisions that do not touch directories ``foo`` or ``bar``::
718
718
719 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
719 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
720
720
721 - forget the current bisection::
721 - forget the current bisection::
722
722
723 hg bisect --reset
723 hg bisect --reset
724
724
725 - use 'make && make tests' to automatically find the first broken
725 - use 'make && make tests' to automatically find the first broken
726 revision::
726 revision::
727
727
728 hg bisect --reset
728 hg bisect --reset
729 hg bisect --bad 34
729 hg bisect --bad 34
730 hg bisect --good 12
730 hg bisect --good 12
731 hg bisect --command "make && make tests"
731 hg bisect --command "make && make tests"
732
732
733 - see all changesets whose states are already known in the current
733 - see all changesets whose states are already known in the current
734 bisection::
734 bisection::
735
735
736 hg log -r "bisect(pruned)"
736 hg log -r "bisect(pruned)"
737
737
738 - see the changeset currently being bisected (especially useful
738 - see the changeset currently being bisected (especially useful
739 if running with -U/--noupdate)::
739 if running with -U/--noupdate)::
740
740
741 hg log -r "bisect(current)"
741 hg log -r "bisect(current)"
742
742
743 - see all changesets that took part in the current bisection::
743 - see all changesets that took part in the current bisection::
744
744
745 hg log -r "bisect(range)"
745 hg log -r "bisect(range)"
746
746
747 - you can even get a nice graph::
747 - you can even get a nice graph::
748
748
749 hg log --graph -r "bisect(range)"
749 hg log --graph -r "bisect(range)"
750
750
751 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
751 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
752
752
753 Returns 0 on success.
753 Returns 0 on success.
754 """
754 """
755 # backward compatibility
755 # backward compatibility
756 if rev in "good bad reset init".split():
756 if rev in "good bad reset init".split():
757 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
757 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
758 cmd, rev, extra = rev, extra, None
758 cmd, rev, extra = rev, extra, None
759 if cmd == "good":
759 if cmd == "good":
760 good = True
760 good = True
761 elif cmd == "bad":
761 elif cmd == "bad":
762 bad = True
762 bad = True
763 else:
763 else:
764 reset = True
764 reset = True
765 elif extra:
765 elif extra:
766 raise error.Abort(_('incompatible arguments'))
766 raise error.Abort(_('incompatible arguments'))
767
767
768 incompatibles = {
768 incompatibles = {
769 '--bad': bad,
769 '--bad': bad,
770 '--command': bool(command),
770 '--command': bool(command),
771 '--extend': extend,
771 '--extend': extend,
772 '--good': good,
772 '--good': good,
773 '--reset': reset,
773 '--reset': reset,
774 '--skip': skip,
774 '--skip': skip,
775 }
775 }
776
776
777 enabled = [x for x in incompatibles if incompatibles[x]]
777 enabled = [x for x in incompatibles if incompatibles[x]]
778
778
779 if len(enabled) > 1:
779 if len(enabled) > 1:
780 raise error.Abort(_('%s and %s are incompatible') %
780 raise error.Abort(_('%s and %s are incompatible') %
781 tuple(sorted(enabled)[0:2]))
781 tuple(sorted(enabled)[0:2]))
782
782
783 if reset:
783 if reset:
784 hbisect.resetstate(repo)
784 hbisect.resetstate(repo)
785 return
785 return
786
786
787 state = hbisect.load_state(repo)
787 state = hbisect.load_state(repo)
788
788
789 # update state
789 # update state
790 if good or bad or skip:
790 if good or bad or skip:
791 if rev:
791 if rev:
792 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
792 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
793 else:
793 else:
794 nodes = [repo.lookup('.')]
794 nodes = [repo.lookup('.')]
795 if good:
795 if good:
796 state['good'] += nodes
796 state['good'] += nodes
797 elif bad:
797 elif bad:
798 state['bad'] += nodes
798 state['bad'] += nodes
799 elif skip:
799 elif skip:
800 state['skip'] += nodes
800 state['skip'] += nodes
801 hbisect.save_state(repo, state)
801 hbisect.save_state(repo, state)
802 if not (state['good'] and state['bad']):
802 if not (state['good'] and state['bad']):
803 return
803 return
804
804
805 def mayupdate(repo, node, show_stats=True):
805 def mayupdate(repo, node, show_stats=True):
806 """common used update sequence"""
806 """common used update sequence"""
807 if noupdate:
807 if noupdate:
808 return
808 return
809 cmdutil.checkunfinished(repo)
809 cmdutil.checkunfinished(repo)
810 cmdutil.bailifchanged(repo)
810 cmdutil.bailifchanged(repo)
811 return hg.clean(repo, node, show_stats=show_stats)
811 return hg.clean(repo, node, show_stats=show_stats)
812
812
813 displayer = cmdutil.show_changeset(ui, repo, {})
813 displayer = cmdutil.show_changeset(ui, repo, {})
814
814
815 if command:
815 if command:
816 changesets = 1
816 changesets = 1
817 if noupdate:
817 if noupdate:
818 try:
818 try:
819 node = state['current'][0]
819 node = state['current'][0]
820 except LookupError:
820 except LookupError:
821 raise error.Abort(_('current bisect revision is unknown - '
821 raise error.Abort(_('current bisect revision is unknown - '
822 'start a new bisect to fix'))
822 'start a new bisect to fix'))
823 else:
823 else:
824 node, p2 = repo.dirstate.parents()
824 node, p2 = repo.dirstate.parents()
825 if p2 != nullid:
825 if p2 != nullid:
826 raise error.Abort(_('current bisect revision is a merge'))
826 raise error.Abort(_('current bisect revision is a merge'))
827 if rev:
827 if rev:
828 node = repo[scmutil.revsingle(repo, rev, node)].node()
828 node = repo[scmutil.revsingle(repo, rev, node)].node()
829 try:
829 try:
830 while changesets:
830 while changesets:
831 # update state
831 # update state
832 state['current'] = [node]
832 state['current'] = [node]
833 hbisect.save_state(repo, state)
833 hbisect.save_state(repo, state)
834 status = ui.system(command, environ={'HG_NODE': hex(node)},
834 status = ui.system(command, environ={'HG_NODE': hex(node)},
835 blockedtag='bisect_check')
835 blockedtag='bisect_check')
836 if status == 125:
836 if status == 125:
837 transition = "skip"
837 transition = "skip"
838 elif status == 0:
838 elif status == 0:
839 transition = "good"
839 transition = "good"
840 # status < 0 means process was killed
840 # status < 0 means process was killed
841 elif status == 127:
841 elif status == 127:
842 raise error.Abort(_("failed to execute %s") % command)
842 raise error.Abort(_("failed to execute %s") % command)
843 elif status < 0:
843 elif status < 0:
844 raise error.Abort(_("%s killed") % command)
844 raise error.Abort(_("%s killed") % command)
845 else:
845 else:
846 transition = "bad"
846 transition = "bad"
847 state[transition].append(node)
847 state[transition].append(node)
848 ctx = repo[node]
848 ctx = repo[node]
849 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
849 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
850 hbisect.checkstate(state)
850 hbisect.checkstate(state)
851 # bisect
851 # bisect
852 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
852 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
853 # update to next check
853 # update to next check
854 node = nodes[0]
854 node = nodes[0]
855 mayupdate(repo, node, show_stats=False)
855 mayupdate(repo, node, show_stats=False)
856 finally:
856 finally:
857 state['current'] = [node]
857 state['current'] = [node]
858 hbisect.save_state(repo, state)
858 hbisect.save_state(repo, state)
859 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
859 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
860 return
860 return
861
861
862 hbisect.checkstate(state)
862 hbisect.checkstate(state)
863
863
864 # actually bisect
864 # actually bisect
865 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
865 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
866 if extend:
866 if extend:
867 if not changesets:
867 if not changesets:
868 extendnode = hbisect.extendrange(repo, state, nodes, good)
868 extendnode = hbisect.extendrange(repo, state, nodes, good)
869 if extendnode is not None:
869 if extendnode is not None:
870 ui.write(_("Extending search to changeset %d:%s\n")
870 ui.write(_("Extending search to changeset %d:%s\n")
871 % (extendnode.rev(), extendnode))
871 % (extendnode.rev(), extendnode))
872 state['current'] = [extendnode.node()]
872 state['current'] = [extendnode.node()]
873 hbisect.save_state(repo, state)
873 hbisect.save_state(repo, state)
874 return mayupdate(repo, extendnode.node())
874 return mayupdate(repo, extendnode.node())
875 raise error.Abort(_("nothing to extend"))
875 raise error.Abort(_("nothing to extend"))
876
876
877 if changesets == 0:
877 if changesets == 0:
878 hbisect.printresult(ui, repo, state, displayer, nodes, good)
878 hbisect.printresult(ui, repo, state, displayer, nodes, good)
879 else:
879 else:
880 assert len(nodes) == 1 # only a single node can be tested next
880 assert len(nodes) == 1 # only a single node can be tested next
881 node = nodes[0]
881 node = nodes[0]
882 # compute the approximate number of remaining tests
882 # compute the approximate number of remaining tests
883 tests, size = 0, 2
883 tests, size = 0, 2
884 while size <= changesets:
884 while size <= changesets:
885 tests, size = tests + 1, size * 2
885 tests, size = tests + 1, size * 2
886 rev = repo.changelog.rev(node)
886 rev = repo.changelog.rev(node)
887 ui.write(_("Testing changeset %d:%s "
887 ui.write(_("Testing changeset %d:%s "
888 "(%d changesets remaining, ~%d tests)\n")
888 "(%d changesets remaining, ~%d tests)\n")
889 % (rev, short(node), changesets, tests))
889 % (rev, short(node), changesets, tests))
890 state['current'] = [node]
890 state['current'] = [node]
891 hbisect.save_state(repo, state)
891 hbisect.save_state(repo, state)
892 return mayupdate(repo, node)
892 return mayupdate(repo, node)
893
893
894 @command('bookmarks|bookmark',
894 @command('bookmarks|bookmark',
895 [('f', 'force', False, _('force')),
895 [('f', 'force', False, _('force')),
896 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
896 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
897 ('d', 'delete', False, _('delete a given bookmark')),
897 ('d', 'delete', False, _('delete a given bookmark')),
898 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
898 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
899 ('i', 'inactive', False, _('mark a bookmark inactive')),
899 ('i', 'inactive', False, _('mark a bookmark inactive')),
900 ] + formatteropts,
900 ] + formatteropts,
901 _('hg bookmarks [OPTIONS]... [NAME]...'))
901 _('hg bookmarks [OPTIONS]... [NAME]...'))
902 def bookmark(ui, repo, *names, **opts):
902 def bookmark(ui, repo, *names, **opts):
903 '''create a new bookmark or list existing bookmarks
903 '''create a new bookmark or list existing bookmarks
904
904
905 Bookmarks are labels on changesets to help track lines of development.
905 Bookmarks are labels on changesets to help track lines of development.
906 Bookmarks are unversioned and can be moved, renamed and deleted.
906 Bookmarks are unversioned and can be moved, renamed and deleted.
907 Deleting or moving a bookmark has no effect on the associated changesets.
907 Deleting or moving a bookmark has no effect on the associated changesets.
908
908
909 Creating or updating to a bookmark causes it to be marked as 'active'.
909 Creating or updating to a bookmark causes it to be marked as 'active'.
910 The active bookmark is indicated with a '*'.
910 The active bookmark is indicated with a '*'.
911 When a commit is made, the active bookmark will advance to the new commit.
911 When a commit is made, the active bookmark will advance to the new commit.
912 A plain :hg:`update` will also advance an active bookmark, if possible.
912 A plain :hg:`update` will also advance an active bookmark, if possible.
913 Updating away from a bookmark will cause it to be deactivated.
913 Updating away from a bookmark will cause it to be deactivated.
914
914
915 Bookmarks can be pushed and pulled between repositories (see
915 Bookmarks can be pushed and pulled between repositories (see
916 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
916 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
917 diverged, a new 'divergent bookmark' of the form 'name@path' will
917 diverged, a new 'divergent bookmark' of the form 'name@path' will
918 be created. Using :hg:`merge` will resolve the divergence.
918 be created. Using :hg:`merge` will resolve the divergence.
919
919
920 A bookmark named '@' has the special property that :hg:`clone` will
920 A bookmark named '@' has the special property that :hg:`clone` will
921 check it out by default if it exists.
921 check it out by default if it exists.
922
922
923 .. container:: verbose
923 .. container:: verbose
924
924
925 Examples:
925 Examples:
926
926
927 - create an active bookmark for a new line of development::
927 - create an active bookmark for a new line of development::
928
928
929 hg book new-feature
929 hg book new-feature
930
930
931 - create an inactive bookmark as a place marker::
931 - create an inactive bookmark as a place marker::
932
932
933 hg book -i reviewed
933 hg book -i reviewed
934
934
935 - create an inactive bookmark on another changeset::
935 - create an inactive bookmark on another changeset::
936
936
937 hg book -r .^ tested
937 hg book -r .^ tested
938
938
939 - rename bookmark turkey to dinner::
939 - rename bookmark turkey to dinner::
940
940
941 hg book -m turkey dinner
941 hg book -m turkey dinner
942
942
943 - move the '@' bookmark from another branch::
943 - move the '@' bookmark from another branch::
944
944
945 hg book -f @
945 hg book -f @
946 '''
946 '''
947 opts = pycompat.byteskwargs(opts)
947 opts = pycompat.byteskwargs(opts)
948 force = opts.get('force')
948 force = opts.get('force')
949 rev = opts.get('rev')
949 rev = opts.get('rev')
950 delete = opts.get('delete')
950 delete = opts.get('delete')
951 rename = opts.get('rename')
951 rename = opts.get('rename')
952 inactive = opts.get('inactive')
952 inactive = opts.get('inactive')
953
953
954 if delete and rename:
954 if delete and rename:
955 raise error.Abort(_("--delete and --rename are incompatible"))
955 raise error.Abort(_("--delete and --rename are incompatible"))
956 if delete and rev:
956 if delete and rev:
957 raise error.Abort(_("--rev is incompatible with --delete"))
957 raise error.Abort(_("--rev is incompatible with --delete"))
958 if rename and rev:
958 if rename and rev:
959 raise error.Abort(_("--rev is incompatible with --rename"))
959 raise error.Abort(_("--rev is incompatible with --rename"))
960 if not names and (delete or rev):
960 if not names and (delete or rev):
961 raise error.Abort(_("bookmark name required"))
961 raise error.Abort(_("bookmark name required"))
962
962
963 if delete or rename or names or inactive:
963 if delete or rename or names or inactive:
964 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
964 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
965 if delete:
965 if delete:
966 bookmarks.delete(repo, tr, names)
966 bookmarks.delete(repo, tr, names)
967 elif rename:
967 elif rename:
968 if not names:
968 if not names:
969 raise error.Abort(_("new bookmark name required"))
969 raise error.Abort(_("new bookmark name required"))
970 elif len(names) > 1:
970 elif len(names) > 1:
971 raise error.Abort(_("only one new bookmark name allowed"))
971 raise error.Abort(_("only one new bookmark name allowed"))
972 bookmarks.rename(repo, tr, rename, names[0], force, inactive)
972 bookmarks.rename(repo, tr, rename, names[0], force, inactive)
973 elif names:
973 elif names:
974 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
974 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
975 elif inactive:
975 elif inactive:
976 if len(repo._bookmarks) == 0:
976 if len(repo._bookmarks) == 0:
977 ui.status(_("no bookmarks set\n"))
977 ui.status(_("no bookmarks set\n"))
978 elif not repo._activebookmark:
978 elif not repo._activebookmark:
979 ui.status(_("no active bookmark\n"))
979 ui.status(_("no active bookmark\n"))
980 else:
980 else:
981 bookmarks.deactivate(repo)
981 bookmarks.deactivate(repo)
982 else: # show bookmarks
982 else: # show bookmarks
983 bookmarks.printbookmarks(ui, repo, **opts)
983 bookmarks.printbookmarks(ui, repo, **opts)
984
984
985 @command('branch',
985 @command('branch',
986 [('f', 'force', None,
986 [('f', 'force', None,
987 _('set branch name even if it shadows an existing branch')),
987 _('set branch name even if it shadows an existing branch')),
988 ('C', 'clean', None, _('reset branch name to parent branch name'))],
988 ('C', 'clean', None, _('reset branch name to parent branch name'))],
989 _('[-fC] [NAME]'))
989 _('[-fC] [NAME]'))
990 def branch(ui, repo, label=None, **opts):
990 def branch(ui, repo, label=None, **opts):
991 """set or show the current branch name
991 """set or show the current branch name
992
992
993 .. note::
993 .. note::
994
994
995 Branch names are permanent and global. Use :hg:`bookmark` to create a
995 Branch names are permanent and global. Use :hg:`bookmark` to create a
996 light-weight bookmark instead. See :hg:`help glossary` for more
996 light-weight bookmark instead. See :hg:`help glossary` for more
997 information about named branches and bookmarks.
997 information about named branches and bookmarks.
998
998
999 With no argument, show the current branch name. With one argument,
999 With no argument, show the current branch name. With one argument,
1000 set the working directory branch name (the branch will not exist
1000 set the working directory branch name (the branch will not exist
1001 in the repository until the next commit). Standard practice
1001 in the repository until the next commit). Standard practice
1002 recommends that primary development take place on the 'default'
1002 recommends that primary development take place on the 'default'
1003 branch.
1003 branch.
1004
1004
1005 Unless -f/--force is specified, branch will not let you set a
1005 Unless -f/--force is specified, branch will not let you set a
1006 branch name that already exists.
1006 branch name that already exists.
1007
1007
1008 Use -C/--clean to reset the working directory branch to that of
1008 Use -C/--clean to reset the working directory branch to that of
1009 the parent of the working directory, negating a previous branch
1009 the parent of the working directory, negating a previous branch
1010 change.
1010 change.
1011
1011
1012 Use the command :hg:`update` to switch to an existing branch. Use
1012 Use the command :hg:`update` to switch to an existing branch. Use
1013 :hg:`commit --close-branch` to mark this branch head as closed.
1013 :hg:`commit --close-branch` to mark this branch head as closed.
1014 When all heads of a branch are closed, the branch will be
1014 When all heads of a branch are closed, the branch will be
1015 considered closed.
1015 considered closed.
1016
1016
1017 Returns 0 on success.
1017 Returns 0 on success.
1018 """
1018 """
1019 opts = pycompat.byteskwargs(opts)
1019 opts = pycompat.byteskwargs(opts)
1020 if label:
1020 if label:
1021 label = label.strip()
1021 label = label.strip()
1022
1022
1023 if not opts.get('clean') and not label:
1023 if not opts.get('clean') and not label:
1024 ui.write("%s\n" % repo.dirstate.branch())
1024 ui.write("%s\n" % repo.dirstate.branch())
1025 return
1025 return
1026
1026
1027 with repo.wlock():
1027 with repo.wlock():
1028 if opts.get('clean'):
1028 if opts.get('clean'):
1029 label = repo[None].p1().branch()
1029 label = repo[None].p1().branch()
1030 repo.dirstate.setbranch(label)
1030 repo.dirstate.setbranch(label)
1031 ui.status(_('reset working directory to branch %s\n') % label)
1031 ui.status(_('reset working directory to branch %s\n') % label)
1032 elif label:
1032 elif label:
1033 if not opts.get('force') and label in repo.branchmap():
1033 if not opts.get('force') and label in repo.branchmap():
1034 if label not in [p.branch() for p in repo[None].parents()]:
1034 if label not in [p.branch() for p in repo[None].parents()]:
1035 raise error.Abort(_('a branch of the same name already'
1035 raise error.Abort(_('a branch of the same name already'
1036 ' exists'),
1036 ' exists'),
1037 # i18n: "it" refers to an existing branch
1037 # i18n: "it" refers to an existing branch
1038 hint=_("use 'hg update' to switch to it"))
1038 hint=_("use 'hg update' to switch to it"))
1039 scmutil.checknewlabel(repo, label, 'branch')
1039 scmutil.checknewlabel(repo, label, 'branch')
1040 repo.dirstate.setbranch(label)
1040 repo.dirstate.setbranch(label)
1041 ui.status(_('marked working directory as branch %s\n') % label)
1041 ui.status(_('marked working directory as branch %s\n') % label)
1042
1042
1043 # find any open named branches aside from default
1043 # find any open named branches aside from default
1044 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1044 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1045 if n != "default" and not c]
1045 if n != "default" and not c]
1046 if not others:
1046 if not others:
1047 ui.status(_('(branches are permanent and global, '
1047 ui.status(_('(branches are permanent and global, '
1048 'did you want a bookmark?)\n'))
1048 'did you want a bookmark?)\n'))
1049
1049
1050 @command('branches',
1050 @command('branches',
1051 [('a', 'active', False,
1051 [('a', 'active', False,
1052 _('show only branches that have unmerged heads (DEPRECATED)')),
1052 _('show only branches that have unmerged heads (DEPRECATED)')),
1053 ('c', 'closed', False, _('show normal and closed branches')),
1053 ('c', 'closed', False, _('show normal and closed branches')),
1054 ] + formatteropts,
1054 ] + formatteropts,
1055 _('[-c]'))
1055 _('[-c]'))
1056 def branches(ui, repo, active=False, closed=False, **opts):
1056 def branches(ui, repo, active=False, closed=False, **opts):
1057 """list repository named branches
1057 """list repository named branches
1058
1058
1059 List the repository's named branches, indicating which ones are
1059 List the repository's named branches, indicating which ones are
1060 inactive. If -c/--closed is specified, also list branches which have
1060 inactive. If -c/--closed is specified, also list branches which have
1061 been marked closed (see :hg:`commit --close-branch`).
1061 been marked closed (see :hg:`commit --close-branch`).
1062
1062
1063 Use the command :hg:`update` to switch to an existing branch.
1063 Use the command :hg:`update` to switch to an existing branch.
1064
1064
1065 Returns 0.
1065 Returns 0.
1066 """
1066 """
1067
1067
1068 opts = pycompat.byteskwargs(opts)
1068 opts = pycompat.byteskwargs(opts)
1069 ui.pager('branches')
1069 ui.pager('branches')
1070 fm = ui.formatter('branches', opts)
1070 fm = ui.formatter('branches', opts)
1071 hexfunc = fm.hexfunc
1071 hexfunc = fm.hexfunc
1072
1072
1073 allheads = set(repo.heads())
1073 allheads = set(repo.heads())
1074 branches = []
1074 branches = []
1075 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1075 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1076 isactive = not isclosed and bool(set(heads) & allheads)
1076 isactive = not isclosed and bool(set(heads) & allheads)
1077 branches.append((tag, repo[tip], isactive, not isclosed))
1077 branches.append((tag, repo[tip], isactive, not isclosed))
1078 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1078 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1079 reverse=True)
1079 reverse=True)
1080
1080
1081 for tag, ctx, isactive, isopen in branches:
1081 for tag, ctx, isactive, isopen in branches:
1082 if active and not isactive:
1082 if active and not isactive:
1083 continue
1083 continue
1084 if isactive:
1084 if isactive:
1085 label = 'branches.active'
1085 label = 'branches.active'
1086 notice = ''
1086 notice = ''
1087 elif not isopen:
1087 elif not isopen:
1088 if not closed:
1088 if not closed:
1089 continue
1089 continue
1090 label = 'branches.closed'
1090 label = 'branches.closed'
1091 notice = _(' (closed)')
1091 notice = _(' (closed)')
1092 else:
1092 else:
1093 label = 'branches.inactive'
1093 label = 'branches.inactive'
1094 notice = _(' (inactive)')
1094 notice = _(' (inactive)')
1095 current = (tag == repo.dirstate.branch())
1095 current = (tag == repo.dirstate.branch())
1096 if current:
1096 if current:
1097 label = 'branches.current'
1097 label = 'branches.current'
1098
1098
1099 fm.startitem()
1099 fm.startitem()
1100 fm.write('branch', '%s', tag, label=label)
1100 fm.write('branch', '%s', tag, label=label)
1101 rev = ctx.rev()
1101 rev = ctx.rev()
1102 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1102 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1103 fmt = ' ' * padsize + ' %d:%s'
1103 fmt = ' ' * padsize + ' %d:%s'
1104 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1104 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1105 label='log.changeset changeset.%s' % ctx.phasestr())
1105 label='log.changeset changeset.%s' % ctx.phasestr())
1106 fm.context(ctx=ctx)
1106 fm.context(ctx=ctx)
1107 fm.data(active=isactive, closed=not isopen, current=current)
1107 fm.data(active=isactive, closed=not isopen, current=current)
1108 if not ui.quiet:
1108 if not ui.quiet:
1109 fm.plain(notice)
1109 fm.plain(notice)
1110 fm.plain('\n')
1110 fm.plain('\n')
1111 fm.end()
1111 fm.end()
1112
1112
1113 @command('bundle',
1113 @command('bundle',
1114 [('f', 'force', None, _('run even when the destination is unrelated')),
1114 [('f', 'force', None, _('run even when the destination is unrelated')),
1115 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1115 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1116 _('REV')),
1116 _('REV')),
1117 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1117 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1118 _('BRANCH')),
1118 _('BRANCH')),
1119 ('', 'base', [],
1119 ('', 'base', [],
1120 _('a base changeset assumed to be available at the destination'),
1120 _('a base changeset assumed to be available at the destination'),
1121 _('REV')),
1121 _('REV')),
1122 ('a', 'all', None, _('bundle all changesets in the repository')),
1122 ('a', 'all', None, _('bundle all changesets in the repository')),
1123 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1123 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1124 ] + remoteopts,
1124 ] + remoteopts,
1125 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1125 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1126 def bundle(ui, repo, fname, dest=None, **opts):
1126 def bundle(ui, repo, fname, dest=None, **opts):
1127 """create a bundle file
1127 """create a bundle file
1128
1128
1129 Generate a bundle file containing data to be added to a repository.
1129 Generate a bundle file containing data to be added to a repository.
1130
1130
1131 To create a bundle containing all changesets, use -a/--all
1131 To create a bundle containing all changesets, use -a/--all
1132 (or --base null). Otherwise, hg assumes the destination will have
1132 (or --base null). Otherwise, hg assumes the destination will have
1133 all the nodes you specify with --base parameters. Otherwise, hg
1133 all the nodes you specify with --base parameters. Otherwise, hg
1134 will assume the repository has all the nodes in destination, or
1134 will assume the repository has all the nodes in destination, or
1135 default-push/default if no destination is specified.
1135 default-push/default if no destination is specified.
1136
1136
1137 You can change bundle format with the -t/--type option. See
1137 You can change bundle format with the -t/--type option. See
1138 :hg:`help bundlespec` for documentation on this format. By default,
1138 :hg:`help bundlespec` for documentation on this format. By default,
1139 the most appropriate format is used and compression defaults to
1139 the most appropriate format is used and compression defaults to
1140 bzip2.
1140 bzip2.
1141
1141
1142 The bundle file can then be transferred using conventional means
1142 The bundle file can then be transferred using conventional means
1143 and applied to another repository with the unbundle or pull
1143 and applied to another repository with the unbundle or pull
1144 command. This is useful when direct push and pull are not
1144 command. This is useful when direct push and pull are not
1145 available or when exporting an entire repository is undesirable.
1145 available or when exporting an entire repository is undesirable.
1146
1146
1147 Applying bundles preserves all changeset contents including
1147 Applying bundles preserves all changeset contents including
1148 permissions, copy/rename information, and revision history.
1148 permissions, copy/rename information, and revision history.
1149
1149
1150 Returns 0 on success, 1 if no changes found.
1150 Returns 0 on success, 1 if no changes found.
1151 """
1151 """
1152 opts = pycompat.byteskwargs(opts)
1152 opts = pycompat.byteskwargs(opts)
1153 revs = None
1153 revs = None
1154 if 'rev' in opts:
1154 if 'rev' in opts:
1155 revstrings = opts['rev']
1155 revstrings = opts['rev']
1156 revs = scmutil.revrange(repo, revstrings)
1156 revs = scmutil.revrange(repo, revstrings)
1157 if revstrings and not revs:
1157 if revstrings and not revs:
1158 raise error.Abort(_('no commits to bundle'))
1158 raise error.Abort(_('no commits to bundle'))
1159
1159
1160 bundletype = opts.get('type', 'bzip2').lower()
1160 bundletype = opts.get('type', 'bzip2').lower()
1161 try:
1161 try:
1162 bcompression, cgversion, params = exchange.parsebundlespec(
1162 bcompression, cgversion, params = exchange.parsebundlespec(
1163 repo, bundletype, strict=False)
1163 repo, bundletype, strict=False)
1164 except error.UnsupportedBundleSpecification as e:
1164 except error.UnsupportedBundleSpecification as e:
1165 raise error.Abort(str(e),
1165 raise error.Abort(str(e),
1166 hint=_("see 'hg help bundlespec' for supported "
1166 hint=_("see 'hg help bundlespec' for supported "
1167 "values for --type"))
1167 "values for --type"))
1168
1168
1169 # Packed bundles are a pseudo bundle format for now.
1169 # Packed bundles are a pseudo bundle format for now.
1170 if cgversion == 's1':
1170 if cgversion == 's1':
1171 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1171 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1172 hint=_("use 'hg debugcreatestreamclonebundle'"))
1172 hint=_("use 'hg debugcreatestreamclonebundle'"))
1173
1173
1174 if opts.get('all'):
1174 if opts.get('all'):
1175 if dest:
1175 if dest:
1176 raise error.Abort(_("--all is incompatible with specifying "
1176 raise error.Abort(_("--all is incompatible with specifying "
1177 "a destination"))
1177 "a destination"))
1178 if opts.get('base'):
1178 if opts.get('base'):
1179 ui.warn(_("ignoring --base because --all was specified\n"))
1179 ui.warn(_("ignoring --base because --all was specified\n"))
1180 base = ['null']
1180 base = ['null']
1181 else:
1181 else:
1182 base = scmutil.revrange(repo, opts.get('base'))
1182 base = scmutil.revrange(repo, opts.get('base'))
1183 if cgversion not in changegroup.supportedoutgoingversions(repo):
1183 if cgversion not in changegroup.supportedoutgoingversions(repo):
1184 raise error.Abort(_("repository does not support bundle version %s") %
1184 raise error.Abort(_("repository does not support bundle version %s") %
1185 cgversion)
1185 cgversion)
1186
1186
1187 if base:
1187 if base:
1188 if dest:
1188 if dest:
1189 raise error.Abort(_("--base is incompatible with specifying "
1189 raise error.Abort(_("--base is incompatible with specifying "
1190 "a destination"))
1190 "a destination"))
1191 common = [repo.lookup(rev) for rev in base]
1191 common = [repo.lookup(rev) for rev in base]
1192 heads = revs and map(repo.lookup, revs) or None
1192 heads = revs and map(repo.lookup, revs) or None
1193 outgoing = discovery.outgoing(repo, common, heads)
1193 outgoing = discovery.outgoing(repo, common, heads)
1194 else:
1194 else:
1195 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1195 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1196 dest, branches = hg.parseurl(dest, opts.get('branch'))
1196 dest, branches = hg.parseurl(dest, opts.get('branch'))
1197 other = hg.peer(repo, opts, dest)
1197 other = hg.peer(repo, opts, dest)
1198 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1198 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1199 heads = revs and map(repo.lookup, revs) or revs
1199 heads = revs and map(repo.lookup, revs) or revs
1200 outgoing = discovery.findcommonoutgoing(repo, other,
1200 outgoing = discovery.findcommonoutgoing(repo, other,
1201 onlyheads=heads,
1201 onlyheads=heads,
1202 force=opts.get('force'),
1202 force=opts.get('force'),
1203 portable=True)
1203 portable=True)
1204
1204
1205 if not outgoing.missing:
1205 if not outgoing.missing:
1206 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1206 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1207 return 1
1207 return 1
1208
1208
1209 if cgversion == '01': #bundle1
1209 if cgversion == '01': #bundle1
1210 if bcompression is None:
1210 if bcompression is None:
1211 bcompression = 'UN'
1211 bcompression = 'UN'
1212 bversion = 'HG10' + bcompression
1212 bversion = 'HG10' + bcompression
1213 bcompression = None
1213 bcompression = None
1214 elif cgversion in ('02', '03'):
1214 elif cgversion in ('02', '03'):
1215 bversion = 'HG20'
1215 bversion = 'HG20'
1216 else:
1216 else:
1217 raise error.ProgrammingError(
1217 raise error.ProgrammingError(
1218 'bundle: unexpected changegroup version %s' % cgversion)
1218 'bundle: unexpected changegroup version %s' % cgversion)
1219
1219
1220 # TODO compression options should be derived from bundlespec parsing.
1220 # TODO compression options should be derived from bundlespec parsing.
1221 # This is a temporary hack to allow adjusting bundle compression
1221 # This is a temporary hack to allow adjusting bundle compression
1222 # level without a) formalizing the bundlespec changes to declare it
1222 # level without a) formalizing the bundlespec changes to declare it
1223 # b) introducing a command flag.
1223 # b) introducing a command flag.
1224 compopts = {}
1224 compopts = {}
1225 complevel = ui.configint('experimental', 'bundlecomplevel')
1225 complevel = ui.configint('experimental', 'bundlecomplevel')
1226 if complevel is not None:
1226 if complevel is not None:
1227 compopts['level'] = complevel
1227 compopts['level'] = complevel
1228
1228
1229
1229
1230 contentopts = {'cg.version': cgversion}
1230 contentopts = {'cg.version': cgversion}
1231 if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker', False):
1231 if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker', False):
1232 contentopts['obsolescence'] = True
1232 contentopts['obsolescence'] = True
1233 if repo.ui.configbool('experimental', 'bundle-phases', False):
1233 if repo.ui.configbool('experimental', 'bundle-phases', False):
1234 contentopts['phases'] = True
1234 contentopts['phases'] = True
1235 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1235 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1236 contentopts, compression=bcompression,
1236 contentopts, compression=bcompression,
1237 compopts=compopts)
1237 compopts=compopts)
1238
1238
1239 @command('cat',
1239 @command('cat',
1240 [('o', 'output', '',
1240 [('o', 'output', '',
1241 _('print output to file with formatted name'), _('FORMAT')),
1241 _('print output to file with formatted name'), _('FORMAT')),
1242 ('r', 'rev', '', _('print the given revision'), _('REV')),
1242 ('r', 'rev', '', _('print the given revision'), _('REV')),
1243 ('', 'decode', None, _('apply any matching decode filter')),
1243 ('', 'decode', None, _('apply any matching decode filter')),
1244 ] + walkopts + formatteropts,
1244 ] + walkopts + formatteropts,
1245 _('[OPTION]... FILE...'),
1245 _('[OPTION]... FILE...'),
1246 inferrepo=True)
1246 inferrepo=True)
1247 def cat(ui, repo, file1, *pats, **opts):
1247 def cat(ui, repo, file1, *pats, **opts):
1248 """output the current or given revision of files
1248 """output the current or given revision of files
1249
1249
1250 Print the specified files as they were at the given revision. If
1250 Print the specified files as they were at the given revision. If
1251 no revision is given, the parent of the working directory is used.
1251 no revision is given, the parent of the working directory is used.
1252
1252
1253 Output may be to a file, in which case the name of the file is
1253 Output may be to a file, in which case the name of the file is
1254 given using a format string. The formatting rules as follows:
1254 given using a format string. The formatting rules as follows:
1255
1255
1256 :``%%``: literal "%" character
1256 :``%%``: literal "%" character
1257 :``%s``: basename of file being printed
1257 :``%s``: basename of file being printed
1258 :``%d``: dirname of file being printed, or '.' if in repository root
1258 :``%d``: dirname of file being printed, or '.' if in repository root
1259 :``%p``: root-relative path name of file being printed
1259 :``%p``: root-relative path name of file being printed
1260 :``%H``: changeset hash (40 hexadecimal digits)
1260 :``%H``: changeset hash (40 hexadecimal digits)
1261 :``%R``: changeset revision number
1261 :``%R``: changeset revision number
1262 :``%h``: short-form changeset hash (12 hexadecimal digits)
1262 :``%h``: short-form changeset hash (12 hexadecimal digits)
1263 :``%r``: zero-padded changeset revision number
1263 :``%r``: zero-padded changeset revision number
1264 :``%b``: basename of the exporting repository
1264 :``%b``: basename of the exporting repository
1265
1265
1266 Returns 0 on success.
1266 Returns 0 on success.
1267 """
1267 """
1268 ctx = scmutil.revsingle(repo, opts.get('rev'))
1268 ctx = scmutil.revsingle(repo, opts.get('rev'))
1269 m = scmutil.match(ctx, (file1,) + pats, opts)
1269 m = scmutil.match(ctx, (file1,) + pats, opts)
1270 fntemplate = opts.pop('output', '')
1270 fntemplate = opts.pop('output', '')
1271 if cmdutil.isstdiofilename(fntemplate):
1271 if cmdutil.isstdiofilename(fntemplate):
1272 fntemplate = ''
1272 fntemplate = ''
1273
1273
1274 if fntemplate:
1274 if fntemplate:
1275 fm = formatter.nullformatter(ui, 'cat')
1275 fm = formatter.nullformatter(ui, 'cat')
1276 else:
1276 else:
1277 ui.pager('cat')
1277 ui.pager('cat')
1278 fm = ui.formatter('cat', opts)
1278 fm = ui.formatter('cat', opts)
1279 with fm:
1279 with fm:
1280 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '', **opts)
1280 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '', **opts)
1281
1281
1282 @command('^clone',
1282 @command('^clone',
1283 [('U', 'noupdate', None, _('the clone will include an empty working '
1283 [('U', 'noupdate', None, _('the clone will include an empty working '
1284 'directory (only a repository)')),
1284 'directory (only a repository)')),
1285 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1285 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1286 _('REV')),
1286 _('REV')),
1287 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1287 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1288 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1288 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1289 ('', 'pull', None, _('use pull protocol to copy metadata')),
1289 ('', 'pull', None, _('use pull protocol to copy metadata')),
1290 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1290 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1291 ] + remoteopts,
1291 ] + remoteopts,
1292 _('[OPTION]... SOURCE [DEST]'),
1292 _('[OPTION]... SOURCE [DEST]'),
1293 norepo=True)
1293 norepo=True)
1294 def clone(ui, source, dest=None, **opts):
1294 def clone(ui, source, dest=None, **opts):
1295 """make a copy of an existing repository
1295 """make a copy of an existing repository
1296
1296
1297 Create a copy of an existing repository in a new directory.
1297 Create a copy of an existing repository in a new directory.
1298
1298
1299 If no destination directory name is specified, it defaults to the
1299 If no destination directory name is specified, it defaults to the
1300 basename of the source.
1300 basename of the source.
1301
1301
1302 The location of the source is added to the new repository's
1302 The location of the source is added to the new repository's
1303 ``.hg/hgrc`` file, as the default to be used for future pulls.
1303 ``.hg/hgrc`` file, as the default to be used for future pulls.
1304
1304
1305 Only local paths and ``ssh://`` URLs are supported as
1305 Only local paths and ``ssh://`` URLs are supported as
1306 destinations. For ``ssh://`` destinations, no working directory or
1306 destinations. For ``ssh://`` destinations, no working directory or
1307 ``.hg/hgrc`` will be created on the remote side.
1307 ``.hg/hgrc`` will be created on the remote side.
1308
1308
1309 If the source repository has a bookmark called '@' set, that
1309 If the source repository has a bookmark called '@' set, that
1310 revision will be checked out in the new repository by default.
1310 revision will be checked out in the new repository by default.
1311
1311
1312 To check out a particular version, use -u/--update, or
1312 To check out a particular version, use -u/--update, or
1313 -U/--noupdate to create a clone with no working directory.
1313 -U/--noupdate to create a clone with no working directory.
1314
1314
1315 To pull only a subset of changesets, specify one or more revisions
1315 To pull only a subset of changesets, specify one or more revisions
1316 identifiers with -r/--rev or branches with -b/--branch. The
1316 identifiers with -r/--rev or branches with -b/--branch. The
1317 resulting clone will contain only the specified changesets and
1317 resulting clone will contain only the specified changesets and
1318 their ancestors. These options (or 'clone src#rev dest') imply
1318 their ancestors. These options (or 'clone src#rev dest') imply
1319 --pull, even for local source repositories.
1319 --pull, even for local source repositories.
1320
1320
1321 .. note::
1321 .. note::
1322
1322
1323 Specifying a tag will include the tagged changeset but not the
1323 Specifying a tag will include the tagged changeset but not the
1324 changeset containing the tag.
1324 changeset containing the tag.
1325
1325
1326 .. container:: verbose
1326 .. container:: verbose
1327
1327
1328 For efficiency, hardlinks are used for cloning whenever the
1328 For efficiency, hardlinks are used for cloning whenever the
1329 source and destination are on the same filesystem (note this
1329 source and destination are on the same filesystem (note this
1330 applies only to the repository data, not to the working
1330 applies only to the repository data, not to the working
1331 directory). Some filesystems, such as AFS, implement hardlinking
1331 directory). Some filesystems, such as AFS, implement hardlinking
1332 incorrectly, but do not report errors. In these cases, use the
1332 incorrectly, but do not report errors. In these cases, use the
1333 --pull option to avoid hardlinking.
1333 --pull option to avoid hardlinking.
1334
1334
1335 In some cases, you can clone repositories and the working
1335 In some cases, you can clone repositories and the working
1336 directory using full hardlinks with ::
1336 directory using full hardlinks with ::
1337
1337
1338 $ cp -al REPO REPOCLONE
1338 $ cp -al REPO REPOCLONE
1339
1339
1340 This is the fastest way to clone, but it is not always safe. The
1340 This is the fastest way to clone, but it is not always safe. The
1341 operation is not atomic (making sure REPO is not modified during
1341 operation is not atomic (making sure REPO is not modified during
1342 the operation is up to you) and you have to make sure your
1342 the operation is up to you) and you have to make sure your
1343 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1343 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1344 so). Also, this is not compatible with certain extensions that
1344 so). Also, this is not compatible with certain extensions that
1345 place their metadata under the .hg directory, such as mq.
1345 place their metadata under the .hg directory, such as mq.
1346
1346
1347 Mercurial will update the working directory to the first applicable
1347 Mercurial will update the working directory to the first applicable
1348 revision from this list:
1348 revision from this list:
1349
1349
1350 a) null if -U or the source repository has no changesets
1350 a) null if -U or the source repository has no changesets
1351 b) if -u . and the source repository is local, the first parent of
1351 b) if -u . and the source repository is local, the first parent of
1352 the source repository's working directory
1352 the source repository's working directory
1353 c) the changeset specified with -u (if a branch name, this means the
1353 c) the changeset specified with -u (if a branch name, this means the
1354 latest head of that branch)
1354 latest head of that branch)
1355 d) the changeset specified with -r
1355 d) the changeset specified with -r
1356 e) the tipmost head specified with -b
1356 e) the tipmost head specified with -b
1357 f) the tipmost head specified with the url#branch source syntax
1357 f) the tipmost head specified with the url#branch source syntax
1358 g) the revision marked with the '@' bookmark, if present
1358 g) the revision marked with the '@' bookmark, if present
1359 h) the tipmost head of the default branch
1359 h) the tipmost head of the default branch
1360 i) tip
1360 i) tip
1361
1361
1362 When cloning from servers that support it, Mercurial may fetch
1362 When cloning from servers that support it, Mercurial may fetch
1363 pre-generated data from a server-advertised URL. When this is done,
1363 pre-generated data from a server-advertised URL. When this is done,
1364 hooks operating on incoming changesets and changegroups may fire twice,
1364 hooks operating on incoming changesets and changegroups may fire twice,
1365 once for the bundle fetched from the URL and another for any additional
1365 once for the bundle fetched from the URL and another for any additional
1366 data not fetched from this URL. In addition, if an error occurs, the
1366 data not fetched from this URL. In addition, if an error occurs, the
1367 repository may be rolled back to a partial clone. This behavior may
1367 repository may be rolled back to a partial clone. This behavior may
1368 change in future releases. See :hg:`help -e clonebundles` for more.
1368 change in future releases. See :hg:`help -e clonebundles` for more.
1369
1369
1370 Examples:
1370 Examples:
1371
1371
1372 - clone a remote repository to a new directory named hg/::
1372 - clone a remote repository to a new directory named hg/::
1373
1373
1374 hg clone https://www.mercurial-scm.org/repo/hg/
1374 hg clone https://www.mercurial-scm.org/repo/hg/
1375
1375
1376 - create a lightweight local clone::
1376 - create a lightweight local clone::
1377
1377
1378 hg clone project/ project-feature/
1378 hg clone project/ project-feature/
1379
1379
1380 - clone from an absolute path on an ssh server (note double-slash)::
1380 - clone from an absolute path on an ssh server (note double-slash)::
1381
1381
1382 hg clone ssh://user@server//home/projects/alpha/
1382 hg clone ssh://user@server//home/projects/alpha/
1383
1383
1384 - do a high-speed clone over a LAN while checking out a
1384 - do a high-speed clone over a LAN while checking out a
1385 specified version::
1385 specified version::
1386
1386
1387 hg clone --uncompressed http://server/repo -u 1.5
1387 hg clone --uncompressed http://server/repo -u 1.5
1388
1388
1389 - create a repository without changesets after a particular revision::
1389 - create a repository without changesets after a particular revision::
1390
1390
1391 hg clone -r 04e544 experimental/ good/
1391 hg clone -r 04e544 experimental/ good/
1392
1392
1393 - clone (and track) a particular named branch::
1393 - clone (and track) a particular named branch::
1394
1394
1395 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1395 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1396
1396
1397 See :hg:`help urls` for details on specifying URLs.
1397 See :hg:`help urls` for details on specifying URLs.
1398
1398
1399 Returns 0 on success.
1399 Returns 0 on success.
1400 """
1400 """
1401 opts = pycompat.byteskwargs(opts)
1401 opts = pycompat.byteskwargs(opts)
1402 if opts.get('noupdate') and opts.get('updaterev'):
1402 if opts.get('noupdate') and opts.get('updaterev'):
1403 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1403 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1404
1404
1405 r = hg.clone(ui, opts, source, dest,
1405 r = hg.clone(ui, opts, source, dest,
1406 pull=opts.get('pull'),
1406 pull=opts.get('pull'),
1407 stream=opts.get('uncompressed'),
1407 stream=opts.get('uncompressed'),
1408 rev=opts.get('rev'),
1408 rev=opts.get('rev'),
1409 update=opts.get('updaterev') or not opts.get('noupdate'),
1409 update=opts.get('updaterev') or not opts.get('noupdate'),
1410 branch=opts.get('branch'),
1410 branch=opts.get('branch'),
1411 shareopts=opts.get('shareopts'))
1411 shareopts=opts.get('shareopts'))
1412
1412
1413 return r is None
1413 return r is None
1414
1414
1415 @command('^commit|ci',
1415 @command('^commit|ci',
1416 [('A', 'addremove', None,
1416 [('A', 'addremove', None,
1417 _('mark new/missing files as added/removed before committing')),
1417 _('mark new/missing files as added/removed before committing')),
1418 ('', 'close-branch', None,
1418 ('', 'close-branch', None,
1419 _('mark a branch head as closed')),
1419 _('mark a branch head as closed')),
1420 ('', 'amend', None, _('amend the parent of the working directory')),
1420 ('', 'amend', None, _('amend the parent of the working directory')),
1421 ('s', 'secret', None, _('use the secret phase for committing')),
1421 ('s', 'secret', None, _('use the secret phase for committing')),
1422 ('e', 'edit', None, _('invoke editor on commit messages')),
1422 ('e', 'edit', None, _('invoke editor on commit messages')),
1423 ('i', 'interactive', None, _('use interactive mode')),
1423 ('i', 'interactive', None, _('use interactive mode')),
1424 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1424 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1425 _('[OPTION]... [FILE]...'),
1425 _('[OPTION]... [FILE]...'),
1426 inferrepo=True)
1426 inferrepo=True)
1427 def commit(ui, repo, *pats, **opts):
1427 def commit(ui, repo, *pats, **opts):
1428 """commit the specified files or all outstanding changes
1428 """commit the specified files or all outstanding changes
1429
1429
1430 Commit changes to the given files into the repository. Unlike a
1430 Commit changes to the given files into the repository. Unlike a
1431 centralized SCM, this operation is a local operation. See
1431 centralized SCM, this operation is a local operation. See
1432 :hg:`push` for a way to actively distribute your changes.
1432 :hg:`push` for a way to actively distribute your changes.
1433
1433
1434 If a list of files is omitted, all changes reported by :hg:`status`
1434 If a list of files is omitted, all changes reported by :hg:`status`
1435 will be committed.
1435 will be committed.
1436
1436
1437 If you are committing the result of a merge, do not provide any
1437 If you are committing the result of a merge, do not provide any
1438 filenames or -I/-X filters.
1438 filenames or -I/-X filters.
1439
1439
1440 If no commit message is specified, Mercurial starts your
1440 If no commit message is specified, Mercurial starts your
1441 configured editor where you can enter a message. In case your
1441 configured editor where you can enter a message. In case your
1442 commit fails, you will find a backup of your message in
1442 commit fails, you will find a backup of your message in
1443 ``.hg/last-message.txt``.
1443 ``.hg/last-message.txt``.
1444
1444
1445 The --close-branch flag can be used to mark the current branch
1445 The --close-branch flag can be used to mark the current branch
1446 head closed. When all heads of a branch are closed, the branch
1446 head closed. When all heads of a branch are closed, the branch
1447 will be considered closed and no longer listed.
1447 will be considered closed and no longer listed.
1448
1448
1449 The --amend flag can be used to amend the parent of the
1449 The --amend flag can be used to amend the parent of the
1450 working directory with a new commit that contains the changes
1450 working directory with a new commit that contains the changes
1451 in the parent in addition to those currently reported by :hg:`status`,
1451 in the parent in addition to those currently reported by :hg:`status`,
1452 if there are any. The old commit is stored in a backup bundle in
1452 if there are any. The old commit is stored in a backup bundle in
1453 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1453 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1454 on how to restore it).
1454 on how to restore it).
1455
1455
1456 Message, user and date are taken from the amended commit unless
1456 Message, user and date are taken from the amended commit unless
1457 specified. When a message isn't specified on the command line,
1457 specified. When a message isn't specified on the command line,
1458 the editor will open with the message of the amended commit.
1458 the editor will open with the message of the amended commit.
1459
1459
1460 It is not possible to amend public changesets (see :hg:`help phases`)
1460 It is not possible to amend public changesets (see :hg:`help phases`)
1461 or changesets that have children.
1461 or changesets that have children.
1462
1462
1463 See :hg:`help dates` for a list of formats valid for -d/--date.
1463 See :hg:`help dates` for a list of formats valid for -d/--date.
1464
1464
1465 Returns 0 on success, 1 if nothing changed.
1465 Returns 0 on success, 1 if nothing changed.
1466
1466
1467 .. container:: verbose
1467 .. container:: verbose
1468
1468
1469 Examples:
1469 Examples:
1470
1470
1471 - commit all files ending in .py::
1471 - commit all files ending in .py::
1472
1472
1473 hg commit --include "set:**.py"
1473 hg commit --include "set:**.py"
1474
1474
1475 - commit all non-binary files::
1475 - commit all non-binary files::
1476
1476
1477 hg commit --exclude "set:binary()"
1477 hg commit --exclude "set:binary()"
1478
1478
1479 - amend the current commit and set the date to now::
1479 - amend the current commit and set the date to now::
1480
1480
1481 hg commit --amend --date now
1481 hg commit --amend --date now
1482 """
1482 """
1483 wlock = lock = None
1483 wlock = lock = None
1484 try:
1484 try:
1485 wlock = repo.wlock()
1485 wlock = repo.wlock()
1486 lock = repo.lock()
1486 lock = repo.lock()
1487 return _docommit(ui, repo, *pats, **opts)
1487 return _docommit(ui, repo, *pats, **opts)
1488 finally:
1488 finally:
1489 release(lock, wlock)
1489 release(lock, wlock)
1490
1490
1491 def _docommit(ui, repo, *pats, **opts):
1491 def _docommit(ui, repo, *pats, **opts):
1492 if opts.get(r'interactive'):
1492 if opts.get(r'interactive'):
1493 opts.pop(r'interactive')
1493 opts.pop(r'interactive')
1494 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1494 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1495 cmdutil.recordfilter, *pats,
1495 cmdutil.recordfilter, *pats,
1496 **opts)
1496 **opts)
1497 # ret can be 0 (no changes to record) or the value returned by
1497 # ret can be 0 (no changes to record) or the value returned by
1498 # commit(), 1 if nothing changed or None on success.
1498 # commit(), 1 if nothing changed or None on success.
1499 return 1 if ret == 0 else ret
1499 return 1 if ret == 0 else ret
1500
1500
1501 opts = pycompat.byteskwargs(opts)
1501 opts = pycompat.byteskwargs(opts)
1502 if opts.get('subrepos'):
1502 if opts.get('subrepos'):
1503 if opts.get('amend'):
1503 if opts.get('amend'):
1504 raise error.Abort(_('cannot amend with --subrepos'))
1504 raise error.Abort(_('cannot amend with --subrepos'))
1505 # Let --subrepos on the command line override config setting.
1505 # Let --subrepos on the command line override config setting.
1506 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1506 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1507
1507
1508 cmdutil.checkunfinished(repo, commit=True)
1508 cmdutil.checkunfinished(repo, commit=True)
1509
1509
1510 branch = repo[None].branch()
1510 branch = repo[None].branch()
1511 bheads = repo.branchheads(branch)
1511 bheads = repo.branchheads(branch)
1512
1512
1513 extra = {}
1513 extra = {}
1514 if opts.get('close_branch'):
1514 if opts.get('close_branch'):
1515 extra['close'] = 1
1515 extra['close'] = 1
1516
1516
1517 if not bheads:
1517 if not bheads:
1518 raise error.Abort(_('can only close branch heads'))
1518 raise error.Abort(_('can only close branch heads'))
1519 elif opts.get('amend'):
1519 elif opts.get('amend'):
1520 if repo[None].parents()[0].p1().branch() != branch and \
1520 if repo[None].parents()[0].p1().branch() != branch and \
1521 repo[None].parents()[0].p2().branch() != branch:
1521 repo[None].parents()[0].p2().branch() != branch:
1522 raise error.Abort(_('can only close branch heads'))
1522 raise error.Abort(_('can only close branch heads'))
1523
1523
1524 if opts.get('amend'):
1524 if opts.get('amend'):
1525 if ui.configbool('ui', 'commitsubrepos'):
1525 if ui.configbool('ui', 'commitsubrepos'):
1526 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1526 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1527
1527
1528 old = repo['.']
1528 old = repo['.']
1529 if not old.mutable():
1529 if not old.mutable():
1530 raise error.Abort(_('cannot amend public changesets'))
1530 raise error.Abort(_('cannot amend public changesets'))
1531 if len(repo[None].parents()) > 1:
1531 if len(repo[None].parents()) > 1:
1532 raise error.Abort(_('cannot amend while merging'))
1532 raise error.Abort(_('cannot amend while merging'))
1533 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1533 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1534 if not allowunstable and old.children():
1534 if not allowunstable and old.children():
1535 raise error.Abort(_('cannot amend changeset with children'))
1535 raise error.Abort(_('cannot amend changeset with children'))
1536
1536
1537 # Currently histedit gets confused if an amend happens while histedit
1537 # Currently histedit gets confused if an amend happens while histedit
1538 # is in progress. Since we have a checkunfinished command, we are
1538 # is in progress. Since we have a checkunfinished command, we are
1539 # temporarily honoring it.
1539 # temporarily honoring it.
1540 #
1540 #
1541 # Note: eventually this guard will be removed. Please do not expect
1541 # Note: eventually this guard will be removed. Please do not expect
1542 # this behavior to remain.
1542 # this behavior to remain.
1543 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1543 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1544 cmdutil.checkunfinished(repo)
1544 cmdutil.checkunfinished(repo)
1545
1545
1546 # commitfunc is used only for temporary amend commit by cmdutil.amend
1546 # commitfunc is used only for temporary amend commit by cmdutil.amend
1547 def commitfunc(ui, repo, message, match, opts):
1547 def commitfunc(ui, repo, message, match, opts):
1548 return repo.commit(message,
1548 return repo.commit(message,
1549 opts.get('user') or old.user(),
1549 opts.get('user') or old.user(),
1550 opts.get('date') or old.date(),
1550 opts.get('date') or old.date(),
1551 match,
1551 match,
1552 extra=extra)
1552 extra=extra)
1553
1553
1554 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1554 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1555 if node == old.node():
1555 if node == old.node():
1556 ui.status(_("nothing changed\n"))
1556 ui.status(_("nothing changed\n"))
1557 return 1
1557 return 1
1558 else:
1558 else:
1559 def commitfunc(ui, repo, message, match, opts):
1559 def commitfunc(ui, repo, message, match, opts):
1560 overrides = {}
1560 overrides = {}
1561 if opts.get('secret'):
1561 if opts.get('secret'):
1562 overrides[('phases', 'new-commit')] = 'secret'
1562 overrides[('phases', 'new-commit')] = 'secret'
1563
1563
1564 baseui = repo.baseui
1564 baseui = repo.baseui
1565 with baseui.configoverride(overrides, 'commit'):
1565 with baseui.configoverride(overrides, 'commit'):
1566 with ui.configoverride(overrides, 'commit'):
1566 with ui.configoverride(overrides, 'commit'):
1567 editform = cmdutil.mergeeditform(repo[None],
1567 editform = cmdutil.mergeeditform(repo[None],
1568 'commit.normal')
1568 'commit.normal')
1569 editor = cmdutil.getcommiteditor(
1569 editor = cmdutil.getcommiteditor(
1570 editform=editform, **pycompat.strkwargs(opts))
1570 editform=editform, **pycompat.strkwargs(opts))
1571 return repo.commit(message,
1571 return repo.commit(message,
1572 opts.get('user'),
1572 opts.get('user'),
1573 opts.get('date'),
1573 opts.get('date'),
1574 match,
1574 match,
1575 editor=editor,
1575 editor=editor,
1576 extra=extra)
1576 extra=extra)
1577
1577
1578 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1578 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1579
1579
1580 if not node:
1580 if not node:
1581 stat = cmdutil.postcommitstatus(repo, pats, opts)
1581 stat = cmdutil.postcommitstatus(repo, pats, opts)
1582 if stat[3]:
1582 if stat[3]:
1583 ui.status(_("nothing changed (%d missing files, see "
1583 ui.status(_("nothing changed (%d missing files, see "
1584 "'hg status')\n") % len(stat[3]))
1584 "'hg status')\n") % len(stat[3]))
1585 else:
1585 else:
1586 ui.status(_("nothing changed\n"))
1586 ui.status(_("nothing changed\n"))
1587 return 1
1587 return 1
1588
1588
1589 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1589 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1590
1590
1591 @command('config|showconfig|debugconfig',
1591 @command('config|showconfig|debugconfig',
1592 [('u', 'untrusted', None, _('show untrusted configuration options')),
1592 [('u', 'untrusted', None, _('show untrusted configuration options')),
1593 ('e', 'edit', None, _('edit user config')),
1593 ('e', 'edit', None, _('edit user config')),
1594 ('l', 'local', None, _('edit repository config')),
1594 ('l', 'local', None, _('edit repository config')),
1595 ('g', 'global', None, _('edit global config'))] + formatteropts,
1595 ('g', 'global', None, _('edit global config'))] + formatteropts,
1596 _('[-u] [NAME]...'),
1596 _('[-u] [NAME]...'),
1597 optionalrepo=True)
1597 optionalrepo=True)
1598 def config(ui, repo, *values, **opts):
1598 def config(ui, repo, *values, **opts):
1599 """show combined config settings from all hgrc files
1599 """show combined config settings from all hgrc files
1600
1600
1601 With no arguments, print names and values of all config items.
1601 With no arguments, print names and values of all config items.
1602
1602
1603 With one argument of the form section.name, print just the value
1603 With one argument of the form section.name, print just the value
1604 of that config item.
1604 of that config item.
1605
1605
1606 With multiple arguments, print names and values of all config
1606 With multiple arguments, print names and values of all config
1607 items with matching section names.
1607 items with matching section names.
1608
1608
1609 With --edit, start an editor on the user-level config file. With
1609 With --edit, start an editor on the user-level config file. With
1610 --global, edit the system-wide config file. With --local, edit the
1610 --global, edit the system-wide config file. With --local, edit the
1611 repository-level config file.
1611 repository-level config file.
1612
1612
1613 With --debug, the source (filename and line number) is printed
1613 With --debug, the source (filename and line number) is printed
1614 for each config item.
1614 for each config item.
1615
1615
1616 See :hg:`help config` for more information about config files.
1616 See :hg:`help config` for more information about config files.
1617
1617
1618 Returns 0 on success, 1 if NAME does not exist.
1618 Returns 0 on success, 1 if NAME does not exist.
1619
1619
1620 """
1620 """
1621
1621
1622 opts = pycompat.byteskwargs(opts)
1622 opts = pycompat.byteskwargs(opts)
1623 if opts.get('edit') or opts.get('local') or opts.get('global'):
1623 if opts.get('edit') or opts.get('local') or opts.get('global'):
1624 if opts.get('local') and opts.get('global'):
1624 if opts.get('local') and opts.get('global'):
1625 raise error.Abort(_("can't use --local and --global together"))
1625 raise error.Abort(_("can't use --local and --global together"))
1626
1626
1627 if opts.get('local'):
1627 if opts.get('local'):
1628 if not repo:
1628 if not repo:
1629 raise error.Abort(_("can't use --local outside a repository"))
1629 raise error.Abort(_("can't use --local outside a repository"))
1630 paths = [repo.vfs.join('hgrc')]
1630 paths = [repo.vfs.join('hgrc')]
1631 elif opts.get('global'):
1631 elif opts.get('global'):
1632 paths = rcutil.systemrcpath()
1632 paths = rcutil.systemrcpath()
1633 else:
1633 else:
1634 paths = rcutil.userrcpath()
1634 paths = rcutil.userrcpath()
1635
1635
1636 for f in paths:
1636 for f in paths:
1637 if os.path.exists(f):
1637 if os.path.exists(f):
1638 break
1638 break
1639 else:
1639 else:
1640 if opts.get('global'):
1640 if opts.get('global'):
1641 samplehgrc = uimod.samplehgrcs['global']
1641 samplehgrc = uimod.samplehgrcs['global']
1642 elif opts.get('local'):
1642 elif opts.get('local'):
1643 samplehgrc = uimod.samplehgrcs['local']
1643 samplehgrc = uimod.samplehgrcs['local']
1644 else:
1644 else:
1645 samplehgrc = uimod.samplehgrcs['user']
1645 samplehgrc = uimod.samplehgrcs['user']
1646
1646
1647 f = paths[0]
1647 f = paths[0]
1648 fp = open(f, "w")
1648 fp = open(f, "w")
1649 fp.write(samplehgrc)
1649 fp.write(samplehgrc)
1650 fp.close()
1650 fp.close()
1651
1651
1652 editor = ui.geteditor()
1652 editor = ui.geteditor()
1653 ui.system("%s \"%s\"" % (editor, f),
1653 ui.system("%s \"%s\"" % (editor, f),
1654 onerr=error.Abort, errprefix=_("edit failed"),
1654 onerr=error.Abort, errprefix=_("edit failed"),
1655 blockedtag='config_edit')
1655 blockedtag='config_edit')
1656 return
1656 return
1657 ui.pager('config')
1657 ui.pager('config')
1658 fm = ui.formatter('config', opts)
1658 fm = ui.formatter('config', opts)
1659 for t, f in rcutil.rccomponents():
1659 for t, f in rcutil.rccomponents():
1660 if t == 'path':
1660 if t == 'path':
1661 ui.debug('read config from: %s\n' % f)
1661 ui.debug('read config from: %s\n' % f)
1662 elif t == 'items':
1662 elif t == 'items':
1663 for section, name, value, source in f:
1663 for section, name, value, source in f:
1664 ui.debug('set config by: %s\n' % source)
1664 ui.debug('set config by: %s\n' % source)
1665 else:
1665 else:
1666 raise error.ProgrammingError('unknown rctype: %s' % t)
1666 raise error.ProgrammingError('unknown rctype: %s' % t)
1667 untrusted = bool(opts.get('untrusted'))
1667 untrusted = bool(opts.get('untrusted'))
1668 if values:
1668 if values:
1669 sections = [v for v in values if '.' not in v]
1669 sections = [v for v in values if '.' not in v]
1670 items = [v for v in values if '.' in v]
1670 items = [v for v in values if '.' in v]
1671 if len(items) > 1 or items and sections:
1671 if len(items) > 1 or items and sections:
1672 raise error.Abort(_('only one config item permitted'))
1672 raise error.Abort(_('only one config item permitted'))
1673 matched = False
1673 matched = False
1674 for section, name, value in ui.walkconfig(untrusted=untrusted):
1674 for section, name, value in ui.walkconfig(untrusted=untrusted):
1675 source = ui.configsource(section, name, untrusted)
1675 source = ui.configsource(section, name, untrusted)
1676 value = pycompat.bytestr(value)
1676 value = pycompat.bytestr(value)
1677 if fm.isplain():
1677 if fm.isplain():
1678 source = source or 'none'
1678 source = source or 'none'
1679 value = value.replace('\n', '\\n')
1679 value = value.replace('\n', '\\n')
1680 entryname = section + '.' + name
1680 entryname = section + '.' + name
1681 if values:
1681 if values:
1682 for v in values:
1682 for v in values:
1683 if v == section:
1683 if v == section:
1684 fm.startitem()
1684 fm.startitem()
1685 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1685 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1686 fm.write('name value', '%s=%s\n', entryname, value)
1686 fm.write('name value', '%s=%s\n', entryname, value)
1687 matched = True
1687 matched = True
1688 elif v == entryname:
1688 elif v == entryname:
1689 fm.startitem()
1689 fm.startitem()
1690 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1690 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1691 fm.write('value', '%s\n', value)
1691 fm.write('value', '%s\n', value)
1692 fm.data(name=entryname)
1692 fm.data(name=entryname)
1693 matched = True
1693 matched = True
1694 else:
1694 else:
1695 fm.startitem()
1695 fm.startitem()
1696 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1696 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1697 fm.write('name value', '%s=%s\n', entryname, value)
1697 fm.write('name value', '%s=%s\n', entryname, value)
1698 matched = True
1698 matched = True
1699 fm.end()
1699 fm.end()
1700 if matched:
1700 if matched:
1701 return 0
1701 return 0
1702 return 1
1702 return 1
1703
1703
1704 @command('copy|cp',
1704 @command('copy|cp',
1705 [('A', 'after', None, _('record a copy that has already occurred')),
1705 [('A', 'after', None, _('record a copy that has already occurred')),
1706 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1706 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1707 ] + walkopts + dryrunopts,
1707 ] + walkopts + dryrunopts,
1708 _('[OPTION]... [SOURCE]... DEST'))
1708 _('[OPTION]... [SOURCE]... DEST'))
1709 def copy(ui, repo, *pats, **opts):
1709 def copy(ui, repo, *pats, **opts):
1710 """mark files as copied for the next commit
1710 """mark files as copied for the next commit
1711
1711
1712 Mark dest as having copies of source files. If dest is a
1712 Mark dest as having copies of source files. If dest is a
1713 directory, copies are put in that directory. If dest is a file,
1713 directory, copies are put in that directory. If dest is a file,
1714 the source must be a single file.
1714 the source must be a single file.
1715
1715
1716 By default, this command copies the contents of files as they
1716 By default, this command copies the contents of files as they
1717 exist in the working directory. If invoked with -A/--after, the
1717 exist in the working directory. If invoked with -A/--after, the
1718 operation is recorded, but no copying is performed.
1718 operation is recorded, but no copying is performed.
1719
1719
1720 This command takes effect with the next commit. To undo a copy
1720 This command takes effect with the next commit. To undo a copy
1721 before that, see :hg:`revert`.
1721 before that, see :hg:`revert`.
1722
1722
1723 Returns 0 on success, 1 if errors are encountered.
1723 Returns 0 on success, 1 if errors are encountered.
1724 """
1724 """
1725 opts = pycompat.byteskwargs(opts)
1725 opts = pycompat.byteskwargs(opts)
1726 with repo.wlock(False):
1726 with repo.wlock(False):
1727 return cmdutil.copy(ui, repo, pats, opts)
1727 return cmdutil.copy(ui, repo, pats, opts)
1728
1728
1729 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1729 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1730 def debugcommands(ui, cmd='', *args):
1730 def debugcommands(ui, cmd='', *args):
1731 """list all available commands and options"""
1731 """list all available commands and options"""
1732 for cmd, vals in sorted(table.iteritems()):
1732 for cmd, vals in sorted(table.iteritems()):
1733 cmd = cmd.split('|')[0].strip('^')
1733 cmd = cmd.split('|')[0].strip('^')
1734 opts = ', '.join([i[1] for i in vals[1]])
1734 opts = ', '.join([i[1] for i in vals[1]])
1735 ui.write('%s: %s\n' % (cmd, opts))
1735 ui.write('%s: %s\n' % (cmd, opts))
1736
1736
1737 @command('debugcomplete',
1737 @command('debugcomplete',
1738 [('o', 'options', None, _('show the command options'))],
1738 [('o', 'options', None, _('show the command options'))],
1739 _('[-o] CMD'),
1739 _('[-o] CMD'),
1740 norepo=True)
1740 norepo=True)
1741 def debugcomplete(ui, cmd='', **opts):
1741 def debugcomplete(ui, cmd='', **opts):
1742 """returns the completion list associated with the given command"""
1742 """returns the completion list associated with the given command"""
1743
1743
1744 if opts.get('options'):
1744 if opts.get('options'):
1745 options = []
1745 options = []
1746 otables = [globalopts]
1746 otables = [globalopts]
1747 if cmd:
1747 if cmd:
1748 aliases, entry = cmdutil.findcmd(cmd, table, False)
1748 aliases, entry = cmdutil.findcmd(cmd, table, False)
1749 otables.append(entry[1])
1749 otables.append(entry[1])
1750 for t in otables:
1750 for t in otables:
1751 for o in t:
1751 for o in t:
1752 if "(DEPRECATED)" in o[3]:
1752 if "(DEPRECATED)" in o[3]:
1753 continue
1753 continue
1754 if o[0]:
1754 if o[0]:
1755 options.append('-%s' % o[0])
1755 options.append('-%s' % o[0])
1756 options.append('--%s' % o[1])
1756 options.append('--%s' % o[1])
1757 ui.write("%s\n" % "\n".join(options))
1757 ui.write("%s\n" % "\n".join(options))
1758 return
1758 return
1759
1759
1760 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1760 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1761 if ui.verbose:
1761 if ui.verbose:
1762 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1762 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1763 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1763 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1764
1764
1765 @command('^diff',
1765 @command('^diff',
1766 [('r', 'rev', [], _('revision'), _('REV')),
1766 [('r', 'rev', [], _('revision'), _('REV')),
1767 ('c', 'change', '', _('change made by revision'), _('REV'))
1767 ('c', 'change', '', _('change made by revision'), _('REV'))
1768 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1768 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1769 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1769 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1770 inferrepo=True)
1770 inferrepo=True)
1771 def diff(ui, repo, *pats, **opts):
1771 def diff(ui, repo, *pats, **opts):
1772 """diff repository (or selected files)
1772 """diff repository (or selected files)
1773
1773
1774 Show differences between revisions for the specified files.
1774 Show differences between revisions for the specified files.
1775
1775
1776 Differences between files are shown using the unified diff format.
1776 Differences between files are shown using the unified diff format.
1777
1777
1778 .. note::
1778 .. note::
1779
1779
1780 :hg:`diff` may generate unexpected results for merges, as it will
1780 :hg:`diff` may generate unexpected results for merges, as it will
1781 default to comparing against the working directory's first
1781 default to comparing against the working directory's first
1782 parent changeset if no revisions are specified.
1782 parent changeset if no revisions are specified.
1783
1783
1784 When two revision arguments are given, then changes are shown
1784 When two revision arguments are given, then changes are shown
1785 between those revisions. If only one revision is specified then
1785 between those revisions. If only one revision is specified then
1786 that revision is compared to the working directory, and, when no
1786 that revision is compared to the working directory, and, when no
1787 revisions are specified, the working directory files are compared
1787 revisions are specified, the working directory files are compared
1788 to its first parent.
1788 to its first parent.
1789
1789
1790 Alternatively you can specify -c/--change with a revision to see
1790 Alternatively you can specify -c/--change with a revision to see
1791 the changes in that changeset relative to its first parent.
1791 the changes in that changeset relative to its first parent.
1792
1792
1793 Without the -a/--text option, diff will avoid generating diffs of
1793 Without the -a/--text option, diff will avoid generating diffs of
1794 files it detects as binary. With -a, diff will generate a diff
1794 files it detects as binary. With -a, diff will generate a diff
1795 anyway, probably with undesirable results.
1795 anyway, probably with undesirable results.
1796
1796
1797 Use the -g/--git option to generate diffs in the git extended diff
1797 Use the -g/--git option to generate diffs in the git extended diff
1798 format. For more information, read :hg:`help diffs`.
1798 format. For more information, read :hg:`help diffs`.
1799
1799
1800 .. container:: verbose
1800 .. container:: verbose
1801
1801
1802 Examples:
1802 Examples:
1803
1803
1804 - compare a file in the current working directory to its parent::
1804 - compare a file in the current working directory to its parent::
1805
1805
1806 hg diff foo.c
1806 hg diff foo.c
1807
1807
1808 - compare two historical versions of a directory, with rename info::
1808 - compare two historical versions of a directory, with rename info::
1809
1809
1810 hg diff --git -r 1.0:1.2 lib/
1810 hg diff --git -r 1.0:1.2 lib/
1811
1811
1812 - get change stats relative to the last change on some date::
1812 - get change stats relative to the last change on some date::
1813
1813
1814 hg diff --stat -r "date('may 2')"
1814 hg diff --stat -r "date('may 2')"
1815
1815
1816 - diff all newly-added files that contain a keyword::
1816 - diff all newly-added files that contain a keyword::
1817
1817
1818 hg diff "set:added() and grep(GNU)"
1818 hg diff "set:added() and grep(GNU)"
1819
1819
1820 - compare a revision and its parents::
1820 - compare a revision and its parents::
1821
1821
1822 hg diff -c 9353 # compare against first parent
1822 hg diff -c 9353 # compare against first parent
1823 hg diff -r 9353^:9353 # same using revset syntax
1823 hg diff -r 9353^:9353 # same using revset syntax
1824 hg diff -r 9353^2:9353 # compare against the second parent
1824 hg diff -r 9353^2:9353 # compare against the second parent
1825
1825
1826 Returns 0 on success.
1826 Returns 0 on success.
1827 """
1827 """
1828
1828
1829 opts = pycompat.byteskwargs(opts)
1829 opts = pycompat.byteskwargs(opts)
1830 revs = opts.get('rev')
1830 revs = opts.get('rev')
1831 change = opts.get('change')
1831 change = opts.get('change')
1832 stat = opts.get('stat')
1832 stat = opts.get('stat')
1833 reverse = opts.get('reverse')
1833 reverse = opts.get('reverse')
1834
1834
1835 if revs and change:
1835 if revs and change:
1836 msg = _('cannot specify --rev and --change at the same time')
1836 msg = _('cannot specify --rev and --change at the same time')
1837 raise error.Abort(msg)
1837 raise error.Abort(msg)
1838 elif change:
1838 elif change:
1839 node2 = scmutil.revsingle(repo, change, None).node()
1839 node2 = scmutil.revsingle(repo, change, None).node()
1840 node1 = repo[node2].p1().node()
1840 node1 = repo[node2].p1().node()
1841 else:
1841 else:
1842 node1, node2 = scmutil.revpair(repo, revs)
1842 node1, node2 = scmutil.revpair(repo, revs)
1843
1843
1844 if reverse:
1844 if reverse:
1845 node1, node2 = node2, node1
1845 node1, node2 = node2, node1
1846
1846
1847 diffopts = patch.diffallopts(ui, opts)
1847 diffopts = patch.diffallopts(ui, opts)
1848 m = scmutil.match(repo[node2], pats, opts)
1848 m = scmutil.match(repo[node2], pats, opts)
1849 ui.pager('diff')
1849 ui.pager('diff')
1850 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1850 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1851 listsubrepos=opts.get('subrepos'),
1851 listsubrepos=opts.get('subrepos'),
1852 root=opts.get('root'))
1852 root=opts.get('root'))
1853
1853
1854 @command('^export',
1854 @command('^export',
1855 [('o', 'output', '',
1855 [('o', 'output', '',
1856 _('print output to file with formatted name'), _('FORMAT')),
1856 _('print output to file with formatted name'), _('FORMAT')),
1857 ('', 'switch-parent', None, _('diff against the second parent')),
1857 ('', 'switch-parent', None, _('diff against the second parent')),
1858 ('r', 'rev', [], _('revisions to export'), _('REV')),
1858 ('r', 'rev', [], _('revisions to export'), _('REV')),
1859 ] + diffopts,
1859 ] + diffopts,
1860 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
1860 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
1861 def export(ui, repo, *changesets, **opts):
1861 def export(ui, repo, *changesets, **opts):
1862 """dump the header and diffs for one or more changesets
1862 """dump the header and diffs for one or more changesets
1863
1863
1864 Print the changeset header and diffs for one or more revisions.
1864 Print the changeset header and diffs for one or more revisions.
1865 If no revision is given, the parent of the working directory is used.
1865 If no revision is given, the parent of the working directory is used.
1866
1866
1867 The information shown in the changeset header is: author, date,
1867 The information shown in the changeset header is: author, date,
1868 branch name (if non-default), changeset hash, parent(s) and commit
1868 branch name (if non-default), changeset hash, parent(s) and commit
1869 comment.
1869 comment.
1870
1870
1871 .. note::
1871 .. note::
1872
1872
1873 :hg:`export` may generate unexpected diff output for merge
1873 :hg:`export` may generate unexpected diff output for merge
1874 changesets, as it will compare the merge changeset against its
1874 changesets, as it will compare the merge changeset against its
1875 first parent only.
1875 first parent only.
1876
1876
1877 Output may be to a file, in which case the name of the file is
1877 Output may be to a file, in which case the name of the file is
1878 given using a format string. The formatting rules are as follows:
1878 given using a format string. The formatting rules are as follows:
1879
1879
1880 :``%%``: literal "%" character
1880 :``%%``: literal "%" character
1881 :``%H``: changeset hash (40 hexadecimal digits)
1881 :``%H``: changeset hash (40 hexadecimal digits)
1882 :``%N``: number of patches being generated
1882 :``%N``: number of patches being generated
1883 :``%R``: changeset revision number
1883 :``%R``: changeset revision number
1884 :``%b``: basename of the exporting repository
1884 :``%b``: basename of the exporting repository
1885 :``%h``: short-form changeset hash (12 hexadecimal digits)
1885 :``%h``: short-form changeset hash (12 hexadecimal digits)
1886 :``%m``: first line of the commit message (only alphanumeric characters)
1886 :``%m``: first line of the commit message (only alphanumeric characters)
1887 :``%n``: zero-padded sequence number, starting at 1
1887 :``%n``: zero-padded sequence number, starting at 1
1888 :``%r``: zero-padded changeset revision number
1888 :``%r``: zero-padded changeset revision number
1889
1889
1890 Without the -a/--text option, export will avoid generating diffs
1890 Without the -a/--text option, export will avoid generating diffs
1891 of files it detects as binary. With -a, export will generate a
1891 of files it detects as binary. With -a, export will generate a
1892 diff anyway, probably with undesirable results.
1892 diff anyway, probably with undesirable results.
1893
1893
1894 Use the -g/--git option to generate diffs in the git extended diff
1894 Use the -g/--git option to generate diffs in the git extended diff
1895 format. See :hg:`help diffs` for more information.
1895 format. See :hg:`help diffs` for more information.
1896
1896
1897 With the --switch-parent option, the diff will be against the
1897 With the --switch-parent option, the diff will be against the
1898 second parent. It can be useful to review a merge.
1898 second parent. It can be useful to review a merge.
1899
1899
1900 .. container:: verbose
1900 .. container:: verbose
1901
1901
1902 Examples:
1902 Examples:
1903
1903
1904 - use export and import to transplant a bugfix to the current
1904 - use export and import to transplant a bugfix to the current
1905 branch::
1905 branch::
1906
1906
1907 hg export -r 9353 | hg import -
1907 hg export -r 9353 | hg import -
1908
1908
1909 - export all the changesets between two revisions to a file with
1909 - export all the changesets between two revisions to a file with
1910 rename information::
1910 rename information::
1911
1911
1912 hg export --git -r 123:150 > changes.txt
1912 hg export --git -r 123:150 > changes.txt
1913
1913
1914 - split outgoing changes into a series of patches with
1914 - split outgoing changes into a series of patches with
1915 descriptive names::
1915 descriptive names::
1916
1916
1917 hg export -r "outgoing()" -o "%n-%m.patch"
1917 hg export -r "outgoing()" -o "%n-%m.patch"
1918
1918
1919 Returns 0 on success.
1919 Returns 0 on success.
1920 """
1920 """
1921 opts = pycompat.byteskwargs(opts)
1921 opts = pycompat.byteskwargs(opts)
1922 changesets += tuple(opts.get('rev', []))
1922 changesets += tuple(opts.get('rev', []))
1923 if not changesets:
1923 if not changesets:
1924 changesets = ['.']
1924 changesets = ['.']
1925 revs = scmutil.revrange(repo, changesets)
1925 revs = scmutil.revrange(repo, changesets)
1926 if not revs:
1926 if not revs:
1927 raise error.Abort(_("export requires at least one changeset"))
1927 raise error.Abort(_("export requires at least one changeset"))
1928 if len(revs) > 1:
1928 if len(revs) > 1:
1929 ui.note(_('exporting patches:\n'))
1929 ui.note(_('exporting patches:\n'))
1930 else:
1930 else:
1931 ui.note(_('exporting patch:\n'))
1931 ui.note(_('exporting patch:\n'))
1932 ui.pager('export')
1932 ui.pager('export')
1933 cmdutil.export(repo, revs, fntemplate=opts.get('output'),
1933 cmdutil.export(repo, revs, fntemplate=opts.get('output'),
1934 switch_parent=opts.get('switch_parent'),
1934 switch_parent=opts.get('switch_parent'),
1935 opts=patch.diffallopts(ui, opts))
1935 opts=patch.diffallopts(ui, opts))
1936
1936
1937 @command('files',
1937 @command('files',
1938 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
1938 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
1939 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
1939 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
1940 ] + walkopts + formatteropts + subrepoopts,
1940 ] + walkopts + formatteropts + subrepoopts,
1941 _('[OPTION]... [FILE]...'))
1941 _('[OPTION]... [FILE]...'))
1942 def files(ui, repo, *pats, **opts):
1942 def files(ui, repo, *pats, **opts):
1943 """list tracked files
1943 """list tracked files
1944
1944
1945 Print files under Mercurial control in the working directory or
1945 Print files under Mercurial control in the working directory or
1946 specified revision for given files (excluding removed files).
1946 specified revision for given files (excluding removed files).
1947 Files can be specified as filenames or filesets.
1947 Files can be specified as filenames or filesets.
1948
1948
1949 If no files are given to match, this command prints the names
1949 If no files are given to match, this command prints the names
1950 of all files under Mercurial control.
1950 of all files under Mercurial control.
1951
1951
1952 .. container:: verbose
1952 .. container:: verbose
1953
1953
1954 Examples:
1954 Examples:
1955
1955
1956 - list all files under the current directory::
1956 - list all files under the current directory::
1957
1957
1958 hg files .
1958 hg files .
1959
1959
1960 - shows sizes and flags for current revision::
1960 - shows sizes and flags for current revision::
1961
1961
1962 hg files -vr .
1962 hg files -vr .
1963
1963
1964 - list all files named README::
1964 - list all files named README::
1965
1965
1966 hg files -I "**/README"
1966 hg files -I "**/README"
1967
1967
1968 - list all binary files::
1968 - list all binary files::
1969
1969
1970 hg files "set:binary()"
1970 hg files "set:binary()"
1971
1971
1972 - find files containing a regular expression::
1972 - find files containing a regular expression::
1973
1973
1974 hg files "set:grep('bob')"
1974 hg files "set:grep('bob')"
1975
1975
1976 - search tracked file contents with xargs and grep::
1976 - search tracked file contents with xargs and grep::
1977
1977
1978 hg files -0 | xargs -0 grep foo
1978 hg files -0 | xargs -0 grep foo
1979
1979
1980 See :hg:`help patterns` and :hg:`help filesets` for more information
1980 See :hg:`help patterns` and :hg:`help filesets` for more information
1981 on specifying file patterns.
1981 on specifying file patterns.
1982
1982
1983 Returns 0 if a match is found, 1 otherwise.
1983 Returns 0 if a match is found, 1 otherwise.
1984
1984
1985 """
1985 """
1986
1986
1987 opts = pycompat.byteskwargs(opts)
1987 opts = pycompat.byteskwargs(opts)
1988 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
1988 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
1989
1989
1990 end = '\n'
1990 end = '\n'
1991 if opts.get('print0'):
1991 if opts.get('print0'):
1992 end = '\0'
1992 end = '\0'
1993 fmt = '%s' + end
1993 fmt = '%s' + end
1994
1994
1995 m = scmutil.match(ctx, pats, opts)
1995 m = scmutil.match(ctx, pats, opts)
1996 ui.pager('files')
1996 ui.pager('files')
1997 with ui.formatter('files', opts) as fm:
1997 with ui.formatter('files', opts) as fm:
1998 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
1998 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
1999
1999
2000 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
2000 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
2001 def forget(ui, repo, *pats, **opts):
2001 def forget(ui, repo, *pats, **opts):
2002 """forget the specified files on the next commit
2002 """forget the specified files on the next commit
2003
2003
2004 Mark the specified files so they will no longer be tracked
2004 Mark the specified files so they will no longer be tracked
2005 after the next commit.
2005 after the next commit.
2006
2006
2007 This only removes files from the current branch, not from the
2007 This only removes files from the current branch, not from the
2008 entire project history, and it does not delete them from the
2008 entire project history, and it does not delete them from the
2009 working directory.
2009 working directory.
2010
2010
2011 To delete the file from the working directory, see :hg:`remove`.
2011 To delete the file from the working directory, see :hg:`remove`.
2012
2012
2013 To undo a forget before the next commit, see :hg:`add`.
2013 To undo a forget before the next commit, see :hg:`add`.
2014
2014
2015 .. container:: verbose
2015 .. container:: verbose
2016
2016
2017 Examples:
2017 Examples:
2018
2018
2019 - forget newly-added binary files::
2019 - forget newly-added binary files::
2020
2020
2021 hg forget "set:added() and binary()"
2021 hg forget "set:added() and binary()"
2022
2022
2023 - forget files that would be excluded by .hgignore::
2023 - forget files that would be excluded by .hgignore::
2024
2024
2025 hg forget "set:hgignore()"
2025 hg forget "set:hgignore()"
2026
2026
2027 Returns 0 on success.
2027 Returns 0 on success.
2028 """
2028 """
2029
2029
2030 opts = pycompat.byteskwargs(opts)
2030 opts = pycompat.byteskwargs(opts)
2031 if not pats:
2031 if not pats:
2032 raise error.Abort(_('no files specified'))
2032 raise error.Abort(_('no files specified'))
2033
2033
2034 m = scmutil.match(repo[None], pats, opts)
2034 m = scmutil.match(repo[None], pats, opts)
2035 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2035 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2036 return rejected and 1 or 0
2036 return rejected and 1 or 0
2037
2037
2038 @command(
2038 @command(
2039 'graft',
2039 'graft',
2040 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2040 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2041 ('c', 'continue', False, _('resume interrupted graft')),
2041 ('c', 'continue', False, _('resume interrupted graft')),
2042 ('e', 'edit', False, _('invoke editor on commit messages')),
2042 ('e', 'edit', False, _('invoke editor on commit messages')),
2043 ('', 'log', None, _('append graft info to log message')),
2043 ('', 'log', None, _('append graft info to log message')),
2044 ('f', 'force', False, _('force graft')),
2044 ('f', 'force', False, _('force graft')),
2045 ('D', 'currentdate', False,
2045 ('D', 'currentdate', False,
2046 _('record the current date as commit date')),
2046 _('record the current date as commit date')),
2047 ('U', 'currentuser', False,
2047 ('U', 'currentuser', False,
2048 _('record the current user as committer'), _('DATE'))]
2048 _('record the current user as committer'), _('DATE'))]
2049 + commitopts2 + mergetoolopts + dryrunopts,
2049 + commitopts2 + mergetoolopts + dryrunopts,
2050 _('[OPTION]... [-r REV]... REV...'))
2050 _('[OPTION]... [-r REV]... REV...'))
2051 def graft(ui, repo, *revs, **opts):
2051 def graft(ui, repo, *revs, **opts):
2052 '''copy changes from other branches onto the current branch
2052 '''copy changes from other branches onto the current branch
2053
2053
2054 This command uses Mercurial's merge logic to copy individual
2054 This command uses Mercurial's merge logic to copy individual
2055 changes from other branches without merging branches in the
2055 changes from other branches without merging branches in the
2056 history graph. This is sometimes known as 'backporting' or
2056 history graph. This is sometimes known as 'backporting' or
2057 'cherry-picking'. By default, graft will copy user, date, and
2057 'cherry-picking'. By default, graft will copy user, date, and
2058 description from the source changesets.
2058 description from the source changesets.
2059
2059
2060 Changesets that are ancestors of the current revision, that have
2060 Changesets that are ancestors of the current revision, that have
2061 already been grafted, or that are merges will be skipped.
2061 already been grafted, or that are merges will be skipped.
2062
2062
2063 If --log is specified, log messages will have a comment appended
2063 If --log is specified, log messages will have a comment appended
2064 of the form::
2064 of the form::
2065
2065
2066 (grafted from CHANGESETHASH)
2066 (grafted from CHANGESETHASH)
2067
2067
2068 If --force is specified, revisions will be grafted even if they
2068 If --force is specified, revisions will be grafted even if they
2069 are already ancestors of or have been grafted to the destination.
2069 are already ancestors of or have been grafted to the destination.
2070 This is useful when the revisions have since been backed out.
2070 This is useful when the revisions have since been backed out.
2071
2071
2072 If a graft merge results in conflicts, the graft process is
2072 If a graft merge results in conflicts, the graft process is
2073 interrupted so that the current merge can be manually resolved.
2073 interrupted so that the current merge can be manually resolved.
2074 Once all conflicts are addressed, the graft process can be
2074 Once all conflicts are addressed, the graft process can be
2075 continued with the -c/--continue option.
2075 continued with the -c/--continue option.
2076
2076
2077 .. note::
2077 .. note::
2078
2078
2079 The -c/--continue option does not reapply earlier options, except
2079 The -c/--continue option does not reapply earlier options, except
2080 for --force.
2080 for --force.
2081
2081
2082 .. container:: verbose
2082 .. container:: verbose
2083
2083
2084 Examples:
2084 Examples:
2085
2085
2086 - copy a single change to the stable branch and edit its description::
2086 - copy a single change to the stable branch and edit its description::
2087
2087
2088 hg update stable
2088 hg update stable
2089 hg graft --edit 9393
2089 hg graft --edit 9393
2090
2090
2091 - graft a range of changesets with one exception, updating dates::
2091 - graft a range of changesets with one exception, updating dates::
2092
2092
2093 hg graft -D "2085::2093 and not 2091"
2093 hg graft -D "2085::2093 and not 2091"
2094
2094
2095 - continue a graft after resolving conflicts::
2095 - continue a graft after resolving conflicts::
2096
2096
2097 hg graft -c
2097 hg graft -c
2098
2098
2099 - show the source of a grafted changeset::
2099 - show the source of a grafted changeset::
2100
2100
2101 hg log --debug -r .
2101 hg log --debug -r .
2102
2102
2103 - show revisions sorted by date::
2103 - show revisions sorted by date::
2104
2104
2105 hg log -r "sort(all(), date)"
2105 hg log -r "sort(all(), date)"
2106
2106
2107 See :hg:`help revisions` for more about specifying revisions.
2107 See :hg:`help revisions` for more about specifying revisions.
2108
2108
2109 Returns 0 on successful completion.
2109 Returns 0 on successful completion.
2110 '''
2110 '''
2111 with repo.wlock():
2111 with repo.wlock():
2112 return _dograft(ui, repo, *revs, **opts)
2112 return _dograft(ui, repo, *revs, **opts)
2113
2113
2114 def _dograft(ui, repo, *revs, **opts):
2114 def _dograft(ui, repo, *revs, **opts):
2115 opts = pycompat.byteskwargs(opts)
2115 opts = pycompat.byteskwargs(opts)
2116 if revs and opts.get('rev'):
2116 if revs and opts.get('rev'):
2117 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2117 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2118 'revision ordering!\n'))
2118 'revision ordering!\n'))
2119
2119
2120 revs = list(revs)
2120 revs = list(revs)
2121 revs.extend(opts.get('rev'))
2121 revs.extend(opts.get('rev'))
2122
2122
2123 if not opts.get('user') and opts.get('currentuser'):
2123 if not opts.get('user') and opts.get('currentuser'):
2124 opts['user'] = ui.username()
2124 opts['user'] = ui.username()
2125 if not opts.get('date') and opts.get('currentdate'):
2125 if not opts.get('date') and opts.get('currentdate'):
2126 opts['date'] = "%d %d" % util.makedate()
2126 opts['date'] = "%d %d" % util.makedate()
2127
2127
2128 editor = cmdutil.getcommiteditor(editform='graft',
2128 editor = cmdutil.getcommiteditor(editform='graft',
2129 **pycompat.strkwargs(opts))
2129 **pycompat.strkwargs(opts))
2130
2130
2131 cont = False
2131 cont = False
2132 if opts.get('continue'):
2132 if opts.get('continue'):
2133 cont = True
2133 cont = True
2134 if revs:
2134 if revs:
2135 raise error.Abort(_("can't specify --continue and revisions"))
2135 raise error.Abort(_("can't specify --continue and revisions"))
2136 # read in unfinished revisions
2136 # read in unfinished revisions
2137 try:
2137 try:
2138 nodes = repo.vfs.read('graftstate').splitlines()
2138 nodes = repo.vfs.read('graftstate').splitlines()
2139 revs = [repo[node].rev() for node in nodes]
2139 revs = [repo[node].rev() for node in nodes]
2140 except IOError as inst:
2140 except IOError as inst:
2141 if inst.errno != errno.ENOENT:
2141 if inst.errno != errno.ENOENT:
2142 raise
2142 raise
2143 cmdutil.wrongtooltocontinue(repo, _('graft'))
2143 cmdutil.wrongtooltocontinue(repo, _('graft'))
2144 else:
2144 else:
2145 cmdutil.checkunfinished(repo)
2145 cmdutil.checkunfinished(repo)
2146 cmdutil.bailifchanged(repo)
2146 cmdutil.bailifchanged(repo)
2147 if not revs:
2147 if not revs:
2148 raise error.Abort(_('no revisions specified'))
2148 raise error.Abort(_('no revisions specified'))
2149 revs = scmutil.revrange(repo, revs)
2149 revs = scmutil.revrange(repo, revs)
2150
2150
2151 skipped = set()
2151 skipped = set()
2152 # check for merges
2152 # check for merges
2153 for rev in repo.revs('%ld and merge()', revs):
2153 for rev in repo.revs('%ld and merge()', revs):
2154 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2154 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2155 skipped.add(rev)
2155 skipped.add(rev)
2156 revs = [r for r in revs if r not in skipped]
2156 revs = [r for r in revs if r not in skipped]
2157 if not revs:
2157 if not revs:
2158 return -1
2158 return -1
2159
2159
2160 # Don't check in the --continue case, in effect retaining --force across
2160 # Don't check in the --continue case, in effect retaining --force across
2161 # --continues. That's because without --force, any revisions we decided to
2161 # --continues. That's because without --force, any revisions we decided to
2162 # skip would have been filtered out here, so they wouldn't have made their
2162 # skip would have been filtered out here, so they wouldn't have made their
2163 # way to the graftstate. With --force, any revisions we would have otherwise
2163 # way to the graftstate. With --force, any revisions we would have otherwise
2164 # skipped would not have been filtered out, and if they hadn't been applied
2164 # skipped would not have been filtered out, and if they hadn't been applied
2165 # already, they'd have been in the graftstate.
2165 # already, they'd have been in the graftstate.
2166 if not (cont or opts.get('force')):
2166 if not (cont or opts.get('force')):
2167 # check for ancestors of dest branch
2167 # check for ancestors of dest branch
2168 crev = repo['.'].rev()
2168 crev = repo['.'].rev()
2169 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2169 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2170 # XXX make this lazy in the future
2170 # XXX make this lazy in the future
2171 # don't mutate while iterating, create a copy
2171 # don't mutate while iterating, create a copy
2172 for rev in list(revs):
2172 for rev in list(revs):
2173 if rev in ancestors:
2173 if rev in ancestors:
2174 ui.warn(_('skipping ancestor revision %d:%s\n') %
2174 ui.warn(_('skipping ancestor revision %d:%s\n') %
2175 (rev, repo[rev]))
2175 (rev, repo[rev]))
2176 # XXX remove on list is slow
2176 # XXX remove on list is slow
2177 revs.remove(rev)
2177 revs.remove(rev)
2178 if not revs:
2178 if not revs:
2179 return -1
2179 return -1
2180
2180
2181 # analyze revs for earlier grafts
2181 # analyze revs for earlier grafts
2182 ids = {}
2182 ids = {}
2183 for ctx in repo.set("%ld", revs):
2183 for ctx in repo.set("%ld", revs):
2184 ids[ctx.hex()] = ctx.rev()
2184 ids[ctx.hex()] = ctx.rev()
2185 n = ctx.extra().get('source')
2185 n = ctx.extra().get('source')
2186 if n:
2186 if n:
2187 ids[n] = ctx.rev()
2187 ids[n] = ctx.rev()
2188
2188
2189 # check ancestors for earlier grafts
2189 # check ancestors for earlier grafts
2190 ui.debug('scanning for duplicate grafts\n')
2190 ui.debug('scanning for duplicate grafts\n')
2191
2191
2192 # The only changesets we can be sure doesn't contain grafts of any
2192 # The only changesets we can be sure doesn't contain grafts of any
2193 # revs, are the ones that are common ancestors of *all* revs:
2193 # revs, are the ones that are common ancestors of *all* revs:
2194 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2194 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2195 ctx = repo[rev]
2195 ctx = repo[rev]
2196 n = ctx.extra().get('source')
2196 n = ctx.extra().get('source')
2197 if n in ids:
2197 if n in ids:
2198 try:
2198 try:
2199 r = repo[n].rev()
2199 r = repo[n].rev()
2200 except error.RepoLookupError:
2200 except error.RepoLookupError:
2201 r = None
2201 r = None
2202 if r in revs:
2202 if r in revs:
2203 ui.warn(_('skipping revision %d:%s '
2203 ui.warn(_('skipping revision %d:%s '
2204 '(already grafted to %d:%s)\n')
2204 '(already grafted to %d:%s)\n')
2205 % (r, repo[r], rev, ctx))
2205 % (r, repo[r], rev, ctx))
2206 revs.remove(r)
2206 revs.remove(r)
2207 elif ids[n] in revs:
2207 elif ids[n] in revs:
2208 if r is None:
2208 if r is None:
2209 ui.warn(_('skipping already grafted revision %d:%s '
2209 ui.warn(_('skipping already grafted revision %d:%s '
2210 '(%d:%s also has unknown origin %s)\n')
2210 '(%d:%s also has unknown origin %s)\n')
2211 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2211 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2212 else:
2212 else:
2213 ui.warn(_('skipping already grafted revision %d:%s '
2213 ui.warn(_('skipping already grafted revision %d:%s '
2214 '(%d:%s also has origin %d:%s)\n')
2214 '(%d:%s also has origin %d:%s)\n')
2215 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2215 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2216 revs.remove(ids[n])
2216 revs.remove(ids[n])
2217 elif ctx.hex() in ids:
2217 elif ctx.hex() in ids:
2218 r = ids[ctx.hex()]
2218 r = ids[ctx.hex()]
2219 ui.warn(_('skipping already grafted revision %d:%s '
2219 ui.warn(_('skipping already grafted revision %d:%s '
2220 '(was grafted from %d:%s)\n') %
2220 '(was grafted from %d:%s)\n') %
2221 (r, repo[r], rev, ctx))
2221 (r, repo[r], rev, ctx))
2222 revs.remove(r)
2222 revs.remove(r)
2223 if not revs:
2223 if not revs:
2224 return -1
2224 return -1
2225
2225
2226 for pos, ctx in enumerate(repo.set("%ld", revs)):
2226 for pos, ctx in enumerate(repo.set("%ld", revs)):
2227 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2227 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2228 ctx.description().split('\n', 1)[0])
2228 ctx.description().split('\n', 1)[0])
2229 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2229 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2230 if names:
2230 if names:
2231 desc += ' (%s)' % ' '.join(names)
2231 desc += ' (%s)' % ' '.join(names)
2232 ui.status(_('grafting %s\n') % desc)
2232 ui.status(_('grafting %s\n') % desc)
2233 if opts.get('dry_run'):
2233 if opts.get('dry_run'):
2234 continue
2234 continue
2235
2235
2236 source = ctx.extra().get('source')
2236 source = ctx.extra().get('source')
2237 extra = {}
2237 extra = {}
2238 if source:
2238 if source:
2239 extra['source'] = source
2239 extra['source'] = source
2240 extra['intermediate-source'] = ctx.hex()
2240 extra['intermediate-source'] = ctx.hex()
2241 else:
2241 else:
2242 extra['source'] = ctx.hex()
2242 extra['source'] = ctx.hex()
2243 user = ctx.user()
2243 user = ctx.user()
2244 if opts.get('user'):
2244 if opts.get('user'):
2245 user = opts['user']
2245 user = opts['user']
2246 date = ctx.date()
2246 date = ctx.date()
2247 if opts.get('date'):
2247 if opts.get('date'):
2248 date = opts['date']
2248 date = opts['date']
2249 message = ctx.description()
2249 message = ctx.description()
2250 if opts.get('log'):
2250 if opts.get('log'):
2251 message += '\n(grafted from %s)' % ctx.hex()
2251 message += '\n(grafted from %s)' % ctx.hex()
2252
2252
2253 # we don't merge the first commit when continuing
2253 # we don't merge the first commit when continuing
2254 if not cont:
2254 if not cont:
2255 # perform the graft merge with p1(rev) as 'ancestor'
2255 # perform the graft merge with p1(rev) as 'ancestor'
2256 try:
2256 try:
2257 # ui.forcemerge is an internal variable, do not document
2257 # ui.forcemerge is an internal variable, do not document
2258 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
2258 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
2259 'graft')
2259 'graft')
2260 stats = mergemod.graft(repo, ctx, ctx.p1(),
2260 stats = mergemod.graft(repo, ctx, ctx.p1(),
2261 ['local', 'graft'])
2261 ['local', 'graft'])
2262 finally:
2262 finally:
2263 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
2263 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
2264 # report any conflicts
2264 # report any conflicts
2265 if stats and stats[3] > 0:
2265 if stats and stats[3] > 0:
2266 # write out state for --continue
2266 # write out state for --continue
2267 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2267 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2268 repo.vfs.write('graftstate', ''.join(nodelines))
2268 repo.vfs.write('graftstate', ''.join(nodelines))
2269 extra = ''
2269 extra = ''
2270 if opts.get('user'):
2270 if opts.get('user'):
2271 extra += ' --user %s' % util.shellquote(opts['user'])
2271 extra += ' --user %s' % util.shellquote(opts['user'])
2272 if opts.get('date'):
2272 if opts.get('date'):
2273 extra += ' --date %s' % util.shellquote(opts['date'])
2273 extra += ' --date %s' % util.shellquote(opts['date'])
2274 if opts.get('log'):
2274 if opts.get('log'):
2275 extra += ' --log'
2275 extra += ' --log'
2276 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
2276 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
2277 raise error.Abort(
2277 raise error.Abort(
2278 _("unresolved conflicts, can't continue"),
2278 _("unresolved conflicts, can't continue"),
2279 hint=hint)
2279 hint=hint)
2280 else:
2280 else:
2281 cont = False
2281 cont = False
2282
2282
2283 # commit
2283 # commit
2284 node = repo.commit(text=message, user=user,
2284 node = repo.commit(text=message, user=user,
2285 date=date, extra=extra, editor=editor)
2285 date=date, extra=extra, editor=editor)
2286 if node is None:
2286 if node is None:
2287 ui.warn(
2287 ui.warn(
2288 _('note: graft of %d:%s created no changes to commit\n') %
2288 _('note: graft of %d:%s created no changes to commit\n') %
2289 (ctx.rev(), ctx))
2289 (ctx.rev(), ctx))
2290
2290
2291 # remove state when we complete successfully
2291 # remove state when we complete successfully
2292 if not opts.get('dry_run'):
2292 if not opts.get('dry_run'):
2293 repo.vfs.unlinkpath('graftstate', ignoremissing=True)
2293 repo.vfs.unlinkpath('graftstate', ignoremissing=True)
2294
2294
2295 return 0
2295 return 0
2296
2296
2297 @command('grep',
2297 @command('grep',
2298 [('0', 'print0', None, _('end fields with NUL')),
2298 [('0', 'print0', None, _('end fields with NUL')),
2299 ('', 'all', None, _('print all revisions that match')),
2299 ('', 'all', None, _('print all revisions that match')),
2300 ('a', 'text', None, _('treat all files as text')),
2300 ('a', 'text', None, _('treat all files as text')),
2301 ('f', 'follow', None,
2301 ('f', 'follow', None,
2302 _('follow changeset history,'
2302 _('follow changeset history,'
2303 ' or file history across copies and renames')),
2303 ' or file history across copies and renames')),
2304 ('i', 'ignore-case', None, _('ignore case when matching')),
2304 ('i', 'ignore-case', None, _('ignore case when matching')),
2305 ('l', 'files-with-matches', None,
2305 ('l', 'files-with-matches', None,
2306 _('print only filenames and revisions that match')),
2306 _('print only filenames and revisions that match')),
2307 ('n', 'line-number', None, _('print matching line numbers')),
2307 ('n', 'line-number', None, _('print matching line numbers')),
2308 ('r', 'rev', [],
2308 ('r', 'rev', [],
2309 _('only search files changed within revision range'), _('REV')),
2309 _('only search files changed within revision range'), _('REV')),
2310 ('u', 'user', None, _('list the author (long with -v)')),
2310 ('u', 'user', None, _('list the author (long with -v)')),
2311 ('d', 'date', None, _('list the date (short with -q)')),
2311 ('d', 'date', None, _('list the date (short with -q)')),
2312 ] + formatteropts + walkopts,
2312 ] + formatteropts + walkopts,
2313 _('[OPTION]... PATTERN [FILE]...'),
2313 _('[OPTION]... PATTERN [FILE]...'),
2314 inferrepo=True)
2314 inferrepo=True)
2315 def grep(ui, repo, pattern, *pats, **opts):
2315 def grep(ui, repo, pattern, *pats, **opts):
2316 """search revision history for a pattern in specified files
2316 """search revision history for a pattern in specified files
2317
2317
2318 Search revision history for a regular expression in the specified
2318 Search revision history for a regular expression in the specified
2319 files or the entire project.
2319 files or the entire project.
2320
2320
2321 By default, grep prints the most recent revision number for each
2321 By default, grep prints the most recent revision number for each
2322 file in which it finds a match. To get it to print every revision
2322 file in which it finds a match. To get it to print every revision
2323 that contains a change in match status ("-" for a match that becomes
2323 that contains a change in match status ("-" for a match that becomes
2324 a non-match, or "+" for a non-match that becomes a match), use the
2324 a non-match, or "+" for a non-match that becomes a match), use the
2325 --all flag.
2325 --all flag.
2326
2326
2327 PATTERN can be any Python (roughly Perl-compatible) regular
2327 PATTERN can be any Python (roughly Perl-compatible) regular
2328 expression.
2328 expression.
2329
2329
2330 If no FILEs are specified (and -f/--follow isn't set), all files in
2330 If no FILEs are specified (and -f/--follow isn't set), all files in
2331 the repository are searched, including those that don't exist in the
2331 the repository are searched, including those that don't exist in the
2332 current branch or have been deleted in a prior changeset.
2332 current branch or have been deleted in a prior changeset.
2333
2333
2334 Returns 0 if a match is found, 1 otherwise.
2334 Returns 0 if a match is found, 1 otherwise.
2335 """
2335 """
2336 opts = pycompat.byteskwargs(opts)
2336 opts = pycompat.byteskwargs(opts)
2337 reflags = re.M
2337 reflags = re.M
2338 if opts.get('ignore_case'):
2338 if opts.get('ignore_case'):
2339 reflags |= re.I
2339 reflags |= re.I
2340 try:
2340 try:
2341 regexp = util.re.compile(pattern, reflags)
2341 regexp = util.re.compile(pattern, reflags)
2342 except re.error as inst:
2342 except re.error as inst:
2343 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2343 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2344 return 1
2344 return 1
2345 sep, eol = ':', '\n'
2345 sep, eol = ':', '\n'
2346 if opts.get('print0'):
2346 if opts.get('print0'):
2347 sep = eol = '\0'
2347 sep = eol = '\0'
2348
2348
2349 getfile = util.lrucachefunc(repo.file)
2349 getfile = util.lrucachefunc(repo.file)
2350
2350
2351 def matchlines(body):
2351 def matchlines(body):
2352 begin = 0
2352 begin = 0
2353 linenum = 0
2353 linenum = 0
2354 while begin < len(body):
2354 while begin < len(body):
2355 match = regexp.search(body, begin)
2355 match = regexp.search(body, begin)
2356 if not match:
2356 if not match:
2357 break
2357 break
2358 mstart, mend = match.span()
2358 mstart, mend = match.span()
2359 linenum += body.count('\n', begin, mstart) + 1
2359 linenum += body.count('\n', begin, mstart) + 1
2360 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2360 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2361 begin = body.find('\n', mend) + 1 or len(body) + 1
2361 begin = body.find('\n', mend) + 1 or len(body) + 1
2362 lend = begin - 1
2362 lend = begin - 1
2363 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2363 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2364
2364
2365 class linestate(object):
2365 class linestate(object):
2366 def __init__(self, line, linenum, colstart, colend):
2366 def __init__(self, line, linenum, colstart, colend):
2367 self.line = line
2367 self.line = line
2368 self.linenum = linenum
2368 self.linenum = linenum
2369 self.colstart = colstart
2369 self.colstart = colstart
2370 self.colend = colend
2370 self.colend = colend
2371
2371
2372 def __hash__(self):
2372 def __hash__(self):
2373 return hash((self.linenum, self.line))
2373 return hash((self.linenum, self.line))
2374
2374
2375 def __eq__(self, other):
2375 def __eq__(self, other):
2376 return self.line == other.line
2376 return self.line == other.line
2377
2377
2378 def findpos(self):
2378 def findpos(self):
2379 """Iterate all (start, end) indices of matches"""
2379 """Iterate all (start, end) indices of matches"""
2380 yield self.colstart, self.colend
2380 yield self.colstart, self.colend
2381 p = self.colend
2381 p = self.colend
2382 while p < len(self.line):
2382 while p < len(self.line):
2383 m = regexp.search(self.line, p)
2383 m = regexp.search(self.line, p)
2384 if not m:
2384 if not m:
2385 break
2385 break
2386 yield m.span()
2386 yield m.span()
2387 p = m.end()
2387 p = m.end()
2388
2388
2389 matches = {}
2389 matches = {}
2390 copies = {}
2390 copies = {}
2391 def grepbody(fn, rev, body):
2391 def grepbody(fn, rev, body):
2392 matches[rev].setdefault(fn, [])
2392 matches[rev].setdefault(fn, [])
2393 m = matches[rev][fn]
2393 m = matches[rev][fn]
2394 for lnum, cstart, cend, line in matchlines(body):
2394 for lnum, cstart, cend, line in matchlines(body):
2395 s = linestate(line, lnum, cstart, cend)
2395 s = linestate(line, lnum, cstart, cend)
2396 m.append(s)
2396 m.append(s)
2397
2397
2398 def difflinestates(a, b):
2398 def difflinestates(a, b):
2399 sm = difflib.SequenceMatcher(None, a, b)
2399 sm = difflib.SequenceMatcher(None, a, b)
2400 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2400 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2401 if tag == 'insert':
2401 if tag == 'insert':
2402 for i in xrange(blo, bhi):
2402 for i in xrange(blo, bhi):
2403 yield ('+', b[i])
2403 yield ('+', b[i])
2404 elif tag == 'delete':
2404 elif tag == 'delete':
2405 for i in xrange(alo, ahi):
2405 for i in xrange(alo, ahi):
2406 yield ('-', a[i])
2406 yield ('-', a[i])
2407 elif tag == 'replace':
2407 elif tag == 'replace':
2408 for i in xrange(alo, ahi):
2408 for i in xrange(alo, ahi):
2409 yield ('-', a[i])
2409 yield ('-', a[i])
2410 for i in xrange(blo, bhi):
2410 for i in xrange(blo, bhi):
2411 yield ('+', b[i])
2411 yield ('+', b[i])
2412
2412
2413 def display(fm, fn, ctx, pstates, states):
2413 def display(fm, fn, ctx, pstates, states):
2414 rev = ctx.rev()
2414 rev = ctx.rev()
2415 if fm.isplain():
2415 if fm.isplain():
2416 formatuser = ui.shortuser
2416 formatuser = ui.shortuser
2417 else:
2417 else:
2418 formatuser = str
2418 formatuser = str
2419 if ui.quiet:
2419 if ui.quiet:
2420 datefmt = '%Y-%m-%d'
2420 datefmt = '%Y-%m-%d'
2421 else:
2421 else:
2422 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2422 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2423 found = False
2423 found = False
2424 @util.cachefunc
2424 @util.cachefunc
2425 def binary():
2425 def binary():
2426 flog = getfile(fn)
2426 flog = getfile(fn)
2427 return util.binary(flog.read(ctx.filenode(fn)))
2427 return util.binary(flog.read(ctx.filenode(fn)))
2428
2428
2429 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
2429 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
2430 if opts.get('all'):
2430 if opts.get('all'):
2431 iter = difflinestates(pstates, states)
2431 iter = difflinestates(pstates, states)
2432 else:
2432 else:
2433 iter = [('', l) for l in states]
2433 iter = [('', l) for l in states]
2434 for change, l in iter:
2434 for change, l in iter:
2435 fm.startitem()
2435 fm.startitem()
2436 fm.data(node=fm.hexfunc(ctx.node()))
2436 fm.data(node=fm.hexfunc(ctx.node()))
2437 cols = [
2437 cols = [
2438 ('filename', fn, True),
2438 ('filename', fn, True),
2439 ('rev', rev, True),
2439 ('rev', rev, True),
2440 ('linenumber', l.linenum, opts.get('line_number')),
2440 ('linenumber', l.linenum, opts.get('line_number')),
2441 ]
2441 ]
2442 if opts.get('all'):
2442 if opts.get('all'):
2443 cols.append(('change', change, True))
2443 cols.append(('change', change, True))
2444 cols.extend([
2444 cols.extend([
2445 ('user', formatuser(ctx.user()), opts.get('user')),
2445 ('user', formatuser(ctx.user()), opts.get('user')),
2446 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
2446 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
2447 ])
2447 ])
2448 lastcol = next(name for name, data, cond in reversed(cols) if cond)
2448 lastcol = next(name for name, data, cond in reversed(cols) if cond)
2449 for name, data, cond in cols:
2449 for name, data, cond in cols:
2450 field = fieldnamemap.get(name, name)
2450 field = fieldnamemap.get(name, name)
2451 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
2451 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
2452 if cond and name != lastcol:
2452 if cond and name != lastcol:
2453 fm.plain(sep, label='grep.sep')
2453 fm.plain(sep, label='grep.sep')
2454 if not opts.get('files_with_matches'):
2454 if not opts.get('files_with_matches'):
2455 fm.plain(sep, label='grep.sep')
2455 fm.plain(sep, label='grep.sep')
2456 if not opts.get('text') and binary():
2456 if not opts.get('text') and binary():
2457 fm.plain(_(" Binary file matches"))
2457 fm.plain(_(" Binary file matches"))
2458 else:
2458 else:
2459 displaymatches(fm.nested('texts'), l)
2459 displaymatches(fm.nested('texts'), l)
2460 fm.plain(eol)
2460 fm.plain(eol)
2461 found = True
2461 found = True
2462 if opts.get('files_with_matches'):
2462 if opts.get('files_with_matches'):
2463 break
2463 break
2464 return found
2464 return found
2465
2465
2466 def displaymatches(fm, l):
2466 def displaymatches(fm, l):
2467 p = 0
2467 p = 0
2468 for s, e in l.findpos():
2468 for s, e in l.findpos():
2469 if p < s:
2469 if p < s:
2470 fm.startitem()
2470 fm.startitem()
2471 fm.write('text', '%s', l.line[p:s])
2471 fm.write('text', '%s', l.line[p:s])
2472 fm.data(matched=False)
2472 fm.data(matched=False)
2473 fm.startitem()
2473 fm.startitem()
2474 fm.write('text', '%s', l.line[s:e], label='grep.match')
2474 fm.write('text', '%s', l.line[s:e], label='grep.match')
2475 fm.data(matched=True)
2475 fm.data(matched=True)
2476 p = e
2476 p = e
2477 if p < len(l.line):
2477 if p < len(l.line):
2478 fm.startitem()
2478 fm.startitem()
2479 fm.write('text', '%s', l.line[p:])
2479 fm.write('text', '%s', l.line[p:])
2480 fm.data(matched=False)
2480 fm.data(matched=False)
2481 fm.end()
2481 fm.end()
2482
2482
2483 skip = {}
2483 skip = {}
2484 revfiles = {}
2484 revfiles = {}
2485 matchfn = scmutil.match(repo[None], pats, opts)
2485 matchfn = scmutil.match(repo[None], pats, opts)
2486 found = False
2486 found = False
2487 follow = opts.get('follow')
2487 follow = opts.get('follow')
2488
2488
2489 def prep(ctx, fns):
2489 def prep(ctx, fns):
2490 rev = ctx.rev()
2490 rev = ctx.rev()
2491 pctx = ctx.p1()
2491 pctx = ctx.p1()
2492 parent = pctx.rev()
2492 parent = pctx.rev()
2493 matches.setdefault(rev, {})
2493 matches.setdefault(rev, {})
2494 matches.setdefault(parent, {})
2494 matches.setdefault(parent, {})
2495 files = revfiles.setdefault(rev, [])
2495 files = revfiles.setdefault(rev, [])
2496 for fn in fns:
2496 for fn in fns:
2497 flog = getfile(fn)
2497 flog = getfile(fn)
2498 try:
2498 try:
2499 fnode = ctx.filenode(fn)
2499 fnode = ctx.filenode(fn)
2500 except error.LookupError:
2500 except error.LookupError:
2501 continue
2501 continue
2502
2502
2503 copied = flog.renamed(fnode)
2503 copied = flog.renamed(fnode)
2504 copy = follow and copied and copied[0]
2504 copy = follow and copied and copied[0]
2505 if copy:
2505 if copy:
2506 copies.setdefault(rev, {})[fn] = copy
2506 copies.setdefault(rev, {})[fn] = copy
2507 if fn in skip:
2507 if fn in skip:
2508 if copy:
2508 if copy:
2509 skip[copy] = True
2509 skip[copy] = True
2510 continue
2510 continue
2511 files.append(fn)
2511 files.append(fn)
2512
2512
2513 if fn not in matches[rev]:
2513 if fn not in matches[rev]:
2514 grepbody(fn, rev, flog.read(fnode))
2514 grepbody(fn, rev, flog.read(fnode))
2515
2515
2516 pfn = copy or fn
2516 pfn = copy or fn
2517 if pfn not in matches[parent]:
2517 if pfn not in matches[parent]:
2518 try:
2518 try:
2519 fnode = pctx.filenode(pfn)
2519 fnode = pctx.filenode(pfn)
2520 grepbody(pfn, parent, flog.read(fnode))
2520 grepbody(pfn, parent, flog.read(fnode))
2521 except error.LookupError:
2521 except error.LookupError:
2522 pass
2522 pass
2523
2523
2524 ui.pager('grep')
2524 ui.pager('grep')
2525 fm = ui.formatter('grep', opts)
2525 fm = ui.formatter('grep', opts)
2526 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2526 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2527 rev = ctx.rev()
2527 rev = ctx.rev()
2528 parent = ctx.p1().rev()
2528 parent = ctx.p1().rev()
2529 for fn in sorted(revfiles.get(rev, [])):
2529 for fn in sorted(revfiles.get(rev, [])):
2530 states = matches[rev][fn]
2530 states = matches[rev][fn]
2531 copy = copies.get(rev, {}).get(fn)
2531 copy = copies.get(rev, {}).get(fn)
2532 if fn in skip:
2532 if fn in skip:
2533 if copy:
2533 if copy:
2534 skip[copy] = True
2534 skip[copy] = True
2535 continue
2535 continue
2536 pstates = matches.get(parent, {}).get(copy or fn, [])
2536 pstates = matches.get(parent, {}).get(copy or fn, [])
2537 if pstates or states:
2537 if pstates or states:
2538 r = display(fm, fn, ctx, pstates, states)
2538 r = display(fm, fn, ctx, pstates, states)
2539 found = found or r
2539 found = found or r
2540 if r and not opts.get('all'):
2540 if r and not opts.get('all'):
2541 skip[fn] = True
2541 skip[fn] = True
2542 if copy:
2542 if copy:
2543 skip[copy] = True
2543 skip[copy] = True
2544 del matches[rev]
2544 del matches[rev]
2545 del revfiles[rev]
2545 del revfiles[rev]
2546 fm.end()
2546 fm.end()
2547
2547
2548 return not found
2548 return not found
2549
2549
2550 @command('heads',
2550 @command('heads',
2551 [('r', 'rev', '',
2551 [('r', 'rev', '',
2552 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2552 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2553 ('t', 'topo', False, _('show topological heads only')),
2553 ('t', 'topo', False, _('show topological heads only')),
2554 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2554 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2555 ('c', 'closed', False, _('show normal and closed branch heads')),
2555 ('c', 'closed', False, _('show normal and closed branch heads')),
2556 ] + templateopts,
2556 ] + templateopts,
2557 _('[-ct] [-r STARTREV] [REV]...'))
2557 _('[-ct] [-r STARTREV] [REV]...'))
2558 def heads(ui, repo, *branchrevs, **opts):
2558 def heads(ui, repo, *branchrevs, **opts):
2559 """show branch heads
2559 """show branch heads
2560
2560
2561 With no arguments, show all open branch heads in the repository.
2561 With no arguments, show all open branch heads in the repository.
2562 Branch heads are changesets that have no descendants on the
2562 Branch heads are changesets that have no descendants on the
2563 same branch. They are where development generally takes place and
2563 same branch. They are where development generally takes place and
2564 are the usual targets for update and merge operations.
2564 are the usual targets for update and merge operations.
2565
2565
2566 If one or more REVs are given, only open branch heads on the
2566 If one or more REVs are given, only open branch heads on the
2567 branches associated with the specified changesets are shown. This
2567 branches associated with the specified changesets are shown. This
2568 means that you can use :hg:`heads .` to see the heads on the
2568 means that you can use :hg:`heads .` to see the heads on the
2569 currently checked-out branch.
2569 currently checked-out branch.
2570
2570
2571 If -c/--closed is specified, also show branch heads marked closed
2571 If -c/--closed is specified, also show branch heads marked closed
2572 (see :hg:`commit --close-branch`).
2572 (see :hg:`commit --close-branch`).
2573
2573
2574 If STARTREV is specified, only those heads that are descendants of
2574 If STARTREV is specified, only those heads that are descendants of
2575 STARTREV will be displayed.
2575 STARTREV will be displayed.
2576
2576
2577 If -t/--topo is specified, named branch mechanics will be ignored and only
2577 If -t/--topo is specified, named branch mechanics will be ignored and only
2578 topological heads (changesets with no children) will be shown.
2578 topological heads (changesets with no children) will be shown.
2579
2579
2580 Returns 0 if matching heads are found, 1 if not.
2580 Returns 0 if matching heads are found, 1 if not.
2581 """
2581 """
2582
2582
2583 opts = pycompat.byteskwargs(opts)
2583 opts = pycompat.byteskwargs(opts)
2584 start = None
2584 start = None
2585 if 'rev' in opts:
2585 if 'rev' in opts:
2586 start = scmutil.revsingle(repo, opts['rev'], None).node()
2586 start = scmutil.revsingle(repo, opts['rev'], None).node()
2587
2587
2588 if opts.get('topo'):
2588 if opts.get('topo'):
2589 heads = [repo[h] for h in repo.heads(start)]
2589 heads = [repo[h] for h in repo.heads(start)]
2590 else:
2590 else:
2591 heads = []
2591 heads = []
2592 for branch in repo.branchmap():
2592 for branch in repo.branchmap():
2593 heads += repo.branchheads(branch, start, opts.get('closed'))
2593 heads += repo.branchheads(branch, start, opts.get('closed'))
2594 heads = [repo[h] for h in heads]
2594 heads = [repo[h] for h in heads]
2595
2595
2596 if branchrevs:
2596 if branchrevs:
2597 branches = set(repo[br].branch() for br in branchrevs)
2597 branches = set(repo[br].branch() for br in branchrevs)
2598 heads = [h for h in heads if h.branch() in branches]
2598 heads = [h for h in heads if h.branch() in branches]
2599
2599
2600 if opts.get('active') and branchrevs:
2600 if opts.get('active') and branchrevs:
2601 dagheads = repo.heads(start)
2601 dagheads = repo.heads(start)
2602 heads = [h for h in heads if h.node() in dagheads]
2602 heads = [h for h in heads if h.node() in dagheads]
2603
2603
2604 if branchrevs:
2604 if branchrevs:
2605 haveheads = set(h.branch() for h in heads)
2605 haveheads = set(h.branch() for h in heads)
2606 if branches - haveheads:
2606 if branches - haveheads:
2607 headless = ', '.join(b for b in branches - haveheads)
2607 headless = ', '.join(b for b in branches - haveheads)
2608 msg = _('no open branch heads found on branches %s')
2608 msg = _('no open branch heads found on branches %s')
2609 if opts.get('rev'):
2609 if opts.get('rev'):
2610 msg += _(' (started at %s)') % opts['rev']
2610 msg += _(' (started at %s)') % opts['rev']
2611 ui.warn((msg + '\n') % headless)
2611 ui.warn((msg + '\n') % headless)
2612
2612
2613 if not heads:
2613 if not heads:
2614 return 1
2614 return 1
2615
2615
2616 ui.pager('heads')
2616 ui.pager('heads')
2617 heads = sorted(heads, key=lambda x: -x.rev())
2617 heads = sorted(heads, key=lambda x: -x.rev())
2618 displayer = cmdutil.show_changeset(ui, repo, opts)
2618 displayer = cmdutil.show_changeset(ui, repo, opts)
2619 for ctx in heads:
2619 for ctx in heads:
2620 displayer.show(ctx)
2620 displayer.show(ctx)
2621 displayer.close()
2621 displayer.close()
2622
2622
2623 @command('help',
2623 @command('help',
2624 [('e', 'extension', None, _('show only help for extensions')),
2624 [('e', 'extension', None, _('show only help for extensions')),
2625 ('c', 'command', None, _('show only help for commands')),
2625 ('c', 'command', None, _('show only help for commands')),
2626 ('k', 'keyword', None, _('show topics matching keyword')),
2626 ('k', 'keyword', None, _('show topics matching keyword')),
2627 ('s', 'system', [], _('show help for specific platform(s)')),
2627 ('s', 'system', [], _('show help for specific platform(s)')),
2628 ],
2628 ],
2629 _('[-ecks] [TOPIC]'),
2629 _('[-ecks] [TOPIC]'),
2630 norepo=True)
2630 norepo=True)
2631 def help_(ui, name=None, **opts):
2631 def help_(ui, name=None, **opts):
2632 """show help for a given topic or a help overview
2632 """show help for a given topic or a help overview
2633
2633
2634 With no arguments, print a list of commands with short help messages.
2634 With no arguments, print a list of commands with short help messages.
2635
2635
2636 Given a topic, extension, or command name, print help for that
2636 Given a topic, extension, or command name, print help for that
2637 topic.
2637 topic.
2638
2638
2639 Returns 0 if successful.
2639 Returns 0 if successful.
2640 """
2640 """
2641
2641
2642 keep = opts.get(r'system') or []
2642 keep = opts.get(r'system') or []
2643 if len(keep) == 0:
2643 if len(keep) == 0:
2644 if pycompat.sysplatform.startswith('win'):
2644 if pycompat.sysplatform.startswith('win'):
2645 keep.append('windows')
2645 keep.append('windows')
2646 elif pycompat.sysplatform == 'OpenVMS':
2646 elif pycompat.sysplatform == 'OpenVMS':
2647 keep.append('vms')
2647 keep.append('vms')
2648 elif pycompat.sysplatform == 'plan9':
2648 elif pycompat.sysplatform == 'plan9':
2649 keep.append('plan9')
2649 keep.append('plan9')
2650 else:
2650 else:
2651 keep.append('unix')
2651 keep.append('unix')
2652 keep.append(pycompat.sysplatform.lower())
2652 keep.append(pycompat.sysplatform.lower())
2653 if ui.verbose:
2653 if ui.verbose:
2654 keep.append('verbose')
2654 keep.append('verbose')
2655
2655
2656 commands = sys.modules[__name__]
2656 commands = sys.modules[__name__]
2657 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
2657 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
2658 ui.pager('help')
2658 ui.pager('help')
2659 ui.write(formatted)
2659 ui.write(formatted)
2660
2660
2661
2661
2662 @command('identify|id',
2662 @command('identify|id',
2663 [('r', 'rev', '',
2663 [('r', 'rev', '',
2664 _('identify the specified revision'), _('REV')),
2664 _('identify the specified revision'), _('REV')),
2665 ('n', 'num', None, _('show local revision number')),
2665 ('n', 'num', None, _('show local revision number')),
2666 ('i', 'id', None, _('show global revision id')),
2666 ('i', 'id', None, _('show global revision id')),
2667 ('b', 'branch', None, _('show branch')),
2667 ('b', 'branch', None, _('show branch')),
2668 ('t', 'tags', None, _('show tags')),
2668 ('t', 'tags', None, _('show tags')),
2669 ('B', 'bookmarks', None, _('show bookmarks')),
2669 ('B', 'bookmarks', None, _('show bookmarks')),
2670 ] + remoteopts,
2670 ] + remoteopts,
2671 _('[-nibtB] [-r REV] [SOURCE]'),
2671 _('[-nibtB] [-r REV] [SOURCE]'),
2672 optionalrepo=True)
2672 optionalrepo=True)
2673 def identify(ui, repo, source=None, rev=None,
2673 def identify(ui, repo, source=None, rev=None,
2674 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
2674 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
2675 """identify the working directory or specified revision
2675 """identify the working directory or specified revision
2676
2676
2677 Print a summary identifying the repository state at REV using one or
2677 Print a summary identifying the repository state at REV using one or
2678 two parent hash identifiers, followed by a "+" if the working
2678 two parent hash identifiers, followed by a "+" if the working
2679 directory has uncommitted changes, the branch name (if not default),
2679 directory has uncommitted changes, the branch name (if not default),
2680 a list of tags, and a list of bookmarks.
2680 a list of tags, and a list of bookmarks.
2681
2681
2682 When REV is not given, print a summary of the current state of the
2682 When REV is not given, print a summary of the current state of the
2683 repository.
2683 repository.
2684
2684
2685 Specifying a path to a repository root or Mercurial bundle will
2685 Specifying a path to a repository root or Mercurial bundle will
2686 cause lookup to operate on that repository/bundle.
2686 cause lookup to operate on that repository/bundle.
2687
2687
2688 .. container:: verbose
2688 .. container:: verbose
2689
2689
2690 Examples:
2690 Examples:
2691
2691
2692 - generate a build identifier for the working directory::
2692 - generate a build identifier for the working directory::
2693
2693
2694 hg id --id > build-id.dat
2694 hg id --id > build-id.dat
2695
2695
2696 - find the revision corresponding to a tag::
2696 - find the revision corresponding to a tag::
2697
2697
2698 hg id -n -r 1.3
2698 hg id -n -r 1.3
2699
2699
2700 - check the most recent revision of a remote repository::
2700 - check the most recent revision of a remote repository::
2701
2701
2702 hg id -r tip https://www.mercurial-scm.org/repo/hg/
2702 hg id -r tip https://www.mercurial-scm.org/repo/hg/
2703
2703
2704 See :hg:`log` for generating more information about specific revisions,
2704 See :hg:`log` for generating more information about specific revisions,
2705 including full hash identifiers.
2705 including full hash identifiers.
2706
2706
2707 Returns 0 if successful.
2707 Returns 0 if successful.
2708 """
2708 """
2709
2709
2710 opts = pycompat.byteskwargs(opts)
2710 opts = pycompat.byteskwargs(opts)
2711 if not repo and not source:
2711 if not repo and not source:
2712 raise error.Abort(_("there is no Mercurial repository here "
2712 raise error.Abort(_("there is no Mercurial repository here "
2713 "(.hg not found)"))
2713 "(.hg not found)"))
2714
2714
2715 if ui.debugflag:
2715 if ui.debugflag:
2716 hexfunc = hex
2716 hexfunc = hex
2717 else:
2717 else:
2718 hexfunc = short
2718 hexfunc = short
2719 default = not (num or id or branch or tags or bookmarks)
2719 default = not (num or id or branch or tags or bookmarks)
2720 output = []
2720 output = []
2721 revs = []
2721 revs = []
2722
2722
2723 if source:
2723 if source:
2724 source, branches = hg.parseurl(ui.expandpath(source))
2724 source, branches = hg.parseurl(ui.expandpath(source))
2725 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
2725 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
2726 repo = peer.local()
2726 repo = peer.local()
2727 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
2727 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
2728
2728
2729 if not repo:
2729 if not repo:
2730 if num or branch or tags:
2730 if num or branch or tags:
2731 raise error.Abort(
2731 raise error.Abort(
2732 _("can't query remote revision number, branch, or tags"))
2732 _("can't query remote revision number, branch, or tags"))
2733 if not rev and revs:
2733 if not rev and revs:
2734 rev = revs[0]
2734 rev = revs[0]
2735 if not rev:
2735 if not rev:
2736 rev = "tip"
2736 rev = "tip"
2737
2737
2738 remoterev = peer.lookup(rev)
2738 remoterev = peer.lookup(rev)
2739 if default or id:
2739 if default or id:
2740 output = [hexfunc(remoterev)]
2740 output = [hexfunc(remoterev)]
2741
2741
2742 def getbms():
2742 def getbms():
2743 bms = []
2743 bms = []
2744
2744
2745 if 'bookmarks' in peer.listkeys('namespaces'):
2745 if 'bookmarks' in peer.listkeys('namespaces'):
2746 hexremoterev = hex(remoterev)
2746 hexremoterev = hex(remoterev)
2747 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
2747 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
2748 if bmr == hexremoterev]
2748 if bmr == hexremoterev]
2749
2749
2750 return sorted(bms)
2750 return sorted(bms)
2751
2751
2752 if bookmarks:
2752 if bookmarks:
2753 output.extend(getbms())
2753 output.extend(getbms())
2754 elif default and not ui.quiet:
2754 elif default and not ui.quiet:
2755 # multiple bookmarks for a single parent separated by '/'
2755 # multiple bookmarks for a single parent separated by '/'
2756 bm = '/'.join(getbms())
2756 bm = '/'.join(getbms())
2757 if bm:
2757 if bm:
2758 output.append(bm)
2758 output.append(bm)
2759 else:
2759 else:
2760 ctx = scmutil.revsingle(repo, rev, None)
2760 ctx = scmutil.revsingle(repo, rev, None)
2761
2761
2762 if ctx.rev() is None:
2762 if ctx.rev() is None:
2763 ctx = repo[None]
2763 ctx = repo[None]
2764 parents = ctx.parents()
2764 parents = ctx.parents()
2765 taglist = []
2765 taglist = []
2766 for p in parents:
2766 for p in parents:
2767 taglist.extend(p.tags())
2767 taglist.extend(p.tags())
2768
2768
2769 changed = ""
2769 changed = ""
2770 if default or id or num:
2770 if default or id or num:
2771 if (any(repo.status())
2771 if (any(repo.status())
2772 or any(ctx.sub(s).dirty() for s in ctx.substate)):
2772 or any(ctx.sub(s).dirty() for s in ctx.substate)):
2773 changed = '+'
2773 changed = '+'
2774 if default or id:
2774 if default or id:
2775 output = ["%s%s" %
2775 output = ["%s%s" %
2776 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
2776 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
2777 if num:
2777 if num:
2778 output.append("%s%s" %
2778 output.append("%s%s" %
2779 ('+'.join(["%d" % p.rev() for p in parents]), changed))
2779 ('+'.join(["%d" % p.rev() for p in parents]), changed))
2780 else:
2780 else:
2781 if default or id:
2781 if default or id:
2782 output = [hexfunc(ctx.node())]
2782 output = [hexfunc(ctx.node())]
2783 if num:
2783 if num:
2784 output.append(pycompat.bytestr(ctx.rev()))
2784 output.append(pycompat.bytestr(ctx.rev()))
2785 taglist = ctx.tags()
2785 taglist = ctx.tags()
2786
2786
2787 if default and not ui.quiet:
2787 if default and not ui.quiet:
2788 b = ctx.branch()
2788 b = ctx.branch()
2789 if b != 'default':
2789 if b != 'default':
2790 output.append("(%s)" % b)
2790 output.append("(%s)" % b)
2791
2791
2792 # multiple tags for a single parent separated by '/'
2792 # multiple tags for a single parent separated by '/'
2793 t = '/'.join(taglist)
2793 t = '/'.join(taglist)
2794 if t:
2794 if t:
2795 output.append(t)
2795 output.append(t)
2796
2796
2797 # multiple bookmarks for a single parent separated by '/'
2797 # multiple bookmarks for a single parent separated by '/'
2798 bm = '/'.join(ctx.bookmarks())
2798 bm = '/'.join(ctx.bookmarks())
2799 if bm:
2799 if bm:
2800 output.append(bm)
2800 output.append(bm)
2801 else:
2801 else:
2802 if branch:
2802 if branch:
2803 output.append(ctx.branch())
2803 output.append(ctx.branch())
2804
2804
2805 if tags:
2805 if tags:
2806 output.extend(taglist)
2806 output.extend(taglist)
2807
2807
2808 if bookmarks:
2808 if bookmarks:
2809 output.extend(ctx.bookmarks())
2809 output.extend(ctx.bookmarks())
2810
2810
2811 ui.write("%s\n" % ' '.join(output))
2811 ui.write("%s\n" % ' '.join(output))
2812
2812
2813 @command('import|patch',
2813 @command('import|patch',
2814 [('p', 'strip', 1,
2814 [('p', 'strip', 1,
2815 _('directory strip option for patch. This has the same '
2815 _('directory strip option for patch. This has the same '
2816 'meaning as the corresponding patch option'), _('NUM')),
2816 'meaning as the corresponding patch option'), _('NUM')),
2817 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
2817 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
2818 ('e', 'edit', False, _('invoke editor on commit messages')),
2818 ('e', 'edit', False, _('invoke editor on commit messages')),
2819 ('f', 'force', None,
2819 ('f', 'force', None,
2820 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
2820 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
2821 ('', 'no-commit', None,
2821 ('', 'no-commit', None,
2822 _("don't commit, just update the working directory")),
2822 _("don't commit, just update the working directory")),
2823 ('', 'bypass', None,
2823 ('', 'bypass', None,
2824 _("apply patch without touching the working directory")),
2824 _("apply patch without touching the working directory")),
2825 ('', 'partial', None,
2825 ('', 'partial', None,
2826 _('commit even if some hunks fail')),
2826 _('commit even if some hunks fail')),
2827 ('', 'exact', None,
2827 ('', 'exact', None,
2828 _('abort if patch would apply lossily')),
2828 _('abort if patch would apply lossily')),
2829 ('', 'prefix', '',
2829 ('', 'prefix', '',
2830 _('apply patch to subdirectory'), _('DIR')),
2830 _('apply patch to subdirectory'), _('DIR')),
2831 ('', 'import-branch', None,
2831 ('', 'import-branch', None,
2832 _('use any branch information in patch (implied by --exact)'))] +
2832 _('use any branch information in patch (implied by --exact)'))] +
2833 commitopts + commitopts2 + similarityopts,
2833 commitopts + commitopts2 + similarityopts,
2834 _('[OPTION]... PATCH...'))
2834 _('[OPTION]... PATCH...'))
2835 def import_(ui, repo, patch1=None, *patches, **opts):
2835 def import_(ui, repo, patch1=None, *patches, **opts):
2836 """import an ordered set of patches
2836 """import an ordered set of patches
2837
2837
2838 Import a list of patches and commit them individually (unless
2838 Import a list of patches and commit them individually (unless
2839 --no-commit is specified).
2839 --no-commit is specified).
2840
2840
2841 To read a patch from standard input (stdin), use "-" as the patch
2841 To read a patch from standard input (stdin), use "-" as the patch
2842 name. If a URL is specified, the patch will be downloaded from
2842 name. If a URL is specified, the patch will be downloaded from
2843 there.
2843 there.
2844
2844
2845 Import first applies changes to the working directory (unless
2845 Import first applies changes to the working directory (unless
2846 --bypass is specified), import will abort if there are outstanding
2846 --bypass is specified), import will abort if there are outstanding
2847 changes.
2847 changes.
2848
2848
2849 Use --bypass to apply and commit patches directly to the
2849 Use --bypass to apply and commit patches directly to the
2850 repository, without affecting the working directory. Without
2850 repository, without affecting the working directory. Without
2851 --exact, patches will be applied on top of the working directory
2851 --exact, patches will be applied on top of the working directory
2852 parent revision.
2852 parent revision.
2853
2853
2854 You can import a patch straight from a mail message. Even patches
2854 You can import a patch straight from a mail message. Even patches
2855 as attachments work (to use the body part, it must have type
2855 as attachments work (to use the body part, it must have type
2856 text/plain or text/x-patch). From and Subject headers of email
2856 text/plain or text/x-patch). From and Subject headers of email
2857 message are used as default committer and commit message. All
2857 message are used as default committer and commit message. All
2858 text/plain body parts before first diff are added to the commit
2858 text/plain body parts before first diff are added to the commit
2859 message.
2859 message.
2860
2860
2861 If the imported patch was generated by :hg:`export`, user and
2861 If the imported patch was generated by :hg:`export`, user and
2862 description from patch override values from message headers and
2862 description from patch override values from message headers and
2863 body. Values given on command line with -m/--message and -u/--user
2863 body. Values given on command line with -m/--message and -u/--user
2864 override these.
2864 override these.
2865
2865
2866 If --exact is specified, import will set the working directory to
2866 If --exact is specified, import will set the working directory to
2867 the parent of each patch before applying it, and will abort if the
2867 the parent of each patch before applying it, and will abort if the
2868 resulting changeset has a different ID than the one recorded in
2868 resulting changeset has a different ID than the one recorded in
2869 the patch. This will guard against various ways that portable
2869 the patch. This will guard against various ways that portable
2870 patch formats and mail systems might fail to transfer Mercurial
2870 patch formats and mail systems might fail to transfer Mercurial
2871 data or metadata. See :hg:`bundle` for lossless transmission.
2871 data or metadata. See :hg:`bundle` for lossless transmission.
2872
2872
2873 Use --partial to ensure a changeset will be created from the patch
2873 Use --partial to ensure a changeset will be created from the patch
2874 even if some hunks fail to apply. Hunks that fail to apply will be
2874 even if some hunks fail to apply. Hunks that fail to apply will be
2875 written to a <target-file>.rej file. Conflicts can then be resolved
2875 written to a <target-file>.rej file. Conflicts can then be resolved
2876 by hand before :hg:`commit --amend` is run to update the created
2876 by hand before :hg:`commit --amend` is run to update the created
2877 changeset. This flag exists to let people import patches that
2877 changeset. This flag exists to let people import patches that
2878 partially apply without losing the associated metadata (author,
2878 partially apply without losing the associated metadata (author,
2879 date, description, ...).
2879 date, description, ...).
2880
2880
2881 .. note::
2881 .. note::
2882
2882
2883 When no hunks apply cleanly, :hg:`import --partial` will create
2883 When no hunks apply cleanly, :hg:`import --partial` will create
2884 an empty changeset, importing only the patch metadata.
2884 an empty changeset, importing only the patch metadata.
2885
2885
2886 With -s/--similarity, hg will attempt to discover renames and
2886 With -s/--similarity, hg will attempt to discover renames and
2887 copies in the patch in the same way as :hg:`addremove`.
2887 copies in the patch in the same way as :hg:`addremove`.
2888
2888
2889 It is possible to use external patch programs to perform the patch
2889 It is possible to use external patch programs to perform the patch
2890 by setting the ``ui.patch`` configuration option. For the default
2890 by setting the ``ui.patch`` configuration option. For the default
2891 internal tool, the fuzz can also be configured via ``patch.fuzz``.
2891 internal tool, the fuzz can also be configured via ``patch.fuzz``.
2892 See :hg:`help config` for more information about configuration
2892 See :hg:`help config` for more information about configuration
2893 files and how to use these options.
2893 files and how to use these options.
2894
2894
2895 See :hg:`help dates` for a list of formats valid for -d/--date.
2895 See :hg:`help dates` for a list of formats valid for -d/--date.
2896
2896
2897 .. container:: verbose
2897 .. container:: verbose
2898
2898
2899 Examples:
2899 Examples:
2900
2900
2901 - import a traditional patch from a website and detect renames::
2901 - import a traditional patch from a website and detect renames::
2902
2902
2903 hg import -s 80 http://example.com/bugfix.patch
2903 hg import -s 80 http://example.com/bugfix.patch
2904
2904
2905 - import a changeset from an hgweb server::
2905 - import a changeset from an hgweb server::
2906
2906
2907 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
2907 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
2908
2908
2909 - import all the patches in an Unix-style mbox::
2909 - import all the patches in an Unix-style mbox::
2910
2910
2911 hg import incoming-patches.mbox
2911 hg import incoming-patches.mbox
2912
2912
2913 - import patches from stdin::
2913 - import patches from stdin::
2914
2914
2915 hg import -
2915 hg import -
2916
2916
2917 - attempt to exactly restore an exported changeset (not always
2917 - attempt to exactly restore an exported changeset (not always
2918 possible)::
2918 possible)::
2919
2919
2920 hg import --exact proposed-fix.patch
2920 hg import --exact proposed-fix.patch
2921
2921
2922 - use an external tool to apply a patch which is too fuzzy for
2922 - use an external tool to apply a patch which is too fuzzy for
2923 the default internal tool.
2923 the default internal tool.
2924
2924
2925 hg import --config ui.patch="patch --merge" fuzzy.patch
2925 hg import --config ui.patch="patch --merge" fuzzy.patch
2926
2926
2927 - change the default fuzzing from 2 to a less strict 7
2927 - change the default fuzzing from 2 to a less strict 7
2928
2928
2929 hg import --config ui.fuzz=7 fuzz.patch
2929 hg import --config ui.fuzz=7 fuzz.patch
2930
2930
2931 Returns 0 on success, 1 on partial success (see --partial).
2931 Returns 0 on success, 1 on partial success (see --partial).
2932 """
2932 """
2933
2933
2934 opts = pycompat.byteskwargs(opts)
2934 opts = pycompat.byteskwargs(opts)
2935 if not patch1:
2935 if not patch1:
2936 raise error.Abort(_('need at least one patch to import'))
2936 raise error.Abort(_('need at least one patch to import'))
2937
2937
2938 patches = (patch1,) + patches
2938 patches = (patch1,) + patches
2939
2939
2940 date = opts.get('date')
2940 date = opts.get('date')
2941 if date:
2941 if date:
2942 opts['date'] = util.parsedate(date)
2942 opts['date'] = util.parsedate(date)
2943
2943
2944 exact = opts.get('exact')
2944 exact = opts.get('exact')
2945 update = not opts.get('bypass')
2945 update = not opts.get('bypass')
2946 if not update and opts.get('no_commit'):
2946 if not update and opts.get('no_commit'):
2947 raise error.Abort(_('cannot use --no-commit with --bypass'))
2947 raise error.Abort(_('cannot use --no-commit with --bypass'))
2948 try:
2948 try:
2949 sim = float(opts.get('similarity') or 0)
2949 sim = float(opts.get('similarity') or 0)
2950 except ValueError:
2950 except ValueError:
2951 raise error.Abort(_('similarity must be a number'))
2951 raise error.Abort(_('similarity must be a number'))
2952 if sim < 0 or sim > 100:
2952 if sim < 0 or sim > 100:
2953 raise error.Abort(_('similarity must be between 0 and 100'))
2953 raise error.Abort(_('similarity must be between 0 and 100'))
2954 if sim and not update:
2954 if sim and not update:
2955 raise error.Abort(_('cannot use --similarity with --bypass'))
2955 raise error.Abort(_('cannot use --similarity with --bypass'))
2956 if exact:
2956 if exact:
2957 if opts.get('edit'):
2957 if opts.get('edit'):
2958 raise error.Abort(_('cannot use --exact with --edit'))
2958 raise error.Abort(_('cannot use --exact with --edit'))
2959 if opts.get('prefix'):
2959 if opts.get('prefix'):
2960 raise error.Abort(_('cannot use --exact with --prefix'))
2960 raise error.Abort(_('cannot use --exact with --prefix'))
2961
2961
2962 base = opts["base"]
2962 base = opts["base"]
2963 wlock = dsguard = lock = tr = None
2963 wlock = dsguard = lock = tr = None
2964 msgs = []
2964 msgs = []
2965 ret = 0
2965 ret = 0
2966
2966
2967
2967
2968 try:
2968 try:
2969 wlock = repo.wlock()
2969 wlock = repo.wlock()
2970
2970
2971 if update:
2971 if update:
2972 cmdutil.checkunfinished(repo)
2972 cmdutil.checkunfinished(repo)
2973 if (exact or not opts.get('force')):
2973 if (exact or not opts.get('force')):
2974 cmdutil.bailifchanged(repo)
2974 cmdutil.bailifchanged(repo)
2975
2975
2976 if not opts.get('no_commit'):
2976 if not opts.get('no_commit'):
2977 lock = repo.lock()
2977 lock = repo.lock()
2978 tr = repo.transaction('import')
2978 tr = repo.transaction('import')
2979 else:
2979 else:
2980 dsguard = dirstateguard.dirstateguard(repo, 'import')
2980 dsguard = dirstateguard.dirstateguard(repo, 'import')
2981 parents = repo[None].parents()
2981 parents = repo[None].parents()
2982 for patchurl in patches:
2982 for patchurl in patches:
2983 if patchurl == '-':
2983 if patchurl == '-':
2984 ui.status(_('applying patch from stdin\n'))
2984 ui.status(_('applying patch from stdin\n'))
2985 patchfile = ui.fin
2985 patchfile = ui.fin
2986 patchurl = 'stdin' # for error message
2986 patchurl = 'stdin' # for error message
2987 else:
2987 else:
2988 patchurl = os.path.join(base, patchurl)
2988 patchurl = os.path.join(base, patchurl)
2989 ui.status(_('applying %s\n') % patchurl)
2989 ui.status(_('applying %s\n') % patchurl)
2990 patchfile = hg.openpath(ui, patchurl)
2990 patchfile = hg.openpath(ui, patchurl)
2991
2991
2992 haspatch = False
2992 haspatch = False
2993 for hunk in patch.split(patchfile):
2993 for hunk in patch.split(patchfile):
2994 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
2994 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
2995 parents, opts,
2995 parents, opts,
2996 msgs, hg.clean)
2996 msgs, hg.clean)
2997 if msg:
2997 if msg:
2998 haspatch = True
2998 haspatch = True
2999 ui.note(msg + '\n')
2999 ui.note(msg + '\n')
3000 if update or exact:
3000 if update or exact:
3001 parents = repo[None].parents()
3001 parents = repo[None].parents()
3002 else:
3002 else:
3003 parents = [repo[node]]
3003 parents = [repo[node]]
3004 if rej:
3004 if rej:
3005 ui.write_err(_("patch applied partially\n"))
3005 ui.write_err(_("patch applied partially\n"))
3006 ui.write_err(_("(fix the .rej files and run "
3006 ui.write_err(_("(fix the .rej files and run "
3007 "`hg commit --amend`)\n"))
3007 "`hg commit --amend`)\n"))
3008 ret = 1
3008 ret = 1
3009 break
3009 break
3010
3010
3011 if not haspatch:
3011 if not haspatch:
3012 raise error.Abort(_('%s: no diffs found') % patchurl)
3012 raise error.Abort(_('%s: no diffs found') % patchurl)
3013
3013
3014 if tr:
3014 if tr:
3015 tr.close()
3015 tr.close()
3016 if msgs:
3016 if msgs:
3017 repo.savecommitmessage('\n* * *\n'.join(msgs))
3017 repo.savecommitmessage('\n* * *\n'.join(msgs))
3018 if dsguard:
3018 if dsguard:
3019 dsguard.close()
3019 dsguard.close()
3020 return ret
3020 return ret
3021 finally:
3021 finally:
3022 if tr:
3022 if tr:
3023 tr.release()
3023 tr.release()
3024 release(lock, dsguard, wlock)
3024 release(lock, dsguard, wlock)
3025
3025
3026 @command('incoming|in',
3026 @command('incoming|in',
3027 [('f', 'force', None,
3027 [('f', 'force', None,
3028 _('run even if remote repository is unrelated')),
3028 _('run even if remote repository is unrelated')),
3029 ('n', 'newest-first', None, _('show newest record first')),
3029 ('n', 'newest-first', None, _('show newest record first')),
3030 ('', 'bundle', '',
3030 ('', 'bundle', '',
3031 _('file to store the bundles into'), _('FILE')),
3031 _('file to store the bundles into'), _('FILE')),
3032 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3032 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3033 ('B', 'bookmarks', False, _("compare bookmarks")),
3033 ('B', 'bookmarks', False, _("compare bookmarks")),
3034 ('b', 'branch', [],
3034 ('b', 'branch', [],
3035 _('a specific branch you would like to pull'), _('BRANCH')),
3035 _('a specific branch you would like to pull'), _('BRANCH')),
3036 ] + logopts + remoteopts + subrepoopts,
3036 ] + logopts + remoteopts + subrepoopts,
3037 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3037 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3038 def incoming(ui, repo, source="default", **opts):
3038 def incoming(ui, repo, source="default", **opts):
3039 """show new changesets found in source
3039 """show new changesets found in source
3040
3040
3041 Show new changesets found in the specified path/URL or the default
3041 Show new changesets found in the specified path/URL or the default
3042 pull location. These are the changesets that would have been pulled
3042 pull location. These are the changesets that would have been pulled
3043 if a pull at the time you issued this command.
3043 if a pull at the time you issued this command.
3044
3044
3045 See pull for valid source format details.
3045 See pull for valid source format details.
3046
3046
3047 .. container:: verbose
3047 .. container:: verbose
3048
3048
3049 With -B/--bookmarks, the result of bookmark comparison between
3049 With -B/--bookmarks, the result of bookmark comparison between
3050 local and remote repositories is displayed. With -v/--verbose,
3050 local and remote repositories is displayed. With -v/--verbose,
3051 status is also displayed for each bookmark like below::
3051 status is also displayed for each bookmark like below::
3052
3052
3053 BM1 01234567890a added
3053 BM1 01234567890a added
3054 BM2 1234567890ab advanced
3054 BM2 1234567890ab advanced
3055 BM3 234567890abc diverged
3055 BM3 234567890abc diverged
3056 BM4 34567890abcd changed
3056 BM4 34567890abcd changed
3057
3057
3058 The action taken locally when pulling depends on the
3058 The action taken locally when pulling depends on the
3059 status of each bookmark:
3059 status of each bookmark:
3060
3060
3061 :``added``: pull will create it
3061 :``added``: pull will create it
3062 :``advanced``: pull will update it
3062 :``advanced``: pull will update it
3063 :``diverged``: pull will create a divergent bookmark
3063 :``diverged``: pull will create a divergent bookmark
3064 :``changed``: result depends on remote changesets
3064 :``changed``: result depends on remote changesets
3065
3065
3066 From the point of view of pulling behavior, bookmark
3066 From the point of view of pulling behavior, bookmark
3067 existing only in the remote repository are treated as ``added``,
3067 existing only in the remote repository are treated as ``added``,
3068 even if it is in fact locally deleted.
3068 even if it is in fact locally deleted.
3069
3069
3070 .. container:: verbose
3070 .. container:: verbose
3071
3071
3072 For remote repository, using --bundle avoids downloading the
3072 For remote repository, using --bundle avoids downloading the
3073 changesets twice if the incoming is followed by a pull.
3073 changesets twice if the incoming is followed by a pull.
3074
3074
3075 Examples:
3075 Examples:
3076
3076
3077 - show incoming changes with patches and full description::
3077 - show incoming changes with patches and full description::
3078
3078
3079 hg incoming -vp
3079 hg incoming -vp
3080
3080
3081 - show incoming changes excluding merges, store a bundle::
3081 - show incoming changes excluding merges, store a bundle::
3082
3082
3083 hg in -vpM --bundle incoming.hg
3083 hg in -vpM --bundle incoming.hg
3084 hg pull incoming.hg
3084 hg pull incoming.hg
3085
3085
3086 - briefly list changes inside a bundle::
3086 - briefly list changes inside a bundle::
3087
3087
3088 hg in changes.hg -T "{desc|firstline}\\n"
3088 hg in changes.hg -T "{desc|firstline}\\n"
3089
3089
3090 Returns 0 if there are incoming changes, 1 otherwise.
3090 Returns 0 if there are incoming changes, 1 otherwise.
3091 """
3091 """
3092 opts = pycompat.byteskwargs(opts)
3092 opts = pycompat.byteskwargs(opts)
3093 if opts.get('graph'):
3093 if opts.get('graph'):
3094 cmdutil.checkunsupportedgraphflags([], opts)
3094 cmdutil.checkunsupportedgraphflags([], opts)
3095 def display(other, chlist, displayer):
3095 def display(other, chlist, displayer):
3096 revdag = cmdutil.graphrevs(other, chlist, opts)
3096 revdag = cmdutil.graphrevs(other, chlist, opts)
3097 cmdutil.displaygraph(ui, repo, revdag, displayer,
3097 cmdutil.displaygraph(ui, repo, revdag, displayer,
3098 graphmod.asciiedges)
3098 graphmod.asciiedges)
3099
3099
3100 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3100 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3101 return 0
3101 return 0
3102
3102
3103 if opts.get('bundle') and opts.get('subrepos'):
3103 if opts.get('bundle') and opts.get('subrepos'):
3104 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3104 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3105
3105
3106 if opts.get('bookmarks'):
3106 if opts.get('bookmarks'):
3107 source, branches = hg.parseurl(ui.expandpath(source),
3107 source, branches = hg.parseurl(ui.expandpath(source),
3108 opts.get('branch'))
3108 opts.get('branch'))
3109 other = hg.peer(repo, opts, source)
3109 other = hg.peer(repo, opts, source)
3110 if 'bookmarks' not in other.listkeys('namespaces'):
3110 if 'bookmarks' not in other.listkeys('namespaces'):
3111 ui.warn(_("remote doesn't support bookmarks\n"))
3111 ui.warn(_("remote doesn't support bookmarks\n"))
3112 return 0
3112 return 0
3113 ui.pager('incoming')
3113 ui.pager('incoming')
3114 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3114 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3115 return bookmarks.incoming(ui, repo, other)
3115 return bookmarks.incoming(ui, repo, other)
3116
3116
3117 repo._subtoppath = ui.expandpath(source)
3117 repo._subtoppath = ui.expandpath(source)
3118 try:
3118 try:
3119 return hg.incoming(ui, repo, source, opts)
3119 return hg.incoming(ui, repo, source, opts)
3120 finally:
3120 finally:
3121 del repo._subtoppath
3121 del repo._subtoppath
3122
3122
3123
3123
3124 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3124 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3125 norepo=True)
3125 norepo=True)
3126 def init(ui, dest=".", **opts):
3126 def init(ui, dest=".", **opts):
3127 """create a new repository in the given directory
3127 """create a new repository in the given directory
3128
3128
3129 Initialize a new repository in the given directory. If the given
3129 Initialize a new repository in the given directory. If the given
3130 directory does not exist, it will be created.
3130 directory does not exist, it will be created.
3131
3131
3132 If no directory is given, the current directory is used.
3132 If no directory is given, the current directory is used.
3133
3133
3134 It is possible to specify an ``ssh://`` URL as the destination.
3134 It is possible to specify an ``ssh://`` URL as the destination.
3135 See :hg:`help urls` for more information.
3135 See :hg:`help urls` for more information.
3136
3136
3137 Returns 0 on success.
3137 Returns 0 on success.
3138 """
3138 """
3139 opts = pycompat.byteskwargs(opts)
3139 opts = pycompat.byteskwargs(opts)
3140 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3140 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3141
3141
3142 @command('locate',
3142 @command('locate',
3143 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3143 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3144 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3144 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3145 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3145 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3146 ] + walkopts,
3146 ] + walkopts,
3147 _('[OPTION]... [PATTERN]...'))
3147 _('[OPTION]... [PATTERN]...'))
3148 def locate(ui, repo, *pats, **opts):
3148 def locate(ui, repo, *pats, **opts):
3149 """locate files matching specific patterns (DEPRECATED)
3149 """locate files matching specific patterns (DEPRECATED)
3150
3150
3151 Print files under Mercurial control in the working directory whose
3151 Print files under Mercurial control in the working directory whose
3152 names match the given patterns.
3152 names match the given patterns.
3153
3153
3154 By default, this command searches all directories in the working
3154 By default, this command searches all directories in the working
3155 directory. To search just the current directory and its
3155 directory. To search just the current directory and its
3156 subdirectories, use "--include .".
3156 subdirectories, use "--include .".
3157
3157
3158 If no patterns are given to match, this command prints the names
3158 If no patterns are given to match, this command prints the names
3159 of all files under Mercurial control in the working directory.
3159 of all files under Mercurial control in the working directory.
3160
3160
3161 If you want to feed the output of this command into the "xargs"
3161 If you want to feed the output of this command into the "xargs"
3162 command, use the -0 option to both this command and "xargs". This
3162 command, use the -0 option to both this command and "xargs". This
3163 will avoid the problem of "xargs" treating single filenames that
3163 will avoid the problem of "xargs" treating single filenames that
3164 contain whitespace as multiple filenames.
3164 contain whitespace as multiple filenames.
3165
3165
3166 See :hg:`help files` for a more versatile command.
3166 See :hg:`help files` for a more versatile command.
3167
3167
3168 Returns 0 if a match is found, 1 otherwise.
3168 Returns 0 if a match is found, 1 otherwise.
3169 """
3169 """
3170 opts = pycompat.byteskwargs(opts)
3170 opts = pycompat.byteskwargs(opts)
3171 if opts.get('print0'):
3171 if opts.get('print0'):
3172 end = '\0'
3172 end = '\0'
3173 else:
3173 else:
3174 end = '\n'
3174 end = '\n'
3175 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3175 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3176
3176
3177 ret = 1
3177 ret = 1
3178 ctx = repo[rev]
3178 ctx = repo[rev]
3179 m = scmutil.match(ctx, pats, opts, default='relglob',
3179 m = scmutil.match(ctx, pats, opts, default='relglob',
3180 badfn=lambda x, y: False)
3180 badfn=lambda x, y: False)
3181
3181
3182 ui.pager('locate')
3182 ui.pager('locate')
3183 for abs in ctx.matches(m):
3183 for abs in ctx.matches(m):
3184 if opts.get('fullpath'):
3184 if opts.get('fullpath'):
3185 ui.write(repo.wjoin(abs), end)
3185 ui.write(repo.wjoin(abs), end)
3186 else:
3186 else:
3187 ui.write(((pats and m.rel(abs)) or abs), end)
3187 ui.write(((pats and m.rel(abs)) or abs), end)
3188 ret = 0
3188 ret = 0
3189
3189
3190 return ret
3190 return ret
3191
3191
3192 @command('^log|history',
3192 @command('^log|history',
3193 [('f', 'follow', None,
3193 [('f', 'follow', None,
3194 _('follow changeset history, or file history across copies and renames')),
3194 _('follow changeset history, or file history across copies and renames')),
3195 ('', 'follow-first', None,
3195 ('', 'follow-first', None,
3196 _('only follow the first parent of merge changesets (DEPRECATED)')),
3196 _('only follow the first parent of merge changesets (DEPRECATED)')),
3197 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3197 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3198 ('C', 'copies', None, _('show copied files')),
3198 ('C', 'copies', None, _('show copied files')),
3199 ('k', 'keyword', [],
3199 ('k', 'keyword', [],
3200 _('do case-insensitive search for a given text'), _('TEXT')),
3200 _('do case-insensitive search for a given text'), _('TEXT')),
3201 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3201 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3202 ('', 'removed', None, _('include revisions where files were removed')),
3202 ('', 'removed', None, _('include revisions where files were removed')),
3203 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3203 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3204 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3204 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3205 ('', 'only-branch', [],
3205 ('', 'only-branch', [],
3206 _('show only changesets within the given named branch (DEPRECATED)'),
3206 _('show only changesets within the given named branch (DEPRECATED)'),
3207 _('BRANCH')),
3207 _('BRANCH')),
3208 ('b', 'branch', [],
3208 ('b', 'branch', [],
3209 _('show changesets within the given named branch'), _('BRANCH')),
3209 _('show changesets within the given named branch'), _('BRANCH')),
3210 ('P', 'prune', [],
3210 ('P', 'prune', [],
3211 _('do not display revision or any of its ancestors'), _('REV')),
3211 _('do not display revision or any of its ancestors'), _('REV')),
3212 ] + logopts + walkopts,
3212 ] + logopts + walkopts,
3213 _('[OPTION]... [FILE]'),
3213 _('[OPTION]... [FILE]'),
3214 inferrepo=True)
3214 inferrepo=True)
3215 def log(ui, repo, *pats, **opts):
3215 def log(ui, repo, *pats, **opts):
3216 """show revision history of entire repository or files
3216 """show revision history of entire repository or files
3217
3217
3218 Print the revision history of the specified files or the entire
3218 Print the revision history of the specified files or the entire
3219 project.
3219 project.
3220
3220
3221 If no revision range is specified, the default is ``tip:0`` unless
3221 If no revision range is specified, the default is ``tip:0`` unless
3222 --follow is set, in which case the working directory parent is
3222 --follow is set, in which case the working directory parent is
3223 used as the starting revision.
3223 used as the starting revision.
3224
3224
3225 File history is shown without following rename or copy history of
3225 File history is shown without following rename or copy history of
3226 files. Use -f/--follow with a filename to follow history across
3226 files. Use -f/--follow with a filename to follow history across
3227 renames and copies. --follow without a filename will only show
3227 renames and copies. --follow without a filename will only show
3228 ancestors or descendants of the starting revision.
3228 ancestors or descendants of the starting revision.
3229
3229
3230 By default this command prints revision number and changeset id,
3230 By default this command prints revision number and changeset id,
3231 tags, non-trivial parents, user, date and time, and a summary for
3231 tags, non-trivial parents, user, date and time, and a summary for
3232 each commit. When the -v/--verbose switch is used, the list of
3232 each commit. When the -v/--verbose switch is used, the list of
3233 changed files and full commit message are shown.
3233 changed files and full commit message are shown.
3234
3234
3235 With --graph the revisions are shown as an ASCII art DAG with the most
3235 With --graph the revisions are shown as an ASCII art DAG with the most
3236 recent changeset at the top.
3236 recent changeset at the top.
3237 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
3237 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
3238 and '+' represents a fork where the changeset from the lines below is a
3238 and '+' represents a fork where the changeset from the lines below is a
3239 parent of the 'o' merge on the same line.
3239 parent of the 'o' merge on the same line.
3240 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3240 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3241 of a '|' indicates one or more revisions in a path are omitted.
3241 of a '|' indicates one or more revisions in a path are omitted.
3242
3242
3243 .. note::
3243 .. note::
3244
3244
3245 :hg:`log --patch` may generate unexpected diff output for merge
3245 :hg:`log --patch` may generate unexpected diff output for merge
3246 changesets, as it will only compare the merge changeset against
3246 changesets, as it will only compare the merge changeset against
3247 its first parent. Also, only files different from BOTH parents
3247 its first parent. Also, only files different from BOTH parents
3248 will appear in files:.
3248 will appear in files:.
3249
3249
3250 .. note::
3250 .. note::
3251
3251
3252 For performance reasons, :hg:`log FILE` may omit duplicate changes
3252 For performance reasons, :hg:`log FILE` may omit duplicate changes
3253 made on branches and will not show removals or mode changes. To
3253 made on branches and will not show removals or mode changes. To
3254 see all such changes, use the --removed switch.
3254 see all such changes, use the --removed switch.
3255
3255
3256 .. container:: verbose
3256 .. container:: verbose
3257
3257
3258 Some examples:
3258 Some examples:
3259
3259
3260 - changesets with full descriptions and file lists::
3260 - changesets with full descriptions and file lists::
3261
3261
3262 hg log -v
3262 hg log -v
3263
3263
3264 - changesets ancestral to the working directory::
3264 - changesets ancestral to the working directory::
3265
3265
3266 hg log -f
3266 hg log -f
3267
3267
3268 - last 10 commits on the current branch::
3268 - last 10 commits on the current branch::
3269
3269
3270 hg log -l 10 -b .
3270 hg log -l 10 -b .
3271
3271
3272 - changesets showing all modifications of a file, including removals::
3272 - changesets showing all modifications of a file, including removals::
3273
3273
3274 hg log --removed file.c
3274 hg log --removed file.c
3275
3275
3276 - all changesets that touch a directory, with diffs, excluding merges::
3276 - all changesets that touch a directory, with diffs, excluding merges::
3277
3277
3278 hg log -Mp lib/
3278 hg log -Mp lib/
3279
3279
3280 - all revision numbers that match a keyword::
3280 - all revision numbers that match a keyword::
3281
3281
3282 hg log -k bug --template "{rev}\\n"
3282 hg log -k bug --template "{rev}\\n"
3283
3283
3284 - the full hash identifier of the working directory parent::
3284 - the full hash identifier of the working directory parent::
3285
3285
3286 hg log -r . --template "{node}\\n"
3286 hg log -r . --template "{node}\\n"
3287
3287
3288 - list available log templates::
3288 - list available log templates::
3289
3289
3290 hg log -T list
3290 hg log -T list
3291
3291
3292 - check if a given changeset is included in a tagged release::
3292 - check if a given changeset is included in a tagged release::
3293
3293
3294 hg log -r "a21ccf and ancestor(1.9)"
3294 hg log -r "a21ccf and ancestor(1.9)"
3295
3295
3296 - find all changesets by some user in a date range::
3296 - find all changesets by some user in a date range::
3297
3297
3298 hg log -k alice -d "may 2008 to jul 2008"
3298 hg log -k alice -d "may 2008 to jul 2008"
3299
3299
3300 - summary of all changesets after the last tag::
3300 - summary of all changesets after the last tag::
3301
3301
3302 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3302 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3303
3303
3304 See :hg:`help dates` for a list of formats valid for -d/--date.
3304 See :hg:`help dates` for a list of formats valid for -d/--date.
3305
3305
3306 See :hg:`help revisions` for more about specifying and ordering
3306 See :hg:`help revisions` for more about specifying and ordering
3307 revisions.
3307 revisions.
3308
3308
3309 See :hg:`help templates` for more about pre-packaged styles and
3309 See :hg:`help templates` for more about pre-packaged styles and
3310 specifying custom templates.
3310 specifying custom templates.
3311
3311
3312 Returns 0 on success.
3312 Returns 0 on success.
3313
3313
3314 """
3314 """
3315 opts = pycompat.byteskwargs(opts)
3315 opts = pycompat.byteskwargs(opts)
3316 if opts.get('follow') and opts.get('rev'):
3316 if opts.get('follow') and opts.get('rev'):
3317 opts['rev'] = [revsetlang.formatspec('reverse(::%lr)', opts.get('rev'))]
3317 opts['rev'] = [revsetlang.formatspec('reverse(::%lr)', opts.get('rev'))]
3318 del opts['follow']
3318 del opts['follow']
3319
3319
3320 if opts.get('graph'):
3320 if opts.get('graph'):
3321 return cmdutil.graphlog(ui, repo, pats, opts)
3321 return cmdutil.graphlog(ui, repo, pats, opts)
3322
3322
3323 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
3323 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
3324 limit = cmdutil.loglimit(opts)
3324 limit = cmdutil.loglimit(opts)
3325 count = 0
3325 count = 0
3326
3326
3327 getrenamed = None
3327 getrenamed = None
3328 if opts.get('copies'):
3328 if opts.get('copies'):
3329 endrev = None
3329 endrev = None
3330 if opts.get('rev'):
3330 if opts.get('rev'):
3331 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
3331 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
3332 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3332 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3333
3333
3334 ui.pager('log')
3334 ui.pager('log')
3335 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3335 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3336 for rev in revs:
3336 for rev in revs:
3337 if count == limit:
3337 if count == limit:
3338 break
3338 break
3339 ctx = repo[rev]
3339 ctx = repo[rev]
3340 copies = None
3340 copies = None
3341 if getrenamed is not None and rev:
3341 if getrenamed is not None and rev:
3342 copies = []
3342 copies = []
3343 for fn in ctx.files():
3343 for fn in ctx.files():
3344 rename = getrenamed(fn, rev)
3344 rename = getrenamed(fn, rev)
3345 if rename:
3345 if rename:
3346 copies.append((fn, rename[0]))
3346 copies.append((fn, rename[0]))
3347 if filematcher:
3347 if filematcher:
3348 revmatchfn = filematcher(ctx.rev())
3348 revmatchfn = filematcher(ctx.rev())
3349 else:
3349 else:
3350 revmatchfn = None
3350 revmatchfn = None
3351 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3351 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3352 if displayer.flush(ctx):
3352 if displayer.flush(ctx):
3353 count += 1
3353 count += 1
3354
3354
3355 displayer.close()
3355 displayer.close()
3356
3356
3357 @command('manifest',
3357 @command('manifest',
3358 [('r', 'rev', '', _('revision to display'), _('REV')),
3358 [('r', 'rev', '', _('revision to display'), _('REV')),
3359 ('', 'all', False, _("list files from all revisions"))]
3359 ('', 'all', False, _("list files from all revisions"))]
3360 + formatteropts,
3360 + formatteropts,
3361 _('[-r REV]'))
3361 _('[-r REV]'))
3362 def manifest(ui, repo, node=None, rev=None, **opts):
3362 def manifest(ui, repo, node=None, rev=None, **opts):
3363 """output the current or given revision of the project manifest
3363 """output the current or given revision of the project manifest
3364
3364
3365 Print a list of version controlled files for the given revision.
3365 Print a list of version controlled files for the given revision.
3366 If no revision is given, the first parent of the working directory
3366 If no revision is given, the first parent of the working directory
3367 is used, or the null revision if no revision is checked out.
3367 is used, or the null revision if no revision is checked out.
3368
3368
3369 With -v, print file permissions, symlink and executable bits.
3369 With -v, print file permissions, symlink and executable bits.
3370 With --debug, print file revision hashes.
3370 With --debug, print file revision hashes.
3371
3371
3372 If option --all is specified, the list of all files from all revisions
3372 If option --all is specified, the list of all files from all revisions
3373 is printed. This includes deleted and renamed files.
3373 is printed. This includes deleted and renamed files.
3374
3374
3375 Returns 0 on success.
3375 Returns 0 on success.
3376 """
3376 """
3377 opts = pycompat.byteskwargs(opts)
3377 opts = pycompat.byteskwargs(opts)
3378 fm = ui.formatter('manifest', opts)
3378 fm = ui.formatter('manifest', opts)
3379
3379
3380 if opts.get('all'):
3380 if opts.get('all'):
3381 if rev or node:
3381 if rev or node:
3382 raise error.Abort(_("can't specify a revision with --all"))
3382 raise error.Abort(_("can't specify a revision with --all"))
3383
3383
3384 res = []
3384 res = []
3385 prefix = "data/"
3385 prefix = "data/"
3386 suffix = ".i"
3386 suffix = ".i"
3387 plen = len(prefix)
3387 plen = len(prefix)
3388 slen = len(suffix)
3388 slen = len(suffix)
3389 with repo.lock():
3389 with repo.lock():
3390 for fn, b, size in repo.store.datafiles():
3390 for fn, b, size in repo.store.datafiles():
3391 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3391 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3392 res.append(fn[plen:-slen])
3392 res.append(fn[plen:-slen])
3393 ui.pager('manifest')
3393 ui.pager('manifest')
3394 for f in res:
3394 for f in res:
3395 fm.startitem()
3395 fm.startitem()
3396 fm.write("path", '%s\n', f)
3396 fm.write("path", '%s\n', f)
3397 fm.end()
3397 fm.end()
3398 return
3398 return
3399
3399
3400 if rev and node:
3400 if rev and node:
3401 raise error.Abort(_("please specify just one revision"))
3401 raise error.Abort(_("please specify just one revision"))
3402
3402
3403 if not node:
3403 if not node:
3404 node = rev
3404 node = rev
3405
3405
3406 char = {'l': '@', 'x': '*', '': ''}
3406 char = {'l': '@', 'x': '*', '': ''}
3407 mode = {'l': '644', 'x': '755', '': '644'}
3407 mode = {'l': '644', 'x': '755', '': '644'}
3408 ctx = scmutil.revsingle(repo, node)
3408 ctx = scmutil.revsingle(repo, node)
3409 mf = ctx.manifest()
3409 mf = ctx.manifest()
3410 ui.pager('manifest')
3410 ui.pager('manifest')
3411 for f in ctx:
3411 for f in ctx:
3412 fm.startitem()
3412 fm.startitem()
3413 fl = ctx[f].flags()
3413 fl = ctx[f].flags()
3414 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3414 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3415 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3415 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3416 fm.write('path', '%s\n', f)
3416 fm.write('path', '%s\n', f)
3417 fm.end()
3417 fm.end()
3418
3418
3419 @command('^merge',
3419 @command('^merge',
3420 [('f', 'force', None,
3420 [('f', 'force', None,
3421 _('force a merge including outstanding changes (DEPRECATED)')),
3421 _('force a merge including outstanding changes (DEPRECATED)')),
3422 ('r', 'rev', '', _('revision to merge'), _('REV')),
3422 ('r', 'rev', '', _('revision to merge'), _('REV')),
3423 ('P', 'preview', None,
3423 ('P', 'preview', None,
3424 _('review revisions to merge (no merge is performed)'))
3424 _('review revisions to merge (no merge is performed)'))
3425 ] + mergetoolopts,
3425 ] + mergetoolopts,
3426 _('[-P] [[-r] REV]'))
3426 _('[-P] [[-r] REV]'))
3427 def merge(ui, repo, node=None, **opts):
3427 def merge(ui, repo, node=None, **opts):
3428 """merge another revision into working directory
3428 """merge another revision into working directory
3429
3429
3430 The current working directory is updated with all changes made in
3430 The current working directory is updated with all changes made in
3431 the requested revision since the last common predecessor revision.
3431 the requested revision since the last common predecessor revision.
3432
3432
3433 Files that changed between either parent are marked as changed for
3433 Files that changed between either parent are marked as changed for
3434 the next commit and a commit must be performed before any further
3434 the next commit and a commit must be performed before any further
3435 updates to the repository are allowed. The next commit will have
3435 updates to the repository are allowed. The next commit will have
3436 two parents.
3436 two parents.
3437
3437
3438 ``--tool`` can be used to specify the merge tool used for file
3438 ``--tool`` can be used to specify the merge tool used for file
3439 merges. It overrides the HGMERGE environment variable and your
3439 merges. It overrides the HGMERGE environment variable and your
3440 configuration files. See :hg:`help merge-tools` for options.
3440 configuration files. See :hg:`help merge-tools` for options.
3441
3441
3442 If no revision is specified, the working directory's parent is a
3442 If no revision is specified, the working directory's parent is a
3443 head revision, and the current branch contains exactly one other
3443 head revision, and the current branch contains exactly one other
3444 head, the other head is merged with by default. Otherwise, an
3444 head, the other head is merged with by default. Otherwise, an
3445 explicit revision with which to merge with must be provided.
3445 explicit revision with which to merge with must be provided.
3446
3446
3447 See :hg:`help resolve` for information on handling file conflicts.
3447 See :hg:`help resolve` for information on handling file conflicts.
3448
3448
3449 To undo an uncommitted merge, use :hg:`update --clean .` which
3449 To undo an uncommitted merge, use :hg:`update --clean .` which
3450 will check out a clean copy of the original merge parent, losing
3450 will check out a clean copy of the original merge parent, losing
3451 all changes.
3451 all changes.
3452
3452
3453 Returns 0 on success, 1 if there are unresolved files.
3453 Returns 0 on success, 1 if there are unresolved files.
3454 """
3454 """
3455
3455
3456 opts = pycompat.byteskwargs(opts)
3456 opts = pycompat.byteskwargs(opts)
3457 if opts.get('rev') and node:
3457 if opts.get('rev') and node:
3458 raise error.Abort(_("please specify just one revision"))
3458 raise error.Abort(_("please specify just one revision"))
3459 if not node:
3459 if not node:
3460 node = opts.get('rev')
3460 node = opts.get('rev')
3461
3461
3462 if node:
3462 if node:
3463 node = scmutil.revsingle(repo, node).node()
3463 node = scmutil.revsingle(repo, node).node()
3464
3464
3465 if not node:
3465 if not node:
3466 node = repo[destutil.destmerge(repo)].node()
3466 node = repo[destutil.destmerge(repo)].node()
3467
3467
3468 if opts.get('preview'):
3468 if opts.get('preview'):
3469 # find nodes that are ancestors of p2 but not of p1
3469 # find nodes that are ancestors of p2 but not of p1
3470 p1 = repo.lookup('.')
3470 p1 = repo.lookup('.')
3471 p2 = repo.lookup(node)
3471 p2 = repo.lookup(node)
3472 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3472 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3473
3473
3474 displayer = cmdutil.show_changeset(ui, repo, opts)
3474 displayer = cmdutil.show_changeset(ui, repo, opts)
3475 for node in nodes:
3475 for node in nodes:
3476 displayer.show(repo[node])
3476 displayer.show(repo[node])
3477 displayer.close()
3477 displayer.close()
3478 return 0
3478 return 0
3479
3479
3480 try:
3480 try:
3481 # ui.forcemerge is an internal variable, do not document
3481 # ui.forcemerge is an internal variable, do not document
3482 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
3482 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
3483 force = opts.get('force')
3483 force = opts.get('force')
3484 labels = ['working copy', 'merge rev']
3484 labels = ['working copy', 'merge rev']
3485 return hg.merge(repo, node, force=force, mergeforce=force,
3485 return hg.merge(repo, node, force=force, mergeforce=force,
3486 labels=labels)
3486 labels=labels)
3487 finally:
3487 finally:
3488 ui.setconfig('ui', 'forcemerge', '', 'merge')
3488 ui.setconfig('ui', 'forcemerge', '', 'merge')
3489
3489
3490 @command('outgoing|out',
3490 @command('outgoing|out',
3491 [('f', 'force', None, _('run even when the destination is unrelated')),
3491 [('f', 'force', None, _('run even when the destination is unrelated')),
3492 ('r', 'rev', [],
3492 ('r', 'rev', [],
3493 _('a changeset intended to be included in the destination'), _('REV')),
3493 _('a changeset intended to be included in the destination'), _('REV')),
3494 ('n', 'newest-first', None, _('show newest record first')),
3494 ('n', 'newest-first', None, _('show newest record first')),
3495 ('B', 'bookmarks', False, _('compare bookmarks')),
3495 ('B', 'bookmarks', False, _('compare bookmarks')),
3496 ('b', 'branch', [], _('a specific branch you would like to push'),
3496 ('b', 'branch', [], _('a specific branch you would like to push'),
3497 _('BRANCH')),
3497 _('BRANCH')),
3498 ] + logopts + remoteopts + subrepoopts,
3498 ] + logopts + remoteopts + subrepoopts,
3499 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3499 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3500 def outgoing(ui, repo, dest=None, **opts):
3500 def outgoing(ui, repo, dest=None, **opts):
3501 """show changesets not found in the destination
3501 """show changesets not found in the destination
3502
3502
3503 Show changesets not found in the specified destination repository
3503 Show changesets not found in the specified destination repository
3504 or the default push location. These are the changesets that would
3504 or the default push location. These are the changesets that would
3505 be pushed if a push was requested.
3505 be pushed if a push was requested.
3506
3506
3507 See pull for details of valid destination formats.
3507 See pull for details of valid destination formats.
3508
3508
3509 .. container:: verbose
3509 .. container:: verbose
3510
3510
3511 With -B/--bookmarks, the result of bookmark comparison between
3511 With -B/--bookmarks, the result of bookmark comparison between
3512 local and remote repositories is displayed. With -v/--verbose,
3512 local and remote repositories is displayed. With -v/--verbose,
3513 status is also displayed for each bookmark like below::
3513 status is also displayed for each bookmark like below::
3514
3514
3515 BM1 01234567890a added
3515 BM1 01234567890a added
3516 BM2 deleted
3516 BM2 deleted
3517 BM3 234567890abc advanced
3517 BM3 234567890abc advanced
3518 BM4 34567890abcd diverged
3518 BM4 34567890abcd diverged
3519 BM5 4567890abcde changed
3519 BM5 4567890abcde changed
3520
3520
3521 The action taken when pushing depends on the
3521 The action taken when pushing depends on the
3522 status of each bookmark:
3522 status of each bookmark:
3523
3523
3524 :``added``: push with ``-B`` will create it
3524 :``added``: push with ``-B`` will create it
3525 :``deleted``: push with ``-B`` will delete it
3525 :``deleted``: push with ``-B`` will delete it
3526 :``advanced``: push will update it
3526 :``advanced``: push will update it
3527 :``diverged``: push with ``-B`` will update it
3527 :``diverged``: push with ``-B`` will update it
3528 :``changed``: push with ``-B`` will update it
3528 :``changed``: push with ``-B`` will update it
3529
3529
3530 From the point of view of pushing behavior, bookmarks
3530 From the point of view of pushing behavior, bookmarks
3531 existing only in the remote repository are treated as
3531 existing only in the remote repository are treated as
3532 ``deleted``, even if it is in fact added remotely.
3532 ``deleted``, even if it is in fact added remotely.
3533
3533
3534 Returns 0 if there are outgoing changes, 1 otherwise.
3534 Returns 0 if there are outgoing changes, 1 otherwise.
3535 """
3535 """
3536 opts = pycompat.byteskwargs(opts)
3536 opts = pycompat.byteskwargs(opts)
3537 if opts.get('graph'):
3537 if opts.get('graph'):
3538 cmdutil.checkunsupportedgraphflags([], opts)
3538 cmdutil.checkunsupportedgraphflags([], opts)
3539 o, other = hg._outgoing(ui, repo, dest, opts)
3539 o, other = hg._outgoing(ui, repo, dest, opts)
3540 if not o:
3540 if not o:
3541 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3541 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3542 return
3542 return
3543
3543
3544 revdag = cmdutil.graphrevs(repo, o, opts)
3544 revdag = cmdutil.graphrevs(repo, o, opts)
3545 ui.pager('outgoing')
3545 ui.pager('outgoing')
3546 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3546 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
3547 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
3547 cmdutil.displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges)
3548 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3548 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3549 return 0
3549 return 0
3550
3550
3551 if opts.get('bookmarks'):
3551 if opts.get('bookmarks'):
3552 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3552 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3553 dest, branches = hg.parseurl(dest, opts.get('branch'))
3553 dest, branches = hg.parseurl(dest, opts.get('branch'))
3554 other = hg.peer(repo, opts, dest)
3554 other = hg.peer(repo, opts, dest)
3555 if 'bookmarks' not in other.listkeys('namespaces'):
3555 if 'bookmarks' not in other.listkeys('namespaces'):
3556 ui.warn(_("remote doesn't support bookmarks\n"))
3556 ui.warn(_("remote doesn't support bookmarks\n"))
3557 return 0
3557 return 0
3558 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3558 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3559 ui.pager('outgoing')
3559 ui.pager('outgoing')
3560 return bookmarks.outgoing(ui, repo, other)
3560 return bookmarks.outgoing(ui, repo, other)
3561
3561
3562 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3562 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3563 try:
3563 try:
3564 return hg.outgoing(ui, repo, dest, opts)
3564 return hg.outgoing(ui, repo, dest, opts)
3565 finally:
3565 finally:
3566 del repo._subtoppath
3566 del repo._subtoppath
3567
3567
3568 @command('parents',
3568 @command('parents',
3569 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3569 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3570 ] + templateopts,
3570 ] + templateopts,
3571 _('[-r REV] [FILE]'),
3571 _('[-r REV] [FILE]'),
3572 inferrepo=True)
3572 inferrepo=True)
3573 def parents(ui, repo, file_=None, **opts):
3573 def parents(ui, repo, file_=None, **opts):
3574 """show the parents of the working directory or revision (DEPRECATED)
3574 """show the parents of the working directory or revision (DEPRECATED)
3575
3575
3576 Print the working directory's parent revisions. If a revision is
3576 Print the working directory's parent revisions. If a revision is
3577 given via -r/--rev, the parent of that revision will be printed.
3577 given via -r/--rev, the parent of that revision will be printed.
3578 If a file argument is given, the revision in which the file was
3578 If a file argument is given, the revision in which the file was
3579 last changed (before the working directory revision or the
3579 last changed (before the working directory revision or the
3580 argument to --rev if given) is printed.
3580 argument to --rev if given) is printed.
3581
3581
3582 This command is equivalent to::
3582 This command is equivalent to::
3583
3583
3584 hg log -r "p1()+p2()" or
3584 hg log -r "p1()+p2()" or
3585 hg log -r "p1(REV)+p2(REV)" or
3585 hg log -r "p1(REV)+p2(REV)" or
3586 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
3586 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
3587 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
3587 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
3588
3588
3589 See :hg:`summary` and :hg:`help revsets` for related information.
3589 See :hg:`summary` and :hg:`help revsets` for related information.
3590
3590
3591 Returns 0 on success.
3591 Returns 0 on success.
3592 """
3592 """
3593
3593
3594 opts = pycompat.byteskwargs(opts)
3594 opts = pycompat.byteskwargs(opts)
3595 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3595 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3596
3596
3597 if file_:
3597 if file_:
3598 m = scmutil.match(ctx, (file_,), opts)
3598 m = scmutil.match(ctx, (file_,), opts)
3599 if m.anypats() or len(m.files()) != 1:
3599 if m.anypats() or len(m.files()) != 1:
3600 raise error.Abort(_('can only specify an explicit filename'))
3600 raise error.Abort(_('can only specify an explicit filename'))
3601 file_ = m.files()[0]
3601 file_ = m.files()[0]
3602 filenodes = []
3602 filenodes = []
3603 for cp in ctx.parents():
3603 for cp in ctx.parents():
3604 if not cp:
3604 if not cp:
3605 continue
3605 continue
3606 try:
3606 try:
3607 filenodes.append(cp.filenode(file_))
3607 filenodes.append(cp.filenode(file_))
3608 except error.LookupError:
3608 except error.LookupError:
3609 pass
3609 pass
3610 if not filenodes:
3610 if not filenodes:
3611 raise error.Abort(_("'%s' not found in manifest!") % file_)
3611 raise error.Abort(_("'%s' not found in manifest!") % file_)
3612 p = []
3612 p = []
3613 for fn in filenodes:
3613 for fn in filenodes:
3614 fctx = repo.filectx(file_, fileid=fn)
3614 fctx = repo.filectx(file_, fileid=fn)
3615 p.append(fctx.node())
3615 p.append(fctx.node())
3616 else:
3616 else:
3617 p = [cp.node() for cp in ctx.parents()]
3617 p = [cp.node() for cp in ctx.parents()]
3618
3618
3619 displayer = cmdutil.show_changeset(ui, repo, opts)
3619 displayer = cmdutil.show_changeset(ui, repo, opts)
3620 for n in p:
3620 for n in p:
3621 if n != nullid:
3621 if n != nullid:
3622 displayer.show(repo[n])
3622 displayer.show(repo[n])
3623 displayer.close()
3623 displayer.close()
3624
3624
3625 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True)
3625 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True)
3626 def paths(ui, repo, search=None, **opts):
3626 def paths(ui, repo, search=None, **opts):
3627 """show aliases for remote repositories
3627 """show aliases for remote repositories
3628
3628
3629 Show definition of symbolic path name NAME. If no name is given,
3629 Show definition of symbolic path name NAME. If no name is given,
3630 show definition of all available names.
3630 show definition of all available names.
3631
3631
3632 Option -q/--quiet suppresses all output when searching for NAME
3632 Option -q/--quiet suppresses all output when searching for NAME
3633 and shows only the path names when listing all definitions.
3633 and shows only the path names when listing all definitions.
3634
3634
3635 Path names are defined in the [paths] section of your
3635 Path names are defined in the [paths] section of your
3636 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3636 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3637 repository, ``.hg/hgrc`` is used, too.
3637 repository, ``.hg/hgrc`` is used, too.
3638
3638
3639 The path names ``default`` and ``default-push`` have a special
3639 The path names ``default`` and ``default-push`` have a special
3640 meaning. When performing a push or pull operation, they are used
3640 meaning. When performing a push or pull operation, they are used
3641 as fallbacks if no location is specified on the command-line.
3641 as fallbacks if no location is specified on the command-line.
3642 When ``default-push`` is set, it will be used for push and
3642 When ``default-push`` is set, it will be used for push and
3643 ``default`` will be used for pull; otherwise ``default`` is used
3643 ``default`` will be used for pull; otherwise ``default`` is used
3644 as the fallback for both. When cloning a repository, the clone
3644 as the fallback for both. When cloning a repository, the clone
3645 source is written as ``default`` in ``.hg/hgrc``.
3645 source is written as ``default`` in ``.hg/hgrc``.
3646
3646
3647 .. note::
3647 .. note::
3648
3648
3649 ``default`` and ``default-push`` apply to all inbound (e.g.
3649 ``default`` and ``default-push`` apply to all inbound (e.g.
3650 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
3650 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
3651 and :hg:`bundle`) operations.
3651 and :hg:`bundle`) operations.
3652
3652
3653 See :hg:`help urls` for more information.
3653 See :hg:`help urls` for more information.
3654
3654
3655 Returns 0 on success.
3655 Returns 0 on success.
3656 """
3656 """
3657
3657
3658 opts = pycompat.byteskwargs(opts)
3658 opts = pycompat.byteskwargs(opts)
3659 ui.pager('paths')
3659 ui.pager('paths')
3660 if search:
3660 if search:
3661 pathitems = [(name, path) for name, path in ui.paths.iteritems()
3661 pathitems = [(name, path) for name, path in ui.paths.iteritems()
3662 if name == search]
3662 if name == search]
3663 else:
3663 else:
3664 pathitems = sorted(ui.paths.iteritems())
3664 pathitems = sorted(ui.paths.iteritems())
3665
3665
3666 fm = ui.formatter('paths', opts)
3666 fm = ui.formatter('paths', opts)
3667 if fm.isplain():
3667 if fm.isplain():
3668 hidepassword = util.hidepassword
3668 hidepassword = util.hidepassword
3669 else:
3669 else:
3670 hidepassword = str
3670 hidepassword = str
3671 if ui.quiet:
3671 if ui.quiet:
3672 namefmt = '%s\n'
3672 namefmt = '%s\n'
3673 else:
3673 else:
3674 namefmt = '%s = '
3674 namefmt = '%s = '
3675 showsubopts = not search and not ui.quiet
3675 showsubopts = not search and not ui.quiet
3676
3676
3677 for name, path in pathitems:
3677 for name, path in pathitems:
3678 fm.startitem()
3678 fm.startitem()
3679 fm.condwrite(not search, 'name', namefmt, name)
3679 fm.condwrite(not search, 'name', namefmt, name)
3680 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
3680 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
3681 for subopt, value in sorted(path.suboptions.items()):
3681 for subopt, value in sorted(path.suboptions.items()):
3682 assert subopt not in ('name', 'url')
3682 assert subopt not in ('name', 'url')
3683 if showsubopts:
3683 if showsubopts:
3684 fm.plain('%s:%s = ' % (name, subopt))
3684 fm.plain('%s:%s = ' % (name, subopt))
3685 fm.condwrite(showsubopts, subopt, '%s\n', value)
3685 fm.condwrite(showsubopts, subopt, '%s\n', value)
3686
3686
3687 fm.end()
3687 fm.end()
3688
3688
3689 if search and not pathitems:
3689 if search and not pathitems:
3690 if not ui.quiet:
3690 if not ui.quiet:
3691 ui.warn(_("not found!\n"))
3691 ui.warn(_("not found!\n"))
3692 return 1
3692 return 1
3693 else:
3693 else:
3694 return 0
3694 return 0
3695
3695
3696 @command('phase',
3696 @command('phase',
3697 [('p', 'public', False, _('set changeset phase to public')),
3697 [('p', 'public', False, _('set changeset phase to public')),
3698 ('d', 'draft', False, _('set changeset phase to draft')),
3698 ('d', 'draft', False, _('set changeset phase to draft')),
3699 ('s', 'secret', False, _('set changeset phase to secret')),
3699 ('s', 'secret', False, _('set changeset phase to secret')),
3700 ('f', 'force', False, _('allow to move boundary backward')),
3700 ('f', 'force', False, _('allow to move boundary backward')),
3701 ('r', 'rev', [], _('target revision'), _('REV')),
3701 ('r', 'rev', [], _('target revision'), _('REV')),
3702 ],
3702 ],
3703 _('[-p|-d|-s] [-f] [-r] [REV...]'))
3703 _('[-p|-d|-s] [-f] [-r] [REV...]'))
3704 def phase(ui, repo, *revs, **opts):
3704 def phase(ui, repo, *revs, **opts):
3705 """set or show the current phase name
3705 """set or show the current phase name
3706
3706
3707 With no argument, show the phase name of the current revision(s).
3707 With no argument, show the phase name of the current revision(s).
3708
3708
3709 With one of -p/--public, -d/--draft or -s/--secret, change the
3709 With one of -p/--public, -d/--draft or -s/--secret, change the
3710 phase value of the specified revisions.
3710 phase value of the specified revisions.
3711
3711
3712 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
3712 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
3713 lower phase to an higher phase. Phases are ordered as follows::
3713 lower phase to an higher phase. Phases are ordered as follows::
3714
3714
3715 public < draft < secret
3715 public < draft < secret
3716
3716
3717 Returns 0 on success, 1 if some phases could not be changed.
3717 Returns 0 on success, 1 if some phases could not be changed.
3718
3718
3719 (For more information about the phases concept, see :hg:`help phases`.)
3719 (For more information about the phases concept, see :hg:`help phases`.)
3720 """
3720 """
3721 opts = pycompat.byteskwargs(opts)
3721 opts = pycompat.byteskwargs(opts)
3722 # search for a unique phase argument
3722 # search for a unique phase argument
3723 targetphase = None
3723 targetphase = None
3724 for idx, name in enumerate(phases.phasenames):
3724 for idx, name in enumerate(phases.phasenames):
3725 if opts[name]:
3725 if opts[name]:
3726 if targetphase is not None:
3726 if targetphase is not None:
3727 raise error.Abort(_('only one phase can be specified'))
3727 raise error.Abort(_('only one phase can be specified'))
3728 targetphase = idx
3728 targetphase = idx
3729
3729
3730 # look for specified revision
3730 # look for specified revision
3731 revs = list(revs)
3731 revs = list(revs)
3732 revs.extend(opts['rev'])
3732 revs.extend(opts['rev'])
3733 if not revs:
3733 if not revs:
3734 # display both parents as the second parent phase can influence
3734 # display both parents as the second parent phase can influence
3735 # the phase of a merge commit
3735 # the phase of a merge commit
3736 revs = [c.rev() for c in repo[None].parents()]
3736 revs = [c.rev() for c in repo[None].parents()]
3737
3737
3738 revs = scmutil.revrange(repo, revs)
3738 revs = scmutil.revrange(repo, revs)
3739
3739
3740 lock = None
3740 lock = None
3741 ret = 0
3741 ret = 0
3742 if targetphase is None:
3742 if targetphase is None:
3743 # display
3743 # display
3744 for r in revs:
3744 for r in revs:
3745 ctx = repo[r]
3745 ctx = repo[r]
3746 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
3746 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
3747 else:
3747 else:
3748 tr = None
3748 tr = None
3749 lock = repo.lock()
3749 lock = repo.lock()
3750 try:
3750 try:
3751 tr = repo.transaction("phase")
3751 tr = repo.transaction("phase")
3752 # set phase
3752 # set phase
3753 if not revs:
3753 if not revs:
3754 raise error.Abort(_('empty revision set'))
3754 raise error.Abort(_('empty revision set'))
3755 nodes = [repo[r].node() for r in revs]
3755 nodes = [repo[r].node() for r in revs]
3756 # moving revision from public to draft may hide them
3756 # moving revision from public to draft may hide them
3757 # We have to check result on an unfiltered repository
3757 # We have to check result on an unfiltered repository
3758 unfi = repo.unfiltered()
3758 unfi = repo.unfiltered()
3759 getphase = unfi._phasecache.phase
3759 getphase = unfi._phasecache.phase
3760 olddata = [getphase(unfi, r) for r in unfi]
3760 olddata = [getphase(unfi, r) for r in unfi]
3761 phases.advanceboundary(repo, tr, targetphase, nodes)
3761 phases.advanceboundary(repo, tr, targetphase, nodes)
3762 if opts['force']:
3762 if opts['force']:
3763 phases.retractboundary(repo, tr, targetphase, nodes)
3763 phases.retractboundary(repo, tr, targetphase, nodes)
3764 tr.close()
3764 tr.close()
3765 finally:
3765 finally:
3766 if tr is not None:
3766 if tr is not None:
3767 tr.release()
3767 tr.release()
3768 lock.release()
3768 lock.release()
3769 getphase = unfi._phasecache.phase
3769 getphase = unfi._phasecache.phase
3770 newdata = [getphase(unfi, r) for r in unfi]
3770 newdata = [getphase(unfi, r) for r in unfi]
3771 changes = sum(newdata[r] != olddata[r] for r in unfi)
3771 changes = sum(newdata[r] != olddata[r] for r in unfi)
3772 cl = unfi.changelog
3772 cl = unfi.changelog
3773 rejected = [n for n in nodes
3773 rejected = [n for n in nodes
3774 if newdata[cl.rev(n)] < targetphase]
3774 if newdata[cl.rev(n)] < targetphase]
3775 if rejected:
3775 if rejected:
3776 ui.warn(_('cannot move %i changesets to a higher '
3776 ui.warn(_('cannot move %i changesets to a higher '
3777 'phase, use --force\n') % len(rejected))
3777 'phase, use --force\n') % len(rejected))
3778 ret = 1
3778 ret = 1
3779 if changes:
3779 if changes:
3780 msg = _('phase changed for %i changesets\n') % changes
3780 msg = _('phase changed for %i changesets\n') % changes
3781 if ret:
3781 if ret:
3782 ui.status(msg)
3782 ui.status(msg)
3783 else:
3783 else:
3784 ui.note(msg)
3784 ui.note(msg)
3785 else:
3785 else:
3786 ui.warn(_('no phases changed\n'))
3786 ui.warn(_('no phases changed\n'))
3787 return ret
3787 return ret
3788
3788
3789 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
3789 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
3790 """Run after a changegroup has been added via pull/unbundle
3790 """Run after a changegroup has been added via pull/unbundle
3791
3791
3792 This takes arguments below:
3792 This takes arguments below:
3793
3793
3794 :modheads: change of heads by pull/unbundle
3794 :modheads: change of heads by pull/unbundle
3795 :optupdate: updating working directory is needed or not
3795 :optupdate: updating working directory is needed or not
3796 :checkout: update destination revision (or None to default destination)
3796 :checkout: update destination revision (or None to default destination)
3797 :brev: a name, which might be a bookmark to be activated after updating
3797 :brev: a name, which might be a bookmark to be activated after updating
3798 """
3798 """
3799 if modheads == 0:
3799 if modheads == 0:
3800 return
3800 return
3801 if optupdate:
3801 if optupdate:
3802 try:
3802 try:
3803 return hg.updatetotally(ui, repo, checkout, brev)
3803 return hg.updatetotally(ui, repo, checkout, brev)
3804 except error.UpdateAbort as inst:
3804 except error.UpdateAbort as inst:
3805 msg = _("not updating: %s") % str(inst)
3805 msg = _("not updating: %s") % str(inst)
3806 hint = inst.hint
3806 hint = inst.hint
3807 raise error.UpdateAbort(msg, hint=hint)
3807 raise error.UpdateAbort(msg, hint=hint)
3808 if modheads > 1:
3808 if modheads > 1:
3809 currentbranchheads = len(repo.branchheads())
3809 currentbranchheads = len(repo.branchheads())
3810 if currentbranchheads == modheads:
3810 if currentbranchheads == modheads:
3811 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3811 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3812 elif currentbranchheads > 1:
3812 elif currentbranchheads > 1:
3813 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
3813 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
3814 "merge)\n"))
3814 "merge)\n"))
3815 else:
3815 else:
3816 ui.status(_("(run 'hg heads' to see heads)\n"))
3816 ui.status(_("(run 'hg heads' to see heads)\n"))
3817 else:
3817 else:
3818 ui.status(_("(run 'hg update' to get a working copy)\n"))
3818 ui.status(_("(run 'hg update' to get a working copy)\n"))
3819
3819
3820 @command('^pull',
3820 @command('^pull',
3821 [('u', 'update', None,
3821 [('u', 'update', None,
3822 _('update to new branch head if changesets were pulled')),
3822 _('update to new branch head if changesets were pulled')),
3823 ('f', 'force', None, _('run even when remote repository is unrelated')),
3823 ('f', 'force', None, _('run even when remote repository is unrelated')),
3824 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3824 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3825 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3825 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3826 ('b', 'branch', [], _('a specific branch you would like to pull'),
3826 ('b', 'branch', [], _('a specific branch you would like to pull'),
3827 _('BRANCH')),
3827 _('BRANCH')),
3828 ] + remoteopts,
3828 ] + remoteopts,
3829 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3829 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3830 def pull(ui, repo, source="default", **opts):
3830 def pull(ui, repo, source="default", **opts):
3831 """pull changes from the specified source
3831 """pull changes from the specified source
3832
3832
3833 Pull changes from a remote repository to a local one.
3833 Pull changes from a remote repository to a local one.
3834
3834
3835 This finds all changes from the repository at the specified path
3835 This finds all changes from the repository at the specified path
3836 or URL and adds them to a local repository (the current one unless
3836 or URL and adds them to a local repository (the current one unless
3837 -R is specified). By default, this does not update the copy of the
3837 -R is specified). By default, this does not update the copy of the
3838 project in the working directory.
3838 project in the working directory.
3839
3839
3840 Use :hg:`incoming` if you want to see what would have been added
3840 Use :hg:`incoming` if you want to see what would have been added
3841 by a pull at the time you issued this command. If you then decide
3841 by a pull at the time you issued this command. If you then decide
3842 to add those changes to the repository, you should use :hg:`pull
3842 to add those changes to the repository, you should use :hg:`pull
3843 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3843 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3844
3844
3845 If SOURCE is omitted, the 'default' path will be used.
3845 If SOURCE is omitted, the 'default' path will be used.
3846 See :hg:`help urls` for more information.
3846 See :hg:`help urls` for more information.
3847
3847
3848 Specifying bookmark as ``.`` is equivalent to specifying the active
3848 Specifying bookmark as ``.`` is equivalent to specifying the active
3849 bookmark's name.
3849 bookmark's name.
3850
3850
3851 Returns 0 on success, 1 if an update had unresolved files.
3851 Returns 0 on success, 1 if an update had unresolved files.
3852 """
3852 """
3853
3853
3854 opts = pycompat.byteskwargs(opts)
3854 opts = pycompat.byteskwargs(opts)
3855 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
3855 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
3856 msg = _('update destination required by configuration')
3856 msg = _('update destination required by configuration')
3857 hint = _('use hg pull followed by hg update DEST')
3857 hint = _('use hg pull followed by hg update DEST')
3858 raise error.Abort(msg, hint=hint)
3858 raise error.Abort(msg, hint=hint)
3859
3859
3860 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3860 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3861 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3861 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3862 other = hg.peer(repo, opts, source)
3862 other = hg.peer(repo, opts, source)
3863 try:
3863 try:
3864 revs, checkout = hg.addbranchrevs(repo, other, branches,
3864 revs, checkout = hg.addbranchrevs(repo, other, branches,
3865 opts.get('rev'))
3865 opts.get('rev'))
3866
3866
3867
3867
3868 pullopargs = {}
3868 pullopargs = {}
3869 if opts.get('bookmark'):
3869 if opts.get('bookmark'):
3870 if not revs:
3870 if not revs:
3871 revs = []
3871 revs = []
3872 # The list of bookmark used here is not the one used to actually
3872 # The list of bookmark used here is not the one used to actually
3873 # update the bookmark name. This can result in the revision pulled
3873 # update the bookmark name. This can result in the revision pulled
3874 # not ending up with the name of the bookmark because of a race
3874 # not ending up with the name of the bookmark because of a race
3875 # condition on the server. (See issue 4689 for details)
3875 # condition on the server. (See issue 4689 for details)
3876 remotebookmarks = other.listkeys('bookmarks')
3876 remotebookmarks = other.listkeys('bookmarks')
3877 pullopargs['remotebookmarks'] = remotebookmarks
3877 pullopargs['remotebookmarks'] = remotebookmarks
3878 for b in opts['bookmark']:
3878 for b in opts['bookmark']:
3879 b = repo._bookmarks.expandname(b)
3879 b = repo._bookmarks.expandname(b)
3880 if b not in remotebookmarks:
3880 if b not in remotebookmarks:
3881 raise error.Abort(_('remote bookmark %s not found!') % b)
3881 raise error.Abort(_('remote bookmark %s not found!') % b)
3882 revs.append(remotebookmarks[b])
3882 revs.append(remotebookmarks[b])
3883
3883
3884 if revs:
3884 if revs:
3885 try:
3885 try:
3886 # When 'rev' is a bookmark name, we cannot guarantee that it
3886 # When 'rev' is a bookmark name, we cannot guarantee that it
3887 # will be updated with that name because of a race condition
3887 # will be updated with that name because of a race condition
3888 # server side. (See issue 4689 for details)
3888 # server side. (See issue 4689 for details)
3889 oldrevs = revs
3889 oldrevs = revs
3890 revs = [] # actually, nodes
3890 revs = [] # actually, nodes
3891 for r in oldrevs:
3891 for r in oldrevs:
3892 node = other.lookup(r)
3892 node = other.lookup(r)
3893 revs.append(node)
3893 revs.append(node)
3894 if r == checkout:
3894 if r == checkout:
3895 checkout = node
3895 checkout = node
3896 except error.CapabilityError:
3896 except error.CapabilityError:
3897 err = _("other repository doesn't support revision lookup, "
3897 err = _("other repository doesn't support revision lookup, "
3898 "so a rev cannot be specified.")
3898 "so a rev cannot be specified.")
3899 raise error.Abort(err)
3899 raise error.Abort(err)
3900
3900
3901 pullopargs.update(opts.get('opargs', {}))
3901 pullopargs.update(opts.get('opargs', {}))
3902 modheads = exchange.pull(repo, other, heads=revs,
3902 modheads = exchange.pull(repo, other, heads=revs,
3903 force=opts.get('force'),
3903 force=opts.get('force'),
3904 bookmarks=opts.get('bookmark', ()),
3904 bookmarks=opts.get('bookmark', ()),
3905 opargs=pullopargs).cgresult
3905 opargs=pullopargs).cgresult
3906
3906
3907 # brev is a name, which might be a bookmark to be activated at
3907 # brev is a name, which might be a bookmark to be activated at
3908 # the end of the update. In other words, it is an explicit
3908 # the end of the update. In other words, it is an explicit
3909 # destination of the update
3909 # destination of the update
3910 brev = None
3910 brev = None
3911
3911
3912 if checkout:
3912 if checkout:
3913 checkout = str(repo.changelog.rev(checkout))
3913 checkout = str(repo.changelog.rev(checkout))
3914
3914
3915 # order below depends on implementation of
3915 # order below depends on implementation of
3916 # hg.addbranchrevs(). opts['bookmark'] is ignored,
3916 # hg.addbranchrevs(). opts['bookmark'] is ignored,
3917 # because 'checkout' is determined without it.
3917 # because 'checkout' is determined without it.
3918 if opts.get('rev'):
3918 if opts.get('rev'):
3919 brev = opts['rev'][0]
3919 brev = opts['rev'][0]
3920 elif opts.get('branch'):
3920 elif opts.get('branch'):
3921 brev = opts['branch'][0]
3921 brev = opts['branch'][0]
3922 else:
3922 else:
3923 brev = branches[0]
3923 brev = branches[0]
3924 repo._subtoppath = source
3924 repo._subtoppath = source
3925 try:
3925 try:
3926 ret = postincoming(ui, repo, modheads, opts.get('update'),
3926 ret = postincoming(ui, repo, modheads, opts.get('update'),
3927 checkout, brev)
3927 checkout, brev)
3928
3928
3929 finally:
3929 finally:
3930 del repo._subtoppath
3930 del repo._subtoppath
3931
3931
3932 finally:
3932 finally:
3933 other.close()
3933 other.close()
3934 return ret
3934 return ret
3935
3935
3936 @command('^push',
3936 @command('^push',
3937 [('f', 'force', None, _('force push')),
3937 [('f', 'force', None, _('force push')),
3938 ('r', 'rev', [],
3938 ('r', 'rev', [],
3939 _('a changeset intended to be included in the destination'),
3939 _('a changeset intended to be included in the destination'),
3940 _('REV')),
3940 _('REV')),
3941 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
3941 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
3942 ('b', 'branch', [],
3942 ('b', 'branch', [],
3943 _('a specific branch you would like to push'), _('BRANCH')),
3943 _('a specific branch you would like to push'), _('BRANCH')),
3944 ('', 'new-branch', False, _('allow pushing a new branch')),
3944 ('', 'new-branch', False, _('allow pushing a new branch')),
3945 ] + remoteopts,
3945 ] + remoteopts,
3946 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
3946 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
3947 def push(ui, repo, dest=None, **opts):
3947 def push(ui, repo, dest=None, **opts):
3948 """push changes to the specified destination
3948 """push changes to the specified destination
3949
3949
3950 Push changesets from the local repository to the specified
3950 Push changesets from the local repository to the specified
3951 destination.
3951 destination.
3952
3952
3953 This operation is symmetrical to pull: it is identical to a pull
3953 This operation is symmetrical to pull: it is identical to a pull
3954 in the destination repository from the current one.
3954 in the destination repository from the current one.
3955
3955
3956 By default, push will not allow creation of new heads at the
3956 By default, push will not allow creation of new heads at the
3957 destination, since multiple heads would make it unclear which head
3957 destination, since multiple heads would make it unclear which head
3958 to use. In this situation, it is recommended to pull and merge
3958 to use. In this situation, it is recommended to pull and merge
3959 before pushing.
3959 before pushing.
3960
3960
3961 Use --new-branch if you want to allow push to create a new named
3961 Use --new-branch if you want to allow push to create a new named
3962 branch that is not present at the destination. This allows you to
3962 branch that is not present at the destination. This allows you to
3963 only create a new branch without forcing other changes.
3963 only create a new branch without forcing other changes.
3964
3964
3965 .. note::
3965 .. note::
3966
3966
3967 Extra care should be taken with the -f/--force option,
3967 Extra care should be taken with the -f/--force option,
3968 which will push all new heads on all branches, an action which will
3968 which will push all new heads on all branches, an action which will
3969 almost always cause confusion for collaborators.
3969 almost always cause confusion for collaborators.
3970
3970
3971 If -r/--rev is used, the specified revision and all its ancestors
3971 If -r/--rev is used, the specified revision and all its ancestors
3972 will be pushed to the remote repository.
3972 will be pushed to the remote repository.
3973
3973
3974 If -B/--bookmark is used, the specified bookmarked revision, its
3974 If -B/--bookmark is used, the specified bookmarked revision, its
3975 ancestors, and the bookmark will be pushed to the remote
3975 ancestors, and the bookmark will be pushed to the remote
3976 repository. Specifying ``.`` is equivalent to specifying the active
3976 repository. Specifying ``.`` is equivalent to specifying the active
3977 bookmark's name.
3977 bookmark's name.
3978
3978
3979 Please see :hg:`help urls` for important details about ``ssh://``
3979 Please see :hg:`help urls` for important details about ``ssh://``
3980 URLs. If DESTINATION is omitted, a default path will be used.
3980 URLs. If DESTINATION is omitted, a default path will be used.
3981
3981
3982 Returns 0 if push was successful, 1 if nothing to push.
3982 Returns 0 if push was successful, 1 if nothing to push.
3983 """
3983 """
3984
3984
3985 opts = pycompat.byteskwargs(opts)
3985 opts = pycompat.byteskwargs(opts)
3986 if opts.get('bookmark'):
3986 if opts.get('bookmark'):
3987 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
3987 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
3988 for b in opts['bookmark']:
3988 for b in opts['bookmark']:
3989 # translate -B options to -r so changesets get pushed
3989 # translate -B options to -r so changesets get pushed
3990 b = repo._bookmarks.expandname(b)
3990 b = repo._bookmarks.expandname(b)
3991 if b in repo._bookmarks:
3991 if b in repo._bookmarks:
3992 opts.setdefault('rev', []).append(b)
3992 opts.setdefault('rev', []).append(b)
3993 else:
3993 else:
3994 # if we try to push a deleted bookmark, translate it to null
3994 # if we try to push a deleted bookmark, translate it to null
3995 # this lets simultaneous -r, -b options continue working
3995 # this lets simultaneous -r, -b options continue working
3996 opts.setdefault('rev', []).append("null")
3996 opts.setdefault('rev', []).append("null")
3997
3997
3998 path = ui.paths.getpath(dest, default=('default-push', 'default'))
3998 path = ui.paths.getpath(dest, default=('default-push', 'default'))
3999 if not path:
3999 if not path:
4000 raise error.Abort(_('default repository not configured!'),
4000 raise error.Abort(_('default repository not configured!'),
4001 hint=_("see 'hg help config.paths'"))
4001 hint=_("see 'hg help config.paths'"))
4002 dest = path.pushloc or path.loc
4002 dest = path.pushloc or path.loc
4003 branches = (path.branch, opts.get('branch') or [])
4003 branches = (path.branch, opts.get('branch') or [])
4004 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4004 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4005 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4005 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4006 other = hg.peer(repo, opts, dest)
4006 other = hg.peer(repo, opts, dest)
4007
4007
4008 if revs:
4008 if revs:
4009 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4009 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4010 if not revs:
4010 if not revs:
4011 raise error.Abort(_("specified revisions evaluate to an empty set"),
4011 raise error.Abort(_("specified revisions evaluate to an empty set"),
4012 hint=_("use different revision arguments"))
4012 hint=_("use different revision arguments"))
4013 elif path.pushrev:
4013 elif path.pushrev:
4014 # It doesn't make any sense to specify ancestor revisions. So limit
4014 # It doesn't make any sense to specify ancestor revisions. So limit
4015 # to DAG heads to make discovery simpler.
4015 # to DAG heads to make discovery simpler.
4016 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4016 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4017 revs = scmutil.revrange(repo, [expr])
4017 revs = scmutil.revrange(repo, [expr])
4018 revs = [repo[rev].node() for rev in revs]
4018 revs = [repo[rev].node() for rev in revs]
4019 if not revs:
4019 if not revs:
4020 raise error.Abort(_('default push revset for path evaluates to an '
4020 raise error.Abort(_('default push revset for path evaluates to an '
4021 'empty set'))
4021 'empty set'))
4022
4022
4023 repo._subtoppath = dest
4023 repo._subtoppath = dest
4024 try:
4024 try:
4025 # push subrepos depth-first for coherent ordering
4025 # push subrepos depth-first for coherent ordering
4026 c = repo['']
4026 c = repo['']
4027 subs = c.substate # only repos that are committed
4027 subs = c.substate # only repos that are committed
4028 for s in sorted(subs):
4028 for s in sorted(subs):
4029 result = c.sub(s).push(opts)
4029 result = c.sub(s).push(opts)
4030 if result == 0:
4030 if result == 0:
4031 return not result
4031 return not result
4032 finally:
4032 finally:
4033 del repo._subtoppath
4033 del repo._subtoppath
4034 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4034 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4035 newbranch=opts.get('new_branch'),
4035 newbranch=opts.get('new_branch'),
4036 bookmarks=opts.get('bookmark', ()),
4036 bookmarks=opts.get('bookmark', ()),
4037 opargs=opts.get('opargs'))
4037 opargs=opts.get('opargs'))
4038
4038
4039 result = not pushop.cgresult
4039 result = not pushop.cgresult
4040
4040
4041 if pushop.bkresult is not None:
4041 if pushop.bkresult is not None:
4042 if pushop.bkresult == 2:
4042 if pushop.bkresult == 2:
4043 result = 2
4043 result = 2
4044 elif not result and pushop.bkresult:
4044 elif not result and pushop.bkresult:
4045 result = 2
4045 result = 2
4046
4046
4047 return result
4047 return result
4048
4048
4049 @command('recover', [])
4049 @command('recover', [])
4050 def recover(ui, repo):
4050 def recover(ui, repo):
4051 """roll back an interrupted transaction
4051 """roll back an interrupted transaction
4052
4052
4053 Recover from an interrupted commit or pull.
4053 Recover from an interrupted commit or pull.
4054
4054
4055 This command tries to fix the repository status after an
4055 This command tries to fix the repository status after an
4056 interrupted operation. It should only be necessary when Mercurial
4056 interrupted operation. It should only be necessary when Mercurial
4057 suggests it.
4057 suggests it.
4058
4058
4059 Returns 0 if successful, 1 if nothing to recover or verify fails.
4059 Returns 0 if successful, 1 if nothing to recover or verify fails.
4060 """
4060 """
4061 if repo.recover():
4061 if repo.recover():
4062 return hg.verify(repo)
4062 return hg.verify(repo)
4063 return 1
4063 return 1
4064
4064
4065 @command('^remove|rm',
4065 @command('^remove|rm',
4066 [('A', 'after', None, _('record delete for missing files')),
4066 [('A', 'after', None, _('record delete for missing files')),
4067 ('f', 'force', None,
4067 ('f', 'force', None,
4068 _('forget added files, delete modified files')),
4068 _('forget added files, delete modified files')),
4069 ] + subrepoopts + walkopts,
4069 ] + subrepoopts + walkopts,
4070 _('[OPTION]... FILE...'),
4070 _('[OPTION]... FILE...'),
4071 inferrepo=True)
4071 inferrepo=True)
4072 def remove(ui, repo, *pats, **opts):
4072 def remove(ui, repo, *pats, **opts):
4073 """remove the specified files on the next commit
4073 """remove the specified files on the next commit
4074
4074
4075 Schedule the indicated files for removal from the current branch.
4075 Schedule the indicated files for removal from the current branch.
4076
4076
4077 This command schedules the files to be removed at the next commit.
4077 This command schedules the files to be removed at the next commit.
4078 To undo a remove before that, see :hg:`revert`. To undo added
4078 To undo a remove before that, see :hg:`revert`. To undo added
4079 files, see :hg:`forget`.
4079 files, see :hg:`forget`.
4080
4080
4081 .. container:: verbose
4081 .. container:: verbose
4082
4082
4083 -A/--after can be used to remove only files that have already
4083 -A/--after can be used to remove only files that have already
4084 been deleted, -f/--force can be used to force deletion, and -Af
4084 been deleted, -f/--force can be used to force deletion, and -Af
4085 can be used to remove files from the next revision without
4085 can be used to remove files from the next revision without
4086 deleting them from the working directory.
4086 deleting them from the working directory.
4087
4087
4088 The following table details the behavior of remove for different
4088 The following table details the behavior of remove for different
4089 file states (columns) and option combinations (rows). The file
4089 file states (columns) and option combinations (rows). The file
4090 states are Added [A], Clean [C], Modified [M] and Missing [!]
4090 states are Added [A], Clean [C], Modified [M] and Missing [!]
4091 (as reported by :hg:`status`). The actions are Warn, Remove
4091 (as reported by :hg:`status`). The actions are Warn, Remove
4092 (from branch) and Delete (from disk):
4092 (from branch) and Delete (from disk):
4093
4093
4094 ========= == == == ==
4094 ========= == == == ==
4095 opt/state A C M !
4095 opt/state A C M !
4096 ========= == == == ==
4096 ========= == == == ==
4097 none W RD W R
4097 none W RD W R
4098 -f R RD RD R
4098 -f R RD RD R
4099 -A W W W R
4099 -A W W W R
4100 -Af R R R R
4100 -Af R R R R
4101 ========= == == == ==
4101 ========= == == == ==
4102
4102
4103 .. note::
4103 .. note::
4104
4104
4105 :hg:`remove` never deletes files in Added [A] state from the
4105 :hg:`remove` never deletes files in Added [A] state from the
4106 working directory, not even if ``--force`` is specified.
4106 working directory, not even if ``--force`` is specified.
4107
4107
4108 Returns 0 on success, 1 if any warnings encountered.
4108 Returns 0 on success, 1 if any warnings encountered.
4109 """
4109 """
4110
4110
4111 opts = pycompat.byteskwargs(opts)
4111 opts = pycompat.byteskwargs(opts)
4112 after, force = opts.get('after'), opts.get('force')
4112 after, force = opts.get('after'), opts.get('force')
4113 if not pats and not after:
4113 if not pats and not after:
4114 raise error.Abort(_('no files specified'))
4114 raise error.Abort(_('no files specified'))
4115
4115
4116 m = scmutil.match(repo[None], pats, opts)
4116 m = scmutil.match(repo[None], pats, opts)
4117 subrepos = opts.get('subrepos')
4117 subrepos = opts.get('subrepos')
4118 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
4118 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
4119
4119
4120 @command('rename|move|mv',
4120 @command('rename|move|mv',
4121 [('A', 'after', None, _('record a rename that has already occurred')),
4121 [('A', 'after', None, _('record a rename that has already occurred')),
4122 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4122 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4123 ] + walkopts + dryrunopts,
4123 ] + walkopts + dryrunopts,
4124 _('[OPTION]... SOURCE... DEST'))
4124 _('[OPTION]... SOURCE... DEST'))
4125 def rename(ui, repo, *pats, **opts):
4125 def rename(ui, repo, *pats, **opts):
4126 """rename files; equivalent of copy + remove
4126 """rename files; equivalent of copy + remove
4127
4127
4128 Mark dest as copies of sources; mark sources for deletion. If dest
4128 Mark dest as copies of sources; mark sources for deletion. If dest
4129 is a directory, copies are put in that directory. If dest is a
4129 is a directory, copies are put in that directory. If dest is a
4130 file, there can only be one source.
4130 file, there can only be one source.
4131
4131
4132 By default, this command copies the contents of files as they
4132 By default, this command copies the contents of files as they
4133 exist in the working directory. If invoked with -A/--after, the
4133 exist in the working directory. If invoked with -A/--after, the
4134 operation is recorded, but no copying is performed.
4134 operation is recorded, but no copying is performed.
4135
4135
4136 This command takes effect at the next commit. To undo a rename
4136 This command takes effect at the next commit. To undo a rename
4137 before that, see :hg:`revert`.
4137 before that, see :hg:`revert`.
4138
4138
4139 Returns 0 on success, 1 if errors are encountered.
4139 Returns 0 on success, 1 if errors are encountered.
4140 """
4140 """
4141 opts = pycompat.byteskwargs(opts)
4141 opts = pycompat.byteskwargs(opts)
4142 with repo.wlock(False):
4142 with repo.wlock(False):
4143 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4143 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4144
4144
4145 @command('resolve',
4145 @command('resolve',
4146 [('a', 'all', None, _('select all unresolved files')),
4146 [('a', 'all', None, _('select all unresolved files')),
4147 ('l', 'list', None, _('list state of files needing merge')),
4147 ('l', 'list', None, _('list state of files needing merge')),
4148 ('m', 'mark', None, _('mark files as resolved')),
4148 ('m', 'mark', None, _('mark files as resolved')),
4149 ('u', 'unmark', None, _('mark files as unresolved')),
4149 ('u', 'unmark', None, _('mark files as unresolved')),
4150 ('n', 'no-status', None, _('hide status prefix'))]
4150 ('n', 'no-status', None, _('hide status prefix'))]
4151 + mergetoolopts + walkopts + formatteropts,
4151 + mergetoolopts + walkopts + formatteropts,
4152 _('[OPTION]... [FILE]...'),
4152 _('[OPTION]... [FILE]...'),
4153 inferrepo=True)
4153 inferrepo=True)
4154 def resolve(ui, repo, *pats, **opts):
4154 def resolve(ui, repo, *pats, **opts):
4155 """redo merges or set/view the merge status of files
4155 """redo merges or set/view the merge status of files
4156
4156
4157 Merges with unresolved conflicts are often the result of
4157 Merges with unresolved conflicts are often the result of
4158 non-interactive merging using the ``internal:merge`` configuration
4158 non-interactive merging using the ``internal:merge`` configuration
4159 setting, or a command-line merge tool like ``diff3``. The resolve
4159 setting, or a command-line merge tool like ``diff3``. The resolve
4160 command is used to manage the files involved in a merge, after
4160 command is used to manage the files involved in a merge, after
4161 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4161 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4162 working directory must have two parents). See :hg:`help
4162 working directory must have two parents). See :hg:`help
4163 merge-tools` for information on configuring merge tools.
4163 merge-tools` for information on configuring merge tools.
4164
4164
4165 The resolve command can be used in the following ways:
4165 The resolve command can be used in the following ways:
4166
4166
4167 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4167 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4168 files, discarding any previous merge attempts. Re-merging is not
4168 files, discarding any previous merge attempts. Re-merging is not
4169 performed for files already marked as resolved. Use ``--all/-a``
4169 performed for files already marked as resolved. Use ``--all/-a``
4170 to select all unresolved files. ``--tool`` can be used to specify
4170 to select all unresolved files. ``--tool`` can be used to specify
4171 the merge tool used for the given files. It overrides the HGMERGE
4171 the merge tool used for the given files. It overrides the HGMERGE
4172 environment variable and your configuration files. Previous file
4172 environment variable and your configuration files. Previous file
4173 contents are saved with a ``.orig`` suffix.
4173 contents are saved with a ``.orig`` suffix.
4174
4174
4175 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4175 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4176 (e.g. after having manually fixed-up the files). The default is
4176 (e.g. after having manually fixed-up the files). The default is
4177 to mark all unresolved files.
4177 to mark all unresolved files.
4178
4178
4179 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4179 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4180 default is to mark all resolved files.
4180 default is to mark all resolved files.
4181
4181
4182 - :hg:`resolve -l`: list files which had or still have conflicts.
4182 - :hg:`resolve -l`: list files which had or still have conflicts.
4183 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4183 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4184 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4184 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4185 the list. See :hg:`help filesets` for details.
4185 the list. See :hg:`help filesets` for details.
4186
4186
4187 .. note::
4187 .. note::
4188
4188
4189 Mercurial will not let you commit files with unresolved merge
4189 Mercurial will not let you commit files with unresolved merge
4190 conflicts. You must use :hg:`resolve -m ...` before you can
4190 conflicts. You must use :hg:`resolve -m ...` before you can
4191 commit after a conflicting merge.
4191 commit after a conflicting merge.
4192
4192
4193 Returns 0 on success, 1 if any files fail a resolve attempt.
4193 Returns 0 on success, 1 if any files fail a resolve attempt.
4194 """
4194 """
4195
4195
4196 opts = pycompat.byteskwargs(opts)
4196 opts = pycompat.byteskwargs(opts)
4197 flaglist = 'all mark unmark list no_status'.split()
4197 flaglist = 'all mark unmark list no_status'.split()
4198 all, mark, unmark, show, nostatus = \
4198 all, mark, unmark, show, nostatus = \
4199 [opts.get(o) for o in flaglist]
4199 [opts.get(o) for o in flaglist]
4200
4200
4201 if (show and (mark or unmark)) or (mark and unmark):
4201 if (show and (mark or unmark)) or (mark and unmark):
4202 raise error.Abort(_("too many options specified"))
4202 raise error.Abort(_("too many options specified"))
4203 if pats and all:
4203 if pats and all:
4204 raise error.Abort(_("can't specify --all and patterns"))
4204 raise error.Abort(_("can't specify --all and patterns"))
4205 if not (all or pats or show or mark or unmark):
4205 if not (all or pats or show or mark or unmark):
4206 raise error.Abort(_('no files or directories specified'),
4206 raise error.Abort(_('no files or directories specified'),
4207 hint=('use --all to re-merge all unresolved files'))
4207 hint=('use --all to re-merge all unresolved files'))
4208
4208
4209 if show:
4209 if show:
4210 ui.pager('resolve')
4210 ui.pager('resolve')
4211 fm = ui.formatter('resolve', opts)
4211 fm = ui.formatter('resolve', opts)
4212 ms = mergemod.mergestate.read(repo)
4212 ms = mergemod.mergestate.read(repo)
4213 m = scmutil.match(repo[None], pats, opts)
4213 m = scmutil.match(repo[None], pats, opts)
4214 for f in ms:
4214 for f in ms:
4215 if not m(f):
4215 if not m(f):
4216 continue
4216 continue
4217 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved',
4217 l = 'resolve.' + {'u': 'unresolved', 'r': 'resolved',
4218 'd': 'driverresolved'}[ms[f]]
4218 'd': 'driverresolved'}[ms[f]]
4219 fm.startitem()
4219 fm.startitem()
4220 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
4220 fm.condwrite(not nostatus, 'status', '%s ', ms[f].upper(), label=l)
4221 fm.write('path', '%s\n', f, label=l)
4221 fm.write('path', '%s\n', f, label=l)
4222 fm.end()
4222 fm.end()
4223 return 0
4223 return 0
4224
4224
4225 with repo.wlock():
4225 with repo.wlock():
4226 ms = mergemod.mergestate.read(repo)
4226 ms = mergemod.mergestate.read(repo)
4227
4227
4228 if not (ms.active() or repo.dirstate.p2() != nullid):
4228 if not (ms.active() or repo.dirstate.p2() != nullid):
4229 raise error.Abort(
4229 raise error.Abort(
4230 _('resolve command not applicable when not merging'))
4230 _('resolve command not applicable when not merging'))
4231
4231
4232 wctx = repo[None]
4232 wctx = repo[None]
4233
4233
4234 if ms.mergedriver and ms.mdstate() == 'u':
4234 if ms.mergedriver and ms.mdstate() == 'u':
4235 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4235 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4236 ms.commit()
4236 ms.commit()
4237 # allow mark and unmark to go through
4237 # allow mark and unmark to go through
4238 if not mark and not unmark and not proceed:
4238 if not mark and not unmark and not proceed:
4239 return 1
4239 return 1
4240
4240
4241 m = scmutil.match(wctx, pats, opts)
4241 m = scmutil.match(wctx, pats, opts)
4242 ret = 0
4242 ret = 0
4243 didwork = False
4243 didwork = False
4244 runconclude = False
4244 runconclude = False
4245
4245
4246 tocomplete = []
4246 tocomplete = []
4247 for f in ms:
4247 for f in ms:
4248 if not m(f):
4248 if not m(f):
4249 continue
4249 continue
4250
4250
4251 didwork = True
4251 didwork = True
4252
4252
4253 # don't let driver-resolved files be marked, and run the conclude
4253 # don't let driver-resolved files be marked, and run the conclude
4254 # step if asked to resolve
4254 # step if asked to resolve
4255 if ms[f] == "d":
4255 if ms[f] == "d":
4256 exact = m.exact(f)
4256 exact = m.exact(f)
4257 if mark:
4257 if mark:
4258 if exact:
4258 if exact:
4259 ui.warn(_('not marking %s as it is driver-resolved\n')
4259 ui.warn(_('not marking %s as it is driver-resolved\n')
4260 % f)
4260 % f)
4261 elif unmark:
4261 elif unmark:
4262 if exact:
4262 if exact:
4263 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4263 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4264 % f)
4264 % f)
4265 else:
4265 else:
4266 runconclude = True
4266 runconclude = True
4267 continue
4267 continue
4268
4268
4269 if mark:
4269 if mark:
4270 ms.mark(f, "r")
4270 ms.mark(f, "r")
4271 elif unmark:
4271 elif unmark:
4272 ms.mark(f, "u")
4272 ms.mark(f, "u")
4273 else:
4273 else:
4274 # backup pre-resolve (merge uses .orig for its own purposes)
4274 # backup pre-resolve (merge uses .orig for its own purposes)
4275 a = repo.wjoin(f)
4275 a = repo.wjoin(f)
4276 try:
4276 try:
4277 util.copyfile(a, a + ".resolve")
4277 util.copyfile(a, a + ".resolve")
4278 except (IOError, OSError) as inst:
4278 except (IOError, OSError) as inst:
4279 if inst.errno != errno.ENOENT:
4279 if inst.errno != errno.ENOENT:
4280 raise
4280 raise
4281
4281
4282 try:
4282 try:
4283 # preresolve file
4283 # preresolve file
4284 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4284 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4285 'resolve')
4285 'resolve')
4286 complete, r = ms.preresolve(f, wctx)
4286 complete, r = ms.preresolve(f, wctx)
4287 if not complete:
4287 if not complete:
4288 tocomplete.append(f)
4288 tocomplete.append(f)
4289 elif r:
4289 elif r:
4290 ret = 1
4290 ret = 1
4291 finally:
4291 finally:
4292 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4292 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4293 ms.commit()
4293 ms.commit()
4294
4294
4295 # replace filemerge's .orig file with our resolve file, but only
4295 # replace filemerge's .orig file with our resolve file, but only
4296 # for merges that are complete
4296 # for merges that are complete
4297 if complete:
4297 if complete:
4298 try:
4298 try:
4299 util.rename(a + ".resolve",
4299 util.rename(a + ".resolve",
4300 scmutil.origpath(ui, repo, a))
4300 scmutil.origpath(ui, repo, a))
4301 except OSError as inst:
4301 except OSError as inst:
4302 if inst.errno != errno.ENOENT:
4302 if inst.errno != errno.ENOENT:
4303 raise
4303 raise
4304
4304
4305 for f in tocomplete:
4305 for f in tocomplete:
4306 try:
4306 try:
4307 # resolve file
4307 # resolve file
4308 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4308 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4309 'resolve')
4309 'resolve')
4310 r = ms.resolve(f, wctx)
4310 r = ms.resolve(f, wctx)
4311 if r:
4311 if r:
4312 ret = 1
4312 ret = 1
4313 finally:
4313 finally:
4314 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4314 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4315 ms.commit()
4315 ms.commit()
4316
4316
4317 # replace filemerge's .orig file with our resolve file
4317 # replace filemerge's .orig file with our resolve file
4318 a = repo.wjoin(f)
4318 a = repo.wjoin(f)
4319 try:
4319 try:
4320 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4320 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4321 except OSError as inst:
4321 except OSError as inst:
4322 if inst.errno != errno.ENOENT:
4322 if inst.errno != errno.ENOENT:
4323 raise
4323 raise
4324
4324
4325 ms.commit()
4325 ms.commit()
4326 ms.recordactions()
4326 ms.recordactions()
4327
4327
4328 if not didwork and pats:
4328 if not didwork and pats:
4329 hint = None
4329 hint = None
4330 if not any([p for p in pats if p.find(':') >= 0]):
4330 if not any([p for p in pats if p.find(':') >= 0]):
4331 pats = ['path:%s' % p for p in pats]
4331 pats = ['path:%s' % p for p in pats]
4332 m = scmutil.match(wctx, pats, opts)
4332 m = scmutil.match(wctx, pats, opts)
4333 for f in ms:
4333 for f in ms:
4334 if not m(f):
4334 if not m(f):
4335 continue
4335 continue
4336 flags = ''.join(['-%s ' % o[0] for o in flaglist
4336 flags = ''.join(['-%s ' % o[0] for o in flaglist
4337 if opts.get(o)])
4337 if opts.get(o)])
4338 hint = _("(try: hg resolve %s%s)\n") % (
4338 hint = _("(try: hg resolve %s%s)\n") % (
4339 flags,
4339 flags,
4340 ' '.join(pats))
4340 ' '.join(pats))
4341 break
4341 break
4342 ui.warn(_("arguments do not match paths that need resolving\n"))
4342 ui.warn(_("arguments do not match paths that need resolving\n"))
4343 if hint:
4343 if hint:
4344 ui.warn(hint)
4344 ui.warn(hint)
4345 elif ms.mergedriver and ms.mdstate() != 's':
4345 elif ms.mergedriver and ms.mdstate() != 's':
4346 # run conclude step when either a driver-resolved file is requested
4346 # run conclude step when either a driver-resolved file is requested
4347 # or there are no driver-resolved files
4347 # or there are no driver-resolved files
4348 # we can't use 'ret' to determine whether any files are unresolved
4348 # we can't use 'ret' to determine whether any files are unresolved
4349 # because we might not have tried to resolve some
4349 # because we might not have tried to resolve some
4350 if ((runconclude or not list(ms.driverresolved()))
4350 if ((runconclude or not list(ms.driverresolved()))
4351 and not list(ms.unresolved())):
4351 and not list(ms.unresolved())):
4352 proceed = mergemod.driverconclude(repo, ms, wctx)
4352 proceed = mergemod.driverconclude(repo, ms, wctx)
4353 ms.commit()
4353 ms.commit()
4354 if not proceed:
4354 if not proceed:
4355 return 1
4355 return 1
4356
4356
4357 # Nudge users into finishing an unfinished operation
4357 # Nudge users into finishing an unfinished operation
4358 unresolvedf = list(ms.unresolved())
4358 unresolvedf = list(ms.unresolved())
4359 driverresolvedf = list(ms.driverresolved())
4359 driverresolvedf = list(ms.driverresolved())
4360 if not unresolvedf and not driverresolvedf:
4360 if not unresolvedf and not driverresolvedf:
4361 ui.status(_('(no more unresolved files)\n'))
4361 ui.status(_('(no more unresolved files)\n'))
4362 cmdutil.checkafterresolved(repo)
4362 cmdutil.checkafterresolved(repo)
4363 elif not unresolvedf:
4363 elif not unresolvedf:
4364 ui.status(_('(no more unresolved files -- '
4364 ui.status(_('(no more unresolved files -- '
4365 'run "hg resolve --all" to conclude)\n'))
4365 'run "hg resolve --all" to conclude)\n'))
4366
4366
4367 return ret
4367 return ret
4368
4368
4369 @command('revert',
4369 @command('revert',
4370 [('a', 'all', None, _('revert all changes when no arguments given')),
4370 [('a', 'all', None, _('revert all changes when no arguments given')),
4371 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4371 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4372 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4372 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4373 ('C', 'no-backup', None, _('do not save backup copies of files')),
4373 ('C', 'no-backup', None, _('do not save backup copies of files')),
4374 ('i', 'interactive', None,
4374 ('i', 'interactive', None,
4375 _('interactively select the changes (EXPERIMENTAL)')),
4375 _('interactively select the changes (EXPERIMENTAL)')),
4376 ] + walkopts + dryrunopts,
4376 ] + walkopts + dryrunopts,
4377 _('[OPTION]... [-r REV] [NAME]...'))
4377 _('[OPTION]... [-r REV] [NAME]...'))
4378 def revert(ui, repo, *pats, **opts):
4378 def revert(ui, repo, *pats, **opts):
4379 """restore files to their checkout state
4379 """restore files to their checkout state
4380
4380
4381 .. note::
4381 .. note::
4382
4382
4383 To check out earlier revisions, you should use :hg:`update REV`.
4383 To check out earlier revisions, you should use :hg:`update REV`.
4384 To cancel an uncommitted merge (and lose your changes),
4384 To cancel an uncommitted merge (and lose your changes),
4385 use :hg:`update --clean .`.
4385 use :hg:`update --clean .`.
4386
4386
4387 With no revision specified, revert the specified files or directories
4387 With no revision specified, revert the specified files or directories
4388 to the contents they had in the parent of the working directory.
4388 to the contents they had in the parent of the working directory.
4389 This restores the contents of files to an unmodified
4389 This restores the contents of files to an unmodified
4390 state and unschedules adds, removes, copies, and renames. If the
4390 state and unschedules adds, removes, copies, and renames. If the
4391 working directory has two parents, you must explicitly specify a
4391 working directory has two parents, you must explicitly specify a
4392 revision.
4392 revision.
4393
4393
4394 Using the -r/--rev or -d/--date options, revert the given files or
4394 Using the -r/--rev or -d/--date options, revert the given files or
4395 directories to their states as of a specific revision. Because
4395 directories to their states as of a specific revision. Because
4396 revert does not change the working directory parents, this will
4396 revert does not change the working directory parents, this will
4397 cause these files to appear modified. This can be helpful to "back
4397 cause these files to appear modified. This can be helpful to "back
4398 out" some or all of an earlier change. See :hg:`backout` for a
4398 out" some or all of an earlier change. See :hg:`backout` for a
4399 related method.
4399 related method.
4400
4400
4401 Modified files are saved with a .orig suffix before reverting.
4401 Modified files are saved with a .orig suffix before reverting.
4402 To disable these backups, use --no-backup. It is possible to store
4402 To disable these backups, use --no-backup. It is possible to store
4403 the backup files in a custom directory relative to the root of the
4403 the backup files in a custom directory relative to the root of the
4404 repository by setting the ``ui.origbackuppath`` configuration
4404 repository by setting the ``ui.origbackuppath`` configuration
4405 option.
4405 option.
4406
4406
4407 See :hg:`help dates` for a list of formats valid for -d/--date.
4407 See :hg:`help dates` for a list of formats valid for -d/--date.
4408
4408
4409 See :hg:`help backout` for a way to reverse the effect of an
4409 See :hg:`help backout` for a way to reverse the effect of an
4410 earlier changeset.
4410 earlier changeset.
4411
4411
4412 Returns 0 on success.
4412 Returns 0 on success.
4413 """
4413 """
4414
4414
4415 if opts.get("date"):
4415 if opts.get("date"):
4416 if opts.get("rev"):
4416 if opts.get("rev"):
4417 raise error.Abort(_("you can't specify a revision and a date"))
4417 raise error.Abort(_("you can't specify a revision and a date"))
4418 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4418 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4419
4419
4420 parent, p2 = repo.dirstate.parents()
4420 parent, p2 = repo.dirstate.parents()
4421 if not opts.get('rev') and p2 != nullid:
4421 if not opts.get('rev') and p2 != nullid:
4422 # revert after merge is a trap for new users (issue2915)
4422 # revert after merge is a trap for new users (issue2915)
4423 raise error.Abort(_('uncommitted merge with no revision specified'),
4423 raise error.Abort(_('uncommitted merge with no revision specified'),
4424 hint=_("use 'hg update' or see 'hg help revert'"))
4424 hint=_("use 'hg update' or see 'hg help revert'"))
4425
4425
4426 ctx = scmutil.revsingle(repo, opts.get('rev'))
4426 ctx = scmutil.revsingle(repo, opts.get('rev'))
4427
4427
4428 if (not (pats or opts.get('include') or opts.get('exclude') or
4428 if (not (pats or opts.get('include') or opts.get('exclude') or
4429 opts.get('all') or opts.get('interactive'))):
4429 opts.get('all') or opts.get('interactive'))):
4430 msg = _("no files or directories specified")
4430 msg = _("no files or directories specified")
4431 if p2 != nullid:
4431 if p2 != nullid:
4432 hint = _("uncommitted merge, use --all to discard all changes,"
4432 hint = _("uncommitted merge, use --all to discard all changes,"
4433 " or 'hg update -C .' to abort the merge")
4433 " or 'hg update -C .' to abort the merge")
4434 raise error.Abort(msg, hint=hint)
4434 raise error.Abort(msg, hint=hint)
4435 dirty = any(repo.status())
4435 dirty = any(repo.status())
4436 node = ctx.node()
4436 node = ctx.node()
4437 if node != parent:
4437 if node != parent:
4438 if dirty:
4438 if dirty:
4439 hint = _("uncommitted changes, use --all to discard all"
4439 hint = _("uncommitted changes, use --all to discard all"
4440 " changes, or 'hg update %s' to update") % ctx.rev()
4440 " changes, or 'hg update %s' to update") % ctx.rev()
4441 else:
4441 else:
4442 hint = _("use --all to revert all files,"
4442 hint = _("use --all to revert all files,"
4443 " or 'hg update %s' to update") % ctx.rev()
4443 " or 'hg update %s' to update") % ctx.rev()
4444 elif dirty:
4444 elif dirty:
4445 hint = _("uncommitted changes, use --all to discard all changes")
4445 hint = _("uncommitted changes, use --all to discard all changes")
4446 else:
4446 else:
4447 hint = _("use --all to revert all files")
4447 hint = _("use --all to revert all files")
4448 raise error.Abort(msg, hint=hint)
4448 raise error.Abort(msg, hint=hint)
4449
4449
4450 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
4450 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
4451
4451
4452 @command('rollback', dryrunopts +
4452 @command('rollback', dryrunopts +
4453 [('f', 'force', False, _('ignore safety measures'))])
4453 [('f', 'force', False, _('ignore safety measures'))])
4454 def rollback(ui, repo, **opts):
4454 def rollback(ui, repo, **opts):
4455 """roll back the last transaction (DANGEROUS) (DEPRECATED)
4455 """roll back the last transaction (DANGEROUS) (DEPRECATED)
4456
4456
4457 Please use :hg:`commit --amend` instead of rollback to correct
4457 Please use :hg:`commit --amend` instead of rollback to correct
4458 mistakes in the last commit.
4458 mistakes in the last commit.
4459
4459
4460 This command should be used with care. There is only one level of
4460 This command should be used with care. There is only one level of
4461 rollback, and there is no way to undo a rollback. It will also
4461 rollback, and there is no way to undo a rollback. It will also
4462 restore the dirstate at the time of the last transaction, losing
4462 restore the dirstate at the time of the last transaction, losing
4463 any dirstate changes since that time. This command does not alter
4463 any dirstate changes since that time. This command does not alter
4464 the working directory.
4464 the working directory.
4465
4465
4466 Transactions are used to encapsulate the effects of all commands
4466 Transactions are used to encapsulate the effects of all commands
4467 that create new changesets or propagate existing changesets into a
4467 that create new changesets or propagate existing changesets into a
4468 repository.
4468 repository.
4469
4469
4470 .. container:: verbose
4470 .. container:: verbose
4471
4471
4472 For example, the following commands are transactional, and their
4472 For example, the following commands are transactional, and their
4473 effects can be rolled back:
4473 effects can be rolled back:
4474
4474
4475 - commit
4475 - commit
4476 - import
4476 - import
4477 - pull
4477 - pull
4478 - push (with this repository as the destination)
4478 - push (with this repository as the destination)
4479 - unbundle
4479 - unbundle
4480
4480
4481 To avoid permanent data loss, rollback will refuse to rollback a
4481 To avoid permanent data loss, rollback will refuse to rollback a
4482 commit transaction if it isn't checked out. Use --force to
4482 commit transaction if it isn't checked out. Use --force to
4483 override this protection.
4483 override this protection.
4484
4484
4485 The rollback command can be entirely disabled by setting the
4485 The rollback command can be entirely disabled by setting the
4486 ``ui.rollback`` configuration setting to false. If you're here
4486 ``ui.rollback`` configuration setting to false. If you're here
4487 because you want to use rollback and it's disabled, you can
4487 because you want to use rollback and it's disabled, you can
4488 re-enable the command by setting ``ui.rollback`` to true.
4488 re-enable the command by setting ``ui.rollback`` to true.
4489
4489
4490 This command is not intended for use on public repositories. Once
4490 This command is not intended for use on public repositories. Once
4491 changes are visible for pull by other users, rolling a transaction
4491 changes are visible for pull by other users, rolling a transaction
4492 back locally is ineffective (someone else may already have pulled
4492 back locally is ineffective (someone else may already have pulled
4493 the changes). Furthermore, a race is possible with readers of the
4493 the changes). Furthermore, a race is possible with readers of the
4494 repository; for example an in-progress pull from the repository
4494 repository; for example an in-progress pull from the repository
4495 may fail if a rollback is performed.
4495 may fail if a rollback is performed.
4496
4496
4497 Returns 0 on success, 1 if no rollback data is available.
4497 Returns 0 on success, 1 if no rollback data is available.
4498 """
4498 """
4499 if not ui.configbool('ui', 'rollback', True):
4499 if not ui.configbool('ui', 'rollback', True):
4500 raise error.Abort(_('rollback is disabled because it is unsafe'),
4500 raise error.Abort(_('rollback is disabled because it is unsafe'),
4501 hint=('see `hg help -v rollback` for information'))
4501 hint=('see `hg help -v rollback` for information'))
4502 return repo.rollback(dryrun=opts.get(r'dry_run'),
4502 return repo.rollback(dryrun=opts.get(r'dry_run'),
4503 force=opts.get(r'force'))
4503 force=opts.get(r'force'))
4504
4504
4505 @command('root', [])
4505 @command('root', [])
4506 def root(ui, repo):
4506 def root(ui, repo):
4507 """print the root (top) of the current working directory
4507 """print the root (top) of the current working directory
4508
4508
4509 Print the root directory of the current repository.
4509 Print the root directory of the current repository.
4510
4510
4511 Returns 0 on success.
4511 Returns 0 on success.
4512 """
4512 """
4513 ui.write(repo.root + "\n")
4513 ui.write(repo.root + "\n")
4514
4514
4515 @command('^serve',
4515 @command('^serve',
4516 [('A', 'accesslog', '', _('name of access log file to write to'),
4516 [('A', 'accesslog', '', _('name of access log file to write to'),
4517 _('FILE')),
4517 _('FILE')),
4518 ('d', 'daemon', None, _('run server in background')),
4518 ('d', 'daemon', None, _('run server in background')),
4519 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
4519 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
4520 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4520 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4521 # use string type, then we can check if something was passed
4521 # use string type, then we can check if something was passed
4522 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4522 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4523 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4523 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4524 _('ADDR')),
4524 _('ADDR')),
4525 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4525 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4526 _('PREFIX')),
4526 _('PREFIX')),
4527 ('n', 'name', '',
4527 ('n', 'name', '',
4528 _('name to show in web pages (default: working directory)'), _('NAME')),
4528 _('name to show in web pages (default: working directory)'), _('NAME')),
4529 ('', 'web-conf', '',
4529 ('', 'web-conf', '',
4530 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
4530 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
4531 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4531 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4532 _('FILE')),
4532 _('FILE')),
4533 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4533 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4534 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
4534 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
4535 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
4535 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
4536 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4536 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4537 ('', 'style', '', _('template style to use'), _('STYLE')),
4537 ('', 'style', '', _('template style to use'), _('STYLE')),
4538 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4538 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4539 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))]
4539 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))]
4540 + subrepoopts,
4540 + subrepoopts,
4541 _('[OPTION]...'),
4541 _('[OPTION]...'),
4542 optionalrepo=True)
4542 optionalrepo=True)
4543 def serve(ui, repo, **opts):
4543 def serve(ui, repo, **opts):
4544 """start stand-alone webserver
4544 """start stand-alone webserver
4545
4545
4546 Start a local HTTP repository browser and pull server. You can use
4546 Start a local HTTP repository browser and pull server. You can use
4547 this for ad-hoc sharing and browsing of repositories. It is
4547 this for ad-hoc sharing and browsing of repositories. It is
4548 recommended to use a real web server to serve a repository for
4548 recommended to use a real web server to serve a repository for
4549 longer periods of time.
4549 longer periods of time.
4550
4550
4551 Please note that the server does not implement access control.
4551 Please note that the server does not implement access control.
4552 This means that, by default, anybody can read from the server and
4552 This means that, by default, anybody can read from the server and
4553 nobody can write to it by default. Set the ``web.allow_push``
4553 nobody can write to it by default. Set the ``web.allow_push``
4554 option to ``*`` to allow everybody to push to the server. You
4554 option to ``*`` to allow everybody to push to the server. You
4555 should use a real web server if you need to authenticate users.
4555 should use a real web server if you need to authenticate users.
4556
4556
4557 By default, the server logs accesses to stdout and errors to
4557 By default, the server logs accesses to stdout and errors to
4558 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4558 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4559 files.
4559 files.
4560
4560
4561 To have the server choose a free port number to listen on, specify
4561 To have the server choose a free port number to listen on, specify
4562 a port number of 0; in this case, the server will print the port
4562 a port number of 0; in this case, the server will print the port
4563 number it uses.
4563 number it uses.
4564
4564
4565 Returns 0 on success.
4565 Returns 0 on success.
4566 """
4566 """
4567
4567
4568 opts = pycompat.byteskwargs(opts)
4568 opts = pycompat.byteskwargs(opts)
4569 if opts["stdio"] and opts["cmdserver"]:
4569 if opts["stdio"] and opts["cmdserver"]:
4570 raise error.Abort(_("cannot use --stdio with --cmdserver"))
4570 raise error.Abort(_("cannot use --stdio with --cmdserver"))
4571
4571
4572 if opts["stdio"]:
4572 if opts["stdio"]:
4573 if repo is None:
4573 if repo is None:
4574 raise error.RepoError(_("there is no Mercurial repository here"
4574 raise error.RepoError(_("there is no Mercurial repository here"
4575 " (.hg not found)"))
4575 " (.hg not found)"))
4576 s = sshserver.sshserver(ui, repo)
4576 s = sshserver.sshserver(ui, repo)
4577 s.serve_forever()
4577 s.serve_forever()
4578
4578
4579 service = server.createservice(ui, repo, opts)
4579 service = server.createservice(ui, repo, opts)
4580 return server.runservice(opts, initfn=service.init, runfn=service.run)
4580 return server.runservice(opts, initfn=service.init, runfn=service.run)
4581
4581
4582 @command('^status|st',
4582 @command('^status|st',
4583 [('A', 'all', None, _('show status of all files')),
4583 [('A', 'all', None, _('show status of all files')),
4584 ('m', 'modified', None, _('show only modified files')),
4584 ('m', 'modified', None, _('show only modified files')),
4585 ('a', 'added', None, _('show only added files')),
4585 ('a', 'added', None, _('show only added files')),
4586 ('r', 'removed', None, _('show only removed files')),
4586 ('r', 'removed', None, _('show only removed files')),
4587 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4587 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4588 ('c', 'clean', None, _('show only files without changes')),
4588 ('c', 'clean', None, _('show only files without changes')),
4589 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4589 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4590 ('i', 'ignored', None, _('show only ignored files')),
4590 ('i', 'ignored', None, _('show only ignored files')),
4591 ('n', 'no-status', None, _('hide status prefix')),
4591 ('n', 'no-status', None, _('hide status prefix')),
4592 ('C', 'copies', None, _('show source of copied files')),
4592 ('C', 'copies', None, _('show source of copied files')),
4593 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4593 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4594 ('', 'rev', [], _('show difference from revision'), _('REV')),
4594 ('', 'rev', [], _('show difference from revision'), _('REV')),
4595 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4595 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4596 ] + walkopts + subrepoopts + formatteropts,
4596 ] + walkopts + subrepoopts + formatteropts,
4597 _('[OPTION]... [FILE]...'),
4597 _('[OPTION]... [FILE]...'),
4598 inferrepo=True)
4598 inferrepo=True)
4599 def status(ui, repo, *pats, **opts):
4599 def status(ui, repo, *pats, **opts):
4600 """show changed files in the working directory
4600 """show changed files in the working directory
4601
4601
4602 Show status of files in the repository. If names are given, only
4602 Show status of files in the repository. If names are given, only
4603 files that match are shown. Files that are clean or ignored or
4603 files that match are shown. Files that are clean or ignored or
4604 the source of a copy/move operation, are not listed unless
4604 the source of a copy/move operation, are not listed unless
4605 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4605 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4606 Unless options described with "show only ..." are given, the
4606 Unless options described with "show only ..." are given, the
4607 options -mardu are used.
4607 options -mardu are used.
4608
4608
4609 Option -q/--quiet hides untracked (unknown and ignored) files
4609 Option -q/--quiet hides untracked (unknown and ignored) files
4610 unless explicitly requested with -u/--unknown or -i/--ignored.
4610 unless explicitly requested with -u/--unknown or -i/--ignored.
4611
4611
4612 .. note::
4612 .. note::
4613
4613
4614 :hg:`status` may appear to disagree with diff if permissions have
4614 :hg:`status` may appear to disagree with diff if permissions have
4615 changed or a merge has occurred. The standard diff format does
4615 changed or a merge has occurred. The standard diff format does
4616 not report permission changes and diff only reports changes
4616 not report permission changes and diff only reports changes
4617 relative to one merge parent.
4617 relative to one merge parent.
4618
4618
4619 If one revision is given, it is used as the base revision.
4619 If one revision is given, it is used as the base revision.
4620 If two revisions are given, the differences between them are
4620 If two revisions are given, the differences between them are
4621 shown. The --change option can also be used as a shortcut to list
4621 shown. The --change option can also be used as a shortcut to list
4622 the changed files of a revision from its first parent.
4622 the changed files of a revision from its first parent.
4623
4623
4624 The codes used to show the status of files are::
4624 The codes used to show the status of files are::
4625
4625
4626 M = modified
4626 M = modified
4627 A = added
4627 A = added
4628 R = removed
4628 R = removed
4629 C = clean
4629 C = clean
4630 ! = missing (deleted by non-hg command, but still tracked)
4630 ! = missing (deleted by non-hg command, but still tracked)
4631 ? = not tracked
4631 ? = not tracked
4632 I = ignored
4632 I = ignored
4633 = origin of the previous file (with --copies)
4633 = origin of the previous file (with --copies)
4634
4634
4635 .. container:: verbose
4635 .. container:: verbose
4636
4636
4637 Examples:
4637 Examples:
4638
4638
4639 - show changes in the working directory relative to a
4639 - show changes in the working directory relative to a
4640 changeset::
4640 changeset::
4641
4641
4642 hg status --rev 9353
4642 hg status --rev 9353
4643
4643
4644 - show changes in the working directory relative to the
4644 - show changes in the working directory relative to the
4645 current directory (see :hg:`help patterns` for more information)::
4645 current directory (see :hg:`help patterns` for more information)::
4646
4646
4647 hg status re:
4647 hg status re:
4648
4648
4649 - show all changes including copies in an existing changeset::
4649 - show all changes including copies in an existing changeset::
4650
4650
4651 hg status --copies --change 9353
4651 hg status --copies --change 9353
4652
4652
4653 - get a NUL separated list of added files, suitable for xargs::
4653 - get a NUL separated list of added files, suitable for xargs::
4654
4654
4655 hg status -an0
4655 hg status -an0
4656
4656
4657 Returns 0 on success.
4657 Returns 0 on success.
4658 """
4658 """
4659
4659
4660 opts = pycompat.byteskwargs(opts)
4660 opts = pycompat.byteskwargs(opts)
4661 revs = opts.get('rev')
4661 revs = opts.get('rev')
4662 change = opts.get('change')
4662 change = opts.get('change')
4663
4663
4664 if revs and change:
4664 if revs and change:
4665 msg = _('cannot specify --rev and --change at the same time')
4665 msg = _('cannot specify --rev and --change at the same time')
4666 raise error.Abort(msg)
4666 raise error.Abort(msg)
4667 elif change:
4667 elif change:
4668 node2 = scmutil.revsingle(repo, change, None).node()
4668 node2 = scmutil.revsingle(repo, change, None).node()
4669 node1 = repo[node2].p1().node()
4669 node1 = repo[node2].p1().node()
4670 else:
4670 else:
4671 node1, node2 = scmutil.revpair(repo, revs)
4671 node1, node2 = scmutil.revpair(repo, revs)
4672
4672
4673 if pats or ui.configbool('commands', 'status.relative'):
4673 if pats or ui.configbool('commands', 'status.relative'):
4674 cwd = repo.getcwd()
4674 cwd = repo.getcwd()
4675 else:
4675 else:
4676 cwd = ''
4676 cwd = ''
4677
4677
4678 if opts.get('print0'):
4678 if opts.get('print0'):
4679 end = '\0'
4679 end = '\0'
4680 else:
4680 else:
4681 end = '\n'
4681 end = '\n'
4682 copy = {}
4682 copy = {}
4683 states = 'modified added removed deleted unknown ignored clean'.split()
4683 states = 'modified added removed deleted unknown ignored clean'.split()
4684 show = [k for k in states if opts.get(k)]
4684 show = [k for k in states if opts.get(k)]
4685 if opts.get('all'):
4685 if opts.get('all'):
4686 show += ui.quiet and (states[:4] + ['clean']) or states
4686 show += ui.quiet and (states[:4] + ['clean']) or states
4687 if not show:
4687 if not show:
4688 if ui.quiet:
4688 if ui.quiet:
4689 show = states[:4]
4689 show = states[:4]
4690 else:
4690 else:
4691 show = states[:5]
4691 show = states[:5]
4692
4692
4693 m = scmutil.match(repo[node2], pats, opts)
4693 m = scmutil.match(repo[node2], pats, opts)
4694 stat = repo.status(node1, node2, m,
4694 stat = repo.status(node1, node2, m,
4695 'ignored' in show, 'clean' in show, 'unknown' in show,
4695 'ignored' in show, 'clean' in show, 'unknown' in show,
4696 opts.get('subrepos'))
4696 opts.get('subrepos'))
4697 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
4697 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
4698
4698
4699 if (opts.get('all') or opts.get('copies')
4699 if (opts.get('all') or opts.get('copies')
4700 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
4700 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
4701 copy = copies.pathcopies(repo[node1], repo[node2], m)
4701 copy = copies.pathcopies(repo[node1], repo[node2], m)
4702
4702
4703 ui.pager('status')
4703 ui.pager('status')
4704 fm = ui.formatter('status', opts)
4704 fm = ui.formatter('status', opts)
4705 fmt = '%s' + end
4705 fmt = '%s' + end
4706 showchar = not opts.get('no_status')
4706 showchar = not opts.get('no_status')
4707
4707
4708 for state, char, files in changestates:
4708 for state, char, files in changestates:
4709 if state in show:
4709 if state in show:
4710 label = 'status.' + state
4710 label = 'status.' + state
4711 for f in files:
4711 for f in files:
4712 fm.startitem()
4712 fm.startitem()
4713 fm.condwrite(showchar, 'status', '%s ', char, label=label)
4713 fm.condwrite(showchar, 'status', '%s ', char, label=label)
4714 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
4714 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
4715 if f in copy:
4715 if f in copy:
4716 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
4716 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
4717 label='status.copied')
4717 label='status.copied')
4718 fm.end()
4718 fm.end()
4719
4719
4720 @command('^summary|sum',
4720 @command('^summary|sum',
4721 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
4721 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
4722 def summary(ui, repo, **opts):
4722 def summary(ui, repo, **opts):
4723 """summarize working directory state
4723 """summarize working directory state
4724
4724
4725 This generates a brief summary of the working directory state,
4725 This generates a brief summary of the working directory state,
4726 including parents, branch, commit status, phase and available updates.
4726 including parents, branch, commit status, phase and available updates.
4727
4727
4728 With the --remote option, this will check the default paths for
4728 With the --remote option, this will check the default paths for
4729 incoming and outgoing changes. This can be time-consuming.
4729 incoming and outgoing changes. This can be time-consuming.
4730
4730
4731 Returns 0 on success.
4731 Returns 0 on success.
4732 """
4732 """
4733
4733
4734 opts = pycompat.byteskwargs(opts)
4734 opts = pycompat.byteskwargs(opts)
4735 ui.pager('summary')
4735 ui.pager('summary')
4736 ctx = repo[None]
4736 ctx = repo[None]
4737 parents = ctx.parents()
4737 parents = ctx.parents()
4738 pnode = parents[0].node()
4738 pnode = parents[0].node()
4739 marks = []
4739 marks = []
4740
4740
4741 ms = None
4741 ms = None
4742 try:
4742 try:
4743 ms = mergemod.mergestate.read(repo)
4743 ms = mergemod.mergestate.read(repo)
4744 except error.UnsupportedMergeRecords as e:
4744 except error.UnsupportedMergeRecords as e:
4745 s = ' '.join(e.recordtypes)
4745 s = ' '.join(e.recordtypes)
4746 ui.warn(
4746 ui.warn(
4747 _('warning: merge state has unsupported record types: %s\n') % s)
4747 _('warning: merge state has unsupported record types: %s\n') % s)
4748 unresolved = 0
4748 unresolved = 0
4749 else:
4749 else:
4750 unresolved = [f for f in ms if ms[f] == 'u']
4750 unresolved = [f for f in ms if ms[f] == 'u']
4751
4751
4752 for p in parents:
4752 for p in parents:
4753 # label with log.changeset (instead of log.parent) since this
4753 # label with log.changeset (instead of log.parent) since this
4754 # shows a working directory parent *changeset*:
4754 # shows a working directory parent *changeset*:
4755 # i18n: column positioning for "hg summary"
4755 # i18n: column positioning for "hg summary"
4756 ui.write(_('parent: %d:%s ') % (p.rev(), p),
4756 ui.write(_('parent: %d:%s ') % (p.rev(), p),
4757 label=cmdutil._changesetlabels(p))
4757 label=cmdutil._changesetlabels(p))
4758 ui.write(' '.join(p.tags()), label='log.tag')
4758 ui.write(' '.join(p.tags()), label='log.tag')
4759 if p.bookmarks():
4759 if p.bookmarks():
4760 marks.extend(p.bookmarks())
4760 marks.extend(p.bookmarks())
4761 if p.rev() == -1:
4761 if p.rev() == -1:
4762 if not len(repo):
4762 if not len(repo):
4763 ui.write(_(' (empty repository)'))
4763 ui.write(_(' (empty repository)'))
4764 else:
4764 else:
4765 ui.write(_(' (no revision checked out)'))
4765 ui.write(_(' (no revision checked out)'))
4766 if p.obsolete():
4766 if p.obsolete():
4767 ui.write(_(' (obsolete)'))
4767 ui.write(_(' (obsolete)'))
4768 if p.troubled():
4768 if p.troubled():
4769 ui.write(' ('
4769 ui.write(' ('
4770 + ', '.join(ui.label(trouble, 'trouble.%s' % trouble)
4770 + ', '.join(ui.label(trouble, 'trouble.%s' % trouble)
4771 for trouble in p.troubles())
4771 for trouble in p.troubles())
4772 + ')')
4772 + ')')
4773 ui.write('\n')
4773 ui.write('\n')
4774 if p.description():
4774 if p.description():
4775 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
4775 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
4776 label='log.summary')
4776 label='log.summary')
4777
4777
4778 branch = ctx.branch()
4778 branch = ctx.branch()
4779 bheads = repo.branchheads(branch)
4779 bheads = repo.branchheads(branch)
4780 # i18n: column positioning for "hg summary"
4780 # i18n: column positioning for "hg summary"
4781 m = _('branch: %s\n') % branch
4781 m = _('branch: %s\n') % branch
4782 if branch != 'default':
4782 if branch != 'default':
4783 ui.write(m, label='log.branch')
4783 ui.write(m, label='log.branch')
4784 else:
4784 else:
4785 ui.status(m, label='log.branch')
4785 ui.status(m, label='log.branch')
4786
4786
4787 if marks:
4787 if marks:
4788 active = repo._activebookmark
4788 active = repo._activebookmark
4789 # i18n: column positioning for "hg summary"
4789 # i18n: column positioning for "hg summary"
4790 ui.write(_('bookmarks:'), label='log.bookmark')
4790 ui.write(_('bookmarks:'), label='log.bookmark')
4791 if active is not None:
4791 if active is not None:
4792 if active in marks:
4792 if active in marks:
4793 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
4793 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
4794 marks.remove(active)
4794 marks.remove(active)
4795 else:
4795 else:
4796 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
4796 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
4797 for m in marks:
4797 for m in marks:
4798 ui.write(' ' + m, label='log.bookmark')
4798 ui.write(' ' + m, label='log.bookmark')
4799 ui.write('\n', label='log.bookmark')
4799 ui.write('\n', label='log.bookmark')
4800
4800
4801 status = repo.status(unknown=True)
4801 status = repo.status(unknown=True)
4802
4802
4803 c = repo.dirstate.copies()
4803 c = repo.dirstate.copies()
4804 copied, renamed = [], []
4804 copied, renamed = [], []
4805 for d, s in c.iteritems():
4805 for d, s in c.iteritems():
4806 if s in status.removed:
4806 if s in status.removed:
4807 status.removed.remove(s)
4807 status.removed.remove(s)
4808 renamed.append(d)
4808 renamed.append(d)
4809 else:
4809 else:
4810 copied.append(d)
4810 copied.append(d)
4811 if d in status.added:
4811 if d in status.added:
4812 status.added.remove(d)
4812 status.added.remove(d)
4813
4813
4814 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
4814 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
4815
4815
4816 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
4816 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
4817 (ui.label(_('%d added'), 'status.added'), status.added),
4817 (ui.label(_('%d added'), 'status.added'), status.added),
4818 (ui.label(_('%d removed'), 'status.removed'), status.removed),
4818 (ui.label(_('%d removed'), 'status.removed'), status.removed),
4819 (ui.label(_('%d renamed'), 'status.copied'), renamed),
4819 (ui.label(_('%d renamed'), 'status.copied'), renamed),
4820 (ui.label(_('%d copied'), 'status.copied'), copied),
4820 (ui.label(_('%d copied'), 'status.copied'), copied),
4821 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
4821 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
4822 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
4822 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
4823 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
4823 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
4824 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
4824 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
4825 t = []
4825 t = []
4826 for l, s in labels:
4826 for l, s in labels:
4827 if s:
4827 if s:
4828 t.append(l % len(s))
4828 t.append(l % len(s))
4829
4829
4830 t = ', '.join(t)
4830 t = ', '.join(t)
4831 cleanworkdir = False
4831 cleanworkdir = False
4832
4832
4833 if repo.vfs.exists('graftstate'):
4833 if repo.vfs.exists('graftstate'):
4834 t += _(' (graft in progress)')
4834 t += _(' (graft in progress)')
4835 if repo.vfs.exists('updatestate'):
4835 if repo.vfs.exists('updatestate'):
4836 t += _(' (interrupted update)')
4836 t += _(' (interrupted update)')
4837 elif len(parents) > 1:
4837 elif len(parents) > 1:
4838 t += _(' (merge)')
4838 t += _(' (merge)')
4839 elif branch != parents[0].branch():
4839 elif branch != parents[0].branch():
4840 t += _(' (new branch)')
4840 t += _(' (new branch)')
4841 elif (parents[0].closesbranch() and
4841 elif (parents[0].closesbranch() and
4842 pnode in repo.branchheads(branch, closed=True)):
4842 pnode in repo.branchheads(branch, closed=True)):
4843 t += _(' (head closed)')
4843 t += _(' (head closed)')
4844 elif not (status.modified or status.added or status.removed or renamed or
4844 elif not (status.modified or status.added or status.removed or renamed or
4845 copied or subs):
4845 copied or subs):
4846 t += _(' (clean)')
4846 t += _(' (clean)')
4847 cleanworkdir = True
4847 cleanworkdir = True
4848 elif pnode not in bheads:
4848 elif pnode not in bheads:
4849 t += _(' (new branch head)')
4849 t += _(' (new branch head)')
4850
4850
4851 if parents:
4851 if parents:
4852 pendingphase = max(p.phase() for p in parents)
4852 pendingphase = max(p.phase() for p in parents)
4853 else:
4853 else:
4854 pendingphase = phases.public
4854 pendingphase = phases.public
4855
4855
4856 if pendingphase > phases.newcommitphase(ui):
4856 if pendingphase > phases.newcommitphase(ui):
4857 t += ' (%s)' % phases.phasenames[pendingphase]
4857 t += ' (%s)' % phases.phasenames[pendingphase]
4858
4858
4859 if cleanworkdir:
4859 if cleanworkdir:
4860 # i18n: column positioning for "hg summary"
4860 # i18n: column positioning for "hg summary"
4861 ui.status(_('commit: %s\n') % t.strip())
4861 ui.status(_('commit: %s\n') % t.strip())
4862 else:
4862 else:
4863 # i18n: column positioning for "hg summary"
4863 # i18n: column positioning for "hg summary"
4864 ui.write(_('commit: %s\n') % t.strip())
4864 ui.write(_('commit: %s\n') % t.strip())
4865
4865
4866 # all ancestors of branch heads - all ancestors of parent = new csets
4866 # all ancestors of branch heads - all ancestors of parent = new csets
4867 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
4867 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
4868 bheads))
4868 bheads))
4869
4869
4870 if new == 0:
4870 if new == 0:
4871 # i18n: column positioning for "hg summary"
4871 # i18n: column positioning for "hg summary"
4872 ui.status(_('update: (current)\n'))
4872 ui.status(_('update: (current)\n'))
4873 elif pnode not in bheads:
4873 elif pnode not in bheads:
4874 # i18n: column positioning for "hg summary"
4874 # i18n: column positioning for "hg summary"
4875 ui.write(_('update: %d new changesets (update)\n') % new)
4875 ui.write(_('update: %d new changesets (update)\n') % new)
4876 else:
4876 else:
4877 # i18n: column positioning for "hg summary"
4877 # i18n: column positioning for "hg summary"
4878 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
4878 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
4879 (new, len(bheads)))
4879 (new, len(bheads)))
4880
4880
4881 t = []
4881 t = []
4882 draft = len(repo.revs('draft()'))
4882 draft = len(repo.revs('draft()'))
4883 if draft:
4883 if draft:
4884 t.append(_('%d draft') % draft)
4884 t.append(_('%d draft') % draft)
4885 secret = len(repo.revs('secret()'))
4885 secret = len(repo.revs('secret()'))
4886 if secret:
4886 if secret:
4887 t.append(_('%d secret') % secret)
4887 t.append(_('%d secret') % secret)
4888
4888
4889 if draft or secret:
4889 if draft or secret:
4890 ui.status(_('phases: %s\n') % ', '.join(t))
4890 ui.status(_('phases: %s\n') % ', '.join(t))
4891
4891
4892 if obsolete.isenabled(repo, obsolete.createmarkersopt):
4892 if obsolete.isenabled(repo, obsolete.createmarkersopt):
4893 for trouble in ("unstable", "divergent", "bumped"):
4893 for trouble in ("unstable", "divergent", "bumped"):
4894 numtrouble = len(repo.revs(trouble + "()"))
4894 numtrouble = len(repo.revs(trouble + "()"))
4895 # We write all the possibilities to ease translation
4895 # We write all the possibilities to ease translation
4896 troublemsg = {
4896 troublemsg = {
4897 "unstable": _("unstable: %d changesets"),
4897 "unstable": _("unstable: %d changesets"),
4898 "divergent": _("divergent: %d changesets"),
4898 "divergent": _("divergent: %d changesets"),
4899 "bumped": _("bumped: %d changesets"),
4899 "bumped": _("bumped: %d changesets"),
4900 }
4900 }
4901 if numtrouble > 0:
4901 if numtrouble > 0:
4902 ui.status(troublemsg[trouble] % numtrouble + "\n")
4902 ui.status(troublemsg[trouble] % numtrouble + "\n")
4903
4903
4904 cmdutil.summaryhooks(ui, repo)
4904 cmdutil.summaryhooks(ui, repo)
4905
4905
4906 if opts.get('remote'):
4906 if opts.get('remote'):
4907 needsincoming, needsoutgoing = True, True
4907 needsincoming, needsoutgoing = True, True
4908 else:
4908 else:
4909 needsincoming, needsoutgoing = False, False
4909 needsincoming, needsoutgoing = False, False
4910 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
4910 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
4911 if i:
4911 if i:
4912 needsincoming = True
4912 needsincoming = True
4913 if o:
4913 if o:
4914 needsoutgoing = True
4914 needsoutgoing = True
4915 if not needsincoming and not needsoutgoing:
4915 if not needsincoming and not needsoutgoing:
4916 return
4916 return
4917
4917
4918 def getincoming():
4918 def getincoming():
4919 source, branches = hg.parseurl(ui.expandpath('default'))
4919 source, branches = hg.parseurl(ui.expandpath('default'))
4920 sbranch = branches[0]
4920 sbranch = branches[0]
4921 try:
4921 try:
4922 other = hg.peer(repo, {}, source)
4922 other = hg.peer(repo, {}, source)
4923 except error.RepoError:
4923 except error.RepoError:
4924 if opts.get('remote'):
4924 if opts.get('remote'):
4925 raise
4925 raise
4926 return source, sbranch, None, None, None
4926 return source, sbranch, None, None, None
4927 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
4927 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
4928 if revs:
4928 if revs:
4929 revs = [other.lookup(rev) for rev in revs]
4929 revs = [other.lookup(rev) for rev in revs]
4930 ui.debug('comparing with %s\n' % util.hidepassword(source))
4930 ui.debug('comparing with %s\n' % util.hidepassword(source))
4931 repo.ui.pushbuffer()
4931 repo.ui.pushbuffer()
4932 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
4932 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
4933 repo.ui.popbuffer()
4933 repo.ui.popbuffer()
4934 return source, sbranch, other, commoninc, commoninc[1]
4934 return source, sbranch, other, commoninc, commoninc[1]
4935
4935
4936 if needsincoming:
4936 if needsincoming:
4937 source, sbranch, sother, commoninc, incoming = getincoming()
4937 source, sbranch, sother, commoninc, incoming = getincoming()
4938 else:
4938 else:
4939 source = sbranch = sother = commoninc = incoming = None
4939 source = sbranch = sother = commoninc = incoming = None
4940
4940
4941 def getoutgoing():
4941 def getoutgoing():
4942 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
4942 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
4943 dbranch = branches[0]
4943 dbranch = branches[0]
4944 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
4944 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
4945 if source != dest:
4945 if source != dest:
4946 try:
4946 try:
4947 dother = hg.peer(repo, {}, dest)
4947 dother = hg.peer(repo, {}, dest)
4948 except error.RepoError:
4948 except error.RepoError:
4949 if opts.get('remote'):
4949 if opts.get('remote'):
4950 raise
4950 raise
4951 return dest, dbranch, None, None
4951 return dest, dbranch, None, None
4952 ui.debug('comparing with %s\n' % util.hidepassword(dest))
4952 ui.debug('comparing with %s\n' % util.hidepassword(dest))
4953 elif sother is None:
4953 elif sother is None:
4954 # there is no explicit destination peer, but source one is invalid
4954 # there is no explicit destination peer, but source one is invalid
4955 return dest, dbranch, None, None
4955 return dest, dbranch, None, None
4956 else:
4956 else:
4957 dother = sother
4957 dother = sother
4958 if (source != dest or (sbranch is not None and sbranch != dbranch)):
4958 if (source != dest or (sbranch is not None and sbranch != dbranch)):
4959 common = None
4959 common = None
4960 else:
4960 else:
4961 common = commoninc
4961 common = commoninc
4962 if revs:
4962 if revs:
4963 revs = [repo.lookup(rev) for rev in revs]
4963 revs = [repo.lookup(rev) for rev in revs]
4964 repo.ui.pushbuffer()
4964 repo.ui.pushbuffer()
4965 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
4965 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
4966 commoninc=common)
4966 commoninc=common)
4967 repo.ui.popbuffer()
4967 repo.ui.popbuffer()
4968 return dest, dbranch, dother, outgoing
4968 return dest, dbranch, dother, outgoing
4969
4969
4970 if needsoutgoing:
4970 if needsoutgoing:
4971 dest, dbranch, dother, outgoing = getoutgoing()
4971 dest, dbranch, dother, outgoing = getoutgoing()
4972 else:
4972 else:
4973 dest = dbranch = dother = outgoing = None
4973 dest = dbranch = dother = outgoing = None
4974
4974
4975 if opts.get('remote'):
4975 if opts.get('remote'):
4976 t = []
4976 t = []
4977 if incoming:
4977 if incoming:
4978 t.append(_('1 or more incoming'))
4978 t.append(_('1 or more incoming'))
4979 o = outgoing.missing
4979 o = outgoing.missing
4980 if o:
4980 if o:
4981 t.append(_('%d outgoing') % len(o))
4981 t.append(_('%d outgoing') % len(o))
4982 other = dother or sother
4982 other = dother or sother
4983 if 'bookmarks' in other.listkeys('namespaces'):
4983 if 'bookmarks' in other.listkeys('namespaces'):
4984 counts = bookmarks.summary(repo, other)
4984 counts = bookmarks.summary(repo, other)
4985 if counts[0] > 0:
4985 if counts[0] > 0:
4986 t.append(_('%d incoming bookmarks') % counts[0])
4986 t.append(_('%d incoming bookmarks') % counts[0])
4987 if counts[1] > 0:
4987 if counts[1] > 0:
4988 t.append(_('%d outgoing bookmarks') % counts[1])
4988 t.append(_('%d outgoing bookmarks') % counts[1])
4989
4989
4990 if t:
4990 if t:
4991 # i18n: column positioning for "hg summary"
4991 # i18n: column positioning for "hg summary"
4992 ui.write(_('remote: %s\n') % (', '.join(t)))
4992 ui.write(_('remote: %s\n') % (', '.join(t)))
4993 else:
4993 else:
4994 # i18n: column positioning for "hg summary"
4994 # i18n: column positioning for "hg summary"
4995 ui.status(_('remote: (synced)\n'))
4995 ui.status(_('remote: (synced)\n'))
4996
4996
4997 cmdutil.summaryremotehooks(ui, repo, opts,
4997 cmdutil.summaryremotehooks(ui, repo, opts,
4998 ((source, sbranch, sother, commoninc),
4998 ((source, sbranch, sother, commoninc),
4999 (dest, dbranch, dother, outgoing)))
4999 (dest, dbranch, dother, outgoing)))
5000
5000
5001 @command('tag',
5001 @command('tag',
5002 [('f', 'force', None, _('force tag')),
5002 [('f', 'force', None, _('force tag')),
5003 ('l', 'local', None, _('make the tag local')),
5003 ('l', 'local', None, _('make the tag local')),
5004 ('r', 'rev', '', _('revision to tag'), _('REV')),
5004 ('r', 'rev', '', _('revision to tag'), _('REV')),
5005 ('', 'remove', None, _('remove a tag')),
5005 ('', 'remove', None, _('remove a tag')),
5006 # -l/--local is already there, commitopts cannot be used
5006 # -l/--local is already there, commitopts cannot be used
5007 ('e', 'edit', None, _('invoke editor on commit messages')),
5007 ('e', 'edit', None, _('invoke editor on commit messages')),
5008 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5008 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5009 ] + commitopts2,
5009 ] + commitopts2,
5010 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5010 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5011 def tag(ui, repo, name1, *names, **opts):
5011 def tag(ui, repo, name1, *names, **opts):
5012 """add one or more tags for the current or given revision
5012 """add one or more tags for the current or given revision
5013
5013
5014 Name a particular revision using <name>.
5014 Name a particular revision using <name>.
5015
5015
5016 Tags are used to name particular revisions of the repository and are
5016 Tags are used to name particular revisions of the repository and are
5017 very useful to compare different revisions, to go back to significant
5017 very useful to compare different revisions, to go back to significant
5018 earlier versions or to mark branch points as releases, etc. Changing
5018 earlier versions or to mark branch points as releases, etc. Changing
5019 an existing tag is normally disallowed; use -f/--force to override.
5019 an existing tag is normally disallowed; use -f/--force to override.
5020
5020
5021 If no revision is given, the parent of the working directory is
5021 If no revision is given, the parent of the working directory is
5022 used.
5022 used.
5023
5023
5024 To facilitate version control, distribution, and merging of tags,
5024 To facilitate version control, distribution, and merging of tags,
5025 they are stored as a file named ".hgtags" which is managed similarly
5025 they are stored as a file named ".hgtags" which is managed similarly
5026 to other project files and can be hand-edited if necessary. This
5026 to other project files and can be hand-edited if necessary. This
5027 also means that tagging creates a new commit. The file
5027 also means that tagging creates a new commit. The file
5028 ".hg/localtags" is used for local tags (not shared among
5028 ".hg/localtags" is used for local tags (not shared among
5029 repositories).
5029 repositories).
5030
5030
5031 Tag commits are usually made at the head of a branch. If the parent
5031 Tag commits are usually made at the head of a branch. If the parent
5032 of the working directory is not a branch head, :hg:`tag` aborts; use
5032 of the working directory is not a branch head, :hg:`tag` aborts; use
5033 -f/--force to force the tag commit to be based on a non-head
5033 -f/--force to force the tag commit to be based on a non-head
5034 changeset.
5034 changeset.
5035
5035
5036 See :hg:`help dates` for a list of formats valid for -d/--date.
5036 See :hg:`help dates` for a list of formats valid for -d/--date.
5037
5037
5038 Since tag names have priority over branch names during revision
5038 Since tag names have priority over branch names during revision
5039 lookup, using an existing branch name as a tag name is discouraged.
5039 lookup, using an existing branch name as a tag name is discouraged.
5040
5040
5041 Returns 0 on success.
5041 Returns 0 on success.
5042 """
5042 """
5043 opts = pycompat.byteskwargs(opts)
5043 opts = pycompat.byteskwargs(opts)
5044 wlock = lock = None
5044 wlock = lock = None
5045 try:
5045 try:
5046 wlock = repo.wlock()
5046 wlock = repo.wlock()
5047 lock = repo.lock()
5047 lock = repo.lock()
5048 rev_ = "."
5048 rev_ = "."
5049 names = [t.strip() for t in (name1,) + names]
5049 names = [t.strip() for t in (name1,) + names]
5050 if len(names) != len(set(names)):
5050 if len(names) != len(set(names)):
5051 raise error.Abort(_('tag names must be unique'))
5051 raise error.Abort(_('tag names must be unique'))
5052 for n in names:
5052 for n in names:
5053 scmutil.checknewlabel(repo, n, 'tag')
5053 scmutil.checknewlabel(repo, n, 'tag')
5054 if not n:
5054 if not n:
5055 raise error.Abort(_('tag names cannot consist entirely of '
5055 raise error.Abort(_('tag names cannot consist entirely of '
5056 'whitespace'))
5056 'whitespace'))
5057 if opts.get('rev') and opts.get('remove'):
5057 if opts.get('rev') and opts.get('remove'):
5058 raise error.Abort(_("--rev and --remove are incompatible"))
5058 raise error.Abort(_("--rev and --remove are incompatible"))
5059 if opts.get('rev'):
5059 if opts.get('rev'):
5060 rev_ = opts['rev']
5060 rev_ = opts['rev']
5061 message = opts.get('message')
5061 message = opts.get('message')
5062 if opts.get('remove'):
5062 if opts.get('remove'):
5063 if opts.get('local'):
5063 if opts.get('local'):
5064 expectedtype = 'local'
5064 expectedtype = 'local'
5065 else:
5065 else:
5066 expectedtype = 'global'
5066 expectedtype = 'global'
5067
5067
5068 for n in names:
5068 for n in names:
5069 if not repo.tagtype(n):
5069 if not repo.tagtype(n):
5070 raise error.Abort(_("tag '%s' does not exist") % n)
5070 raise error.Abort(_("tag '%s' does not exist") % n)
5071 if repo.tagtype(n) != expectedtype:
5071 if repo.tagtype(n) != expectedtype:
5072 if expectedtype == 'global':
5072 if expectedtype == 'global':
5073 raise error.Abort(_("tag '%s' is not a global tag") % n)
5073 raise error.Abort(_("tag '%s' is not a global tag") % n)
5074 else:
5074 else:
5075 raise error.Abort(_("tag '%s' is not a local tag") % n)
5075 raise error.Abort(_("tag '%s' is not a local tag") % n)
5076 rev_ = 'null'
5076 rev_ = 'null'
5077 if not message:
5077 if not message:
5078 # we don't translate commit messages
5078 # we don't translate commit messages
5079 message = 'Removed tag %s' % ', '.join(names)
5079 message = 'Removed tag %s' % ', '.join(names)
5080 elif not opts.get('force'):
5080 elif not opts.get('force'):
5081 for n in names:
5081 for n in names:
5082 if n in repo.tags():
5082 if n in repo.tags():
5083 raise error.Abort(_("tag '%s' already exists "
5083 raise error.Abort(_("tag '%s' already exists "
5084 "(use -f to force)") % n)
5084 "(use -f to force)") % n)
5085 if not opts.get('local'):
5085 if not opts.get('local'):
5086 p1, p2 = repo.dirstate.parents()
5086 p1, p2 = repo.dirstate.parents()
5087 if p2 != nullid:
5087 if p2 != nullid:
5088 raise error.Abort(_('uncommitted merge'))
5088 raise error.Abort(_('uncommitted merge'))
5089 bheads = repo.branchheads()
5089 bheads = repo.branchheads()
5090 if not opts.get('force') and bheads and p1 not in bheads:
5090 if not opts.get('force') and bheads and p1 not in bheads:
5091 raise error.Abort(_('working directory is not at a branch head '
5091 raise error.Abort(_('working directory is not at a branch head '
5092 '(use -f to force)'))
5092 '(use -f to force)'))
5093 r = scmutil.revsingle(repo, rev_).node()
5093 r = scmutil.revsingle(repo, rev_).node()
5094
5094
5095 if not message:
5095 if not message:
5096 # we don't translate commit messages
5096 # we don't translate commit messages
5097 message = ('Added tag %s for changeset %s' %
5097 message = ('Added tag %s for changeset %s' %
5098 (', '.join(names), short(r)))
5098 (', '.join(names), short(r)))
5099
5099
5100 date = opts.get('date')
5100 date = opts.get('date')
5101 if date:
5101 if date:
5102 date = util.parsedate(date)
5102 date = util.parsedate(date)
5103
5103
5104 if opts.get('remove'):
5104 if opts.get('remove'):
5105 editform = 'tag.remove'
5105 editform = 'tag.remove'
5106 else:
5106 else:
5107 editform = 'tag.add'
5107 editform = 'tag.add'
5108 editor = cmdutil.getcommiteditor(editform=editform,
5108 editor = cmdutil.getcommiteditor(editform=editform,
5109 **pycompat.strkwargs(opts))
5109 **pycompat.strkwargs(opts))
5110
5110
5111 # don't allow tagging the null rev
5111 # don't allow tagging the null rev
5112 if (not opts.get('remove') and
5112 if (not opts.get('remove') and
5113 scmutil.revsingle(repo, rev_).rev() == nullrev):
5113 scmutil.revsingle(repo, rev_).rev() == nullrev):
5114 raise error.Abort(_("cannot tag null revision"))
5114 raise error.Abort(_("cannot tag null revision"))
5115
5115
5116 tagsmod.tag(repo, names, r, message, opts.get('local'),
5116 tagsmod.tag(repo, names, r, message, opts.get('local'),
5117 opts.get('user'), date, editor=editor)
5117 opts.get('user'), date, editor=editor)
5118 finally:
5118 finally:
5119 release(lock, wlock)
5119 release(lock, wlock)
5120
5120
5121 @command('tags', formatteropts, '')
5121 @command('tags', formatteropts, '')
5122 def tags(ui, repo, **opts):
5122 def tags(ui, repo, **opts):
5123 """list repository tags
5123 """list repository tags
5124
5124
5125 This lists both regular and local tags. When the -v/--verbose
5125 This lists both regular and local tags. When the -v/--verbose
5126 switch is used, a third column "local" is printed for local tags.
5126 switch is used, a third column "local" is printed for local tags.
5127 When the -q/--quiet switch is used, only the tag name is printed.
5127 When the -q/--quiet switch is used, only the tag name is printed.
5128
5128
5129 Returns 0 on success.
5129 Returns 0 on success.
5130 """
5130 """
5131
5131
5132 opts = pycompat.byteskwargs(opts)
5132 opts = pycompat.byteskwargs(opts)
5133 ui.pager('tags')
5133 ui.pager('tags')
5134 fm = ui.formatter('tags', opts)
5134 fm = ui.formatter('tags', opts)
5135 hexfunc = fm.hexfunc
5135 hexfunc = fm.hexfunc
5136 tagtype = ""
5136 tagtype = ""
5137
5137
5138 for t, n in reversed(repo.tagslist()):
5138 for t, n in reversed(repo.tagslist()):
5139 hn = hexfunc(n)
5139 hn = hexfunc(n)
5140 label = 'tags.normal'
5140 label = 'tags.normal'
5141 tagtype = ''
5141 tagtype = ''
5142 if repo.tagtype(t) == 'local':
5142 if repo.tagtype(t) == 'local':
5143 label = 'tags.local'
5143 label = 'tags.local'
5144 tagtype = 'local'
5144 tagtype = 'local'
5145
5145
5146 fm.startitem()
5146 fm.startitem()
5147 fm.write('tag', '%s', t, label=label)
5147 fm.write('tag', '%s', t, label=label)
5148 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5148 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5149 fm.condwrite(not ui.quiet, 'rev node', fmt,
5149 fm.condwrite(not ui.quiet, 'rev node', fmt,
5150 repo.changelog.rev(n), hn, label=label)
5150 repo.changelog.rev(n), hn, label=label)
5151 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5151 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5152 tagtype, label=label)
5152 tagtype, label=label)
5153 fm.plain('\n')
5153 fm.plain('\n')
5154 fm.end()
5154 fm.end()
5155
5155
5156 @command('tip',
5156 @command('tip',
5157 [('p', 'patch', None, _('show patch')),
5157 [('p', 'patch', None, _('show patch')),
5158 ('g', 'git', None, _('use git extended diff format')),
5158 ('g', 'git', None, _('use git extended diff format')),
5159 ] + templateopts,
5159 ] + templateopts,
5160 _('[-p] [-g]'))
5160 _('[-p] [-g]'))
5161 def tip(ui, repo, **opts):
5161 def tip(ui, repo, **opts):
5162 """show the tip revision (DEPRECATED)
5162 """show the tip revision (DEPRECATED)
5163
5163
5164 The tip revision (usually just called the tip) is the changeset
5164 The tip revision (usually just called the tip) is the changeset
5165 most recently added to the repository (and therefore the most
5165 most recently added to the repository (and therefore the most
5166 recently changed head).
5166 recently changed head).
5167
5167
5168 If you have just made a commit, that commit will be the tip. If
5168 If you have just made a commit, that commit will be the tip. If
5169 you have just pulled changes from another repository, the tip of
5169 you have just pulled changes from another repository, the tip of
5170 that repository becomes the current tip. The "tip" tag is special
5170 that repository becomes the current tip. The "tip" tag is special
5171 and cannot be renamed or assigned to a different changeset.
5171 and cannot be renamed or assigned to a different changeset.
5172
5172
5173 This command is deprecated, please use :hg:`heads` instead.
5173 This command is deprecated, please use :hg:`heads` instead.
5174
5174
5175 Returns 0 on success.
5175 Returns 0 on success.
5176 """
5176 """
5177 opts = pycompat.byteskwargs(opts)
5177 opts = pycompat.byteskwargs(opts)
5178 displayer = cmdutil.show_changeset(ui, repo, opts)
5178 displayer = cmdutil.show_changeset(ui, repo, opts)
5179 displayer.show(repo['tip'])
5179 displayer.show(repo['tip'])
5180 displayer.close()
5180 displayer.close()
5181
5181
5182 @command('unbundle',
5182 @command('unbundle',
5183 [('u', 'update', None,
5183 [('u', 'update', None,
5184 _('update to new branch head if changesets were unbundled'))],
5184 _('update to new branch head if changesets were unbundled'))],
5185 _('[-u] FILE...'))
5185 _('[-u] FILE...'))
5186 def unbundle(ui, repo, fname1, *fnames, **opts):
5186 def unbundle(ui, repo, fname1, *fnames, **opts):
5187 """apply one or more bundle files
5187 """apply one or more bundle files
5188
5188
5189 Apply one or more bundle files generated by :hg:`bundle`.
5189 Apply one or more bundle files generated by :hg:`bundle`.
5190
5190
5191 Returns 0 on success, 1 if an update has unresolved files.
5191 Returns 0 on success, 1 if an update has unresolved files.
5192 """
5192 """
5193 fnames = (fname1,) + fnames
5193 fnames = (fname1,) + fnames
5194
5194
5195 with repo.lock():
5195 with repo.lock():
5196 for fname in fnames:
5196 for fname in fnames:
5197 f = hg.openpath(ui, fname)
5197 f = hg.openpath(ui, fname)
5198 gen = exchange.readbundle(ui, f, fname)
5198 gen = exchange.readbundle(ui, f, fname)
5199 if isinstance(gen, streamclone.streamcloneapplier):
5199 if isinstance(gen, streamclone.streamcloneapplier):
5200 raise error.Abort(
5200 raise error.Abort(
5201 _('packed bundles cannot be applied with '
5201 _('packed bundles cannot be applied with '
5202 '"hg unbundle"'),
5202 '"hg unbundle"'),
5203 hint=_('use "hg debugapplystreamclonebundle"'))
5203 hint=_('use "hg debugapplystreamclonebundle"'))
5204 url = 'bundle:' + fname
5204 url = 'bundle:' + fname
5205 try:
5205 try:
5206 txnname = 'unbundle'
5207 if not isinstance(gen, bundle2.unbundle20):
5208 txnname = 'unbundle\n%s' % util.hidepassword(url)
5209 with repo.transaction(txnname) as tr:
5206 if isinstance(gen, bundle2.unbundle20):
5210 if isinstance(gen, bundle2.unbundle20):
5207 with repo.transaction('unbundle') as tr:
5208 op = bundle2.applybundle(repo, gen, tr,
5211 op = bundle2.applybundle(repo, gen, tr,
5209 source='unbundle',
5212 source='unbundle',
5210 url=url)
5213 url=url)
5211 else:
5214 else:
5212 txnname = 'unbundle\n%s' % util.hidepassword(url)
5213 with repo.transaction(txnname) as tr:
5214 op = bundle2.applybundle1(repo, gen, tr,
5215 op = bundle2.applybundle1(repo, gen, tr,
5215 source='unbundle', url=url)
5216 source='unbundle', url=url)
5216 except error.BundleUnknownFeatureError as exc:
5217 except error.BundleUnknownFeatureError as exc:
5217 raise error.Abort(
5218 raise error.Abort(
5218 _('%s: unknown bundle feature, %s') % (fname, exc),
5219 _('%s: unknown bundle feature, %s') % (fname, exc),
5219 hint=_("see https://mercurial-scm.org/"
5220 hint=_("see https://mercurial-scm.org/"
5220 "wiki/BundleFeature for more "
5221 "wiki/BundleFeature for more "
5221 "information"))
5222 "information"))
5222 modheads = bundle2.combinechangegroupresults(op)
5223 modheads = bundle2.combinechangegroupresults(op)
5223
5224
5224 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5225 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5225
5226
5226 @command('^update|up|checkout|co',
5227 @command('^update|up|checkout|co',
5227 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5228 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5228 ('c', 'check', None, _('require clean working directory')),
5229 ('c', 'check', None, _('require clean working directory')),
5229 ('m', 'merge', None, _('merge uncommitted changes')),
5230 ('m', 'merge', None, _('merge uncommitted changes')),
5230 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5231 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5231 ('r', 'rev', '', _('revision'), _('REV'))
5232 ('r', 'rev', '', _('revision'), _('REV'))
5232 ] + mergetoolopts,
5233 ] + mergetoolopts,
5233 _('[-C|-c|-m] [-d DATE] [[-r] REV]'))
5234 _('[-C|-c|-m] [-d DATE] [[-r] REV]'))
5234 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
5235 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
5235 merge=None, tool=None):
5236 merge=None, tool=None):
5236 """update working directory (or switch revisions)
5237 """update working directory (or switch revisions)
5237
5238
5238 Update the repository's working directory to the specified
5239 Update the repository's working directory to the specified
5239 changeset. If no changeset is specified, update to the tip of the
5240 changeset. If no changeset is specified, update to the tip of the
5240 current named branch and move the active bookmark (see :hg:`help
5241 current named branch and move the active bookmark (see :hg:`help
5241 bookmarks`).
5242 bookmarks`).
5242
5243
5243 Update sets the working directory's parent revision to the specified
5244 Update sets the working directory's parent revision to the specified
5244 changeset (see :hg:`help parents`).
5245 changeset (see :hg:`help parents`).
5245
5246
5246 If the changeset is not a descendant or ancestor of the working
5247 If the changeset is not a descendant or ancestor of the working
5247 directory's parent and there are uncommitted changes, the update is
5248 directory's parent and there are uncommitted changes, the update is
5248 aborted. With the -c/--check option, the working directory is checked
5249 aborted. With the -c/--check option, the working directory is checked
5249 for uncommitted changes; if none are found, the working directory is
5250 for uncommitted changes; if none are found, the working directory is
5250 updated to the specified changeset.
5251 updated to the specified changeset.
5251
5252
5252 .. container:: verbose
5253 .. container:: verbose
5253
5254
5254 The -C/--clean, -c/--check, and -m/--merge options control what
5255 The -C/--clean, -c/--check, and -m/--merge options control what
5255 happens if the working directory contains uncommitted changes.
5256 happens if the working directory contains uncommitted changes.
5256 At most of one of them can be specified.
5257 At most of one of them can be specified.
5257
5258
5258 1. If no option is specified, and if
5259 1. If no option is specified, and if
5259 the requested changeset is an ancestor or descendant of
5260 the requested changeset is an ancestor or descendant of
5260 the working directory's parent, the uncommitted changes
5261 the working directory's parent, the uncommitted changes
5261 are merged into the requested changeset and the merged
5262 are merged into the requested changeset and the merged
5262 result is left uncommitted. If the requested changeset is
5263 result is left uncommitted. If the requested changeset is
5263 not an ancestor or descendant (that is, it is on another
5264 not an ancestor or descendant (that is, it is on another
5264 branch), the update is aborted and the uncommitted changes
5265 branch), the update is aborted and the uncommitted changes
5265 are preserved.
5266 are preserved.
5266
5267
5267 2. With the -m/--merge option, the update is allowed even if the
5268 2. With the -m/--merge option, the update is allowed even if the
5268 requested changeset is not an ancestor or descendant of
5269 requested changeset is not an ancestor or descendant of
5269 the working directory's parent.
5270 the working directory's parent.
5270
5271
5271 3. With the -c/--check option, the update is aborted and the
5272 3. With the -c/--check option, the update is aborted and the
5272 uncommitted changes are preserved.
5273 uncommitted changes are preserved.
5273
5274
5274 4. With the -C/--clean option, uncommitted changes are discarded and
5275 4. With the -C/--clean option, uncommitted changes are discarded and
5275 the working directory is updated to the requested changeset.
5276 the working directory is updated to the requested changeset.
5276
5277
5277 To cancel an uncommitted merge (and lose your changes), use
5278 To cancel an uncommitted merge (and lose your changes), use
5278 :hg:`update --clean .`.
5279 :hg:`update --clean .`.
5279
5280
5280 Use null as the changeset to remove the working directory (like
5281 Use null as the changeset to remove the working directory (like
5281 :hg:`clone -U`).
5282 :hg:`clone -U`).
5282
5283
5283 If you want to revert just one file to an older revision, use
5284 If you want to revert just one file to an older revision, use
5284 :hg:`revert [-r REV] NAME`.
5285 :hg:`revert [-r REV] NAME`.
5285
5286
5286 See :hg:`help dates` for a list of formats valid for -d/--date.
5287 See :hg:`help dates` for a list of formats valid for -d/--date.
5287
5288
5288 Returns 0 on success, 1 if there are unresolved files.
5289 Returns 0 on success, 1 if there are unresolved files.
5289 """
5290 """
5290 if rev and node:
5291 if rev and node:
5291 raise error.Abort(_("please specify just one revision"))
5292 raise error.Abort(_("please specify just one revision"))
5292
5293
5293 if ui.configbool('commands', 'update.requiredest'):
5294 if ui.configbool('commands', 'update.requiredest'):
5294 if not node and not rev and not date:
5295 if not node and not rev and not date:
5295 raise error.Abort(_('you must specify a destination'),
5296 raise error.Abort(_('you must specify a destination'),
5296 hint=_('for example: hg update ".::"'))
5297 hint=_('for example: hg update ".::"'))
5297
5298
5298 if rev is None or rev == '':
5299 if rev is None or rev == '':
5299 rev = node
5300 rev = node
5300
5301
5301 if date and rev is not None:
5302 if date and rev is not None:
5302 raise error.Abort(_("you can't specify a revision and a date"))
5303 raise error.Abort(_("you can't specify a revision and a date"))
5303
5304
5304 if len([x for x in (clean, check, merge) if x]) > 1:
5305 if len([x for x in (clean, check, merge) if x]) > 1:
5305 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
5306 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
5306 "or -m/merge"))
5307 "or -m/merge"))
5307
5308
5308 updatecheck = None
5309 updatecheck = None
5309 if check:
5310 if check:
5310 updatecheck = 'abort'
5311 updatecheck = 'abort'
5311 elif merge:
5312 elif merge:
5312 updatecheck = 'none'
5313 updatecheck = 'none'
5313
5314
5314 with repo.wlock():
5315 with repo.wlock():
5315 cmdutil.clearunfinished(repo)
5316 cmdutil.clearunfinished(repo)
5316
5317
5317 if date:
5318 if date:
5318 rev = cmdutil.finddate(ui, repo, date)
5319 rev = cmdutil.finddate(ui, repo, date)
5319
5320
5320 # if we defined a bookmark, we have to remember the original name
5321 # if we defined a bookmark, we have to remember the original name
5321 brev = rev
5322 brev = rev
5322 rev = scmutil.revsingle(repo, rev, rev).rev()
5323 rev = scmutil.revsingle(repo, rev, rev).rev()
5323
5324
5324 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
5325 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
5325
5326
5326 return hg.updatetotally(ui, repo, rev, brev, clean=clean,
5327 return hg.updatetotally(ui, repo, rev, brev, clean=clean,
5327 updatecheck=updatecheck)
5328 updatecheck=updatecheck)
5328
5329
5329 @command('verify', [])
5330 @command('verify', [])
5330 def verify(ui, repo):
5331 def verify(ui, repo):
5331 """verify the integrity of the repository
5332 """verify the integrity of the repository
5332
5333
5333 Verify the integrity of the current repository.
5334 Verify the integrity of the current repository.
5334
5335
5335 This will perform an extensive check of the repository's
5336 This will perform an extensive check of the repository's
5336 integrity, validating the hashes and checksums of each entry in
5337 integrity, validating the hashes and checksums of each entry in
5337 the changelog, manifest, and tracked files, as well as the
5338 the changelog, manifest, and tracked files, as well as the
5338 integrity of their crosslinks and indices.
5339 integrity of their crosslinks and indices.
5339
5340
5340 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
5341 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
5341 for more information about recovery from corruption of the
5342 for more information about recovery from corruption of the
5342 repository.
5343 repository.
5343
5344
5344 Returns 0 on success, 1 if errors are encountered.
5345 Returns 0 on success, 1 if errors are encountered.
5345 """
5346 """
5346 return hg.verify(repo)
5347 return hg.verify(repo)
5347
5348
5348 @command('version', [] + formatteropts, norepo=True)
5349 @command('version', [] + formatteropts, norepo=True)
5349 def version_(ui, **opts):
5350 def version_(ui, **opts):
5350 """output version and copyright information"""
5351 """output version and copyright information"""
5351 opts = pycompat.byteskwargs(opts)
5352 opts = pycompat.byteskwargs(opts)
5352 if ui.verbose:
5353 if ui.verbose:
5353 ui.pager('version')
5354 ui.pager('version')
5354 fm = ui.formatter("version", opts)
5355 fm = ui.formatter("version", opts)
5355 fm.startitem()
5356 fm.startitem()
5356 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
5357 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
5357 util.version())
5358 util.version())
5358 license = _(
5359 license = _(
5359 "(see https://mercurial-scm.org for more information)\n"
5360 "(see https://mercurial-scm.org for more information)\n"
5360 "\nCopyright (C) 2005-2017 Matt Mackall and others\n"
5361 "\nCopyright (C) 2005-2017 Matt Mackall and others\n"
5361 "This is free software; see the source for copying conditions. "
5362 "This is free software; see the source for copying conditions. "
5362 "There is NO\nwarranty; "
5363 "There is NO\nwarranty; "
5363 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5364 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5364 )
5365 )
5365 if not ui.quiet:
5366 if not ui.quiet:
5366 fm.plain(license)
5367 fm.plain(license)
5367
5368
5368 if ui.verbose:
5369 if ui.verbose:
5369 fm.plain(_("\nEnabled extensions:\n\n"))
5370 fm.plain(_("\nEnabled extensions:\n\n"))
5370 # format names and versions into columns
5371 # format names and versions into columns
5371 names = []
5372 names = []
5372 vers = []
5373 vers = []
5373 isinternals = []
5374 isinternals = []
5374 for name, module in extensions.extensions():
5375 for name, module in extensions.extensions():
5375 names.append(name)
5376 names.append(name)
5376 vers.append(extensions.moduleversion(module) or None)
5377 vers.append(extensions.moduleversion(module) or None)
5377 isinternals.append(extensions.ismoduleinternal(module))
5378 isinternals.append(extensions.ismoduleinternal(module))
5378 fn = fm.nested("extensions")
5379 fn = fm.nested("extensions")
5379 if names:
5380 if names:
5380 namefmt = " %%-%ds " % max(len(n) for n in names)
5381 namefmt = " %%-%ds " % max(len(n) for n in names)
5381 places = [_("external"), _("internal")]
5382 places = [_("external"), _("internal")]
5382 for n, v, p in zip(names, vers, isinternals):
5383 for n, v, p in zip(names, vers, isinternals):
5383 fn.startitem()
5384 fn.startitem()
5384 fn.condwrite(ui.verbose, "name", namefmt, n)
5385 fn.condwrite(ui.verbose, "name", namefmt, n)
5385 if ui.verbose:
5386 if ui.verbose:
5386 fn.plain("%s " % places[p])
5387 fn.plain("%s " % places[p])
5387 fn.data(bundled=p)
5388 fn.data(bundled=p)
5388 fn.condwrite(ui.verbose and v, "ver", "%s", v)
5389 fn.condwrite(ui.verbose and v, "ver", "%s", v)
5389 if ui.verbose:
5390 if ui.verbose:
5390 fn.plain("\n")
5391 fn.plain("\n")
5391 fn.end()
5392 fn.end()
5392 fm.end()
5393 fm.end()
5393
5394
5394 def loadcmdtable(ui, name, cmdtable):
5395 def loadcmdtable(ui, name, cmdtable):
5395 """Load command functions from specified cmdtable
5396 """Load command functions from specified cmdtable
5396 """
5397 """
5397 overrides = [cmd for cmd in cmdtable if cmd in table]
5398 overrides = [cmd for cmd in cmdtable if cmd in table]
5398 if overrides:
5399 if overrides:
5399 ui.warn(_("extension '%s' overrides commands: %s\n")
5400 ui.warn(_("extension '%s' overrides commands: %s\n")
5400 % (name, " ".join(overrides)))
5401 % (name, " ".join(overrides)))
5401 table.update(cmdtable)
5402 table.update(cmdtable)
@@ -1,379 +1,380 b''
1 # repair.py - functions for repository repair for mercurial
1 # repair.py - functions for repository repair for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 # Copyright 2007 Matt Mackall
4 # Copyright 2007 Matt Mackall
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from __future__ import absolute_import
9 from __future__ import absolute_import
10
10
11 import errno
11 import errno
12 import hashlib
12 import hashlib
13
13
14 from .i18n import _
14 from .i18n import _
15 from .node import short
15 from .node import short
16 from . import (
16 from . import (
17 bundle2,
17 bundle2,
18 changegroup,
18 changegroup,
19 discovery,
19 discovery,
20 error,
20 error,
21 exchange,
21 exchange,
22 obsolete,
22 obsolete,
23 util,
23 util,
24 )
24 )
25
25
26 def _bundle(repo, bases, heads, node, suffix, compress=True, obsolescence=True):
26 def _bundle(repo, bases, heads, node, suffix, compress=True, obsolescence=True):
27 """create a bundle with the specified revisions as a backup"""
27 """create a bundle with the specified revisions as a backup"""
28
28
29 backupdir = "strip-backup"
29 backupdir = "strip-backup"
30 vfs = repo.vfs
30 vfs = repo.vfs
31 if not vfs.isdir(backupdir):
31 if not vfs.isdir(backupdir):
32 vfs.mkdir(backupdir)
32 vfs.mkdir(backupdir)
33
33
34 # Include a hash of all the nodes in the filename for uniqueness
34 # Include a hash of all the nodes in the filename for uniqueness
35 allcommits = repo.set('%ln::%ln', bases, heads)
35 allcommits = repo.set('%ln::%ln', bases, heads)
36 allhashes = sorted(c.hex() for c in allcommits)
36 allhashes = sorted(c.hex() for c in allcommits)
37 totalhash = hashlib.sha1(''.join(allhashes)).hexdigest()
37 totalhash = hashlib.sha1(''.join(allhashes)).hexdigest()
38 name = "%s/%s-%s-%s.hg" % (backupdir, short(node), totalhash[:8], suffix)
38 name = "%s/%s-%s-%s.hg" % (backupdir, short(node), totalhash[:8], suffix)
39
39
40 cgversion = changegroup.safeversion(repo)
40 cgversion = changegroup.safeversion(repo)
41 comp = None
41 comp = None
42 if cgversion != '01':
42 if cgversion != '01':
43 bundletype = "HG20"
43 bundletype = "HG20"
44 if compress:
44 if compress:
45 comp = 'BZ'
45 comp = 'BZ'
46 elif compress:
46 elif compress:
47 bundletype = "HG10BZ"
47 bundletype = "HG10BZ"
48 else:
48 else:
49 bundletype = "HG10UN"
49 bundletype = "HG10UN"
50
50
51 outgoing = discovery.outgoing(repo, missingroots=bases, missingheads=heads)
51 outgoing = discovery.outgoing(repo, missingroots=bases, missingheads=heads)
52 contentopts = {
52 contentopts = {
53 'cg.version': cgversion,
53 'cg.version': cgversion,
54 'obsolescence': obsolescence,
54 'obsolescence': obsolescence,
55 'phases': True,
55 'phases': True,
56 }
56 }
57 return bundle2.writenewbundle(repo.ui, repo, 'strip', name, bundletype,
57 return bundle2.writenewbundle(repo.ui, repo, 'strip', name, bundletype,
58 outgoing, contentopts, vfs, compression=comp)
58 outgoing, contentopts, vfs, compression=comp)
59
59
60 def _collectfiles(repo, striprev):
60 def _collectfiles(repo, striprev):
61 """find out the filelogs affected by the strip"""
61 """find out the filelogs affected by the strip"""
62 files = set()
62 files = set()
63
63
64 for x in xrange(striprev, len(repo)):
64 for x in xrange(striprev, len(repo)):
65 files.update(repo[x].files())
65 files.update(repo[x].files())
66
66
67 return sorted(files)
67 return sorted(files)
68
68
69 def _collectbrokencsets(repo, files, striprev):
69 def _collectbrokencsets(repo, files, striprev):
70 """return the changesets which will be broken by the truncation"""
70 """return the changesets which will be broken by the truncation"""
71 s = set()
71 s = set()
72 def collectone(revlog):
72 def collectone(revlog):
73 _, brokenset = revlog.getstrippoint(striprev)
73 _, brokenset = revlog.getstrippoint(striprev)
74 s.update([revlog.linkrev(r) for r in brokenset])
74 s.update([revlog.linkrev(r) for r in brokenset])
75
75
76 collectone(repo.manifestlog._revlog)
76 collectone(repo.manifestlog._revlog)
77 for fname in files:
77 for fname in files:
78 collectone(repo.file(fname))
78 collectone(repo.file(fname))
79
79
80 return s
80 return s
81
81
82 def strip(ui, repo, nodelist, backup=True, topic='backup'):
82 def strip(ui, repo, nodelist, backup=True, topic='backup'):
83 # This function requires the caller to lock the repo, but it operates
83 # This function requires the caller to lock the repo, but it operates
84 # within a transaction of its own, and thus requires there to be no current
84 # within a transaction of its own, and thus requires there to be no current
85 # transaction when it is called.
85 # transaction when it is called.
86 if repo.currenttransaction() is not None:
86 if repo.currenttransaction() is not None:
87 raise error.ProgrammingError('cannot strip from inside a transaction')
87 raise error.ProgrammingError('cannot strip from inside a transaction')
88
88
89 # Simple way to maintain backwards compatibility for this
89 # Simple way to maintain backwards compatibility for this
90 # argument.
90 # argument.
91 if backup in ['none', 'strip']:
91 if backup in ['none', 'strip']:
92 backup = False
92 backup = False
93
93
94 repo = repo.unfiltered()
94 repo = repo.unfiltered()
95 repo.destroying()
95 repo.destroying()
96
96
97 cl = repo.changelog
97 cl = repo.changelog
98 # TODO handle undo of merge sets
98 # TODO handle undo of merge sets
99 if isinstance(nodelist, str):
99 if isinstance(nodelist, str):
100 nodelist = [nodelist]
100 nodelist = [nodelist]
101 striplist = [cl.rev(node) for node in nodelist]
101 striplist = [cl.rev(node) for node in nodelist]
102 striprev = min(striplist)
102 striprev = min(striplist)
103
103
104 files = _collectfiles(repo, striprev)
104 files = _collectfiles(repo, striprev)
105 saverevs = _collectbrokencsets(repo, files, striprev)
105 saverevs = _collectbrokencsets(repo, files, striprev)
106
106
107 # Some revisions with rev > striprev may not be descendants of striprev.
107 # Some revisions with rev > striprev may not be descendants of striprev.
108 # We have to find these revisions and put them in a bundle, so that
108 # We have to find these revisions and put them in a bundle, so that
109 # we can restore them after the truncations.
109 # we can restore them after the truncations.
110 # To create the bundle we use repo.changegroupsubset which requires
110 # To create the bundle we use repo.changegroupsubset which requires
111 # the list of heads and bases of the set of interesting revisions.
111 # the list of heads and bases of the set of interesting revisions.
112 # (head = revision in the set that has no descendant in the set;
112 # (head = revision in the set that has no descendant in the set;
113 # base = revision in the set that has no ancestor in the set)
113 # base = revision in the set that has no ancestor in the set)
114 tostrip = set(striplist)
114 tostrip = set(striplist)
115 saveheads = set(saverevs)
115 saveheads = set(saverevs)
116 for r in cl.revs(start=striprev + 1):
116 for r in cl.revs(start=striprev + 1):
117 if any(p in tostrip for p in cl.parentrevs(r)):
117 if any(p in tostrip for p in cl.parentrevs(r)):
118 tostrip.add(r)
118 tostrip.add(r)
119
119
120 if r not in tostrip:
120 if r not in tostrip:
121 saverevs.add(r)
121 saverevs.add(r)
122 saveheads.difference_update(cl.parentrevs(r))
122 saveheads.difference_update(cl.parentrevs(r))
123 saveheads.add(r)
123 saveheads.add(r)
124 saveheads = [cl.node(r) for r in saveheads]
124 saveheads = [cl.node(r) for r in saveheads]
125
125
126 # compute base nodes
126 # compute base nodes
127 if saverevs:
127 if saverevs:
128 descendants = set(cl.descendants(saverevs))
128 descendants = set(cl.descendants(saverevs))
129 saverevs.difference_update(descendants)
129 saverevs.difference_update(descendants)
130 savebases = [cl.node(r) for r in saverevs]
130 savebases = [cl.node(r) for r in saverevs]
131 stripbases = [cl.node(r) for r in tostrip]
131 stripbases = [cl.node(r) for r in tostrip]
132
132
133 stripobsidx = obsmarkers = ()
133 stripobsidx = obsmarkers = ()
134 if repo.ui.configbool('devel', 'strip-obsmarkers', True):
134 if repo.ui.configbool('devel', 'strip-obsmarkers', True):
135 obsmarkers = obsolete.exclusivemarkers(repo, stripbases)
135 obsmarkers = obsolete.exclusivemarkers(repo, stripbases)
136 if obsmarkers:
136 if obsmarkers:
137 stripobsidx = [i for i, m in enumerate(repo.obsstore)
137 stripobsidx = [i for i, m in enumerate(repo.obsstore)
138 if m in obsmarkers]
138 if m in obsmarkers]
139
139
140 # For a set s, max(parents(s) - s) is the same as max(heads(::s - s)), but
140 # For a set s, max(parents(s) - s) is the same as max(heads(::s - s)), but
141 # is much faster
141 # is much faster
142 newbmtarget = repo.revs('max(parents(%ld) - (%ld))', tostrip, tostrip)
142 newbmtarget = repo.revs('max(parents(%ld) - (%ld))', tostrip, tostrip)
143 if newbmtarget:
143 if newbmtarget:
144 newbmtarget = repo[newbmtarget.first()].node()
144 newbmtarget = repo[newbmtarget.first()].node()
145 else:
145 else:
146 newbmtarget = '.'
146 newbmtarget = '.'
147
147
148 bm = repo._bookmarks
148 bm = repo._bookmarks
149 updatebm = []
149 updatebm = []
150 for m in bm:
150 for m in bm:
151 rev = repo[bm[m]].rev()
151 rev = repo[bm[m]].rev()
152 if rev in tostrip:
152 if rev in tostrip:
153 updatebm.append(m)
153 updatebm.append(m)
154
154
155 # create a changegroup for all the branches we need to keep
155 # create a changegroup for all the branches we need to keep
156 backupfile = None
156 backupfile = None
157 vfs = repo.vfs
157 vfs = repo.vfs
158 node = nodelist[-1]
158 node = nodelist[-1]
159 if backup:
159 if backup:
160 backupfile = _bundle(repo, stripbases, cl.heads(), node, topic)
160 backupfile = _bundle(repo, stripbases, cl.heads(), node, topic)
161 repo.ui.status(_("saved backup bundle to %s\n") %
161 repo.ui.status(_("saved backup bundle to %s\n") %
162 vfs.join(backupfile))
162 vfs.join(backupfile))
163 repo.ui.log("backupbundle", "saved backup bundle to %s\n",
163 repo.ui.log("backupbundle", "saved backup bundle to %s\n",
164 vfs.join(backupfile))
164 vfs.join(backupfile))
165 tmpbundlefile = None
165 tmpbundlefile = None
166 if saveheads:
166 if saveheads:
167 # do not compress temporary bundle if we remove it from disk later
167 # do not compress temporary bundle if we remove it from disk later
168 #
168 #
169 # We do not include obsolescence, it might re-introduce prune markers
169 # We do not include obsolescence, it might re-introduce prune markers
170 # we are trying to strip. This is harmless since the stripped markers
170 # we are trying to strip. This is harmless since the stripped markers
171 # are already backed up and we did not touched the markers for the
171 # are already backed up and we did not touched the markers for the
172 # saved changesets.
172 # saved changesets.
173 tmpbundlefile = _bundle(repo, savebases, saveheads, node, 'temp',
173 tmpbundlefile = _bundle(repo, savebases, saveheads, node, 'temp',
174 compress=False, obsolescence=False)
174 compress=False, obsolescence=False)
175
175
176 mfst = repo.manifestlog._revlog
176 mfst = repo.manifestlog._revlog
177
177
178 try:
178 try:
179 with repo.transaction("strip") as tr:
179 with repo.transaction("strip") as tr:
180 offset = len(tr.entries)
180 offset = len(tr.entries)
181
181
182 tr.startgroup()
182 tr.startgroup()
183 cl.strip(striprev, tr)
183 cl.strip(striprev, tr)
184 mfst.strip(striprev, tr)
184 mfst.strip(striprev, tr)
185 striptrees(repo, tr, striprev, files)
185 striptrees(repo, tr, striprev, files)
186
186
187 for fn in files:
187 for fn in files:
188 repo.file(fn).strip(striprev, tr)
188 repo.file(fn).strip(striprev, tr)
189 tr.endgroup()
189 tr.endgroup()
190
190
191 for i in xrange(offset, len(tr.entries)):
191 for i in xrange(offset, len(tr.entries)):
192 file, troffset, ignore = tr.entries[i]
192 file, troffset, ignore = tr.entries[i]
193 with repo.svfs(file, 'a', checkambig=True) as fp:
193 with repo.svfs(file, 'a', checkambig=True) as fp:
194 fp.truncate(troffset)
194 fp.truncate(troffset)
195 if troffset == 0:
195 if troffset == 0:
196 repo.store.markremoved(file)
196 repo.store.markremoved(file)
197
197
198 deleteobsmarkers(repo.obsstore, stripobsidx)
198 deleteobsmarkers(repo.obsstore, stripobsidx)
199 del repo.obsstore
199 del repo.obsstore
200
200
201 repo._phasecache.filterunknown(repo)
201 repo._phasecache.filterunknown(repo)
202 if tmpbundlefile:
202 if tmpbundlefile:
203 ui.note(_("adding branch\n"))
203 ui.note(_("adding branch\n"))
204 f = vfs.open(tmpbundlefile, "rb")
204 f = vfs.open(tmpbundlefile, "rb")
205 gen = exchange.readbundle(ui, f, tmpbundlefile, vfs)
205 gen = exchange.readbundle(ui, f, tmpbundlefile, vfs)
206 if not repo.ui.verbose:
206 if not repo.ui.verbose:
207 # silence internal shuffling chatter
207 # silence internal shuffling chatter
208 repo.ui.pushbuffer()
208 repo.ui.pushbuffer()
209 tmpbundleurl = 'bundle:' + vfs.join(tmpbundlefile)
209 tmpbundleurl = 'bundle:' + vfs.join(tmpbundlefile)
210 txnname = 'strip'
211 if not isinstance(gen, bundle2.unbundle20):
212 txnname = "strip\n%s" % util.hidepassword(tmpbundleurl)
213 with repo.transaction(txnname) as tr:
210 if isinstance(gen, bundle2.unbundle20):
214 if isinstance(gen, bundle2.unbundle20):
211 with repo.transaction('strip') as tr:
212 bundle2.applybundle(repo, gen, tr, source='strip',
215 bundle2.applybundle(repo, gen, tr, source='strip',
213 url=tmpbundleurl)
216 url=tmpbundleurl)
214 else:
217 else:
215 txnname = "strip\n%s" % util.hidepassword(tmpbundleurl)
216 with repo.transaction(txnname) as tr:
217 bundle2.applybundle1(repo, gen, tr, 'strip', tmpbundleurl,
218 bundle2.applybundle1(repo, gen, tr, 'strip', tmpbundleurl,
218 emptyok=True)
219 emptyok=True)
219 if not repo.ui.verbose:
220 if not repo.ui.verbose:
220 repo.ui.popbuffer()
221 repo.ui.popbuffer()
221 f.close()
222 f.close()
222 repo._phasecache.invalidate()
223 repo._phasecache.invalidate()
223
224
224 for m in updatebm:
225 for m in updatebm:
225 bm[m] = repo[newbmtarget].node()
226 bm[m] = repo[newbmtarget].node()
226
227
227 with repo.transaction('repair') as tr:
228 with repo.transaction('repair') as tr:
228 bm.recordchange(tr)
229 bm.recordchange(tr)
229
230
230 # remove undo files
231 # remove undo files
231 for undovfs, undofile in repo.undofiles():
232 for undovfs, undofile in repo.undofiles():
232 try:
233 try:
233 undovfs.unlink(undofile)
234 undovfs.unlink(undofile)
234 except OSError as e:
235 except OSError as e:
235 if e.errno != errno.ENOENT:
236 if e.errno != errno.ENOENT:
236 ui.warn(_('error removing %s: %s\n') %
237 ui.warn(_('error removing %s: %s\n') %
237 (undovfs.join(undofile), str(e)))
238 (undovfs.join(undofile), str(e)))
238
239
239 except: # re-raises
240 except: # re-raises
240 if backupfile:
241 if backupfile:
241 ui.warn(_("strip failed, backup bundle stored in '%s'\n")
242 ui.warn(_("strip failed, backup bundle stored in '%s'\n")
242 % vfs.join(backupfile))
243 % vfs.join(backupfile))
243 if tmpbundlefile:
244 if tmpbundlefile:
244 ui.warn(_("strip failed, unrecovered changes stored in '%s'\n")
245 ui.warn(_("strip failed, unrecovered changes stored in '%s'\n")
245 % vfs.join(tmpbundlefile))
246 % vfs.join(tmpbundlefile))
246 ui.warn(_("(fix the problem, then recover the changesets with "
247 ui.warn(_("(fix the problem, then recover the changesets with "
247 "\"hg unbundle '%s'\")\n") % vfs.join(tmpbundlefile))
248 "\"hg unbundle '%s'\")\n") % vfs.join(tmpbundlefile))
248 raise
249 raise
249 else:
250 else:
250 if tmpbundlefile:
251 if tmpbundlefile:
251 # Remove temporary bundle only if there were no exceptions
252 # Remove temporary bundle only if there were no exceptions
252 vfs.unlink(tmpbundlefile)
253 vfs.unlink(tmpbundlefile)
253
254
254 repo.destroyed()
255 repo.destroyed()
255 # return the backup file path (or None if 'backup' was False) so
256 # return the backup file path (or None if 'backup' was False) so
256 # extensions can use it
257 # extensions can use it
257 return backupfile
258 return backupfile
258
259
259 def striptrees(repo, tr, striprev, files):
260 def striptrees(repo, tr, striprev, files):
260 if 'treemanifest' in repo.requirements: # safe but unnecessary
261 if 'treemanifest' in repo.requirements: # safe but unnecessary
261 # otherwise
262 # otherwise
262 for unencoded, encoded, size in repo.store.datafiles():
263 for unencoded, encoded, size in repo.store.datafiles():
263 if (unencoded.startswith('meta/') and
264 if (unencoded.startswith('meta/') and
264 unencoded.endswith('00manifest.i')):
265 unencoded.endswith('00manifest.i')):
265 dir = unencoded[5:-12]
266 dir = unencoded[5:-12]
266 repo.manifestlog._revlog.dirlog(dir).strip(striprev, tr)
267 repo.manifestlog._revlog.dirlog(dir).strip(striprev, tr)
267
268
268 def rebuildfncache(ui, repo):
269 def rebuildfncache(ui, repo):
269 """Rebuilds the fncache file from repo history.
270 """Rebuilds the fncache file from repo history.
270
271
271 Missing entries will be added. Extra entries will be removed.
272 Missing entries will be added. Extra entries will be removed.
272 """
273 """
273 repo = repo.unfiltered()
274 repo = repo.unfiltered()
274
275
275 if 'fncache' not in repo.requirements:
276 if 'fncache' not in repo.requirements:
276 ui.warn(_('(not rebuilding fncache because repository does not '
277 ui.warn(_('(not rebuilding fncache because repository does not '
277 'support fncache)\n'))
278 'support fncache)\n'))
278 return
279 return
279
280
280 with repo.lock():
281 with repo.lock():
281 fnc = repo.store.fncache
282 fnc = repo.store.fncache
282 # Trigger load of fncache.
283 # Trigger load of fncache.
283 if 'irrelevant' in fnc:
284 if 'irrelevant' in fnc:
284 pass
285 pass
285
286
286 oldentries = set(fnc.entries)
287 oldentries = set(fnc.entries)
287 newentries = set()
288 newentries = set()
288 seenfiles = set()
289 seenfiles = set()
289
290
290 repolen = len(repo)
291 repolen = len(repo)
291 for rev in repo:
292 for rev in repo:
292 ui.progress(_('rebuilding'), rev, total=repolen,
293 ui.progress(_('rebuilding'), rev, total=repolen,
293 unit=_('changesets'))
294 unit=_('changesets'))
294
295
295 ctx = repo[rev]
296 ctx = repo[rev]
296 for f in ctx.files():
297 for f in ctx.files():
297 # This is to minimize I/O.
298 # This is to minimize I/O.
298 if f in seenfiles:
299 if f in seenfiles:
299 continue
300 continue
300 seenfiles.add(f)
301 seenfiles.add(f)
301
302
302 i = 'data/%s.i' % f
303 i = 'data/%s.i' % f
303 d = 'data/%s.d' % f
304 d = 'data/%s.d' % f
304
305
305 if repo.store._exists(i):
306 if repo.store._exists(i):
306 newentries.add(i)
307 newentries.add(i)
307 if repo.store._exists(d):
308 if repo.store._exists(d):
308 newentries.add(d)
309 newentries.add(d)
309
310
310 ui.progress(_('rebuilding'), None)
311 ui.progress(_('rebuilding'), None)
311
312
312 if 'treemanifest' in repo.requirements: # safe but unnecessary otherwise
313 if 'treemanifest' in repo.requirements: # safe but unnecessary otherwise
313 for dir in util.dirs(seenfiles):
314 for dir in util.dirs(seenfiles):
314 i = 'meta/%s/00manifest.i' % dir
315 i = 'meta/%s/00manifest.i' % dir
315 d = 'meta/%s/00manifest.d' % dir
316 d = 'meta/%s/00manifest.d' % dir
316
317
317 if repo.store._exists(i):
318 if repo.store._exists(i):
318 newentries.add(i)
319 newentries.add(i)
319 if repo.store._exists(d):
320 if repo.store._exists(d):
320 newentries.add(d)
321 newentries.add(d)
321
322
322 addcount = len(newentries - oldentries)
323 addcount = len(newentries - oldentries)
323 removecount = len(oldentries - newentries)
324 removecount = len(oldentries - newentries)
324 for p in sorted(oldentries - newentries):
325 for p in sorted(oldentries - newentries):
325 ui.write(_('removing %s\n') % p)
326 ui.write(_('removing %s\n') % p)
326 for p in sorted(newentries - oldentries):
327 for p in sorted(newentries - oldentries):
327 ui.write(_('adding %s\n') % p)
328 ui.write(_('adding %s\n') % p)
328
329
329 if addcount or removecount:
330 if addcount or removecount:
330 ui.write(_('%d items added, %d removed from fncache\n') %
331 ui.write(_('%d items added, %d removed from fncache\n') %
331 (addcount, removecount))
332 (addcount, removecount))
332 fnc.entries = newentries
333 fnc.entries = newentries
333 fnc._dirty = True
334 fnc._dirty = True
334
335
335 with repo.transaction('fncache') as tr:
336 with repo.transaction('fncache') as tr:
336 fnc.write(tr)
337 fnc.write(tr)
337 else:
338 else:
338 ui.write(_('fncache already up to date\n'))
339 ui.write(_('fncache already up to date\n'))
339
340
340 def stripbmrevset(repo, mark):
341 def stripbmrevset(repo, mark):
341 """
342 """
342 The revset to strip when strip is called with -B mark
343 The revset to strip when strip is called with -B mark
343
344
344 Needs to live here so extensions can use it and wrap it even when strip is
345 Needs to live here so extensions can use it and wrap it even when strip is
345 not enabled or not present on a box.
346 not enabled or not present on a box.
346 """
347 """
347 return repo.revs("ancestors(bookmark(%s)) - "
348 return repo.revs("ancestors(bookmark(%s)) - "
348 "ancestors(head() and not bookmark(%s)) - "
349 "ancestors(head() and not bookmark(%s)) - "
349 "ancestors(bookmark() and not bookmark(%s))",
350 "ancestors(bookmark() and not bookmark(%s))",
350 mark, mark, mark)
351 mark, mark, mark)
351
352
352 def deleteobsmarkers(obsstore, indices):
353 def deleteobsmarkers(obsstore, indices):
353 """Delete some obsmarkers from obsstore and return how many were deleted
354 """Delete some obsmarkers from obsstore and return how many were deleted
354
355
355 'indices' is a list of ints which are the indices
356 'indices' is a list of ints which are the indices
356 of the markers to be deleted.
357 of the markers to be deleted.
357
358
358 Every invocation of this function completely rewrites the obsstore file,
359 Every invocation of this function completely rewrites the obsstore file,
359 skipping the markers we want to be removed. The new temporary file is
360 skipping the markers we want to be removed. The new temporary file is
360 created, remaining markers are written there and on .close() this file
361 created, remaining markers are written there and on .close() this file
361 gets atomically renamed to obsstore, thus guaranteeing consistency."""
362 gets atomically renamed to obsstore, thus guaranteeing consistency."""
362 if not indices:
363 if not indices:
363 # we don't want to rewrite the obsstore with the same content
364 # we don't want to rewrite the obsstore with the same content
364 return
365 return
365
366
366 left = []
367 left = []
367 current = obsstore._all
368 current = obsstore._all
368 n = 0
369 n = 0
369 for i, m in enumerate(current):
370 for i, m in enumerate(current):
370 if i in indices:
371 if i in indices:
371 n += 1
372 n += 1
372 continue
373 continue
373 left.append(m)
374 left.append(m)
374
375
375 newobsstorefile = obsstore.svfs('obsstore', 'w', atomictemp=True)
376 newobsstorefile = obsstore.svfs('obsstore', 'w', atomictemp=True)
376 for bytes in obsolete.encodemarkers(left, True, obsstore._version):
377 for bytes in obsolete.encodemarkers(left, True, obsstore._version):
377 newobsstorefile.write(bytes)
378 newobsstorefile.write(bytes)
378 newobsstorefile.close()
379 newobsstorefile.close()
379 return n
380 return n
General Comments 0
You need to be logged in to leave comments. Login now