##// END OF EJS Templates
addremove: pass command-level similarity value down to scmutil.addremove()...
Yuya Nishihara -
r37322:6942c73f default
parent child Browse files
Show More
@@ -1,5641 +1,5636
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 logcmdutil,
44 logcmdutil,
45 merge as mergemod,
45 merge as mergemod,
46 obsolete,
46 obsolete,
47 obsutil,
47 obsutil,
48 patch,
48 patch,
49 phases,
49 phases,
50 pycompat,
50 pycompat,
51 rcutil,
51 rcutil,
52 registrar,
52 registrar,
53 revsetlang,
53 revsetlang,
54 rewriteutil,
54 rewriteutil,
55 scmutil,
55 scmutil,
56 server,
56 server,
57 streamclone,
57 streamclone,
58 tags as tagsmod,
58 tags as tagsmod,
59 templatekw,
59 templatekw,
60 ui as uimod,
60 ui as uimod,
61 util,
61 util,
62 wireprotoserver,
62 wireprotoserver,
63 )
63 )
64 from .utils import (
64 from .utils import (
65 dateutil,
65 dateutil,
66 procutil,
66 procutil,
67 stringutil,
67 stringutil,
68 )
68 )
69
69
70 release = lockmod.release
70 release = lockmod.release
71
71
72 table = {}
72 table = {}
73 table.update(debugcommandsmod.command._table)
73 table.update(debugcommandsmod.command._table)
74
74
75 command = registrar.command(table)
75 command = registrar.command(table)
76 readonly = registrar.command.readonly
76 readonly = registrar.command.readonly
77
77
78 # common command options
78 # common command options
79
79
80 globalopts = [
80 globalopts = [
81 ('R', 'repository', '',
81 ('R', 'repository', '',
82 _('repository root directory or name of overlay bundle file'),
82 _('repository root directory or name of overlay bundle file'),
83 _('REPO')),
83 _('REPO')),
84 ('', 'cwd', '',
84 ('', 'cwd', '',
85 _('change working directory'), _('DIR')),
85 _('change working directory'), _('DIR')),
86 ('y', 'noninteractive', None,
86 ('y', 'noninteractive', None,
87 _('do not prompt, automatically pick the first choice for all prompts')),
87 _('do not prompt, automatically pick the first choice for all prompts')),
88 ('q', 'quiet', None, _('suppress output')),
88 ('q', 'quiet', None, _('suppress output')),
89 ('v', 'verbose', None, _('enable additional output')),
89 ('v', 'verbose', None, _('enable additional output')),
90 ('', 'color', '',
90 ('', 'color', '',
91 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
91 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
92 # and should not be translated
92 # and should not be translated
93 _("when to colorize (boolean, always, auto, never, or debug)"),
93 _("when to colorize (boolean, always, auto, never, or debug)"),
94 _('TYPE')),
94 _('TYPE')),
95 ('', 'config', [],
95 ('', 'config', [],
96 _('set/override config option (use \'section.name=value\')'),
96 _('set/override config option (use \'section.name=value\')'),
97 _('CONFIG')),
97 _('CONFIG')),
98 ('', 'debug', None, _('enable debugging output')),
98 ('', 'debug', None, _('enable debugging output')),
99 ('', 'debugger', None, _('start debugger')),
99 ('', 'debugger', None, _('start debugger')),
100 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
100 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
101 _('ENCODE')),
101 _('ENCODE')),
102 ('', 'encodingmode', encoding.encodingmode,
102 ('', 'encodingmode', encoding.encodingmode,
103 _('set the charset encoding mode'), _('MODE')),
103 _('set the charset encoding mode'), _('MODE')),
104 ('', 'traceback', None, _('always print a traceback on exception')),
104 ('', 'traceback', None, _('always print a traceback on exception')),
105 ('', 'time', None, _('time how long the command takes')),
105 ('', 'time', None, _('time how long the command takes')),
106 ('', 'profile', None, _('print command execution profile')),
106 ('', 'profile', None, _('print command execution profile')),
107 ('', 'version', None, _('output version information and exit')),
107 ('', 'version', None, _('output version information and exit')),
108 ('h', 'help', None, _('display help and exit')),
108 ('h', 'help', None, _('display help and exit')),
109 ('', 'hidden', False, _('consider hidden changesets')),
109 ('', 'hidden', False, _('consider hidden changesets')),
110 ('', 'pager', 'auto',
110 ('', 'pager', 'auto',
111 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
111 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
112 ]
112 ]
113
113
114 dryrunopts = cmdutil.dryrunopts
114 dryrunopts = cmdutil.dryrunopts
115 remoteopts = cmdutil.remoteopts
115 remoteopts = cmdutil.remoteopts
116 walkopts = cmdutil.walkopts
116 walkopts = cmdutil.walkopts
117 commitopts = cmdutil.commitopts
117 commitopts = cmdutil.commitopts
118 commitopts2 = cmdutil.commitopts2
118 commitopts2 = cmdutil.commitopts2
119 formatteropts = cmdutil.formatteropts
119 formatteropts = cmdutil.formatteropts
120 templateopts = cmdutil.templateopts
120 templateopts = cmdutil.templateopts
121 logopts = cmdutil.logopts
121 logopts = cmdutil.logopts
122 diffopts = cmdutil.diffopts
122 diffopts = cmdutil.diffopts
123 diffwsopts = cmdutil.diffwsopts
123 diffwsopts = cmdutil.diffwsopts
124 diffopts2 = cmdutil.diffopts2
124 diffopts2 = cmdutil.diffopts2
125 mergetoolopts = cmdutil.mergetoolopts
125 mergetoolopts = cmdutil.mergetoolopts
126 similarityopts = cmdutil.similarityopts
126 similarityopts = cmdutil.similarityopts
127 subrepoopts = cmdutil.subrepoopts
127 subrepoopts = cmdutil.subrepoopts
128 debugrevlogopts = cmdutil.debugrevlogopts
128 debugrevlogopts = cmdutil.debugrevlogopts
129
129
130 # Commands start here, listed alphabetically
130 # Commands start here, listed alphabetically
131
131
132 @command('^add',
132 @command('^add',
133 walkopts + subrepoopts + dryrunopts,
133 walkopts + subrepoopts + dryrunopts,
134 _('[OPTION]... [FILE]...'),
134 _('[OPTION]... [FILE]...'),
135 inferrepo=True)
135 inferrepo=True)
136 def add(ui, repo, *pats, **opts):
136 def add(ui, repo, *pats, **opts):
137 """add the specified files on the next commit
137 """add the specified files on the next commit
138
138
139 Schedule files to be version controlled and added to the
139 Schedule files to be version controlled and added to the
140 repository.
140 repository.
141
141
142 The files will be added to the repository at the next commit. To
142 The files will be added to the repository at the next commit. To
143 undo an add before that, see :hg:`forget`.
143 undo an add before that, see :hg:`forget`.
144
144
145 If no names are given, add all files to the repository (except
145 If no names are given, add all files to the repository (except
146 files matching ``.hgignore``).
146 files matching ``.hgignore``).
147
147
148 .. container:: verbose
148 .. container:: verbose
149
149
150 Examples:
150 Examples:
151
151
152 - New (unknown) files are added
152 - New (unknown) files are added
153 automatically by :hg:`add`::
153 automatically by :hg:`add`::
154
154
155 $ ls
155 $ ls
156 foo.c
156 foo.c
157 $ hg status
157 $ hg status
158 ? foo.c
158 ? foo.c
159 $ hg add
159 $ hg add
160 adding foo.c
160 adding foo.c
161 $ hg status
161 $ hg status
162 A foo.c
162 A foo.c
163
163
164 - Specific files to be added can be specified::
164 - Specific files to be added can be specified::
165
165
166 $ ls
166 $ ls
167 bar.c foo.c
167 bar.c foo.c
168 $ hg status
168 $ hg status
169 ? bar.c
169 ? bar.c
170 ? foo.c
170 ? foo.c
171 $ hg add bar.c
171 $ hg add bar.c
172 $ hg status
172 $ hg status
173 A bar.c
173 A bar.c
174 ? foo.c
174 ? foo.c
175
175
176 Returns 0 if all files are successfully added.
176 Returns 0 if all files are successfully added.
177 """
177 """
178
178
179 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
179 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
180 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
180 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
181 return rejected and 1 or 0
181 return rejected and 1 or 0
182
182
183 @command('addremove',
183 @command('addremove',
184 similarityopts + subrepoopts + walkopts + dryrunopts,
184 similarityopts + subrepoopts + walkopts + dryrunopts,
185 _('[OPTION]... [FILE]...'),
185 _('[OPTION]... [FILE]...'),
186 inferrepo=True)
186 inferrepo=True)
187 def addremove(ui, repo, *pats, **opts):
187 def addremove(ui, repo, *pats, **opts):
188 """add all new files, delete all missing files
188 """add all new files, delete all missing files
189
189
190 Add all new files and remove all missing files from the
190 Add all new files and remove all missing files from the
191 repository.
191 repository.
192
192
193 Unless names are given, new files are ignored if they match any of
193 Unless names are given, new files are ignored if they match any of
194 the patterns in ``.hgignore``. As with add, these changes take
194 the patterns in ``.hgignore``. As with add, these changes take
195 effect at the next commit.
195 effect at the next commit.
196
196
197 Use the -s/--similarity option to detect renamed files. This
197 Use the -s/--similarity option to detect renamed files. This
198 option takes a percentage between 0 (disabled) and 100 (files must
198 option takes a percentage between 0 (disabled) and 100 (files must
199 be identical) as its parameter. With a parameter greater than 0,
199 be identical) as its parameter. With a parameter greater than 0,
200 this compares every removed file with every added file and records
200 this compares every removed file with every added file and records
201 those similar enough as renames. Detecting renamed files this way
201 those similar enough as renames. Detecting renamed files this way
202 can be expensive. After using this option, :hg:`status -C` can be
202 can be expensive. After using this option, :hg:`status -C` can be
203 used to check which files were identified as moved or renamed. If
203 used to check which files were identified as moved or renamed. If
204 not specified, -s/--similarity defaults to 100 and only renames of
204 not specified, -s/--similarity defaults to 100 and only renames of
205 identical files are detected.
205 identical files are detected.
206
206
207 .. container:: verbose
207 .. container:: verbose
208
208
209 Examples:
209 Examples:
210
210
211 - A number of files (bar.c and foo.c) are new,
211 - A number of files (bar.c and foo.c) are new,
212 while foobar.c has been removed (without using :hg:`remove`)
212 while foobar.c has been removed (without using :hg:`remove`)
213 from the repository::
213 from the repository::
214
214
215 $ ls
215 $ ls
216 bar.c foo.c
216 bar.c foo.c
217 $ hg status
217 $ hg status
218 ! foobar.c
218 ! foobar.c
219 ? bar.c
219 ? bar.c
220 ? foo.c
220 ? foo.c
221 $ hg addremove
221 $ hg addremove
222 adding bar.c
222 adding bar.c
223 adding foo.c
223 adding foo.c
224 removing foobar.c
224 removing foobar.c
225 $ hg status
225 $ hg status
226 A bar.c
226 A bar.c
227 A foo.c
227 A foo.c
228 R foobar.c
228 R foobar.c
229
229
230 - A file foobar.c was moved to foo.c without using :hg:`rename`.
230 - A file foobar.c was moved to foo.c without using :hg:`rename`.
231 Afterwards, it was edited slightly::
231 Afterwards, it was edited slightly::
232
232
233 $ ls
233 $ ls
234 foo.c
234 foo.c
235 $ hg status
235 $ hg status
236 ! foobar.c
236 ! foobar.c
237 ? foo.c
237 ? foo.c
238 $ hg addremove --similarity 90
238 $ hg addremove --similarity 90
239 removing foobar.c
239 removing foobar.c
240 adding foo.c
240 adding foo.c
241 recording removal of foobar.c as rename to foo.c (94% similar)
241 recording removal of foobar.c as rename to foo.c (94% similar)
242 $ hg status -C
242 $ hg status -C
243 A foo.c
243 A foo.c
244 foobar.c
244 foobar.c
245 R foobar.c
245 R foobar.c
246
246
247 Returns 0 if all files are successfully added.
247 Returns 0 if all files are successfully added.
248 """
248 """
249 opts = pycompat.byteskwargs(opts)
249 opts = pycompat.byteskwargs(opts)
250 try:
250 if not opts.get('similarity'):
251 sim = float(opts.get('similarity') or 100)
251 opts['similarity'] = '100'
252 except ValueError:
253 raise error.Abort(_('similarity must be a number'))
254 if sim < 0 or sim > 100:
255 raise error.Abort(_('similarity must be between 0 and 100'))
256 opts['similarity'] = sim / 100.0
257 matcher = scmutil.match(repo[None], pats, opts)
252 matcher = scmutil.match(repo[None], pats, opts)
258 return scmutil.addremove(repo, matcher, "", opts)
253 return scmutil.addremove(repo, matcher, "", opts)
259
254
260 @command('^annotate|blame',
255 @command('^annotate|blame',
261 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
256 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
262 ('', 'follow', None,
257 ('', 'follow', None,
263 _('follow copies/renames and list the filename (DEPRECATED)')),
258 _('follow copies/renames and list the filename (DEPRECATED)')),
264 ('', 'no-follow', None, _("don't follow copies and renames")),
259 ('', 'no-follow', None, _("don't follow copies and renames")),
265 ('a', 'text', None, _('treat all files as text')),
260 ('a', 'text', None, _('treat all files as text')),
266 ('u', 'user', None, _('list the author (long with -v)')),
261 ('u', 'user', None, _('list the author (long with -v)')),
267 ('f', 'file', None, _('list the filename')),
262 ('f', 'file', None, _('list the filename')),
268 ('d', 'date', None, _('list the date (short with -q)')),
263 ('d', 'date', None, _('list the date (short with -q)')),
269 ('n', 'number', None, _('list the revision number (default)')),
264 ('n', 'number', None, _('list the revision number (default)')),
270 ('c', 'changeset', None, _('list the changeset')),
265 ('c', 'changeset', None, _('list the changeset')),
271 ('l', 'line-number', None, _('show line number at the first appearance')),
266 ('l', 'line-number', None, _('show line number at the first appearance')),
272 ('', 'skip', [], _('revision to not display (EXPERIMENTAL)'), _('REV')),
267 ('', 'skip', [], _('revision to not display (EXPERIMENTAL)'), _('REV')),
273 ] + diffwsopts + walkopts + formatteropts,
268 ] + diffwsopts + walkopts + formatteropts,
274 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
269 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
275 inferrepo=True)
270 inferrepo=True)
276 def annotate(ui, repo, *pats, **opts):
271 def annotate(ui, repo, *pats, **opts):
277 """show changeset information by line for each file
272 """show changeset information by line for each file
278
273
279 List changes in files, showing the revision id responsible for
274 List changes in files, showing the revision id responsible for
280 each line.
275 each line.
281
276
282 This command is useful for discovering when a change was made and
277 This command is useful for discovering when a change was made and
283 by whom.
278 by whom.
284
279
285 If you include --file, --user, or --date, the revision number is
280 If you include --file, --user, or --date, the revision number is
286 suppressed unless you also include --number.
281 suppressed unless you also include --number.
287
282
288 Without the -a/--text option, annotate will avoid processing files
283 Without the -a/--text option, annotate will avoid processing files
289 it detects as binary. With -a, annotate will annotate the file
284 it detects as binary. With -a, annotate will annotate the file
290 anyway, although the results will probably be neither useful
285 anyway, although the results will probably be neither useful
291 nor desirable.
286 nor desirable.
292
287
293 Returns 0 on success.
288 Returns 0 on success.
294 """
289 """
295 opts = pycompat.byteskwargs(opts)
290 opts = pycompat.byteskwargs(opts)
296 if not pats:
291 if not pats:
297 raise error.Abort(_('at least one filename or pattern is required'))
292 raise error.Abort(_('at least one filename or pattern is required'))
298
293
299 if opts.get('follow'):
294 if opts.get('follow'):
300 # --follow is deprecated and now just an alias for -f/--file
295 # --follow is deprecated and now just an alias for -f/--file
301 # to mimic the behavior of Mercurial before version 1.5
296 # to mimic the behavior of Mercurial before version 1.5
302 opts['file'] = True
297 opts['file'] = True
303
298
304 rev = opts.get('rev')
299 rev = opts.get('rev')
305 if rev:
300 if rev:
306 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
301 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
307 ctx = scmutil.revsingle(repo, rev)
302 ctx = scmutil.revsingle(repo, rev)
308
303
309 rootfm = ui.formatter('annotate', opts)
304 rootfm = ui.formatter('annotate', opts)
310 if ui.quiet:
305 if ui.quiet:
311 datefunc = dateutil.shortdate
306 datefunc = dateutil.shortdate
312 else:
307 else:
313 datefunc = dateutil.datestr
308 datefunc = dateutil.datestr
314 if ctx.rev() is None:
309 if ctx.rev() is None:
315 def hexfn(node):
310 def hexfn(node):
316 if node is None:
311 if node is None:
317 return None
312 return None
318 else:
313 else:
319 return rootfm.hexfunc(node)
314 return rootfm.hexfunc(node)
320 if opts.get('changeset'):
315 if opts.get('changeset'):
321 # omit "+" suffix which is appended to node hex
316 # omit "+" suffix which is appended to node hex
322 def formatrev(rev):
317 def formatrev(rev):
323 if rev is None:
318 if rev is None:
324 return '%d' % ctx.p1().rev()
319 return '%d' % ctx.p1().rev()
325 else:
320 else:
326 return '%d' % rev
321 return '%d' % rev
327 else:
322 else:
328 def formatrev(rev):
323 def formatrev(rev):
329 if rev is None:
324 if rev is None:
330 return '%d+' % ctx.p1().rev()
325 return '%d+' % ctx.p1().rev()
331 else:
326 else:
332 return '%d ' % rev
327 return '%d ' % rev
333 def formathex(hex):
328 def formathex(hex):
334 if hex is None:
329 if hex is None:
335 return '%s+' % rootfm.hexfunc(ctx.p1().node())
330 return '%s+' % rootfm.hexfunc(ctx.p1().node())
336 else:
331 else:
337 return '%s ' % hex
332 return '%s ' % hex
338 else:
333 else:
339 hexfn = rootfm.hexfunc
334 hexfn = rootfm.hexfunc
340 formatrev = formathex = pycompat.bytestr
335 formatrev = formathex = pycompat.bytestr
341
336
342 opmap = [('user', ' ', lambda x: x.fctx.user(), ui.shortuser),
337 opmap = [('user', ' ', lambda x: x.fctx.user(), ui.shortuser),
343 ('number', ' ', lambda x: x.fctx.rev(), formatrev),
338 ('number', ' ', lambda x: x.fctx.rev(), formatrev),
344 ('changeset', ' ', lambda x: hexfn(x.fctx.node()), formathex),
339 ('changeset', ' ', lambda x: hexfn(x.fctx.node()), formathex),
345 ('date', ' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
340 ('date', ' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
346 ('file', ' ', lambda x: x.fctx.path(), pycompat.bytestr),
341 ('file', ' ', lambda x: x.fctx.path(), pycompat.bytestr),
347 ('line_number', ':', lambda x: x.lineno, pycompat.bytestr),
342 ('line_number', ':', lambda x: x.lineno, pycompat.bytestr),
348 ]
343 ]
349 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
344 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
350
345
351 if (not opts.get('user') and not opts.get('changeset')
346 if (not opts.get('user') and not opts.get('changeset')
352 and not opts.get('date') and not opts.get('file')):
347 and not opts.get('date') and not opts.get('file')):
353 opts['number'] = True
348 opts['number'] = True
354
349
355 linenumber = opts.get('line_number') is not None
350 linenumber = opts.get('line_number') is not None
356 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
351 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
357 raise error.Abort(_('at least one of -n/-c is required for -l'))
352 raise error.Abort(_('at least one of -n/-c is required for -l'))
358
353
359 ui.pager('annotate')
354 ui.pager('annotate')
360
355
361 if rootfm.isplain():
356 if rootfm.isplain():
362 def makefunc(get, fmt):
357 def makefunc(get, fmt):
363 return lambda x: fmt(get(x))
358 return lambda x: fmt(get(x))
364 else:
359 else:
365 def makefunc(get, fmt):
360 def makefunc(get, fmt):
366 return get
361 return get
367 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
362 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
368 if opts.get(op)]
363 if opts.get(op)]
369 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
364 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
370 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
365 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
371 if opts.get(op))
366 if opts.get(op))
372
367
373 def bad(x, y):
368 def bad(x, y):
374 raise error.Abort("%s: %s" % (x, y))
369 raise error.Abort("%s: %s" % (x, y))
375
370
376 m = scmutil.match(ctx, pats, opts, badfn=bad)
371 m = scmutil.match(ctx, pats, opts, badfn=bad)
377
372
378 follow = not opts.get('no_follow')
373 follow = not opts.get('no_follow')
379 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
374 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
380 whitespace=True)
375 whitespace=True)
381 skiprevs = opts.get('skip')
376 skiprevs = opts.get('skip')
382 if skiprevs:
377 if skiprevs:
383 skiprevs = scmutil.revrange(repo, skiprevs)
378 skiprevs = scmutil.revrange(repo, skiprevs)
384
379
385 for abs in ctx.walk(m):
380 for abs in ctx.walk(m):
386 fctx = ctx[abs]
381 fctx = ctx[abs]
387 rootfm.startitem()
382 rootfm.startitem()
388 rootfm.data(abspath=abs, path=m.rel(abs))
383 rootfm.data(abspath=abs, path=m.rel(abs))
389 if not opts.get('text') and fctx.isbinary():
384 if not opts.get('text') and fctx.isbinary():
390 rootfm.plain(_("%s: binary file\n")
385 rootfm.plain(_("%s: binary file\n")
391 % ((pats and m.rel(abs)) or abs))
386 % ((pats and m.rel(abs)) or abs))
392 continue
387 continue
393
388
394 fm = rootfm.nested('lines')
389 fm = rootfm.nested('lines')
395 lines = fctx.annotate(follow=follow, skiprevs=skiprevs,
390 lines = fctx.annotate(follow=follow, skiprevs=skiprevs,
396 diffopts=diffopts)
391 diffopts=diffopts)
397 if not lines:
392 if not lines:
398 fm.end()
393 fm.end()
399 continue
394 continue
400 formats = []
395 formats = []
401 pieces = []
396 pieces = []
402
397
403 for f, sep in funcmap:
398 for f, sep in funcmap:
404 l = [f(n) for n in lines]
399 l = [f(n) for n in lines]
405 if fm.isplain():
400 if fm.isplain():
406 sizes = [encoding.colwidth(x) for x in l]
401 sizes = [encoding.colwidth(x) for x in l]
407 ml = max(sizes)
402 ml = max(sizes)
408 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
403 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
409 else:
404 else:
410 formats.append(['%s' for x in l])
405 formats.append(['%s' for x in l])
411 pieces.append(l)
406 pieces.append(l)
412
407
413 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
408 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
414 fm.startitem()
409 fm.startitem()
415 fm.context(fctx=n.fctx)
410 fm.context(fctx=n.fctx)
416 fm.write(fields, "".join(f), *p)
411 fm.write(fields, "".join(f), *p)
417 if n.skip:
412 if n.skip:
418 fmt = "* %s"
413 fmt = "* %s"
419 else:
414 else:
420 fmt = ": %s"
415 fmt = ": %s"
421 fm.write('line', fmt, n.text)
416 fm.write('line', fmt, n.text)
422
417
423 if not lines[-1].text.endswith('\n'):
418 if not lines[-1].text.endswith('\n'):
424 fm.plain('\n')
419 fm.plain('\n')
425 fm.end()
420 fm.end()
426
421
427 rootfm.end()
422 rootfm.end()
428
423
429 @command('archive',
424 @command('archive',
430 [('', 'no-decode', None, _('do not pass files through decoders')),
425 [('', 'no-decode', None, _('do not pass files through decoders')),
431 ('p', 'prefix', '', _('directory prefix for files in archive'),
426 ('p', 'prefix', '', _('directory prefix for files in archive'),
432 _('PREFIX')),
427 _('PREFIX')),
433 ('r', 'rev', '', _('revision to distribute'), _('REV')),
428 ('r', 'rev', '', _('revision to distribute'), _('REV')),
434 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
429 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
435 ] + subrepoopts + walkopts,
430 ] + subrepoopts + walkopts,
436 _('[OPTION]... DEST'))
431 _('[OPTION]... DEST'))
437 def archive(ui, repo, dest, **opts):
432 def archive(ui, repo, dest, **opts):
438 '''create an unversioned archive of a repository revision
433 '''create an unversioned archive of a repository revision
439
434
440 By default, the revision used is the parent of the working
435 By default, the revision used is the parent of the working
441 directory; use -r/--rev to specify a different revision.
436 directory; use -r/--rev to specify a different revision.
442
437
443 The archive type is automatically detected based on file
438 The archive type is automatically detected based on file
444 extension (to override, use -t/--type).
439 extension (to override, use -t/--type).
445
440
446 .. container:: verbose
441 .. container:: verbose
447
442
448 Examples:
443 Examples:
449
444
450 - create a zip file containing the 1.0 release::
445 - create a zip file containing the 1.0 release::
451
446
452 hg archive -r 1.0 project-1.0.zip
447 hg archive -r 1.0 project-1.0.zip
453
448
454 - create a tarball excluding .hg files::
449 - create a tarball excluding .hg files::
455
450
456 hg archive project.tar.gz -X ".hg*"
451 hg archive project.tar.gz -X ".hg*"
457
452
458 Valid types are:
453 Valid types are:
459
454
460 :``files``: a directory full of files (default)
455 :``files``: a directory full of files (default)
461 :``tar``: tar archive, uncompressed
456 :``tar``: tar archive, uncompressed
462 :``tbz2``: tar archive, compressed using bzip2
457 :``tbz2``: tar archive, compressed using bzip2
463 :``tgz``: tar archive, compressed using gzip
458 :``tgz``: tar archive, compressed using gzip
464 :``uzip``: zip archive, uncompressed
459 :``uzip``: zip archive, uncompressed
465 :``zip``: zip archive, compressed using deflate
460 :``zip``: zip archive, compressed using deflate
466
461
467 The exact name of the destination archive or directory is given
462 The exact name of the destination archive or directory is given
468 using a format string; see :hg:`help export` for details.
463 using a format string; see :hg:`help export` for details.
469
464
470 Each member added to an archive file has a directory prefix
465 Each member added to an archive file has a directory prefix
471 prepended. Use -p/--prefix to specify a format string for the
466 prepended. Use -p/--prefix to specify a format string for the
472 prefix. The default is the basename of the archive, with suffixes
467 prefix. The default is the basename of the archive, with suffixes
473 removed.
468 removed.
474
469
475 Returns 0 on success.
470 Returns 0 on success.
476 '''
471 '''
477
472
478 opts = pycompat.byteskwargs(opts)
473 opts = pycompat.byteskwargs(opts)
479 rev = opts.get('rev')
474 rev = opts.get('rev')
480 if rev:
475 if rev:
481 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
476 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
482 ctx = scmutil.revsingle(repo, rev)
477 ctx = scmutil.revsingle(repo, rev)
483 if not ctx:
478 if not ctx:
484 raise error.Abort(_('no working directory: please specify a revision'))
479 raise error.Abort(_('no working directory: please specify a revision'))
485 node = ctx.node()
480 node = ctx.node()
486 dest = cmdutil.makefilename(ctx, dest)
481 dest = cmdutil.makefilename(ctx, dest)
487 if os.path.realpath(dest) == repo.root:
482 if os.path.realpath(dest) == repo.root:
488 raise error.Abort(_('repository root cannot be destination'))
483 raise error.Abort(_('repository root cannot be destination'))
489
484
490 kind = opts.get('type') or archival.guesskind(dest) or 'files'
485 kind = opts.get('type') or archival.guesskind(dest) or 'files'
491 prefix = opts.get('prefix')
486 prefix = opts.get('prefix')
492
487
493 if dest == '-':
488 if dest == '-':
494 if kind == 'files':
489 if kind == 'files':
495 raise error.Abort(_('cannot archive plain files to stdout'))
490 raise error.Abort(_('cannot archive plain files to stdout'))
496 dest = cmdutil.makefileobj(ctx, dest)
491 dest = cmdutil.makefileobj(ctx, dest)
497 if not prefix:
492 if not prefix:
498 prefix = os.path.basename(repo.root) + '-%h'
493 prefix = os.path.basename(repo.root) + '-%h'
499
494
500 prefix = cmdutil.makefilename(ctx, prefix)
495 prefix = cmdutil.makefilename(ctx, prefix)
501 match = scmutil.match(ctx, [], opts)
496 match = scmutil.match(ctx, [], opts)
502 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
497 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
503 match, prefix, subrepos=opts.get('subrepos'))
498 match, prefix, subrepos=opts.get('subrepos'))
504
499
505 @command('backout',
500 @command('backout',
506 [('', 'merge', None, _('merge with old dirstate parent after backout')),
501 [('', 'merge', None, _('merge with old dirstate parent after backout')),
507 ('', 'commit', None,
502 ('', 'commit', None,
508 _('commit if no conflicts were encountered (DEPRECATED)')),
503 _('commit if no conflicts were encountered (DEPRECATED)')),
509 ('', 'no-commit', None, _('do not commit')),
504 ('', 'no-commit', None, _('do not commit')),
510 ('', 'parent', '',
505 ('', 'parent', '',
511 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
506 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
512 ('r', 'rev', '', _('revision to backout'), _('REV')),
507 ('r', 'rev', '', _('revision to backout'), _('REV')),
513 ('e', 'edit', False, _('invoke editor on commit messages')),
508 ('e', 'edit', False, _('invoke editor on commit messages')),
514 ] + mergetoolopts + walkopts + commitopts + commitopts2,
509 ] + mergetoolopts + walkopts + commitopts + commitopts2,
515 _('[OPTION]... [-r] REV'))
510 _('[OPTION]... [-r] REV'))
516 def backout(ui, repo, node=None, rev=None, **opts):
511 def backout(ui, repo, node=None, rev=None, **opts):
517 '''reverse effect of earlier changeset
512 '''reverse effect of earlier changeset
518
513
519 Prepare a new changeset with the effect of REV undone in the
514 Prepare a new changeset with the effect of REV undone in the
520 current working directory. If no conflicts were encountered,
515 current working directory. If no conflicts were encountered,
521 it will be committed immediately.
516 it will be committed immediately.
522
517
523 If REV is the parent of the working directory, then this new changeset
518 If REV is the parent of the working directory, then this new changeset
524 is committed automatically (unless --no-commit is specified).
519 is committed automatically (unless --no-commit is specified).
525
520
526 .. note::
521 .. note::
527
522
528 :hg:`backout` cannot be used to fix either an unwanted or
523 :hg:`backout` cannot be used to fix either an unwanted or
529 incorrect merge.
524 incorrect merge.
530
525
531 .. container:: verbose
526 .. container:: verbose
532
527
533 Examples:
528 Examples:
534
529
535 - Reverse the effect of the parent of the working directory.
530 - Reverse the effect of the parent of the working directory.
536 This backout will be committed immediately::
531 This backout will be committed immediately::
537
532
538 hg backout -r .
533 hg backout -r .
539
534
540 - Reverse the effect of previous bad revision 23::
535 - Reverse the effect of previous bad revision 23::
541
536
542 hg backout -r 23
537 hg backout -r 23
543
538
544 - Reverse the effect of previous bad revision 23 and
539 - Reverse the effect of previous bad revision 23 and
545 leave changes uncommitted::
540 leave changes uncommitted::
546
541
547 hg backout -r 23 --no-commit
542 hg backout -r 23 --no-commit
548 hg commit -m "Backout revision 23"
543 hg commit -m "Backout revision 23"
549
544
550 By default, the pending changeset will have one parent,
545 By default, the pending changeset will have one parent,
551 maintaining a linear history. With --merge, the pending
546 maintaining a linear history. With --merge, the pending
552 changeset will instead have two parents: the old parent of the
547 changeset will instead have two parents: the old parent of the
553 working directory and a new child of REV that simply undoes REV.
548 working directory and a new child of REV that simply undoes REV.
554
549
555 Before version 1.7, the behavior without --merge was equivalent
550 Before version 1.7, the behavior without --merge was equivalent
556 to specifying --merge followed by :hg:`update --clean .` to
551 to specifying --merge followed by :hg:`update --clean .` to
557 cancel the merge and leave the child of REV as a head to be
552 cancel the merge and leave the child of REV as a head to be
558 merged separately.
553 merged separately.
559
554
560 See :hg:`help dates` for a list of formats valid for -d/--date.
555 See :hg:`help dates` for a list of formats valid for -d/--date.
561
556
562 See :hg:`help revert` for a way to restore files to the state
557 See :hg:`help revert` for a way to restore files to the state
563 of another revision.
558 of another revision.
564
559
565 Returns 0 on success, 1 if nothing to backout or there are unresolved
560 Returns 0 on success, 1 if nothing to backout or there are unresolved
566 files.
561 files.
567 '''
562 '''
568 wlock = lock = None
563 wlock = lock = None
569 try:
564 try:
570 wlock = repo.wlock()
565 wlock = repo.wlock()
571 lock = repo.lock()
566 lock = repo.lock()
572 return _dobackout(ui, repo, node, rev, **opts)
567 return _dobackout(ui, repo, node, rev, **opts)
573 finally:
568 finally:
574 release(lock, wlock)
569 release(lock, wlock)
575
570
576 def _dobackout(ui, repo, node=None, rev=None, **opts):
571 def _dobackout(ui, repo, node=None, rev=None, **opts):
577 opts = pycompat.byteskwargs(opts)
572 opts = pycompat.byteskwargs(opts)
578 if opts.get('commit') and opts.get('no_commit'):
573 if opts.get('commit') and opts.get('no_commit'):
579 raise error.Abort(_("cannot use --commit with --no-commit"))
574 raise error.Abort(_("cannot use --commit with --no-commit"))
580 if opts.get('merge') and opts.get('no_commit'):
575 if opts.get('merge') and opts.get('no_commit'):
581 raise error.Abort(_("cannot use --merge with --no-commit"))
576 raise error.Abort(_("cannot use --merge with --no-commit"))
582
577
583 if rev and node:
578 if rev and node:
584 raise error.Abort(_("please specify just one revision"))
579 raise error.Abort(_("please specify just one revision"))
585
580
586 if not rev:
581 if not rev:
587 rev = node
582 rev = node
588
583
589 if not rev:
584 if not rev:
590 raise error.Abort(_("please specify a revision to backout"))
585 raise error.Abort(_("please specify a revision to backout"))
591
586
592 date = opts.get('date')
587 date = opts.get('date')
593 if date:
588 if date:
594 opts['date'] = dateutil.parsedate(date)
589 opts['date'] = dateutil.parsedate(date)
595
590
596 cmdutil.checkunfinished(repo)
591 cmdutil.checkunfinished(repo)
597 cmdutil.bailifchanged(repo)
592 cmdutil.bailifchanged(repo)
598 node = scmutil.revsingle(repo, rev).node()
593 node = scmutil.revsingle(repo, rev).node()
599
594
600 op1, op2 = repo.dirstate.parents()
595 op1, op2 = repo.dirstate.parents()
601 if not repo.changelog.isancestor(node, op1):
596 if not repo.changelog.isancestor(node, op1):
602 raise error.Abort(_('cannot backout change that is not an ancestor'))
597 raise error.Abort(_('cannot backout change that is not an ancestor'))
603
598
604 p1, p2 = repo.changelog.parents(node)
599 p1, p2 = repo.changelog.parents(node)
605 if p1 == nullid:
600 if p1 == nullid:
606 raise error.Abort(_('cannot backout a change with no parents'))
601 raise error.Abort(_('cannot backout a change with no parents'))
607 if p2 != nullid:
602 if p2 != nullid:
608 if not opts.get('parent'):
603 if not opts.get('parent'):
609 raise error.Abort(_('cannot backout a merge changeset'))
604 raise error.Abort(_('cannot backout a merge changeset'))
610 p = repo.lookup(opts['parent'])
605 p = repo.lookup(opts['parent'])
611 if p not in (p1, p2):
606 if p not in (p1, p2):
612 raise error.Abort(_('%s is not a parent of %s') %
607 raise error.Abort(_('%s is not a parent of %s') %
613 (short(p), short(node)))
608 (short(p), short(node)))
614 parent = p
609 parent = p
615 else:
610 else:
616 if opts.get('parent'):
611 if opts.get('parent'):
617 raise error.Abort(_('cannot use --parent on non-merge changeset'))
612 raise error.Abort(_('cannot use --parent on non-merge changeset'))
618 parent = p1
613 parent = p1
619
614
620 # the backout should appear on the same branch
615 # the backout should appear on the same branch
621 branch = repo.dirstate.branch()
616 branch = repo.dirstate.branch()
622 bheads = repo.branchheads(branch)
617 bheads = repo.branchheads(branch)
623 rctx = scmutil.revsingle(repo, hex(parent))
618 rctx = scmutil.revsingle(repo, hex(parent))
624 if not opts.get('merge') and op1 != node:
619 if not opts.get('merge') and op1 != node:
625 dsguard = dirstateguard.dirstateguard(repo, 'backout')
620 dsguard = dirstateguard.dirstateguard(repo, 'backout')
626 try:
621 try:
627 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
622 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
628 'backout')
623 'backout')
629 stats = mergemod.update(repo, parent, True, True, node, False)
624 stats = mergemod.update(repo, parent, True, True, node, False)
630 repo.setparents(op1, op2)
625 repo.setparents(op1, op2)
631 dsguard.close()
626 dsguard.close()
632 hg._showstats(repo, stats)
627 hg._showstats(repo, stats)
633 if stats.unresolvedcount:
628 if stats.unresolvedcount:
634 repo.ui.status(_("use 'hg resolve' to retry unresolved "
629 repo.ui.status(_("use 'hg resolve' to retry unresolved "
635 "file merges\n"))
630 "file merges\n"))
636 return 1
631 return 1
637 finally:
632 finally:
638 ui.setconfig('ui', 'forcemerge', '', '')
633 ui.setconfig('ui', 'forcemerge', '', '')
639 lockmod.release(dsguard)
634 lockmod.release(dsguard)
640 else:
635 else:
641 hg.clean(repo, node, show_stats=False)
636 hg.clean(repo, node, show_stats=False)
642 repo.dirstate.setbranch(branch)
637 repo.dirstate.setbranch(branch)
643 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
638 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
644
639
645 if opts.get('no_commit'):
640 if opts.get('no_commit'):
646 msg = _("changeset %s backed out, "
641 msg = _("changeset %s backed out, "
647 "don't forget to commit.\n")
642 "don't forget to commit.\n")
648 ui.status(msg % short(node))
643 ui.status(msg % short(node))
649 return 0
644 return 0
650
645
651 def commitfunc(ui, repo, message, match, opts):
646 def commitfunc(ui, repo, message, match, opts):
652 editform = 'backout'
647 editform = 'backout'
653 e = cmdutil.getcommiteditor(editform=editform,
648 e = cmdutil.getcommiteditor(editform=editform,
654 **pycompat.strkwargs(opts))
649 **pycompat.strkwargs(opts))
655 if not message:
650 if not message:
656 # we don't translate commit messages
651 # we don't translate commit messages
657 message = "Backed out changeset %s" % short(node)
652 message = "Backed out changeset %s" % short(node)
658 e = cmdutil.getcommiteditor(edit=True, editform=editform)
653 e = cmdutil.getcommiteditor(edit=True, editform=editform)
659 return repo.commit(message, opts.get('user'), opts.get('date'),
654 return repo.commit(message, opts.get('user'), opts.get('date'),
660 match, editor=e)
655 match, editor=e)
661 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
656 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
662 if not newnode:
657 if not newnode:
663 ui.status(_("nothing changed\n"))
658 ui.status(_("nothing changed\n"))
664 return 1
659 return 1
665 cmdutil.commitstatus(repo, newnode, branch, bheads)
660 cmdutil.commitstatus(repo, newnode, branch, bheads)
666
661
667 def nice(node):
662 def nice(node):
668 return '%d:%s' % (repo.changelog.rev(node), short(node))
663 return '%d:%s' % (repo.changelog.rev(node), short(node))
669 ui.status(_('changeset %s backs out changeset %s\n') %
664 ui.status(_('changeset %s backs out changeset %s\n') %
670 (nice(repo.changelog.tip()), nice(node)))
665 (nice(repo.changelog.tip()), nice(node)))
671 if opts.get('merge') and op1 != node:
666 if opts.get('merge') and op1 != node:
672 hg.clean(repo, op1, show_stats=False)
667 hg.clean(repo, op1, show_stats=False)
673 ui.status(_('merging with changeset %s\n')
668 ui.status(_('merging with changeset %s\n')
674 % nice(repo.changelog.tip()))
669 % nice(repo.changelog.tip()))
675 try:
670 try:
676 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
671 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
677 'backout')
672 'backout')
678 return hg.merge(repo, hex(repo.changelog.tip()))
673 return hg.merge(repo, hex(repo.changelog.tip()))
679 finally:
674 finally:
680 ui.setconfig('ui', 'forcemerge', '', '')
675 ui.setconfig('ui', 'forcemerge', '', '')
681 return 0
676 return 0
682
677
683 @command('bisect',
678 @command('bisect',
684 [('r', 'reset', False, _('reset bisect state')),
679 [('r', 'reset', False, _('reset bisect state')),
685 ('g', 'good', False, _('mark changeset good')),
680 ('g', 'good', False, _('mark changeset good')),
686 ('b', 'bad', False, _('mark changeset bad')),
681 ('b', 'bad', False, _('mark changeset bad')),
687 ('s', 'skip', False, _('skip testing changeset')),
682 ('s', 'skip', False, _('skip testing changeset')),
688 ('e', 'extend', False, _('extend the bisect range')),
683 ('e', 'extend', False, _('extend the bisect range')),
689 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
684 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
690 ('U', 'noupdate', False, _('do not update to target'))],
685 ('U', 'noupdate', False, _('do not update to target'))],
691 _("[-gbsr] [-U] [-c CMD] [REV]"))
686 _("[-gbsr] [-U] [-c CMD] [REV]"))
692 def bisect(ui, repo, rev=None, extra=None, command=None,
687 def bisect(ui, repo, rev=None, extra=None, command=None,
693 reset=None, good=None, bad=None, skip=None, extend=None,
688 reset=None, good=None, bad=None, skip=None, extend=None,
694 noupdate=None):
689 noupdate=None):
695 """subdivision search of changesets
690 """subdivision search of changesets
696
691
697 This command helps to find changesets which introduce problems. To
692 This command helps to find changesets which introduce problems. To
698 use, mark the earliest changeset you know exhibits the problem as
693 use, mark the earliest changeset you know exhibits the problem as
699 bad, then mark the latest changeset which is free from the problem
694 bad, then mark the latest changeset which is free from the problem
700 as good. Bisect will update your working directory to a revision
695 as good. Bisect will update your working directory to a revision
701 for testing (unless the -U/--noupdate option is specified). Once
696 for testing (unless the -U/--noupdate option is specified). Once
702 you have performed tests, mark the working directory as good or
697 you have performed tests, mark the working directory as good or
703 bad, and bisect will either update to another candidate changeset
698 bad, and bisect will either update to another candidate changeset
704 or announce that it has found the bad revision.
699 or announce that it has found the bad revision.
705
700
706 As a shortcut, you can also use the revision argument to mark a
701 As a shortcut, you can also use the revision argument to mark a
707 revision as good or bad without checking it out first.
702 revision as good or bad without checking it out first.
708
703
709 If you supply a command, it will be used for automatic bisection.
704 If you supply a command, it will be used for automatic bisection.
710 The environment variable HG_NODE will contain the ID of the
705 The environment variable HG_NODE will contain the ID of the
711 changeset being tested. The exit status of the command will be
706 changeset being tested. The exit status of the command will be
712 used to mark revisions as good or bad: status 0 means good, 125
707 used to mark revisions as good or bad: status 0 means good, 125
713 means to skip the revision, 127 (command not found) will abort the
708 means to skip the revision, 127 (command not found) will abort the
714 bisection, and any other non-zero exit status means the revision
709 bisection, and any other non-zero exit status means the revision
715 is bad.
710 is bad.
716
711
717 .. container:: verbose
712 .. container:: verbose
718
713
719 Some examples:
714 Some examples:
720
715
721 - start a bisection with known bad revision 34, and good revision 12::
716 - start a bisection with known bad revision 34, and good revision 12::
722
717
723 hg bisect --bad 34
718 hg bisect --bad 34
724 hg bisect --good 12
719 hg bisect --good 12
725
720
726 - advance the current bisection by marking current revision as good or
721 - advance the current bisection by marking current revision as good or
727 bad::
722 bad::
728
723
729 hg bisect --good
724 hg bisect --good
730 hg bisect --bad
725 hg bisect --bad
731
726
732 - mark the current revision, or a known revision, to be skipped (e.g. if
727 - mark the current revision, or a known revision, to be skipped (e.g. if
733 that revision is not usable because of another issue)::
728 that revision is not usable because of another issue)::
734
729
735 hg bisect --skip
730 hg bisect --skip
736 hg bisect --skip 23
731 hg bisect --skip 23
737
732
738 - skip all revisions that do not touch directories ``foo`` or ``bar``::
733 - skip all revisions that do not touch directories ``foo`` or ``bar``::
739
734
740 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
735 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
741
736
742 - forget the current bisection::
737 - forget the current bisection::
743
738
744 hg bisect --reset
739 hg bisect --reset
745
740
746 - use 'make && make tests' to automatically find the first broken
741 - use 'make && make tests' to automatically find the first broken
747 revision::
742 revision::
748
743
749 hg bisect --reset
744 hg bisect --reset
750 hg bisect --bad 34
745 hg bisect --bad 34
751 hg bisect --good 12
746 hg bisect --good 12
752 hg bisect --command "make && make tests"
747 hg bisect --command "make && make tests"
753
748
754 - see all changesets whose states are already known in the current
749 - see all changesets whose states are already known in the current
755 bisection::
750 bisection::
756
751
757 hg log -r "bisect(pruned)"
752 hg log -r "bisect(pruned)"
758
753
759 - see the changeset currently being bisected (especially useful
754 - see the changeset currently being bisected (especially useful
760 if running with -U/--noupdate)::
755 if running with -U/--noupdate)::
761
756
762 hg log -r "bisect(current)"
757 hg log -r "bisect(current)"
763
758
764 - see all changesets that took part in the current bisection::
759 - see all changesets that took part in the current bisection::
765
760
766 hg log -r "bisect(range)"
761 hg log -r "bisect(range)"
767
762
768 - you can even get a nice graph::
763 - you can even get a nice graph::
769
764
770 hg log --graph -r "bisect(range)"
765 hg log --graph -r "bisect(range)"
771
766
772 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
767 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
773
768
774 Returns 0 on success.
769 Returns 0 on success.
775 """
770 """
776 # backward compatibility
771 # backward compatibility
777 if rev in "good bad reset init".split():
772 if rev in "good bad reset init".split():
778 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
773 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
779 cmd, rev, extra = rev, extra, None
774 cmd, rev, extra = rev, extra, None
780 if cmd == "good":
775 if cmd == "good":
781 good = True
776 good = True
782 elif cmd == "bad":
777 elif cmd == "bad":
783 bad = True
778 bad = True
784 else:
779 else:
785 reset = True
780 reset = True
786 elif extra:
781 elif extra:
787 raise error.Abort(_('incompatible arguments'))
782 raise error.Abort(_('incompatible arguments'))
788
783
789 incompatibles = {
784 incompatibles = {
790 '--bad': bad,
785 '--bad': bad,
791 '--command': bool(command),
786 '--command': bool(command),
792 '--extend': extend,
787 '--extend': extend,
793 '--good': good,
788 '--good': good,
794 '--reset': reset,
789 '--reset': reset,
795 '--skip': skip,
790 '--skip': skip,
796 }
791 }
797
792
798 enabled = [x for x in incompatibles if incompatibles[x]]
793 enabled = [x for x in incompatibles if incompatibles[x]]
799
794
800 if len(enabled) > 1:
795 if len(enabled) > 1:
801 raise error.Abort(_('%s and %s are incompatible') %
796 raise error.Abort(_('%s and %s are incompatible') %
802 tuple(sorted(enabled)[0:2]))
797 tuple(sorted(enabled)[0:2]))
803
798
804 if reset:
799 if reset:
805 hbisect.resetstate(repo)
800 hbisect.resetstate(repo)
806 return
801 return
807
802
808 state = hbisect.load_state(repo)
803 state = hbisect.load_state(repo)
809
804
810 # update state
805 # update state
811 if good or bad or skip:
806 if good or bad or skip:
812 if rev:
807 if rev:
813 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
808 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
814 else:
809 else:
815 nodes = [repo.lookup('.')]
810 nodes = [repo.lookup('.')]
816 if good:
811 if good:
817 state['good'] += nodes
812 state['good'] += nodes
818 elif bad:
813 elif bad:
819 state['bad'] += nodes
814 state['bad'] += nodes
820 elif skip:
815 elif skip:
821 state['skip'] += nodes
816 state['skip'] += nodes
822 hbisect.save_state(repo, state)
817 hbisect.save_state(repo, state)
823 if not (state['good'] and state['bad']):
818 if not (state['good'] and state['bad']):
824 return
819 return
825
820
826 def mayupdate(repo, node, show_stats=True):
821 def mayupdate(repo, node, show_stats=True):
827 """common used update sequence"""
822 """common used update sequence"""
828 if noupdate:
823 if noupdate:
829 return
824 return
830 cmdutil.checkunfinished(repo)
825 cmdutil.checkunfinished(repo)
831 cmdutil.bailifchanged(repo)
826 cmdutil.bailifchanged(repo)
832 return hg.clean(repo, node, show_stats=show_stats)
827 return hg.clean(repo, node, show_stats=show_stats)
833
828
834 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
829 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
835
830
836 if command:
831 if command:
837 changesets = 1
832 changesets = 1
838 if noupdate:
833 if noupdate:
839 try:
834 try:
840 node = state['current'][0]
835 node = state['current'][0]
841 except LookupError:
836 except LookupError:
842 raise error.Abort(_('current bisect revision is unknown - '
837 raise error.Abort(_('current bisect revision is unknown - '
843 'start a new bisect to fix'))
838 'start a new bisect to fix'))
844 else:
839 else:
845 node, p2 = repo.dirstate.parents()
840 node, p2 = repo.dirstate.parents()
846 if p2 != nullid:
841 if p2 != nullid:
847 raise error.Abort(_('current bisect revision is a merge'))
842 raise error.Abort(_('current bisect revision is a merge'))
848 if rev:
843 if rev:
849 node = repo[scmutil.revsingle(repo, rev, node)].node()
844 node = repo[scmutil.revsingle(repo, rev, node)].node()
850 try:
845 try:
851 while changesets:
846 while changesets:
852 # update state
847 # update state
853 state['current'] = [node]
848 state['current'] = [node]
854 hbisect.save_state(repo, state)
849 hbisect.save_state(repo, state)
855 status = ui.system(command, environ={'HG_NODE': hex(node)},
850 status = ui.system(command, environ={'HG_NODE': hex(node)},
856 blockedtag='bisect_check')
851 blockedtag='bisect_check')
857 if status == 125:
852 if status == 125:
858 transition = "skip"
853 transition = "skip"
859 elif status == 0:
854 elif status == 0:
860 transition = "good"
855 transition = "good"
861 # status < 0 means process was killed
856 # status < 0 means process was killed
862 elif status == 127:
857 elif status == 127:
863 raise error.Abort(_("failed to execute %s") % command)
858 raise error.Abort(_("failed to execute %s") % command)
864 elif status < 0:
859 elif status < 0:
865 raise error.Abort(_("%s killed") % command)
860 raise error.Abort(_("%s killed") % command)
866 else:
861 else:
867 transition = "bad"
862 transition = "bad"
868 state[transition].append(node)
863 state[transition].append(node)
869 ctx = repo[node]
864 ctx = repo[node]
870 ui.status(_('changeset %d:%s: %s\n') % (ctx.rev(), ctx,
865 ui.status(_('changeset %d:%s: %s\n') % (ctx.rev(), ctx,
871 transition))
866 transition))
872 hbisect.checkstate(state)
867 hbisect.checkstate(state)
873 # bisect
868 # bisect
874 nodes, changesets, bgood = hbisect.bisect(repo, state)
869 nodes, changesets, bgood = hbisect.bisect(repo, state)
875 # update to next check
870 # update to next check
876 node = nodes[0]
871 node = nodes[0]
877 mayupdate(repo, node, show_stats=False)
872 mayupdate(repo, node, show_stats=False)
878 finally:
873 finally:
879 state['current'] = [node]
874 state['current'] = [node]
880 hbisect.save_state(repo, state)
875 hbisect.save_state(repo, state)
881 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
876 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
882 return
877 return
883
878
884 hbisect.checkstate(state)
879 hbisect.checkstate(state)
885
880
886 # actually bisect
881 # actually bisect
887 nodes, changesets, good = hbisect.bisect(repo, state)
882 nodes, changesets, good = hbisect.bisect(repo, state)
888 if extend:
883 if extend:
889 if not changesets:
884 if not changesets:
890 extendnode = hbisect.extendrange(repo, state, nodes, good)
885 extendnode = hbisect.extendrange(repo, state, nodes, good)
891 if extendnode is not None:
886 if extendnode is not None:
892 ui.write(_("Extending search to changeset %d:%s\n")
887 ui.write(_("Extending search to changeset %d:%s\n")
893 % (extendnode.rev(), extendnode))
888 % (extendnode.rev(), extendnode))
894 state['current'] = [extendnode.node()]
889 state['current'] = [extendnode.node()]
895 hbisect.save_state(repo, state)
890 hbisect.save_state(repo, state)
896 return mayupdate(repo, extendnode.node())
891 return mayupdate(repo, extendnode.node())
897 raise error.Abort(_("nothing to extend"))
892 raise error.Abort(_("nothing to extend"))
898
893
899 if changesets == 0:
894 if changesets == 0:
900 hbisect.printresult(ui, repo, state, displayer, nodes, good)
895 hbisect.printresult(ui, repo, state, displayer, nodes, good)
901 else:
896 else:
902 assert len(nodes) == 1 # only a single node can be tested next
897 assert len(nodes) == 1 # only a single node can be tested next
903 node = nodes[0]
898 node = nodes[0]
904 # compute the approximate number of remaining tests
899 # compute the approximate number of remaining tests
905 tests, size = 0, 2
900 tests, size = 0, 2
906 while size <= changesets:
901 while size <= changesets:
907 tests, size = tests + 1, size * 2
902 tests, size = tests + 1, size * 2
908 rev = repo.changelog.rev(node)
903 rev = repo.changelog.rev(node)
909 ui.write(_("Testing changeset %d:%s "
904 ui.write(_("Testing changeset %d:%s "
910 "(%d changesets remaining, ~%d tests)\n")
905 "(%d changesets remaining, ~%d tests)\n")
911 % (rev, short(node), changesets, tests))
906 % (rev, short(node), changesets, tests))
912 state['current'] = [node]
907 state['current'] = [node]
913 hbisect.save_state(repo, state)
908 hbisect.save_state(repo, state)
914 return mayupdate(repo, node)
909 return mayupdate(repo, node)
915
910
916 @command('bookmarks|bookmark',
911 @command('bookmarks|bookmark',
917 [('f', 'force', False, _('force')),
912 [('f', 'force', False, _('force')),
918 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
913 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
919 ('d', 'delete', False, _('delete a given bookmark')),
914 ('d', 'delete', False, _('delete a given bookmark')),
920 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
915 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
921 ('i', 'inactive', False, _('mark a bookmark inactive')),
916 ('i', 'inactive', False, _('mark a bookmark inactive')),
922 ] + formatteropts,
917 ] + formatteropts,
923 _('hg bookmarks [OPTIONS]... [NAME]...'))
918 _('hg bookmarks [OPTIONS]... [NAME]...'))
924 def bookmark(ui, repo, *names, **opts):
919 def bookmark(ui, repo, *names, **opts):
925 '''create a new bookmark or list existing bookmarks
920 '''create a new bookmark or list existing bookmarks
926
921
927 Bookmarks are labels on changesets to help track lines of development.
922 Bookmarks are labels on changesets to help track lines of development.
928 Bookmarks are unversioned and can be moved, renamed and deleted.
923 Bookmarks are unversioned and can be moved, renamed and deleted.
929 Deleting or moving a bookmark has no effect on the associated changesets.
924 Deleting or moving a bookmark has no effect on the associated changesets.
930
925
931 Creating or updating to a bookmark causes it to be marked as 'active'.
926 Creating or updating to a bookmark causes it to be marked as 'active'.
932 The active bookmark is indicated with a '*'.
927 The active bookmark is indicated with a '*'.
933 When a commit is made, the active bookmark will advance to the new commit.
928 When a commit is made, the active bookmark will advance to the new commit.
934 A plain :hg:`update` will also advance an active bookmark, if possible.
929 A plain :hg:`update` will also advance an active bookmark, if possible.
935 Updating away from a bookmark will cause it to be deactivated.
930 Updating away from a bookmark will cause it to be deactivated.
936
931
937 Bookmarks can be pushed and pulled between repositories (see
932 Bookmarks can be pushed and pulled between repositories (see
938 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
933 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
939 diverged, a new 'divergent bookmark' of the form 'name@path' will
934 diverged, a new 'divergent bookmark' of the form 'name@path' will
940 be created. Using :hg:`merge` will resolve the divergence.
935 be created. Using :hg:`merge` will resolve the divergence.
941
936
942 Specifying bookmark as '.' to -m or -d options is equivalent to specifying
937 Specifying bookmark as '.' to -m or -d options is equivalent to specifying
943 the active bookmark's name.
938 the active bookmark's name.
944
939
945 A bookmark named '@' has the special property that :hg:`clone` will
940 A bookmark named '@' has the special property that :hg:`clone` will
946 check it out by default if it exists.
941 check it out by default if it exists.
947
942
948 .. container:: verbose
943 .. container:: verbose
949
944
950 Examples:
945 Examples:
951
946
952 - create an active bookmark for a new line of development::
947 - create an active bookmark for a new line of development::
953
948
954 hg book new-feature
949 hg book new-feature
955
950
956 - create an inactive bookmark as a place marker::
951 - create an inactive bookmark as a place marker::
957
952
958 hg book -i reviewed
953 hg book -i reviewed
959
954
960 - create an inactive bookmark on another changeset::
955 - create an inactive bookmark on another changeset::
961
956
962 hg book -r .^ tested
957 hg book -r .^ tested
963
958
964 - rename bookmark turkey to dinner::
959 - rename bookmark turkey to dinner::
965
960
966 hg book -m turkey dinner
961 hg book -m turkey dinner
967
962
968 - move the '@' bookmark from another branch::
963 - move the '@' bookmark from another branch::
969
964
970 hg book -f @
965 hg book -f @
971 '''
966 '''
972 force = opts.get(r'force')
967 force = opts.get(r'force')
973 rev = opts.get(r'rev')
968 rev = opts.get(r'rev')
974 delete = opts.get(r'delete')
969 delete = opts.get(r'delete')
975 rename = opts.get(r'rename')
970 rename = opts.get(r'rename')
976 inactive = opts.get(r'inactive')
971 inactive = opts.get(r'inactive')
977
972
978 if delete and rename:
973 if delete and rename:
979 raise error.Abort(_("--delete and --rename are incompatible"))
974 raise error.Abort(_("--delete and --rename are incompatible"))
980 if delete and rev:
975 if delete and rev:
981 raise error.Abort(_("--rev is incompatible with --delete"))
976 raise error.Abort(_("--rev is incompatible with --delete"))
982 if rename and rev:
977 if rename and rev:
983 raise error.Abort(_("--rev is incompatible with --rename"))
978 raise error.Abort(_("--rev is incompatible with --rename"))
984 if not names and (delete or rev):
979 if not names and (delete or rev):
985 raise error.Abort(_("bookmark name required"))
980 raise error.Abort(_("bookmark name required"))
986
981
987 if delete or rename or names or inactive:
982 if delete or rename or names or inactive:
988 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
983 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
989 if delete:
984 if delete:
990 names = pycompat.maplist(repo._bookmarks.expandname, names)
985 names = pycompat.maplist(repo._bookmarks.expandname, names)
991 bookmarks.delete(repo, tr, names)
986 bookmarks.delete(repo, tr, names)
992 elif rename:
987 elif rename:
993 if not names:
988 if not names:
994 raise error.Abort(_("new bookmark name required"))
989 raise error.Abort(_("new bookmark name required"))
995 elif len(names) > 1:
990 elif len(names) > 1:
996 raise error.Abort(_("only one new bookmark name allowed"))
991 raise error.Abort(_("only one new bookmark name allowed"))
997 rename = repo._bookmarks.expandname(rename)
992 rename = repo._bookmarks.expandname(rename)
998 bookmarks.rename(repo, tr, rename, names[0], force, inactive)
993 bookmarks.rename(repo, tr, rename, names[0], force, inactive)
999 elif names:
994 elif names:
1000 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
995 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1001 elif inactive:
996 elif inactive:
1002 if len(repo._bookmarks) == 0:
997 if len(repo._bookmarks) == 0:
1003 ui.status(_("no bookmarks set\n"))
998 ui.status(_("no bookmarks set\n"))
1004 elif not repo._activebookmark:
999 elif not repo._activebookmark:
1005 ui.status(_("no active bookmark\n"))
1000 ui.status(_("no active bookmark\n"))
1006 else:
1001 else:
1007 bookmarks.deactivate(repo)
1002 bookmarks.deactivate(repo)
1008 else: # show bookmarks
1003 else: # show bookmarks
1009 bookmarks.printbookmarks(ui, repo, **opts)
1004 bookmarks.printbookmarks(ui, repo, **opts)
1010
1005
1011 @command('branch',
1006 @command('branch',
1012 [('f', 'force', None,
1007 [('f', 'force', None,
1013 _('set branch name even if it shadows an existing branch')),
1008 _('set branch name even if it shadows an existing branch')),
1014 ('C', 'clean', None, _('reset branch name to parent branch name')),
1009 ('C', 'clean', None, _('reset branch name to parent branch name')),
1015 ('r', 'rev', [], _('change branches of the given revs (EXPERIMENTAL)')),
1010 ('r', 'rev', [], _('change branches of the given revs (EXPERIMENTAL)')),
1016 ],
1011 ],
1017 _('[-fC] [NAME]'))
1012 _('[-fC] [NAME]'))
1018 def branch(ui, repo, label=None, **opts):
1013 def branch(ui, repo, label=None, **opts):
1019 """set or show the current branch name
1014 """set or show the current branch name
1020
1015
1021 .. note::
1016 .. note::
1022
1017
1023 Branch names are permanent and global. Use :hg:`bookmark` to create a
1018 Branch names are permanent and global. Use :hg:`bookmark` to create a
1024 light-weight bookmark instead. See :hg:`help glossary` for more
1019 light-weight bookmark instead. See :hg:`help glossary` for more
1025 information about named branches and bookmarks.
1020 information about named branches and bookmarks.
1026
1021
1027 With no argument, show the current branch name. With one argument,
1022 With no argument, show the current branch name. With one argument,
1028 set the working directory branch name (the branch will not exist
1023 set the working directory branch name (the branch will not exist
1029 in the repository until the next commit). Standard practice
1024 in the repository until the next commit). Standard practice
1030 recommends that primary development take place on the 'default'
1025 recommends that primary development take place on the 'default'
1031 branch.
1026 branch.
1032
1027
1033 Unless -f/--force is specified, branch will not let you set a
1028 Unless -f/--force is specified, branch will not let you set a
1034 branch name that already exists.
1029 branch name that already exists.
1035
1030
1036 Use -C/--clean to reset the working directory branch to that of
1031 Use -C/--clean to reset the working directory branch to that of
1037 the parent of the working directory, negating a previous branch
1032 the parent of the working directory, negating a previous branch
1038 change.
1033 change.
1039
1034
1040 Use the command :hg:`update` to switch to an existing branch. Use
1035 Use the command :hg:`update` to switch to an existing branch. Use
1041 :hg:`commit --close-branch` to mark this branch head as closed.
1036 :hg:`commit --close-branch` to mark this branch head as closed.
1042 When all heads of a branch are closed, the branch will be
1037 When all heads of a branch are closed, the branch will be
1043 considered closed.
1038 considered closed.
1044
1039
1045 Returns 0 on success.
1040 Returns 0 on success.
1046 """
1041 """
1047 opts = pycompat.byteskwargs(opts)
1042 opts = pycompat.byteskwargs(opts)
1048 revs = opts.get('rev')
1043 revs = opts.get('rev')
1049 if label:
1044 if label:
1050 label = label.strip()
1045 label = label.strip()
1051
1046
1052 if not opts.get('clean') and not label:
1047 if not opts.get('clean') and not label:
1053 if revs:
1048 if revs:
1054 raise error.Abort(_("no branch name specified for the revisions"))
1049 raise error.Abort(_("no branch name specified for the revisions"))
1055 ui.write("%s\n" % repo.dirstate.branch())
1050 ui.write("%s\n" % repo.dirstate.branch())
1056 return
1051 return
1057
1052
1058 with repo.wlock():
1053 with repo.wlock():
1059 if opts.get('clean'):
1054 if opts.get('clean'):
1060 label = repo[None].p1().branch()
1055 label = repo[None].p1().branch()
1061 repo.dirstate.setbranch(label)
1056 repo.dirstate.setbranch(label)
1062 ui.status(_('reset working directory to branch %s\n') % label)
1057 ui.status(_('reset working directory to branch %s\n') % label)
1063 elif label:
1058 elif label:
1064
1059
1065 scmutil.checknewlabel(repo, label, 'branch')
1060 scmutil.checknewlabel(repo, label, 'branch')
1066 if revs:
1061 if revs:
1067 return cmdutil.changebranch(ui, repo, revs, label)
1062 return cmdutil.changebranch(ui, repo, revs, label)
1068
1063
1069 if not opts.get('force') and label in repo.branchmap():
1064 if not opts.get('force') and label in repo.branchmap():
1070 if label not in [p.branch() for p in repo[None].parents()]:
1065 if label not in [p.branch() for p in repo[None].parents()]:
1071 raise error.Abort(_('a branch of the same name already'
1066 raise error.Abort(_('a branch of the same name already'
1072 ' exists'),
1067 ' exists'),
1073 # i18n: "it" refers to an existing branch
1068 # i18n: "it" refers to an existing branch
1074 hint=_("use 'hg update' to switch to it"))
1069 hint=_("use 'hg update' to switch to it"))
1075
1070
1076 repo.dirstate.setbranch(label)
1071 repo.dirstate.setbranch(label)
1077 ui.status(_('marked working directory as branch %s\n') % label)
1072 ui.status(_('marked working directory as branch %s\n') % label)
1078
1073
1079 # find any open named branches aside from default
1074 # find any open named branches aside from default
1080 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1075 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1081 if n != "default" and not c]
1076 if n != "default" and not c]
1082 if not others:
1077 if not others:
1083 ui.status(_('(branches are permanent and global, '
1078 ui.status(_('(branches are permanent and global, '
1084 'did you want a bookmark?)\n'))
1079 'did you want a bookmark?)\n'))
1085
1080
1086 @command('branches',
1081 @command('branches',
1087 [('a', 'active', False,
1082 [('a', 'active', False,
1088 _('show only branches that have unmerged heads (DEPRECATED)')),
1083 _('show only branches that have unmerged heads (DEPRECATED)')),
1089 ('c', 'closed', False, _('show normal and closed branches')),
1084 ('c', 'closed', False, _('show normal and closed branches')),
1090 ] + formatteropts,
1085 ] + formatteropts,
1091 _('[-c]'), cmdtype=readonly)
1086 _('[-c]'), cmdtype=readonly)
1092 def branches(ui, repo, active=False, closed=False, **opts):
1087 def branches(ui, repo, active=False, closed=False, **opts):
1093 """list repository named branches
1088 """list repository named branches
1094
1089
1095 List the repository's named branches, indicating which ones are
1090 List the repository's named branches, indicating which ones are
1096 inactive. If -c/--closed is specified, also list branches which have
1091 inactive. If -c/--closed is specified, also list branches which have
1097 been marked closed (see :hg:`commit --close-branch`).
1092 been marked closed (see :hg:`commit --close-branch`).
1098
1093
1099 Use the command :hg:`update` to switch to an existing branch.
1094 Use the command :hg:`update` to switch to an existing branch.
1100
1095
1101 Returns 0.
1096 Returns 0.
1102 """
1097 """
1103
1098
1104 opts = pycompat.byteskwargs(opts)
1099 opts = pycompat.byteskwargs(opts)
1105 ui.pager('branches')
1100 ui.pager('branches')
1106 fm = ui.formatter('branches', opts)
1101 fm = ui.formatter('branches', opts)
1107 hexfunc = fm.hexfunc
1102 hexfunc = fm.hexfunc
1108
1103
1109 allheads = set(repo.heads())
1104 allheads = set(repo.heads())
1110 branches = []
1105 branches = []
1111 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1106 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1112 isactive = False
1107 isactive = False
1113 if not isclosed:
1108 if not isclosed:
1114 openheads = set(repo.branchmap().iteropen(heads))
1109 openheads = set(repo.branchmap().iteropen(heads))
1115 isactive = bool(openheads & allheads)
1110 isactive = bool(openheads & allheads)
1116 branches.append((tag, repo[tip], isactive, not isclosed))
1111 branches.append((tag, repo[tip], isactive, not isclosed))
1117 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1112 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1118 reverse=True)
1113 reverse=True)
1119
1114
1120 for tag, ctx, isactive, isopen in branches:
1115 for tag, ctx, isactive, isopen in branches:
1121 if active and not isactive:
1116 if active and not isactive:
1122 continue
1117 continue
1123 if isactive:
1118 if isactive:
1124 label = 'branches.active'
1119 label = 'branches.active'
1125 notice = ''
1120 notice = ''
1126 elif not isopen:
1121 elif not isopen:
1127 if not closed:
1122 if not closed:
1128 continue
1123 continue
1129 label = 'branches.closed'
1124 label = 'branches.closed'
1130 notice = _(' (closed)')
1125 notice = _(' (closed)')
1131 else:
1126 else:
1132 label = 'branches.inactive'
1127 label = 'branches.inactive'
1133 notice = _(' (inactive)')
1128 notice = _(' (inactive)')
1134 current = (tag == repo.dirstate.branch())
1129 current = (tag == repo.dirstate.branch())
1135 if current:
1130 if current:
1136 label = 'branches.current'
1131 label = 'branches.current'
1137
1132
1138 fm.startitem()
1133 fm.startitem()
1139 fm.write('branch', '%s', tag, label=label)
1134 fm.write('branch', '%s', tag, label=label)
1140 rev = ctx.rev()
1135 rev = ctx.rev()
1141 padsize = max(31 - len("%d" % rev) - encoding.colwidth(tag), 0)
1136 padsize = max(31 - len("%d" % rev) - encoding.colwidth(tag), 0)
1142 fmt = ' ' * padsize + ' %d:%s'
1137 fmt = ' ' * padsize + ' %d:%s'
1143 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1138 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1144 label='log.changeset changeset.%s' % ctx.phasestr())
1139 label='log.changeset changeset.%s' % ctx.phasestr())
1145 fm.context(ctx=ctx)
1140 fm.context(ctx=ctx)
1146 fm.data(active=isactive, closed=not isopen, current=current)
1141 fm.data(active=isactive, closed=not isopen, current=current)
1147 if not ui.quiet:
1142 if not ui.quiet:
1148 fm.plain(notice)
1143 fm.plain(notice)
1149 fm.plain('\n')
1144 fm.plain('\n')
1150 fm.end()
1145 fm.end()
1151
1146
1152 @command('bundle',
1147 @command('bundle',
1153 [('f', 'force', None, _('run even when the destination is unrelated')),
1148 [('f', 'force', None, _('run even when the destination is unrelated')),
1154 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1149 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1155 _('REV')),
1150 _('REV')),
1156 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1151 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1157 _('BRANCH')),
1152 _('BRANCH')),
1158 ('', 'base', [],
1153 ('', 'base', [],
1159 _('a base changeset assumed to be available at the destination'),
1154 _('a base changeset assumed to be available at the destination'),
1160 _('REV')),
1155 _('REV')),
1161 ('a', 'all', None, _('bundle all changesets in the repository')),
1156 ('a', 'all', None, _('bundle all changesets in the repository')),
1162 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1157 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1163 ] + remoteopts,
1158 ] + remoteopts,
1164 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1159 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1165 def bundle(ui, repo, fname, dest=None, **opts):
1160 def bundle(ui, repo, fname, dest=None, **opts):
1166 """create a bundle file
1161 """create a bundle file
1167
1162
1168 Generate a bundle file containing data to be transferred to another
1163 Generate a bundle file containing data to be transferred to another
1169 repository.
1164 repository.
1170
1165
1171 To create a bundle containing all changesets, use -a/--all
1166 To create a bundle containing all changesets, use -a/--all
1172 (or --base null). Otherwise, hg assumes the destination will have
1167 (or --base null). Otherwise, hg assumes the destination will have
1173 all the nodes you specify with --base parameters. Otherwise, hg
1168 all the nodes you specify with --base parameters. Otherwise, hg
1174 will assume the repository has all the nodes in destination, or
1169 will assume the repository has all the nodes in destination, or
1175 default-push/default if no destination is specified, where destination
1170 default-push/default if no destination is specified, where destination
1176 is the repository you provide through DEST option.
1171 is the repository you provide through DEST option.
1177
1172
1178 You can change bundle format with the -t/--type option. See
1173 You can change bundle format with the -t/--type option. See
1179 :hg:`help bundlespec` for documentation on this format. By default,
1174 :hg:`help bundlespec` for documentation on this format. By default,
1180 the most appropriate format is used and compression defaults to
1175 the most appropriate format is used and compression defaults to
1181 bzip2.
1176 bzip2.
1182
1177
1183 The bundle file can then be transferred using conventional means
1178 The bundle file can then be transferred using conventional means
1184 and applied to another repository with the unbundle or pull
1179 and applied to another repository with the unbundle or pull
1185 command. This is useful when direct push and pull are not
1180 command. This is useful when direct push and pull are not
1186 available or when exporting an entire repository is undesirable.
1181 available or when exporting an entire repository is undesirable.
1187
1182
1188 Applying bundles preserves all changeset contents including
1183 Applying bundles preserves all changeset contents including
1189 permissions, copy/rename information, and revision history.
1184 permissions, copy/rename information, and revision history.
1190
1185
1191 Returns 0 on success, 1 if no changes found.
1186 Returns 0 on success, 1 if no changes found.
1192 """
1187 """
1193 opts = pycompat.byteskwargs(opts)
1188 opts = pycompat.byteskwargs(opts)
1194 revs = None
1189 revs = None
1195 if 'rev' in opts:
1190 if 'rev' in opts:
1196 revstrings = opts['rev']
1191 revstrings = opts['rev']
1197 revs = scmutil.revrange(repo, revstrings)
1192 revs = scmutil.revrange(repo, revstrings)
1198 if revstrings and not revs:
1193 if revstrings and not revs:
1199 raise error.Abort(_('no commits to bundle'))
1194 raise error.Abort(_('no commits to bundle'))
1200
1195
1201 bundletype = opts.get('type', 'bzip2').lower()
1196 bundletype = opts.get('type', 'bzip2').lower()
1202 try:
1197 try:
1203 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1198 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1204 except error.UnsupportedBundleSpecification as e:
1199 except error.UnsupportedBundleSpecification as e:
1205 raise error.Abort(pycompat.bytestr(e),
1200 raise error.Abort(pycompat.bytestr(e),
1206 hint=_("see 'hg help bundlespec' for supported "
1201 hint=_("see 'hg help bundlespec' for supported "
1207 "values for --type"))
1202 "values for --type"))
1208 cgversion = bundlespec.contentopts["cg.version"]
1203 cgversion = bundlespec.contentopts["cg.version"]
1209
1204
1210 # Packed bundles are a pseudo bundle format for now.
1205 # Packed bundles are a pseudo bundle format for now.
1211 if cgversion == 's1':
1206 if cgversion == 's1':
1212 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1207 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1213 hint=_("use 'hg debugcreatestreamclonebundle'"))
1208 hint=_("use 'hg debugcreatestreamclonebundle'"))
1214
1209
1215 if opts.get('all'):
1210 if opts.get('all'):
1216 if dest:
1211 if dest:
1217 raise error.Abort(_("--all is incompatible with specifying "
1212 raise error.Abort(_("--all is incompatible with specifying "
1218 "a destination"))
1213 "a destination"))
1219 if opts.get('base'):
1214 if opts.get('base'):
1220 ui.warn(_("ignoring --base because --all was specified\n"))
1215 ui.warn(_("ignoring --base because --all was specified\n"))
1221 base = ['null']
1216 base = ['null']
1222 else:
1217 else:
1223 base = scmutil.revrange(repo, opts.get('base'))
1218 base = scmutil.revrange(repo, opts.get('base'))
1224 if cgversion not in changegroup.supportedoutgoingversions(repo):
1219 if cgversion not in changegroup.supportedoutgoingversions(repo):
1225 raise error.Abort(_("repository does not support bundle version %s") %
1220 raise error.Abort(_("repository does not support bundle version %s") %
1226 cgversion)
1221 cgversion)
1227
1222
1228 if base:
1223 if base:
1229 if dest:
1224 if dest:
1230 raise error.Abort(_("--base is incompatible with specifying "
1225 raise error.Abort(_("--base is incompatible with specifying "
1231 "a destination"))
1226 "a destination"))
1232 common = [repo.lookup(rev) for rev in base]
1227 common = [repo.lookup(rev) for rev in base]
1233 heads = [repo.lookup(r) for r in revs] if revs else None
1228 heads = [repo.lookup(r) for r in revs] if revs else None
1234 outgoing = discovery.outgoing(repo, common, heads)
1229 outgoing = discovery.outgoing(repo, common, heads)
1235 else:
1230 else:
1236 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1231 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1237 dest, branches = hg.parseurl(dest, opts.get('branch'))
1232 dest, branches = hg.parseurl(dest, opts.get('branch'))
1238 other = hg.peer(repo, opts, dest)
1233 other = hg.peer(repo, opts, dest)
1239 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1234 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1240 heads = revs and map(repo.lookup, revs) or revs
1235 heads = revs and map(repo.lookup, revs) or revs
1241 outgoing = discovery.findcommonoutgoing(repo, other,
1236 outgoing = discovery.findcommonoutgoing(repo, other,
1242 onlyheads=heads,
1237 onlyheads=heads,
1243 force=opts.get('force'),
1238 force=opts.get('force'),
1244 portable=True)
1239 portable=True)
1245
1240
1246 if not outgoing.missing:
1241 if not outgoing.missing:
1247 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1242 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1248 return 1
1243 return 1
1249
1244
1250 bcompression = bundlespec.compression
1245 bcompression = bundlespec.compression
1251 if cgversion == '01': #bundle1
1246 if cgversion == '01': #bundle1
1252 if bcompression is None:
1247 if bcompression is None:
1253 bcompression = 'UN'
1248 bcompression = 'UN'
1254 bversion = 'HG10' + bcompression
1249 bversion = 'HG10' + bcompression
1255 bcompression = None
1250 bcompression = None
1256 elif cgversion in ('02', '03'):
1251 elif cgversion in ('02', '03'):
1257 bversion = 'HG20'
1252 bversion = 'HG20'
1258 else:
1253 else:
1259 raise error.ProgrammingError(
1254 raise error.ProgrammingError(
1260 'bundle: unexpected changegroup version %s' % cgversion)
1255 'bundle: unexpected changegroup version %s' % cgversion)
1261
1256
1262 # TODO compression options should be derived from bundlespec parsing.
1257 # TODO compression options should be derived from bundlespec parsing.
1263 # This is a temporary hack to allow adjusting bundle compression
1258 # This is a temporary hack to allow adjusting bundle compression
1264 # level without a) formalizing the bundlespec changes to declare it
1259 # level without a) formalizing the bundlespec changes to declare it
1265 # b) introducing a command flag.
1260 # b) introducing a command flag.
1266 compopts = {}
1261 compopts = {}
1267 complevel = ui.configint('experimental', 'bundlecomplevel')
1262 complevel = ui.configint('experimental', 'bundlecomplevel')
1268 if complevel is not None:
1263 if complevel is not None:
1269 compopts['level'] = complevel
1264 compopts['level'] = complevel
1270
1265
1271 # Allow overriding the bundling of obsmarker in phases through
1266 # Allow overriding the bundling of obsmarker in phases through
1272 # configuration while we don't have a bundle version that include them
1267 # configuration while we don't have a bundle version that include them
1273 if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker'):
1268 if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker'):
1274 bundlespec.contentopts['obsolescence'] = True
1269 bundlespec.contentopts['obsolescence'] = True
1275 if repo.ui.configbool('experimental', 'bundle-phases'):
1270 if repo.ui.configbool('experimental', 'bundle-phases'):
1276 bundlespec.contentopts['phases'] = True
1271 bundlespec.contentopts['phases'] = True
1277
1272
1278 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1273 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1279 bundlespec.contentopts, compression=bcompression,
1274 bundlespec.contentopts, compression=bcompression,
1280 compopts=compopts)
1275 compopts=compopts)
1281
1276
1282 @command('cat',
1277 @command('cat',
1283 [('o', 'output', '',
1278 [('o', 'output', '',
1284 _('print output to file with formatted name'), _('FORMAT')),
1279 _('print output to file with formatted name'), _('FORMAT')),
1285 ('r', 'rev', '', _('print the given revision'), _('REV')),
1280 ('r', 'rev', '', _('print the given revision'), _('REV')),
1286 ('', 'decode', None, _('apply any matching decode filter')),
1281 ('', 'decode', None, _('apply any matching decode filter')),
1287 ] + walkopts + formatteropts,
1282 ] + walkopts + formatteropts,
1288 _('[OPTION]... FILE...'),
1283 _('[OPTION]... FILE...'),
1289 inferrepo=True, cmdtype=readonly)
1284 inferrepo=True, cmdtype=readonly)
1290 def cat(ui, repo, file1, *pats, **opts):
1285 def cat(ui, repo, file1, *pats, **opts):
1291 """output the current or given revision of files
1286 """output the current or given revision of files
1292
1287
1293 Print the specified files as they were at the given revision. If
1288 Print the specified files as they were at the given revision. If
1294 no revision is given, the parent of the working directory is used.
1289 no revision is given, the parent of the working directory is used.
1295
1290
1296 Output may be to a file, in which case the name of the file is
1291 Output may be to a file, in which case the name of the file is
1297 given using a template string. See :hg:`help templates`. In addition
1292 given using a template string. See :hg:`help templates`. In addition
1298 to the common template keywords, the following formatting rules are
1293 to the common template keywords, the following formatting rules are
1299 supported:
1294 supported:
1300
1295
1301 :``%%``: literal "%" character
1296 :``%%``: literal "%" character
1302 :``%s``: basename of file being printed
1297 :``%s``: basename of file being printed
1303 :``%d``: dirname of file being printed, or '.' if in repository root
1298 :``%d``: dirname of file being printed, or '.' if in repository root
1304 :``%p``: root-relative path name of file being printed
1299 :``%p``: root-relative path name of file being printed
1305 :``%H``: changeset hash (40 hexadecimal digits)
1300 :``%H``: changeset hash (40 hexadecimal digits)
1306 :``%R``: changeset revision number
1301 :``%R``: changeset revision number
1307 :``%h``: short-form changeset hash (12 hexadecimal digits)
1302 :``%h``: short-form changeset hash (12 hexadecimal digits)
1308 :``%r``: zero-padded changeset revision number
1303 :``%r``: zero-padded changeset revision number
1309 :``%b``: basename of the exporting repository
1304 :``%b``: basename of the exporting repository
1310 :``\\``: literal "\\" character
1305 :``\\``: literal "\\" character
1311
1306
1312 Returns 0 on success.
1307 Returns 0 on success.
1313 """
1308 """
1314 opts = pycompat.byteskwargs(opts)
1309 opts = pycompat.byteskwargs(opts)
1315 rev = opts.get('rev')
1310 rev = opts.get('rev')
1316 if rev:
1311 if rev:
1317 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
1312 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
1318 ctx = scmutil.revsingle(repo, rev)
1313 ctx = scmutil.revsingle(repo, rev)
1319 m = scmutil.match(ctx, (file1,) + pats, opts)
1314 m = scmutil.match(ctx, (file1,) + pats, opts)
1320 fntemplate = opts.pop('output', '')
1315 fntemplate = opts.pop('output', '')
1321 if cmdutil.isstdiofilename(fntemplate):
1316 if cmdutil.isstdiofilename(fntemplate):
1322 fntemplate = ''
1317 fntemplate = ''
1323
1318
1324 if fntemplate:
1319 if fntemplate:
1325 fm = formatter.nullformatter(ui, 'cat')
1320 fm = formatter.nullformatter(ui, 'cat')
1326 else:
1321 else:
1327 ui.pager('cat')
1322 ui.pager('cat')
1328 fm = ui.formatter('cat', opts)
1323 fm = ui.formatter('cat', opts)
1329 with fm:
1324 with fm:
1330 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '',
1325 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '',
1331 **pycompat.strkwargs(opts))
1326 **pycompat.strkwargs(opts))
1332
1327
1333 @command('^clone',
1328 @command('^clone',
1334 [('U', 'noupdate', None, _('the clone will include an empty working '
1329 [('U', 'noupdate', None, _('the clone will include an empty working '
1335 'directory (only a repository)')),
1330 'directory (only a repository)')),
1336 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1331 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1337 _('REV')),
1332 _('REV')),
1338 ('r', 'rev', [], _('do not clone everything, but include this changeset'
1333 ('r', 'rev', [], _('do not clone everything, but include this changeset'
1339 ' and its ancestors'), _('REV')),
1334 ' and its ancestors'), _('REV')),
1340 ('b', 'branch', [], _('do not clone everything, but include this branch\'s'
1335 ('b', 'branch', [], _('do not clone everything, but include this branch\'s'
1341 ' changesets and their ancestors'), _('BRANCH')),
1336 ' changesets and their ancestors'), _('BRANCH')),
1342 ('', 'pull', None, _('use pull protocol to copy metadata')),
1337 ('', 'pull', None, _('use pull protocol to copy metadata')),
1343 ('', 'uncompressed', None,
1338 ('', 'uncompressed', None,
1344 _('an alias to --stream (DEPRECATED)')),
1339 _('an alias to --stream (DEPRECATED)')),
1345 ('', 'stream', None,
1340 ('', 'stream', None,
1346 _('clone with minimal data processing')),
1341 _('clone with minimal data processing')),
1347 ] + remoteopts,
1342 ] + remoteopts,
1348 _('[OPTION]... SOURCE [DEST]'),
1343 _('[OPTION]... SOURCE [DEST]'),
1349 norepo=True)
1344 norepo=True)
1350 def clone(ui, source, dest=None, **opts):
1345 def clone(ui, source, dest=None, **opts):
1351 """make a copy of an existing repository
1346 """make a copy of an existing repository
1352
1347
1353 Create a copy of an existing repository in a new directory.
1348 Create a copy of an existing repository in a new directory.
1354
1349
1355 If no destination directory name is specified, it defaults to the
1350 If no destination directory name is specified, it defaults to the
1356 basename of the source.
1351 basename of the source.
1357
1352
1358 The location of the source is added to the new repository's
1353 The location of the source is added to the new repository's
1359 ``.hg/hgrc`` file, as the default to be used for future pulls.
1354 ``.hg/hgrc`` file, as the default to be used for future pulls.
1360
1355
1361 Only local paths and ``ssh://`` URLs are supported as
1356 Only local paths and ``ssh://`` URLs are supported as
1362 destinations. For ``ssh://`` destinations, no working directory or
1357 destinations. For ``ssh://`` destinations, no working directory or
1363 ``.hg/hgrc`` will be created on the remote side.
1358 ``.hg/hgrc`` will be created on the remote side.
1364
1359
1365 If the source repository has a bookmark called '@' set, that
1360 If the source repository has a bookmark called '@' set, that
1366 revision will be checked out in the new repository by default.
1361 revision will be checked out in the new repository by default.
1367
1362
1368 To check out a particular version, use -u/--update, or
1363 To check out a particular version, use -u/--update, or
1369 -U/--noupdate to create a clone with no working directory.
1364 -U/--noupdate to create a clone with no working directory.
1370
1365
1371 To pull only a subset of changesets, specify one or more revisions
1366 To pull only a subset of changesets, specify one or more revisions
1372 identifiers with -r/--rev or branches with -b/--branch. The
1367 identifiers with -r/--rev or branches with -b/--branch. The
1373 resulting clone will contain only the specified changesets and
1368 resulting clone will contain only the specified changesets and
1374 their ancestors. These options (or 'clone src#rev dest') imply
1369 their ancestors. These options (or 'clone src#rev dest') imply
1375 --pull, even for local source repositories.
1370 --pull, even for local source repositories.
1376
1371
1377 In normal clone mode, the remote normalizes repository data into a common
1372 In normal clone mode, the remote normalizes repository data into a common
1378 exchange format and the receiving end translates this data into its local
1373 exchange format and the receiving end translates this data into its local
1379 storage format. --stream activates a different clone mode that essentially
1374 storage format. --stream activates a different clone mode that essentially
1380 copies repository files from the remote with minimal data processing. This
1375 copies repository files from the remote with minimal data processing. This
1381 significantly reduces the CPU cost of a clone both remotely and locally.
1376 significantly reduces the CPU cost of a clone both remotely and locally.
1382 However, it often increases the transferred data size by 30-40%. This can
1377 However, it often increases the transferred data size by 30-40%. This can
1383 result in substantially faster clones where I/O throughput is plentiful,
1378 result in substantially faster clones where I/O throughput is plentiful,
1384 especially for larger repositories. A side-effect of --stream clones is
1379 especially for larger repositories. A side-effect of --stream clones is
1385 that storage settings and requirements on the remote are applied locally:
1380 that storage settings and requirements on the remote are applied locally:
1386 a modern client may inherit legacy or inefficient storage used by the
1381 a modern client may inherit legacy or inefficient storage used by the
1387 remote or a legacy Mercurial client may not be able to clone from a
1382 remote or a legacy Mercurial client may not be able to clone from a
1388 modern Mercurial remote.
1383 modern Mercurial remote.
1389
1384
1390 .. note::
1385 .. note::
1391
1386
1392 Specifying a tag will include the tagged changeset but not the
1387 Specifying a tag will include the tagged changeset but not the
1393 changeset containing the tag.
1388 changeset containing the tag.
1394
1389
1395 .. container:: verbose
1390 .. container:: verbose
1396
1391
1397 For efficiency, hardlinks are used for cloning whenever the
1392 For efficiency, hardlinks are used for cloning whenever the
1398 source and destination are on the same filesystem (note this
1393 source and destination are on the same filesystem (note this
1399 applies only to the repository data, not to the working
1394 applies only to the repository data, not to the working
1400 directory). Some filesystems, such as AFS, implement hardlinking
1395 directory). Some filesystems, such as AFS, implement hardlinking
1401 incorrectly, but do not report errors. In these cases, use the
1396 incorrectly, but do not report errors. In these cases, use the
1402 --pull option to avoid hardlinking.
1397 --pull option to avoid hardlinking.
1403
1398
1404 Mercurial will update the working directory to the first applicable
1399 Mercurial will update the working directory to the first applicable
1405 revision from this list:
1400 revision from this list:
1406
1401
1407 a) null if -U or the source repository has no changesets
1402 a) null if -U or the source repository has no changesets
1408 b) if -u . and the source repository is local, the first parent of
1403 b) if -u . and the source repository is local, the first parent of
1409 the source repository's working directory
1404 the source repository's working directory
1410 c) the changeset specified with -u (if a branch name, this means the
1405 c) the changeset specified with -u (if a branch name, this means the
1411 latest head of that branch)
1406 latest head of that branch)
1412 d) the changeset specified with -r
1407 d) the changeset specified with -r
1413 e) the tipmost head specified with -b
1408 e) the tipmost head specified with -b
1414 f) the tipmost head specified with the url#branch source syntax
1409 f) the tipmost head specified with the url#branch source syntax
1415 g) the revision marked with the '@' bookmark, if present
1410 g) the revision marked with the '@' bookmark, if present
1416 h) the tipmost head of the default branch
1411 h) the tipmost head of the default branch
1417 i) tip
1412 i) tip
1418
1413
1419 When cloning from servers that support it, Mercurial may fetch
1414 When cloning from servers that support it, Mercurial may fetch
1420 pre-generated data from a server-advertised URL. When this is done,
1415 pre-generated data from a server-advertised URL. When this is done,
1421 hooks operating on incoming changesets and changegroups may fire twice,
1416 hooks operating on incoming changesets and changegroups may fire twice,
1422 once for the bundle fetched from the URL and another for any additional
1417 once for the bundle fetched from the URL and another for any additional
1423 data not fetched from this URL. In addition, if an error occurs, the
1418 data not fetched from this URL. In addition, if an error occurs, the
1424 repository may be rolled back to a partial clone. This behavior may
1419 repository may be rolled back to a partial clone. This behavior may
1425 change in future releases. See :hg:`help -e clonebundles` for more.
1420 change in future releases. See :hg:`help -e clonebundles` for more.
1426
1421
1427 Examples:
1422 Examples:
1428
1423
1429 - clone a remote repository to a new directory named hg/::
1424 - clone a remote repository to a new directory named hg/::
1430
1425
1431 hg clone https://www.mercurial-scm.org/repo/hg/
1426 hg clone https://www.mercurial-scm.org/repo/hg/
1432
1427
1433 - create a lightweight local clone::
1428 - create a lightweight local clone::
1434
1429
1435 hg clone project/ project-feature/
1430 hg clone project/ project-feature/
1436
1431
1437 - clone from an absolute path on an ssh server (note double-slash)::
1432 - clone from an absolute path on an ssh server (note double-slash)::
1438
1433
1439 hg clone ssh://user@server//home/projects/alpha/
1434 hg clone ssh://user@server//home/projects/alpha/
1440
1435
1441 - do a streaming clone while checking out a specified version::
1436 - do a streaming clone while checking out a specified version::
1442
1437
1443 hg clone --stream http://server/repo -u 1.5
1438 hg clone --stream http://server/repo -u 1.5
1444
1439
1445 - create a repository without changesets after a particular revision::
1440 - create a repository without changesets after a particular revision::
1446
1441
1447 hg clone -r 04e544 experimental/ good/
1442 hg clone -r 04e544 experimental/ good/
1448
1443
1449 - clone (and track) a particular named branch::
1444 - clone (and track) a particular named branch::
1450
1445
1451 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1446 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1452
1447
1453 See :hg:`help urls` for details on specifying URLs.
1448 See :hg:`help urls` for details on specifying URLs.
1454
1449
1455 Returns 0 on success.
1450 Returns 0 on success.
1456 """
1451 """
1457 opts = pycompat.byteskwargs(opts)
1452 opts = pycompat.byteskwargs(opts)
1458 if opts.get('noupdate') and opts.get('updaterev'):
1453 if opts.get('noupdate') and opts.get('updaterev'):
1459 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1454 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1460
1455
1461 r = hg.clone(ui, opts, source, dest,
1456 r = hg.clone(ui, opts, source, dest,
1462 pull=opts.get('pull'),
1457 pull=opts.get('pull'),
1463 stream=opts.get('stream') or opts.get('uncompressed'),
1458 stream=opts.get('stream') or opts.get('uncompressed'),
1464 revs=opts.get('rev'),
1459 revs=opts.get('rev'),
1465 update=opts.get('updaterev') or not opts.get('noupdate'),
1460 update=opts.get('updaterev') or not opts.get('noupdate'),
1466 branch=opts.get('branch'),
1461 branch=opts.get('branch'),
1467 shareopts=opts.get('shareopts'))
1462 shareopts=opts.get('shareopts'))
1468
1463
1469 return r is None
1464 return r is None
1470
1465
1471 @command('^commit|ci',
1466 @command('^commit|ci',
1472 [('A', 'addremove', None,
1467 [('A', 'addremove', None,
1473 _('mark new/missing files as added/removed before committing')),
1468 _('mark new/missing files as added/removed before committing')),
1474 ('', 'close-branch', None,
1469 ('', 'close-branch', None,
1475 _('mark a branch head as closed')),
1470 _('mark a branch head as closed')),
1476 ('', 'amend', None, _('amend the parent of the working directory')),
1471 ('', 'amend', None, _('amend the parent of the working directory')),
1477 ('s', 'secret', None, _('use the secret phase for committing')),
1472 ('s', 'secret', None, _('use the secret phase for committing')),
1478 ('e', 'edit', None, _('invoke editor on commit messages')),
1473 ('e', 'edit', None, _('invoke editor on commit messages')),
1479 ('i', 'interactive', None, _('use interactive mode')),
1474 ('i', 'interactive', None, _('use interactive mode')),
1480 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1475 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1481 _('[OPTION]... [FILE]...'),
1476 _('[OPTION]... [FILE]...'),
1482 inferrepo=True)
1477 inferrepo=True)
1483 def commit(ui, repo, *pats, **opts):
1478 def commit(ui, repo, *pats, **opts):
1484 """commit the specified files or all outstanding changes
1479 """commit the specified files or all outstanding changes
1485
1480
1486 Commit changes to the given files into the repository. Unlike a
1481 Commit changes to the given files into the repository. Unlike a
1487 centralized SCM, this operation is a local operation. See
1482 centralized SCM, this operation is a local operation. See
1488 :hg:`push` for a way to actively distribute your changes.
1483 :hg:`push` for a way to actively distribute your changes.
1489
1484
1490 If a list of files is omitted, all changes reported by :hg:`status`
1485 If a list of files is omitted, all changes reported by :hg:`status`
1491 will be committed.
1486 will be committed.
1492
1487
1493 If you are committing the result of a merge, do not provide any
1488 If you are committing the result of a merge, do not provide any
1494 filenames or -I/-X filters.
1489 filenames or -I/-X filters.
1495
1490
1496 If no commit message is specified, Mercurial starts your
1491 If no commit message is specified, Mercurial starts your
1497 configured editor where you can enter a message. In case your
1492 configured editor where you can enter a message. In case your
1498 commit fails, you will find a backup of your message in
1493 commit fails, you will find a backup of your message in
1499 ``.hg/last-message.txt``.
1494 ``.hg/last-message.txt``.
1500
1495
1501 The --close-branch flag can be used to mark the current branch
1496 The --close-branch flag can be used to mark the current branch
1502 head closed. When all heads of a branch are closed, the branch
1497 head closed. When all heads of a branch are closed, the branch
1503 will be considered closed and no longer listed.
1498 will be considered closed and no longer listed.
1504
1499
1505 The --amend flag can be used to amend the parent of the
1500 The --amend flag can be used to amend the parent of the
1506 working directory with a new commit that contains the changes
1501 working directory with a new commit that contains the changes
1507 in the parent in addition to those currently reported by :hg:`status`,
1502 in the parent in addition to those currently reported by :hg:`status`,
1508 if there are any. The old commit is stored in a backup bundle in
1503 if there are any. The old commit is stored in a backup bundle in
1509 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1504 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1510 on how to restore it).
1505 on how to restore it).
1511
1506
1512 Message, user and date are taken from the amended commit unless
1507 Message, user and date are taken from the amended commit unless
1513 specified. When a message isn't specified on the command line,
1508 specified. When a message isn't specified on the command line,
1514 the editor will open with the message of the amended commit.
1509 the editor will open with the message of the amended commit.
1515
1510
1516 It is not possible to amend public changesets (see :hg:`help phases`)
1511 It is not possible to amend public changesets (see :hg:`help phases`)
1517 or changesets that have children.
1512 or changesets that have children.
1518
1513
1519 See :hg:`help dates` for a list of formats valid for -d/--date.
1514 See :hg:`help dates` for a list of formats valid for -d/--date.
1520
1515
1521 Returns 0 on success, 1 if nothing changed.
1516 Returns 0 on success, 1 if nothing changed.
1522
1517
1523 .. container:: verbose
1518 .. container:: verbose
1524
1519
1525 Examples:
1520 Examples:
1526
1521
1527 - commit all files ending in .py::
1522 - commit all files ending in .py::
1528
1523
1529 hg commit --include "set:**.py"
1524 hg commit --include "set:**.py"
1530
1525
1531 - commit all non-binary files::
1526 - commit all non-binary files::
1532
1527
1533 hg commit --exclude "set:binary()"
1528 hg commit --exclude "set:binary()"
1534
1529
1535 - amend the current commit and set the date to now::
1530 - amend the current commit and set the date to now::
1536
1531
1537 hg commit --amend --date now
1532 hg commit --amend --date now
1538 """
1533 """
1539 wlock = lock = None
1534 wlock = lock = None
1540 try:
1535 try:
1541 wlock = repo.wlock()
1536 wlock = repo.wlock()
1542 lock = repo.lock()
1537 lock = repo.lock()
1543 return _docommit(ui, repo, *pats, **opts)
1538 return _docommit(ui, repo, *pats, **opts)
1544 finally:
1539 finally:
1545 release(lock, wlock)
1540 release(lock, wlock)
1546
1541
1547 def _docommit(ui, repo, *pats, **opts):
1542 def _docommit(ui, repo, *pats, **opts):
1548 if opts.get(r'interactive'):
1543 if opts.get(r'interactive'):
1549 opts.pop(r'interactive')
1544 opts.pop(r'interactive')
1550 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1545 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1551 cmdutil.recordfilter, *pats,
1546 cmdutil.recordfilter, *pats,
1552 **opts)
1547 **opts)
1553 # ret can be 0 (no changes to record) or the value returned by
1548 # ret can be 0 (no changes to record) or the value returned by
1554 # commit(), 1 if nothing changed or None on success.
1549 # commit(), 1 if nothing changed or None on success.
1555 return 1 if ret == 0 else ret
1550 return 1 if ret == 0 else ret
1556
1551
1557 opts = pycompat.byteskwargs(opts)
1552 opts = pycompat.byteskwargs(opts)
1558 if opts.get('subrepos'):
1553 if opts.get('subrepos'):
1559 if opts.get('amend'):
1554 if opts.get('amend'):
1560 raise error.Abort(_('cannot amend with --subrepos'))
1555 raise error.Abort(_('cannot amend with --subrepos'))
1561 # Let --subrepos on the command line override config setting.
1556 # Let --subrepos on the command line override config setting.
1562 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1557 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1563
1558
1564 cmdutil.checkunfinished(repo, commit=True)
1559 cmdutil.checkunfinished(repo, commit=True)
1565
1560
1566 branch = repo[None].branch()
1561 branch = repo[None].branch()
1567 bheads = repo.branchheads(branch)
1562 bheads = repo.branchheads(branch)
1568
1563
1569 extra = {}
1564 extra = {}
1570 if opts.get('close_branch'):
1565 if opts.get('close_branch'):
1571 extra['close'] = '1'
1566 extra['close'] = '1'
1572
1567
1573 if not bheads:
1568 if not bheads:
1574 raise error.Abort(_('can only close branch heads'))
1569 raise error.Abort(_('can only close branch heads'))
1575 elif opts.get('amend'):
1570 elif opts.get('amend'):
1576 if repo[None].parents()[0].p1().branch() != branch and \
1571 if repo[None].parents()[0].p1().branch() != branch and \
1577 repo[None].parents()[0].p2().branch() != branch:
1572 repo[None].parents()[0].p2().branch() != branch:
1578 raise error.Abort(_('can only close branch heads'))
1573 raise error.Abort(_('can only close branch heads'))
1579
1574
1580 if opts.get('amend'):
1575 if opts.get('amend'):
1581 if ui.configbool('ui', 'commitsubrepos'):
1576 if ui.configbool('ui', 'commitsubrepos'):
1582 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1577 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1583
1578
1584 old = repo['.']
1579 old = repo['.']
1585 rewriteutil.precheck(repo, [old.rev()], 'amend')
1580 rewriteutil.precheck(repo, [old.rev()], 'amend')
1586
1581
1587 # Currently histedit gets confused if an amend happens while histedit
1582 # Currently histedit gets confused if an amend happens while histedit
1588 # is in progress. Since we have a checkunfinished command, we are
1583 # is in progress. Since we have a checkunfinished command, we are
1589 # temporarily honoring it.
1584 # temporarily honoring it.
1590 #
1585 #
1591 # Note: eventually this guard will be removed. Please do not expect
1586 # Note: eventually this guard will be removed. Please do not expect
1592 # this behavior to remain.
1587 # this behavior to remain.
1593 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1588 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1594 cmdutil.checkunfinished(repo)
1589 cmdutil.checkunfinished(repo)
1595
1590
1596 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
1591 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
1597 if node == old.node():
1592 if node == old.node():
1598 ui.status(_("nothing changed\n"))
1593 ui.status(_("nothing changed\n"))
1599 return 1
1594 return 1
1600 else:
1595 else:
1601 def commitfunc(ui, repo, message, match, opts):
1596 def commitfunc(ui, repo, message, match, opts):
1602 overrides = {}
1597 overrides = {}
1603 if opts.get('secret'):
1598 if opts.get('secret'):
1604 overrides[('phases', 'new-commit')] = 'secret'
1599 overrides[('phases', 'new-commit')] = 'secret'
1605
1600
1606 baseui = repo.baseui
1601 baseui = repo.baseui
1607 with baseui.configoverride(overrides, 'commit'):
1602 with baseui.configoverride(overrides, 'commit'):
1608 with ui.configoverride(overrides, 'commit'):
1603 with ui.configoverride(overrides, 'commit'):
1609 editform = cmdutil.mergeeditform(repo[None],
1604 editform = cmdutil.mergeeditform(repo[None],
1610 'commit.normal')
1605 'commit.normal')
1611 editor = cmdutil.getcommiteditor(
1606 editor = cmdutil.getcommiteditor(
1612 editform=editform, **pycompat.strkwargs(opts))
1607 editform=editform, **pycompat.strkwargs(opts))
1613 return repo.commit(message,
1608 return repo.commit(message,
1614 opts.get('user'),
1609 opts.get('user'),
1615 opts.get('date'),
1610 opts.get('date'),
1616 match,
1611 match,
1617 editor=editor,
1612 editor=editor,
1618 extra=extra)
1613 extra=extra)
1619
1614
1620 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1615 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1621
1616
1622 if not node:
1617 if not node:
1623 stat = cmdutil.postcommitstatus(repo, pats, opts)
1618 stat = cmdutil.postcommitstatus(repo, pats, opts)
1624 if stat[3]:
1619 if stat[3]:
1625 ui.status(_("nothing changed (%d missing files, see "
1620 ui.status(_("nothing changed (%d missing files, see "
1626 "'hg status')\n") % len(stat[3]))
1621 "'hg status')\n") % len(stat[3]))
1627 else:
1622 else:
1628 ui.status(_("nothing changed\n"))
1623 ui.status(_("nothing changed\n"))
1629 return 1
1624 return 1
1630
1625
1631 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1626 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1632
1627
1633 @command('config|showconfig|debugconfig',
1628 @command('config|showconfig|debugconfig',
1634 [('u', 'untrusted', None, _('show untrusted configuration options')),
1629 [('u', 'untrusted', None, _('show untrusted configuration options')),
1635 ('e', 'edit', None, _('edit user config')),
1630 ('e', 'edit', None, _('edit user config')),
1636 ('l', 'local', None, _('edit repository config')),
1631 ('l', 'local', None, _('edit repository config')),
1637 ('g', 'global', None, _('edit global config'))] + formatteropts,
1632 ('g', 'global', None, _('edit global config'))] + formatteropts,
1638 _('[-u] [NAME]...'),
1633 _('[-u] [NAME]...'),
1639 optionalrepo=True, cmdtype=readonly)
1634 optionalrepo=True, cmdtype=readonly)
1640 def config(ui, repo, *values, **opts):
1635 def config(ui, repo, *values, **opts):
1641 """show combined config settings from all hgrc files
1636 """show combined config settings from all hgrc files
1642
1637
1643 With no arguments, print names and values of all config items.
1638 With no arguments, print names and values of all config items.
1644
1639
1645 With one argument of the form section.name, print just the value
1640 With one argument of the form section.name, print just the value
1646 of that config item.
1641 of that config item.
1647
1642
1648 With multiple arguments, print names and values of all config
1643 With multiple arguments, print names and values of all config
1649 items with matching section names or section.names.
1644 items with matching section names or section.names.
1650
1645
1651 With --edit, start an editor on the user-level config file. With
1646 With --edit, start an editor on the user-level config file. With
1652 --global, edit the system-wide config file. With --local, edit the
1647 --global, edit the system-wide config file. With --local, edit the
1653 repository-level config file.
1648 repository-level config file.
1654
1649
1655 With --debug, the source (filename and line number) is printed
1650 With --debug, the source (filename and line number) is printed
1656 for each config item.
1651 for each config item.
1657
1652
1658 See :hg:`help config` for more information about config files.
1653 See :hg:`help config` for more information about config files.
1659
1654
1660 Returns 0 on success, 1 if NAME does not exist.
1655 Returns 0 on success, 1 if NAME does not exist.
1661
1656
1662 """
1657 """
1663
1658
1664 opts = pycompat.byteskwargs(opts)
1659 opts = pycompat.byteskwargs(opts)
1665 if opts.get('edit') or opts.get('local') or opts.get('global'):
1660 if opts.get('edit') or opts.get('local') or opts.get('global'):
1666 if opts.get('local') and opts.get('global'):
1661 if opts.get('local') and opts.get('global'):
1667 raise error.Abort(_("can't use --local and --global together"))
1662 raise error.Abort(_("can't use --local and --global together"))
1668
1663
1669 if opts.get('local'):
1664 if opts.get('local'):
1670 if not repo:
1665 if not repo:
1671 raise error.Abort(_("can't use --local outside a repository"))
1666 raise error.Abort(_("can't use --local outside a repository"))
1672 paths = [repo.vfs.join('hgrc')]
1667 paths = [repo.vfs.join('hgrc')]
1673 elif opts.get('global'):
1668 elif opts.get('global'):
1674 paths = rcutil.systemrcpath()
1669 paths = rcutil.systemrcpath()
1675 else:
1670 else:
1676 paths = rcutil.userrcpath()
1671 paths = rcutil.userrcpath()
1677
1672
1678 for f in paths:
1673 for f in paths:
1679 if os.path.exists(f):
1674 if os.path.exists(f):
1680 break
1675 break
1681 else:
1676 else:
1682 if opts.get('global'):
1677 if opts.get('global'):
1683 samplehgrc = uimod.samplehgrcs['global']
1678 samplehgrc = uimod.samplehgrcs['global']
1684 elif opts.get('local'):
1679 elif opts.get('local'):
1685 samplehgrc = uimod.samplehgrcs['local']
1680 samplehgrc = uimod.samplehgrcs['local']
1686 else:
1681 else:
1687 samplehgrc = uimod.samplehgrcs['user']
1682 samplehgrc = uimod.samplehgrcs['user']
1688
1683
1689 f = paths[0]
1684 f = paths[0]
1690 fp = open(f, "wb")
1685 fp = open(f, "wb")
1691 fp.write(util.tonativeeol(samplehgrc))
1686 fp.write(util.tonativeeol(samplehgrc))
1692 fp.close()
1687 fp.close()
1693
1688
1694 editor = ui.geteditor()
1689 editor = ui.geteditor()
1695 ui.system("%s \"%s\"" % (editor, f),
1690 ui.system("%s \"%s\"" % (editor, f),
1696 onerr=error.Abort, errprefix=_("edit failed"),
1691 onerr=error.Abort, errprefix=_("edit failed"),
1697 blockedtag='config_edit')
1692 blockedtag='config_edit')
1698 return
1693 return
1699 ui.pager('config')
1694 ui.pager('config')
1700 fm = ui.formatter('config', opts)
1695 fm = ui.formatter('config', opts)
1701 for t, f in rcutil.rccomponents():
1696 for t, f in rcutil.rccomponents():
1702 if t == 'path':
1697 if t == 'path':
1703 ui.debug('read config from: %s\n' % f)
1698 ui.debug('read config from: %s\n' % f)
1704 elif t == 'items':
1699 elif t == 'items':
1705 for section, name, value, source in f:
1700 for section, name, value, source in f:
1706 ui.debug('set config by: %s\n' % source)
1701 ui.debug('set config by: %s\n' % source)
1707 else:
1702 else:
1708 raise error.ProgrammingError('unknown rctype: %s' % t)
1703 raise error.ProgrammingError('unknown rctype: %s' % t)
1709 untrusted = bool(opts.get('untrusted'))
1704 untrusted = bool(opts.get('untrusted'))
1710
1705
1711 selsections = selentries = []
1706 selsections = selentries = []
1712 if values:
1707 if values:
1713 selsections = [v for v in values if '.' not in v]
1708 selsections = [v for v in values if '.' not in v]
1714 selentries = [v for v in values if '.' in v]
1709 selentries = [v for v in values if '.' in v]
1715 uniquesel = (len(selentries) == 1 and not selsections)
1710 uniquesel = (len(selentries) == 1 and not selsections)
1716 selsections = set(selsections)
1711 selsections = set(selsections)
1717 selentries = set(selentries)
1712 selentries = set(selentries)
1718
1713
1719 matched = False
1714 matched = False
1720 for section, name, value in ui.walkconfig(untrusted=untrusted):
1715 for section, name, value in ui.walkconfig(untrusted=untrusted):
1721 source = ui.configsource(section, name, untrusted)
1716 source = ui.configsource(section, name, untrusted)
1722 value = pycompat.bytestr(value)
1717 value = pycompat.bytestr(value)
1723 if fm.isplain():
1718 if fm.isplain():
1724 source = source or 'none'
1719 source = source or 'none'
1725 value = value.replace('\n', '\\n')
1720 value = value.replace('\n', '\\n')
1726 entryname = section + '.' + name
1721 entryname = section + '.' + name
1727 if values and not (section in selsections or entryname in selentries):
1722 if values and not (section in selsections or entryname in selentries):
1728 continue
1723 continue
1729 fm.startitem()
1724 fm.startitem()
1730 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1725 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1731 if uniquesel:
1726 if uniquesel:
1732 fm.data(name=entryname)
1727 fm.data(name=entryname)
1733 fm.write('value', '%s\n', value)
1728 fm.write('value', '%s\n', value)
1734 else:
1729 else:
1735 fm.write('name value', '%s=%s\n', entryname, value)
1730 fm.write('name value', '%s=%s\n', entryname, value)
1736 matched = True
1731 matched = True
1737 fm.end()
1732 fm.end()
1738 if matched:
1733 if matched:
1739 return 0
1734 return 0
1740 return 1
1735 return 1
1741
1736
1742 @command('copy|cp',
1737 @command('copy|cp',
1743 [('A', 'after', None, _('record a copy that has already occurred')),
1738 [('A', 'after', None, _('record a copy that has already occurred')),
1744 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1739 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1745 ] + walkopts + dryrunopts,
1740 ] + walkopts + dryrunopts,
1746 _('[OPTION]... [SOURCE]... DEST'))
1741 _('[OPTION]... [SOURCE]... DEST'))
1747 def copy(ui, repo, *pats, **opts):
1742 def copy(ui, repo, *pats, **opts):
1748 """mark files as copied for the next commit
1743 """mark files as copied for the next commit
1749
1744
1750 Mark dest as having copies of source files. If dest is a
1745 Mark dest as having copies of source files. If dest is a
1751 directory, copies are put in that directory. If dest is a file,
1746 directory, copies are put in that directory. If dest is a file,
1752 the source must be a single file.
1747 the source must be a single file.
1753
1748
1754 By default, this command copies the contents of files as they
1749 By default, this command copies the contents of files as they
1755 exist in the working directory. If invoked with -A/--after, the
1750 exist in the working directory. If invoked with -A/--after, the
1756 operation is recorded, but no copying is performed.
1751 operation is recorded, but no copying is performed.
1757
1752
1758 This command takes effect with the next commit. To undo a copy
1753 This command takes effect with the next commit. To undo a copy
1759 before that, see :hg:`revert`.
1754 before that, see :hg:`revert`.
1760
1755
1761 Returns 0 on success, 1 if errors are encountered.
1756 Returns 0 on success, 1 if errors are encountered.
1762 """
1757 """
1763 opts = pycompat.byteskwargs(opts)
1758 opts = pycompat.byteskwargs(opts)
1764 with repo.wlock(False):
1759 with repo.wlock(False):
1765 return cmdutil.copy(ui, repo, pats, opts)
1760 return cmdutil.copy(ui, repo, pats, opts)
1766
1761
1767 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1762 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1768 def debugcommands(ui, cmd='', *args):
1763 def debugcommands(ui, cmd='', *args):
1769 """list all available commands and options"""
1764 """list all available commands and options"""
1770 for cmd, vals in sorted(table.iteritems()):
1765 for cmd, vals in sorted(table.iteritems()):
1771 cmd = cmd.split('|')[0].strip('^')
1766 cmd = cmd.split('|')[0].strip('^')
1772 opts = ', '.join([i[1] for i in vals[1]])
1767 opts = ', '.join([i[1] for i in vals[1]])
1773 ui.write('%s: %s\n' % (cmd, opts))
1768 ui.write('%s: %s\n' % (cmd, opts))
1774
1769
1775 @command('debugcomplete',
1770 @command('debugcomplete',
1776 [('o', 'options', None, _('show the command options'))],
1771 [('o', 'options', None, _('show the command options'))],
1777 _('[-o] CMD'),
1772 _('[-o] CMD'),
1778 norepo=True)
1773 norepo=True)
1779 def debugcomplete(ui, cmd='', **opts):
1774 def debugcomplete(ui, cmd='', **opts):
1780 """returns the completion list associated with the given command"""
1775 """returns the completion list associated with the given command"""
1781
1776
1782 if opts.get(r'options'):
1777 if opts.get(r'options'):
1783 options = []
1778 options = []
1784 otables = [globalopts]
1779 otables = [globalopts]
1785 if cmd:
1780 if cmd:
1786 aliases, entry = cmdutil.findcmd(cmd, table, False)
1781 aliases, entry = cmdutil.findcmd(cmd, table, False)
1787 otables.append(entry[1])
1782 otables.append(entry[1])
1788 for t in otables:
1783 for t in otables:
1789 for o in t:
1784 for o in t:
1790 if "(DEPRECATED)" in o[3]:
1785 if "(DEPRECATED)" in o[3]:
1791 continue
1786 continue
1792 if o[0]:
1787 if o[0]:
1793 options.append('-%s' % o[0])
1788 options.append('-%s' % o[0])
1794 options.append('--%s' % o[1])
1789 options.append('--%s' % o[1])
1795 ui.write("%s\n" % "\n".join(options))
1790 ui.write("%s\n" % "\n".join(options))
1796 return
1791 return
1797
1792
1798 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1793 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1799 if ui.verbose:
1794 if ui.verbose:
1800 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1795 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1801 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1796 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1802
1797
1803 @command('^diff',
1798 @command('^diff',
1804 [('r', 'rev', [], _('revision'), _('REV')),
1799 [('r', 'rev', [], _('revision'), _('REV')),
1805 ('c', 'change', '', _('change made by revision'), _('REV'))
1800 ('c', 'change', '', _('change made by revision'), _('REV'))
1806 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1801 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1807 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1802 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1808 inferrepo=True, cmdtype=readonly)
1803 inferrepo=True, cmdtype=readonly)
1809 def diff(ui, repo, *pats, **opts):
1804 def diff(ui, repo, *pats, **opts):
1810 """diff repository (or selected files)
1805 """diff repository (or selected files)
1811
1806
1812 Show differences between revisions for the specified files.
1807 Show differences between revisions for the specified files.
1813
1808
1814 Differences between files are shown using the unified diff format.
1809 Differences between files are shown using the unified diff format.
1815
1810
1816 .. note::
1811 .. note::
1817
1812
1818 :hg:`diff` may generate unexpected results for merges, as it will
1813 :hg:`diff` may generate unexpected results for merges, as it will
1819 default to comparing against the working directory's first
1814 default to comparing against the working directory's first
1820 parent changeset if no revisions are specified.
1815 parent changeset if no revisions are specified.
1821
1816
1822 When two revision arguments are given, then changes are shown
1817 When two revision arguments are given, then changes are shown
1823 between those revisions. If only one revision is specified then
1818 between those revisions. If only one revision is specified then
1824 that revision is compared to the working directory, and, when no
1819 that revision is compared to the working directory, and, when no
1825 revisions are specified, the working directory files are compared
1820 revisions are specified, the working directory files are compared
1826 to its first parent.
1821 to its first parent.
1827
1822
1828 Alternatively you can specify -c/--change with a revision to see
1823 Alternatively you can specify -c/--change with a revision to see
1829 the changes in that changeset relative to its first parent.
1824 the changes in that changeset relative to its first parent.
1830
1825
1831 Without the -a/--text option, diff will avoid generating diffs of
1826 Without the -a/--text option, diff will avoid generating diffs of
1832 files it detects as binary. With -a, diff will generate a diff
1827 files it detects as binary. With -a, diff will generate a diff
1833 anyway, probably with undesirable results.
1828 anyway, probably with undesirable results.
1834
1829
1835 Use the -g/--git option to generate diffs in the git extended diff
1830 Use the -g/--git option to generate diffs in the git extended diff
1836 format. For more information, read :hg:`help diffs`.
1831 format. For more information, read :hg:`help diffs`.
1837
1832
1838 .. container:: verbose
1833 .. container:: verbose
1839
1834
1840 Examples:
1835 Examples:
1841
1836
1842 - compare a file in the current working directory to its parent::
1837 - compare a file in the current working directory to its parent::
1843
1838
1844 hg diff foo.c
1839 hg diff foo.c
1845
1840
1846 - compare two historical versions of a directory, with rename info::
1841 - compare two historical versions of a directory, with rename info::
1847
1842
1848 hg diff --git -r 1.0:1.2 lib/
1843 hg diff --git -r 1.0:1.2 lib/
1849
1844
1850 - get change stats relative to the last change on some date::
1845 - get change stats relative to the last change on some date::
1851
1846
1852 hg diff --stat -r "date('may 2')"
1847 hg diff --stat -r "date('may 2')"
1853
1848
1854 - diff all newly-added files that contain a keyword::
1849 - diff all newly-added files that contain a keyword::
1855
1850
1856 hg diff "set:added() and grep(GNU)"
1851 hg diff "set:added() and grep(GNU)"
1857
1852
1858 - compare a revision and its parents::
1853 - compare a revision and its parents::
1859
1854
1860 hg diff -c 9353 # compare against first parent
1855 hg diff -c 9353 # compare against first parent
1861 hg diff -r 9353^:9353 # same using revset syntax
1856 hg diff -r 9353^:9353 # same using revset syntax
1862 hg diff -r 9353^2:9353 # compare against the second parent
1857 hg diff -r 9353^2:9353 # compare against the second parent
1863
1858
1864 Returns 0 on success.
1859 Returns 0 on success.
1865 """
1860 """
1866
1861
1867 opts = pycompat.byteskwargs(opts)
1862 opts = pycompat.byteskwargs(opts)
1868 revs = opts.get('rev')
1863 revs = opts.get('rev')
1869 change = opts.get('change')
1864 change = opts.get('change')
1870 stat = opts.get('stat')
1865 stat = opts.get('stat')
1871 reverse = opts.get('reverse')
1866 reverse = opts.get('reverse')
1872
1867
1873 if revs and change:
1868 if revs and change:
1874 msg = _('cannot specify --rev and --change at the same time')
1869 msg = _('cannot specify --rev and --change at the same time')
1875 raise error.Abort(msg)
1870 raise error.Abort(msg)
1876 elif change:
1871 elif change:
1877 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
1872 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
1878 ctx2 = scmutil.revsingle(repo, change, None)
1873 ctx2 = scmutil.revsingle(repo, change, None)
1879 ctx1 = ctx2.p1()
1874 ctx1 = ctx2.p1()
1880 else:
1875 else:
1881 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
1876 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
1882 ctx1, ctx2 = scmutil.revpair(repo, revs)
1877 ctx1, ctx2 = scmutil.revpair(repo, revs)
1883 node1, node2 = ctx1.node(), ctx2.node()
1878 node1, node2 = ctx1.node(), ctx2.node()
1884
1879
1885 if reverse:
1880 if reverse:
1886 node1, node2 = node2, node1
1881 node1, node2 = node2, node1
1887
1882
1888 diffopts = patch.diffallopts(ui, opts)
1883 diffopts = patch.diffallopts(ui, opts)
1889 m = scmutil.match(ctx2, pats, opts)
1884 m = scmutil.match(ctx2, pats, opts)
1890 ui.pager('diff')
1885 ui.pager('diff')
1891 logcmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1886 logcmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1892 listsubrepos=opts.get('subrepos'),
1887 listsubrepos=opts.get('subrepos'),
1893 root=opts.get('root'))
1888 root=opts.get('root'))
1894
1889
1895 @command('^export',
1890 @command('^export',
1896 [('o', 'output', '',
1891 [('o', 'output', '',
1897 _('print output to file with formatted name'), _('FORMAT')),
1892 _('print output to file with formatted name'), _('FORMAT')),
1898 ('', 'switch-parent', None, _('diff against the second parent')),
1893 ('', 'switch-parent', None, _('diff against the second parent')),
1899 ('r', 'rev', [], _('revisions to export'), _('REV')),
1894 ('r', 'rev', [], _('revisions to export'), _('REV')),
1900 ] + diffopts,
1895 ] + diffopts,
1901 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'), cmdtype=readonly)
1896 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'), cmdtype=readonly)
1902 def export(ui, repo, *changesets, **opts):
1897 def export(ui, repo, *changesets, **opts):
1903 """dump the header and diffs for one or more changesets
1898 """dump the header and diffs for one or more changesets
1904
1899
1905 Print the changeset header and diffs for one or more revisions.
1900 Print the changeset header and diffs for one or more revisions.
1906 If no revision is given, the parent of the working directory is used.
1901 If no revision is given, the parent of the working directory is used.
1907
1902
1908 The information shown in the changeset header is: author, date,
1903 The information shown in the changeset header is: author, date,
1909 branch name (if non-default), changeset hash, parent(s) and commit
1904 branch name (if non-default), changeset hash, parent(s) and commit
1910 comment.
1905 comment.
1911
1906
1912 .. note::
1907 .. note::
1913
1908
1914 :hg:`export` may generate unexpected diff output for merge
1909 :hg:`export` may generate unexpected diff output for merge
1915 changesets, as it will compare the merge changeset against its
1910 changesets, as it will compare the merge changeset against its
1916 first parent only.
1911 first parent only.
1917
1912
1918 Output may be to a file, in which case the name of the file is
1913 Output may be to a file, in which case the name of the file is
1919 given using a template string. See :hg:`help templates`. In addition
1914 given using a template string. See :hg:`help templates`. In addition
1920 to the common template keywords, the following formatting rules are
1915 to the common template keywords, the following formatting rules are
1921 supported:
1916 supported:
1922
1917
1923 :``%%``: literal "%" character
1918 :``%%``: literal "%" character
1924 :``%H``: changeset hash (40 hexadecimal digits)
1919 :``%H``: changeset hash (40 hexadecimal digits)
1925 :``%N``: number of patches being generated
1920 :``%N``: number of patches being generated
1926 :``%R``: changeset revision number
1921 :``%R``: changeset revision number
1927 :``%b``: basename of the exporting repository
1922 :``%b``: basename of the exporting repository
1928 :``%h``: short-form changeset hash (12 hexadecimal digits)
1923 :``%h``: short-form changeset hash (12 hexadecimal digits)
1929 :``%m``: first line of the commit message (only alphanumeric characters)
1924 :``%m``: first line of the commit message (only alphanumeric characters)
1930 :``%n``: zero-padded sequence number, starting at 1
1925 :``%n``: zero-padded sequence number, starting at 1
1931 :``%r``: zero-padded changeset revision number
1926 :``%r``: zero-padded changeset revision number
1932 :``\\``: literal "\\" character
1927 :``\\``: literal "\\" character
1933
1928
1934 Without the -a/--text option, export will avoid generating diffs
1929 Without the -a/--text option, export will avoid generating diffs
1935 of files it detects as binary. With -a, export will generate a
1930 of files it detects as binary. With -a, export will generate a
1936 diff anyway, probably with undesirable results.
1931 diff anyway, probably with undesirable results.
1937
1932
1938 Use the -g/--git option to generate diffs in the git extended diff
1933 Use the -g/--git option to generate diffs in the git extended diff
1939 format. See :hg:`help diffs` for more information.
1934 format. See :hg:`help diffs` for more information.
1940
1935
1941 With the --switch-parent option, the diff will be against the
1936 With the --switch-parent option, the diff will be against the
1942 second parent. It can be useful to review a merge.
1937 second parent. It can be useful to review a merge.
1943
1938
1944 .. container:: verbose
1939 .. container:: verbose
1945
1940
1946 Examples:
1941 Examples:
1947
1942
1948 - use export and import to transplant a bugfix to the current
1943 - use export and import to transplant a bugfix to the current
1949 branch::
1944 branch::
1950
1945
1951 hg export -r 9353 | hg import -
1946 hg export -r 9353 | hg import -
1952
1947
1953 - export all the changesets between two revisions to a file with
1948 - export all the changesets between two revisions to a file with
1954 rename information::
1949 rename information::
1955
1950
1956 hg export --git -r 123:150 > changes.txt
1951 hg export --git -r 123:150 > changes.txt
1957
1952
1958 - split outgoing changes into a series of patches with
1953 - split outgoing changes into a series of patches with
1959 descriptive names::
1954 descriptive names::
1960
1955
1961 hg export -r "outgoing()" -o "%n-%m.patch"
1956 hg export -r "outgoing()" -o "%n-%m.patch"
1962
1957
1963 Returns 0 on success.
1958 Returns 0 on success.
1964 """
1959 """
1965 opts = pycompat.byteskwargs(opts)
1960 opts = pycompat.byteskwargs(opts)
1966 changesets += tuple(opts.get('rev', []))
1961 changesets += tuple(opts.get('rev', []))
1967 if not changesets:
1962 if not changesets:
1968 changesets = ['.']
1963 changesets = ['.']
1969 repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn')
1964 repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn')
1970 revs = scmutil.revrange(repo, changesets)
1965 revs = scmutil.revrange(repo, changesets)
1971 if not revs:
1966 if not revs:
1972 raise error.Abort(_("export requires at least one changeset"))
1967 raise error.Abort(_("export requires at least one changeset"))
1973 if len(revs) > 1:
1968 if len(revs) > 1:
1974 ui.note(_('exporting patches:\n'))
1969 ui.note(_('exporting patches:\n'))
1975 else:
1970 else:
1976 ui.note(_('exporting patch:\n'))
1971 ui.note(_('exporting patch:\n'))
1977 ui.pager('export')
1972 ui.pager('export')
1978 cmdutil.export(repo, revs, fntemplate=opts.get('output'),
1973 cmdutil.export(repo, revs, fntemplate=opts.get('output'),
1979 switch_parent=opts.get('switch_parent'),
1974 switch_parent=opts.get('switch_parent'),
1980 opts=patch.diffallopts(ui, opts))
1975 opts=patch.diffallopts(ui, opts))
1981
1976
1982 @command('files',
1977 @command('files',
1983 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
1978 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
1984 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
1979 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
1985 ] + walkopts + formatteropts + subrepoopts,
1980 ] + walkopts + formatteropts + subrepoopts,
1986 _('[OPTION]... [FILE]...'), cmdtype=readonly)
1981 _('[OPTION]... [FILE]...'), cmdtype=readonly)
1987 def files(ui, repo, *pats, **opts):
1982 def files(ui, repo, *pats, **opts):
1988 """list tracked files
1983 """list tracked files
1989
1984
1990 Print files under Mercurial control in the working directory or
1985 Print files under Mercurial control in the working directory or
1991 specified revision for given files (excluding removed files).
1986 specified revision for given files (excluding removed files).
1992 Files can be specified as filenames or filesets.
1987 Files can be specified as filenames or filesets.
1993
1988
1994 If no files are given to match, this command prints the names
1989 If no files are given to match, this command prints the names
1995 of all files under Mercurial control.
1990 of all files under Mercurial control.
1996
1991
1997 .. container:: verbose
1992 .. container:: verbose
1998
1993
1999 Examples:
1994 Examples:
2000
1995
2001 - list all files under the current directory::
1996 - list all files under the current directory::
2002
1997
2003 hg files .
1998 hg files .
2004
1999
2005 - shows sizes and flags for current revision::
2000 - shows sizes and flags for current revision::
2006
2001
2007 hg files -vr .
2002 hg files -vr .
2008
2003
2009 - list all files named README::
2004 - list all files named README::
2010
2005
2011 hg files -I "**/README"
2006 hg files -I "**/README"
2012
2007
2013 - list all binary files::
2008 - list all binary files::
2014
2009
2015 hg files "set:binary()"
2010 hg files "set:binary()"
2016
2011
2017 - find files containing a regular expression::
2012 - find files containing a regular expression::
2018
2013
2019 hg files "set:grep('bob')"
2014 hg files "set:grep('bob')"
2020
2015
2021 - search tracked file contents with xargs and grep::
2016 - search tracked file contents with xargs and grep::
2022
2017
2023 hg files -0 | xargs -0 grep foo
2018 hg files -0 | xargs -0 grep foo
2024
2019
2025 See :hg:`help patterns` and :hg:`help filesets` for more information
2020 See :hg:`help patterns` and :hg:`help filesets` for more information
2026 on specifying file patterns.
2021 on specifying file patterns.
2027
2022
2028 Returns 0 if a match is found, 1 otherwise.
2023 Returns 0 if a match is found, 1 otherwise.
2029
2024
2030 """
2025 """
2031
2026
2032 opts = pycompat.byteskwargs(opts)
2027 opts = pycompat.byteskwargs(opts)
2033 rev = opts.get('rev')
2028 rev = opts.get('rev')
2034 if rev:
2029 if rev:
2035 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2030 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2036 ctx = scmutil.revsingle(repo, rev, None)
2031 ctx = scmutil.revsingle(repo, rev, None)
2037
2032
2038 end = '\n'
2033 end = '\n'
2039 if opts.get('print0'):
2034 if opts.get('print0'):
2040 end = '\0'
2035 end = '\0'
2041 fmt = '%s' + end
2036 fmt = '%s' + end
2042
2037
2043 m = scmutil.match(ctx, pats, opts)
2038 m = scmutil.match(ctx, pats, opts)
2044 ui.pager('files')
2039 ui.pager('files')
2045 with ui.formatter('files', opts) as fm:
2040 with ui.formatter('files', opts) as fm:
2046 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2041 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2047
2042
2048 @command(
2043 @command(
2049 '^forget',
2044 '^forget',
2050 walkopts + dryrunopts,
2045 walkopts + dryrunopts,
2051 _('[OPTION]... FILE...'), inferrepo=True)
2046 _('[OPTION]... FILE...'), inferrepo=True)
2052 def forget(ui, repo, *pats, **opts):
2047 def forget(ui, repo, *pats, **opts):
2053 """forget the specified files on the next commit
2048 """forget the specified files on the next commit
2054
2049
2055 Mark the specified files so they will no longer be tracked
2050 Mark the specified files so they will no longer be tracked
2056 after the next commit.
2051 after the next commit.
2057
2052
2058 This only removes files from the current branch, not from the
2053 This only removes files from the current branch, not from the
2059 entire project history, and it does not delete them from the
2054 entire project history, and it does not delete them from the
2060 working directory.
2055 working directory.
2061
2056
2062 To delete the file from the working directory, see :hg:`remove`.
2057 To delete the file from the working directory, see :hg:`remove`.
2063
2058
2064 To undo a forget before the next commit, see :hg:`add`.
2059 To undo a forget before the next commit, see :hg:`add`.
2065
2060
2066 .. container:: verbose
2061 .. container:: verbose
2067
2062
2068 Examples:
2063 Examples:
2069
2064
2070 - forget newly-added binary files::
2065 - forget newly-added binary files::
2071
2066
2072 hg forget "set:added() and binary()"
2067 hg forget "set:added() and binary()"
2073
2068
2074 - forget files that would be excluded by .hgignore::
2069 - forget files that would be excluded by .hgignore::
2075
2070
2076 hg forget "set:hgignore()"
2071 hg forget "set:hgignore()"
2077
2072
2078 Returns 0 on success.
2073 Returns 0 on success.
2079 """
2074 """
2080
2075
2081 opts = pycompat.byteskwargs(opts)
2076 opts = pycompat.byteskwargs(opts)
2082 if not pats:
2077 if not pats:
2083 raise error.Abort(_('no files specified'))
2078 raise error.Abort(_('no files specified'))
2084
2079
2085 m = scmutil.match(repo[None], pats, opts)
2080 m = scmutil.match(repo[None], pats, opts)
2086 dryrun = opts.get(r'dry_run')
2081 dryrun = opts.get(r'dry_run')
2087 rejected = cmdutil.forget(ui, repo, m, prefix="",
2082 rejected = cmdutil.forget(ui, repo, m, prefix="",
2088 explicitonly=False, dryrun=dryrun)[0]
2083 explicitonly=False, dryrun=dryrun)[0]
2089 return rejected and 1 or 0
2084 return rejected and 1 or 0
2090
2085
2091 @command(
2086 @command(
2092 'graft',
2087 'graft',
2093 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2088 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2094 ('c', 'continue', False, _('resume interrupted graft')),
2089 ('c', 'continue', False, _('resume interrupted graft')),
2095 ('e', 'edit', False, _('invoke editor on commit messages')),
2090 ('e', 'edit', False, _('invoke editor on commit messages')),
2096 ('', 'log', None, _('append graft info to log message')),
2091 ('', 'log', None, _('append graft info to log message')),
2097 ('f', 'force', False, _('force graft')),
2092 ('f', 'force', False, _('force graft')),
2098 ('D', 'currentdate', False,
2093 ('D', 'currentdate', False,
2099 _('record the current date as commit date')),
2094 _('record the current date as commit date')),
2100 ('U', 'currentuser', False,
2095 ('U', 'currentuser', False,
2101 _('record the current user as committer'), _('DATE'))]
2096 _('record the current user as committer'), _('DATE'))]
2102 + commitopts2 + mergetoolopts + dryrunopts,
2097 + commitopts2 + mergetoolopts + dryrunopts,
2103 _('[OPTION]... [-r REV]... REV...'))
2098 _('[OPTION]... [-r REV]... REV...'))
2104 def graft(ui, repo, *revs, **opts):
2099 def graft(ui, repo, *revs, **opts):
2105 '''copy changes from other branches onto the current branch
2100 '''copy changes from other branches onto the current branch
2106
2101
2107 This command uses Mercurial's merge logic to copy individual
2102 This command uses Mercurial's merge logic to copy individual
2108 changes from other branches without merging branches in the
2103 changes from other branches without merging branches in the
2109 history graph. This is sometimes known as 'backporting' or
2104 history graph. This is sometimes known as 'backporting' or
2110 'cherry-picking'. By default, graft will copy user, date, and
2105 'cherry-picking'. By default, graft will copy user, date, and
2111 description from the source changesets.
2106 description from the source changesets.
2112
2107
2113 Changesets that are ancestors of the current revision, that have
2108 Changesets that are ancestors of the current revision, that have
2114 already been grafted, or that are merges will be skipped.
2109 already been grafted, or that are merges will be skipped.
2115
2110
2116 If --log is specified, log messages will have a comment appended
2111 If --log is specified, log messages will have a comment appended
2117 of the form::
2112 of the form::
2118
2113
2119 (grafted from CHANGESETHASH)
2114 (grafted from CHANGESETHASH)
2120
2115
2121 If --force is specified, revisions will be grafted even if they
2116 If --force is specified, revisions will be grafted even if they
2122 are already ancestors of, or have been grafted to, the destination.
2117 are already ancestors of, or have been grafted to, the destination.
2123 This is useful when the revisions have since been backed out.
2118 This is useful when the revisions have since been backed out.
2124
2119
2125 If a graft merge results in conflicts, the graft process is
2120 If a graft merge results in conflicts, the graft process is
2126 interrupted so that the current merge can be manually resolved.
2121 interrupted so that the current merge can be manually resolved.
2127 Once all conflicts are addressed, the graft process can be
2122 Once all conflicts are addressed, the graft process can be
2128 continued with the -c/--continue option.
2123 continued with the -c/--continue option.
2129
2124
2130 .. note::
2125 .. note::
2131
2126
2132 The -c/--continue option does not reapply earlier options, except
2127 The -c/--continue option does not reapply earlier options, except
2133 for --force.
2128 for --force.
2134
2129
2135 .. container:: verbose
2130 .. container:: verbose
2136
2131
2137 Examples:
2132 Examples:
2138
2133
2139 - copy a single change to the stable branch and edit its description::
2134 - copy a single change to the stable branch and edit its description::
2140
2135
2141 hg update stable
2136 hg update stable
2142 hg graft --edit 9393
2137 hg graft --edit 9393
2143
2138
2144 - graft a range of changesets with one exception, updating dates::
2139 - graft a range of changesets with one exception, updating dates::
2145
2140
2146 hg graft -D "2085::2093 and not 2091"
2141 hg graft -D "2085::2093 and not 2091"
2147
2142
2148 - continue a graft after resolving conflicts::
2143 - continue a graft after resolving conflicts::
2149
2144
2150 hg graft -c
2145 hg graft -c
2151
2146
2152 - show the source of a grafted changeset::
2147 - show the source of a grafted changeset::
2153
2148
2154 hg log --debug -r .
2149 hg log --debug -r .
2155
2150
2156 - show revisions sorted by date::
2151 - show revisions sorted by date::
2157
2152
2158 hg log -r "sort(all(), date)"
2153 hg log -r "sort(all(), date)"
2159
2154
2160 See :hg:`help revisions` for more about specifying revisions.
2155 See :hg:`help revisions` for more about specifying revisions.
2161
2156
2162 Returns 0 on successful completion.
2157 Returns 0 on successful completion.
2163 '''
2158 '''
2164 with repo.wlock():
2159 with repo.wlock():
2165 return _dograft(ui, repo, *revs, **opts)
2160 return _dograft(ui, repo, *revs, **opts)
2166
2161
2167 def _dograft(ui, repo, *revs, **opts):
2162 def _dograft(ui, repo, *revs, **opts):
2168 opts = pycompat.byteskwargs(opts)
2163 opts = pycompat.byteskwargs(opts)
2169 if revs and opts.get('rev'):
2164 if revs and opts.get('rev'):
2170 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2165 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2171 'revision ordering!\n'))
2166 'revision ordering!\n'))
2172
2167
2173 revs = list(revs)
2168 revs = list(revs)
2174 revs.extend(opts.get('rev'))
2169 revs.extend(opts.get('rev'))
2175
2170
2176 if not opts.get('user') and opts.get('currentuser'):
2171 if not opts.get('user') and opts.get('currentuser'):
2177 opts['user'] = ui.username()
2172 opts['user'] = ui.username()
2178 if not opts.get('date') and opts.get('currentdate'):
2173 if not opts.get('date') and opts.get('currentdate'):
2179 opts['date'] = "%d %d" % dateutil.makedate()
2174 opts['date'] = "%d %d" % dateutil.makedate()
2180
2175
2181 editor = cmdutil.getcommiteditor(editform='graft',
2176 editor = cmdutil.getcommiteditor(editform='graft',
2182 **pycompat.strkwargs(opts))
2177 **pycompat.strkwargs(opts))
2183
2178
2184 cont = False
2179 cont = False
2185 if opts.get('continue'):
2180 if opts.get('continue'):
2186 cont = True
2181 cont = True
2187 if revs:
2182 if revs:
2188 raise error.Abort(_("can't specify --continue and revisions"))
2183 raise error.Abort(_("can't specify --continue and revisions"))
2189 # read in unfinished revisions
2184 # read in unfinished revisions
2190 try:
2185 try:
2191 nodes = repo.vfs.read('graftstate').splitlines()
2186 nodes = repo.vfs.read('graftstate').splitlines()
2192 revs = [repo[node].rev() for node in nodes]
2187 revs = [repo[node].rev() for node in nodes]
2193 except IOError as inst:
2188 except IOError as inst:
2194 if inst.errno != errno.ENOENT:
2189 if inst.errno != errno.ENOENT:
2195 raise
2190 raise
2196 cmdutil.wrongtooltocontinue(repo, _('graft'))
2191 cmdutil.wrongtooltocontinue(repo, _('graft'))
2197 else:
2192 else:
2198 if not revs:
2193 if not revs:
2199 raise error.Abort(_('no revisions specified'))
2194 raise error.Abort(_('no revisions specified'))
2200 cmdutil.checkunfinished(repo)
2195 cmdutil.checkunfinished(repo)
2201 cmdutil.bailifchanged(repo)
2196 cmdutil.bailifchanged(repo)
2202 revs = scmutil.revrange(repo, revs)
2197 revs = scmutil.revrange(repo, revs)
2203
2198
2204 skipped = set()
2199 skipped = set()
2205 # check for merges
2200 # check for merges
2206 for rev in repo.revs('%ld and merge()', revs):
2201 for rev in repo.revs('%ld and merge()', revs):
2207 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2202 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2208 skipped.add(rev)
2203 skipped.add(rev)
2209 revs = [r for r in revs if r not in skipped]
2204 revs = [r for r in revs if r not in skipped]
2210 if not revs:
2205 if not revs:
2211 return -1
2206 return -1
2212
2207
2213 # Don't check in the --continue case, in effect retaining --force across
2208 # Don't check in the --continue case, in effect retaining --force across
2214 # --continues. That's because without --force, any revisions we decided to
2209 # --continues. That's because without --force, any revisions we decided to
2215 # skip would have been filtered out here, so they wouldn't have made their
2210 # skip would have been filtered out here, so they wouldn't have made their
2216 # way to the graftstate. With --force, any revisions we would have otherwise
2211 # way to the graftstate. With --force, any revisions we would have otherwise
2217 # skipped would not have been filtered out, and if they hadn't been applied
2212 # skipped would not have been filtered out, and if they hadn't been applied
2218 # already, they'd have been in the graftstate.
2213 # already, they'd have been in the graftstate.
2219 if not (cont or opts.get('force')):
2214 if not (cont or opts.get('force')):
2220 # check for ancestors of dest branch
2215 # check for ancestors of dest branch
2221 crev = repo['.'].rev()
2216 crev = repo['.'].rev()
2222 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2217 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2223 # XXX make this lazy in the future
2218 # XXX make this lazy in the future
2224 # don't mutate while iterating, create a copy
2219 # don't mutate while iterating, create a copy
2225 for rev in list(revs):
2220 for rev in list(revs):
2226 if rev in ancestors:
2221 if rev in ancestors:
2227 ui.warn(_('skipping ancestor revision %d:%s\n') %
2222 ui.warn(_('skipping ancestor revision %d:%s\n') %
2228 (rev, repo[rev]))
2223 (rev, repo[rev]))
2229 # XXX remove on list is slow
2224 # XXX remove on list is slow
2230 revs.remove(rev)
2225 revs.remove(rev)
2231 if not revs:
2226 if not revs:
2232 return -1
2227 return -1
2233
2228
2234 # analyze revs for earlier grafts
2229 # analyze revs for earlier grafts
2235 ids = {}
2230 ids = {}
2236 for ctx in repo.set("%ld", revs):
2231 for ctx in repo.set("%ld", revs):
2237 ids[ctx.hex()] = ctx.rev()
2232 ids[ctx.hex()] = ctx.rev()
2238 n = ctx.extra().get('source')
2233 n = ctx.extra().get('source')
2239 if n:
2234 if n:
2240 ids[n] = ctx.rev()
2235 ids[n] = ctx.rev()
2241
2236
2242 # check ancestors for earlier grafts
2237 # check ancestors for earlier grafts
2243 ui.debug('scanning for duplicate grafts\n')
2238 ui.debug('scanning for duplicate grafts\n')
2244
2239
2245 # The only changesets we can be sure doesn't contain grafts of any
2240 # The only changesets we can be sure doesn't contain grafts of any
2246 # revs, are the ones that are common ancestors of *all* revs:
2241 # revs, are the ones that are common ancestors of *all* revs:
2247 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2242 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2248 ctx = repo[rev]
2243 ctx = repo[rev]
2249 n = ctx.extra().get('source')
2244 n = ctx.extra().get('source')
2250 if n in ids:
2245 if n in ids:
2251 try:
2246 try:
2252 r = repo[n].rev()
2247 r = repo[n].rev()
2253 except error.RepoLookupError:
2248 except error.RepoLookupError:
2254 r = None
2249 r = None
2255 if r in revs:
2250 if r in revs:
2256 ui.warn(_('skipping revision %d:%s '
2251 ui.warn(_('skipping revision %d:%s '
2257 '(already grafted to %d:%s)\n')
2252 '(already grafted to %d:%s)\n')
2258 % (r, repo[r], rev, ctx))
2253 % (r, repo[r], rev, ctx))
2259 revs.remove(r)
2254 revs.remove(r)
2260 elif ids[n] in revs:
2255 elif ids[n] in revs:
2261 if r is None:
2256 if r is None:
2262 ui.warn(_('skipping already grafted revision %d:%s '
2257 ui.warn(_('skipping already grafted revision %d:%s '
2263 '(%d:%s also has unknown origin %s)\n')
2258 '(%d:%s also has unknown origin %s)\n')
2264 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2259 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2265 else:
2260 else:
2266 ui.warn(_('skipping already grafted revision %d:%s '
2261 ui.warn(_('skipping already grafted revision %d:%s '
2267 '(%d:%s also has origin %d:%s)\n')
2262 '(%d:%s also has origin %d:%s)\n')
2268 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2263 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2269 revs.remove(ids[n])
2264 revs.remove(ids[n])
2270 elif ctx.hex() in ids:
2265 elif ctx.hex() in ids:
2271 r = ids[ctx.hex()]
2266 r = ids[ctx.hex()]
2272 ui.warn(_('skipping already grafted revision %d:%s '
2267 ui.warn(_('skipping already grafted revision %d:%s '
2273 '(was grafted from %d:%s)\n') %
2268 '(was grafted from %d:%s)\n') %
2274 (r, repo[r], rev, ctx))
2269 (r, repo[r], rev, ctx))
2275 revs.remove(r)
2270 revs.remove(r)
2276 if not revs:
2271 if not revs:
2277 return -1
2272 return -1
2278
2273
2279 for pos, ctx in enumerate(repo.set("%ld", revs)):
2274 for pos, ctx in enumerate(repo.set("%ld", revs)):
2280 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2275 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2281 ctx.description().split('\n', 1)[0])
2276 ctx.description().split('\n', 1)[0])
2282 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2277 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2283 if names:
2278 if names:
2284 desc += ' (%s)' % ' '.join(names)
2279 desc += ' (%s)' % ' '.join(names)
2285 ui.status(_('grafting %s\n') % desc)
2280 ui.status(_('grafting %s\n') % desc)
2286 if opts.get('dry_run'):
2281 if opts.get('dry_run'):
2287 continue
2282 continue
2288
2283
2289 source = ctx.extra().get('source')
2284 source = ctx.extra().get('source')
2290 extra = {}
2285 extra = {}
2291 if source:
2286 if source:
2292 extra['source'] = source
2287 extra['source'] = source
2293 extra['intermediate-source'] = ctx.hex()
2288 extra['intermediate-source'] = ctx.hex()
2294 else:
2289 else:
2295 extra['source'] = ctx.hex()
2290 extra['source'] = ctx.hex()
2296 user = ctx.user()
2291 user = ctx.user()
2297 if opts.get('user'):
2292 if opts.get('user'):
2298 user = opts['user']
2293 user = opts['user']
2299 date = ctx.date()
2294 date = ctx.date()
2300 if opts.get('date'):
2295 if opts.get('date'):
2301 date = opts['date']
2296 date = opts['date']
2302 message = ctx.description()
2297 message = ctx.description()
2303 if opts.get('log'):
2298 if opts.get('log'):
2304 message += '\n(grafted from %s)' % ctx.hex()
2299 message += '\n(grafted from %s)' % ctx.hex()
2305
2300
2306 # we don't merge the first commit when continuing
2301 # we don't merge the first commit when continuing
2307 if not cont:
2302 if not cont:
2308 # perform the graft merge with p1(rev) as 'ancestor'
2303 # perform the graft merge with p1(rev) as 'ancestor'
2309 try:
2304 try:
2310 # ui.forcemerge is an internal variable, do not document
2305 # ui.forcemerge is an internal variable, do not document
2311 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
2306 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
2312 'graft')
2307 'graft')
2313 stats = mergemod.graft(repo, ctx, ctx.p1(),
2308 stats = mergemod.graft(repo, ctx, ctx.p1(),
2314 ['local', 'graft'])
2309 ['local', 'graft'])
2315 finally:
2310 finally:
2316 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
2311 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
2317 # report any conflicts
2312 # report any conflicts
2318 if stats.unresolvedcount > 0:
2313 if stats.unresolvedcount > 0:
2319 # write out state for --continue
2314 # write out state for --continue
2320 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2315 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2321 repo.vfs.write('graftstate', ''.join(nodelines))
2316 repo.vfs.write('graftstate', ''.join(nodelines))
2322 extra = ''
2317 extra = ''
2323 if opts.get('user'):
2318 if opts.get('user'):
2324 extra += ' --user %s' % procutil.shellquote(opts['user'])
2319 extra += ' --user %s' % procutil.shellquote(opts['user'])
2325 if opts.get('date'):
2320 if opts.get('date'):
2326 extra += ' --date %s' % procutil.shellquote(opts['date'])
2321 extra += ' --date %s' % procutil.shellquote(opts['date'])
2327 if opts.get('log'):
2322 if opts.get('log'):
2328 extra += ' --log'
2323 extra += ' --log'
2329 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
2324 hint=_("use 'hg resolve' and 'hg graft --continue%s'") % extra
2330 raise error.Abort(
2325 raise error.Abort(
2331 _("unresolved conflicts, can't continue"),
2326 _("unresolved conflicts, can't continue"),
2332 hint=hint)
2327 hint=hint)
2333 else:
2328 else:
2334 cont = False
2329 cont = False
2335
2330
2336 # commit
2331 # commit
2337 node = repo.commit(text=message, user=user,
2332 node = repo.commit(text=message, user=user,
2338 date=date, extra=extra, editor=editor)
2333 date=date, extra=extra, editor=editor)
2339 if node is None:
2334 if node is None:
2340 ui.warn(
2335 ui.warn(
2341 _('note: graft of %d:%s created no changes to commit\n') %
2336 _('note: graft of %d:%s created no changes to commit\n') %
2342 (ctx.rev(), ctx))
2337 (ctx.rev(), ctx))
2343
2338
2344 # remove state when we complete successfully
2339 # remove state when we complete successfully
2345 if not opts.get('dry_run'):
2340 if not opts.get('dry_run'):
2346 repo.vfs.unlinkpath('graftstate', ignoremissing=True)
2341 repo.vfs.unlinkpath('graftstate', ignoremissing=True)
2347
2342
2348 return 0
2343 return 0
2349
2344
2350 @command('grep',
2345 @command('grep',
2351 [('0', 'print0', None, _('end fields with NUL')),
2346 [('0', 'print0', None, _('end fields with NUL')),
2352 ('', 'all', None, _('print all revisions that match')),
2347 ('', 'all', None, _('print all revisions that match')),
2353 ('a', 'text', None, _('treat all files as text')),
2348 ('a', 'text', None, _('treat all files as text')),
2354 ('f', 'follow', None,
2349 ('f', 'follow', None,
2355 _('follow changeset history,'
2350 _('follow changeset history,'
2356 ' or file history across copies and renames')),
2351 ' or file history across copies and renames')),
2357 ('i', 'ignore-case', None, _('ignore case when matching')),
2352 ('i', 'ignore-case', None, _('ignore case when matching')),
2358 ('l', 'files-with-matches', None,
2353 ('l', 'files-with-matches', None,
2359 _('print only filenames and revisions that match')),
2354 _('print only filenames and revisions that match')),
2360 ('n', 'line-number', None, _('print matching line numbers')),
2355 ('n', 'line-number', None, _('print matching line numbers')),
2361 ('r', 'rev', [],
2356 ('r', 'rev', [],
2362 _('only search files changed within revision range'), _('REV')),
2357 _('only search files changed within revision range'), _('REV')),
2363 ('u', 'user', None, _('list the author (long with -v)')),
2358 ('u', 'user', None, _('list the author (long with -v)')),
2364 ('d', 'date', None, _('list the date (short with -q)')),
2359 ('d', 'date', None, _('list the date (short with -q)')),
2365 ] + formatteropts + walkopts,
2360 ] + formatteropts + walkopts,
2366 _('[OPTION]... PATTERN [FILE]...'),
2361 _('[OPTION]... PATTERN [FILE]...'),
2367 inferrepo=True, cmdtype=readonly)
2362 inferrepo=True, cmdtype=readonly)
2368 def grep(ui, repo, pattern, *pats, **opts):
2363 def grep(ui, repo, pattern, *pats, **opts):
2369 """search revision history for a pattern in specified files
2364 """search revision history for a pattern in specified files
2370
2365
2371 Search revision history for a regular expression in the specified
2366 Search revision history for a regular expression in the specified
2372 files or the entire project.
2367 files or the entire project.
2373
2368
2374 By default, grep prints the most recent revision number for each
2369 By default, grep prints the most recent revision number for each
2375 file in which it finds a match. To get it to print every revision
2370 file in which it finds a match. To get it to print every revision
2376 that contains a change in match status ("-" for a match that becomes
2371 that contains a change in match status ("-" for a match that becomes
2377 a non-match, or "+" for a non-match that becomes a match), use the
2372 a non-match, or "+" for a non-match that becomes a match), use the
2378 --all flag.
2373 --all flag.
2379
2374
2380 PATTERN can be any Python (roughly Perl-compatible) regular
2375 PATTERN can be any Python (roughly Perl-compatible) regular
2381 expression.
2376 expression.
2382
2377
2383 If no FILEs are specified (and -f/--follow isn't set), all files in
2378 If no FILEs are specified (and -f/--follow isn't set), all files in
2384 the repository are searched, including those that don't exist in the
2379 the repository are searched, including those that don't exist in the
2385 current branch or have been deleted in a prior changeset.
2380 current branch or have been deleted in a prior changeset.
2386
2381
2387 Returns 0 if a match is found, 1 otherwise.
2382 Returns 0 if a match is found, 1 otherwise.
2388 """
2383 """
2389 opts = pycompat.byteskwargs(opts)
2384 opts = pycompat.byteskwargs(opts)
2390 reflags = re.M
2385 reflags = re.M
2391 if opts.get('ignore_case'):
2386 if opts.get('ignore_case'):
2392 reflags |= re.I
2387 reflags |= re.I
2393 try:
2388 try:
2394 regexp = util.re.compile(pattern, reflags)
2389 regexp = util.re.compile(pattern, reflags)
2395 except re.error as inst:
2390 except re.error as inst:
2396 ui.warn(_("grep: invalid match pattern: %s\n") % pycompat.bytestr(inst))
2391 ui.warn(_("grep: invalid match pattern: %s\n") % pycompat.bytestr(inst))
2397 return 1
2392 return 1
2398 sep, eol = ':', '\n'
2393 sep, eol = ':', '\n'
2399 if opts.get('print0'):
2394 if opts.get('print0'):
2400 sep = eol = '\0'
2395 sep = eol = '\0'
2401
2396
2402 getfile = util.lrucachefunc(repo.file)
2397 getfile = util.lrucachefunc(repo.file)
2403
2398
2404 def matchlines(body):
2399 def matchlines(body):
2405 begin = 0
2400 begin = 0
2406 linenum = 0
2401 linenum = 0
2407 while begin < len(body):
2402 while begin < len(body):
2408 match = regexp.search(body, begin)
2403 match = regexp.search(body, begin)
2409 if not match:
2404 if not match:
2410 break
2405 break
2411 mstart, mend = match.span()
2406 mstart, mend = match.span()
2412 linenum += body.count('\n', begin, mstart) + 1
2407 linenum += body.count('\n', begin, mstart) + 1
2413 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2408 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2414 begin = body.find('\n', mend) + 1 or len(body) + 1
2409 begin = body.find('\n', mend) + 1 or len(body) + 1
2415 lend = begin - 1
2410 lend = begin - 1
2416 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2411 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2417
2412
2418 class linestate(object):
2413 class linestate(object):
2419 def __init__(self, line, linenum, colstart, colend):
2414 def __init__(self, line, linenum, colstart, colend):
2420 self.line = line
2415 self.line = line
2421 self.linenum = linenum
2416 self.linenum = linenum
2422 self.colstart = colstart
2417 self.colstart = colstart
2423 self.colend = colend
2418 self.colend = colend
2424
2419
2425 def __hash__(self):
2420 def __hash__(self):
2426 return hash((self.linenum, self.line))
2421 return hash((self.linenum, self.line))
2427
2422
2428 def __eq__(self, other):
2423 def __eq__(self, other):
2429 return self.line == other.line
2424 return self.line == other.line
2430
2425
2431 def findpos(self):
2426 def findpos(self):
2432 """Iterate all (start, end) indices of matches"""
2427 """Iterate all (start, end) indices of matches"""
2433 yield self.colstart, self.colend
2428 yield self.colstart, self.colend
2434 p = self.colend
2429 p = self.colend
2435 while p < len(self.line):
2430 while p < len(self.line):
2436 m = regexp.search(self.line, p)
2431 m = regexp.search(self.line, p)
2437 if not m:
2432 if not m:
2438 break
2433 break
2439 yield m.span()
2434 yield m.span()
2440 p = m.end()
2435 p = m.end()
2441
2436
2442 matches = {}
2437 matches = {}
2443 copies = {}
2438 copies = {}
2444 def grepbody(fn, rev, body):
2439 def grepbody(fn, rev, body):
2445 matches[rev].setdefault(fn, [])
2440 matches[rev].setdefault(fn, [])
2446 m = matches[rev][fn]
2441 m = matches[rev][fn]
2447 for lnum, cstart, cend, line in matchlines(body):
2442 for lnum, cstart, cend, line in matchlines(body):
2448 s = linestate(line, lnum, cstart, cend)
2443 s = linestate(line, lnum, cstart, cend)
2449 m.append(s)
2444 m.append(s)
2450
2445
2451 def difflinestates(a, b):
2446 def difflinestates(a, b):
2452 sm = difflib.SequenceMatcher(None, a, b)
2447 sm = difflib.SequenceMatcher(None, a, b)
2453 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2448 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2454 if tag == 'insert':
2449 if tag == 'insert':
2455 for i in xrange(blo, bhi):
2450 for i in xrange(blo, bhi):
2456 yield ('+', b[i])
2451 yield ('+', b[i])
2457 elif tag == 'delete':
2452 elif tag == 'delete':
2458 for i in xrange(alo, ahi):
2453 for i in xrange(alo, ahi):
2459 yield ('-', a[i])
2454 yield ('-', a[i])
2460 elif tag == 'replace':
2455 elif tag == 'replace':
2461 for i in xrange(alo, ahi):
2456 for i in xrange(alo, ahi):
2462 yield ('-', a[i])
2457 yield ('-', a[i])
2463 for i in xrange(blo, bhi):
2458 for i in xrange(blo, bhi):
2464 yield ('+', b[i])
2459 yield ('+', b[i])
2465
2460
2466 def display(fm, fn, ctx, pstates, states):
2461 def display(fm, fn, ctx, pstates, states):
2467 rev = ctx.rev()
2462 rev = ctx.rev()
2468 if fm.isplain():
2463 if fm.isplain():
2469 formatuser = ui.shortuser
2464 formatuser = ui.shortuser
2470 else:
2465 else:
2471 formatuser = str
2466 formatuser = str
2472 if ui.quiet:
2467 if ui.quiet:
2473 datefmt = '%Y-%m-%d'
2468 datefmt = '%Y-%m-%d'
2474 else:
2469 else:
2475 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2470 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2476 found = False
2471 found = False
2477 @util.cachefunc
2472 @util.cachefunc
2478 def binary():
2473 def binary():
2479 flog = getfile(fn)
2474 flog = getfile(fn)
2480 return stringutil.binary(flog.read(ctx.filenode(fn)))
2475 return stringutil.binary(flog.read(ctx.filenode(fn)))
2481
2476
2482 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
2477 fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'}
2483 if opts.get('all'):
2478 if opts.get('all'):
2484 iter = difflinestates(pstates, states)
2479 iter = difflinestates(pstates, states)
2485 else:
2480 else:
2486 iter = [('', l) for l in states]
2481 iter = [('', l) for l in states]
2487 for change, l in iter:
2482 for change, l in iter:
2488 fm.startitem()
2483 fm.startitem()
2489 fm.data(node=fm.hexfunc(ctx.node()))
2484 fm.data(node=fm.hexfunc(ctx.node()))
2490 cols = [
2485 cols = [
2491 ('filename', fn, True),
2486 ('filename', fn, True),
2492 ('rev', rev, True),
2487 ('rev', rev, True),
2493 ('linenumber', l.linenum, opts.get('line_number')),
2488 ('linenumber', l.linenum, opts.get('line_number')),
2494 ]
2489 ]
2495 if opts.get('all'):
2490 if opts.get('all'):
2496 cols.append(('change', change, True))
2491 cols.append(('change', change, True))
2497 cols.extend([
2492 cols.extend([
2498 ('user', formatuser(ctx.user()), opts.get('user')),
2493 ('user', formatuser(ctx.user()), opts.get('user')),
2499 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
2494 ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')),
2500 ])
2495 ])
2501 lastcol = next(name for name, data, cond in reversed(cols) if cond)
2496 lastcol = next(name for name, data, cond in reversed(cols) if cond)
2502 for name, data, cond in cols:
2497 for name, data, cond in cols:
2503 field = fieldnamemap.get(name, name)
2498 field = fieldnamemap.get(name, name)
2504 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
2499 fm.condwrite(cond, field, '%s', data, label='grep.%s' % name)
2505 if cond and name != lastcol:
2500 if cond and name != lastcol:
2506 fm.plain(sep, label='grep.sep')
2501 fm.plain(sep, label='grep.sep')
2507 if not opts.get('files_with_matches'):
2502 if not opts.get('files_with_matches'):
2508 fm.plain(sep, label='grep.sep')
2503 fm.plain(sep, label='grep.sep')
2509 if not opts.get('text') and binary():
2504 if not opts.get('text') and binary():
2510 fm.plain(_(" Binary file matches"))
2505 fm.plain(_(" Binary file matches"))
2511 else:
2506 else:
2512 displaymatches(fm.nested('texts'), l)
2507 displaymatches(fm.nested('texts'), l)
2513 fm.plain(eol)
2508 fm.plain(eol)
2514 found = True
2509 found = True
2515 if opts.get('files_with_matches'):
2510 if opts.get('files_with_matches'):
2516 break
2511 break
2517 return found
2512 return found
2518
2513
2519 def displaymatches(fm, l):
2514 def displaymatches(fm, l):
2520 p = 0
2515 p = 0
2521 for s, e in l.findpos():
2516 for s, e in l.findpos():
2522 if p < s:
2517 if p < s:
2523 fm.startitem()
2518 fm.startitem()
2524 fm.write('text', '%s', l.line[p:s])
2519 fm.write('text', '%s', l.line[p:s])
2525 fm.data(matched=False)
2520 fm.data(matched=False)
2526 fm.startitem()
2521 fm.startitem()
2527 fm.write('text', '%s', l.line[s:e], label='grep.match')
2522 fm.write('text', '%s', l.line[s:e], label='grep.match')
2528 fm.data(matched=True)
2523 fm.data(matched=True)
2529 p = e
2524 p = e
2530 if p < len(l.line):
2525 if p < len(l.line):
2531 fm.startitem()
2526 fm.startitem()
2532 fm.write('text', '%s', l.line[p:])
2527 fm.write('text', '%s', l.line[p:])
2533 fm.data(matched=False)
2528 fm.data(matched=False)
2534 fm.end()
2529 fm.end()
2535
2530
2536 skip = {}
2531 skip = {}
2537 revfiles = {}
2532 revfiles = {}
2538 match = scmutil.match(repo[None], pats, opts)
2533 match = scmutil.match(repo[None], pats, opts)
2539 found = False
2534 found = False
2540 follow = opts.get('follow')
2535 follow = opts.get('follow')
2541
2536
2542 def prep(ctx, fns):
2537 def prep(ctx, fns):
2543 rev = ctx.rev()
2538 rev = ctx.rev()
2544 pctx = ctx.p1()
2539 pctx = ctx.p1()
2545 parent = pctx.rev()
2540 parent = pctx.rev()
2546 matches.setdefault(rev, {})
2541 matches.setdefault(rev, {})
2547 matches.setdefault(parent, {})
2542 matches.setdefault(parent, {})
2548 files = revfiles.setdefault(rev, [])
2543 files = revfiles.setdefault(rev, [])
2549 for fn in fns:
2544 for fn in fns:
2550 flog = getfile(fn)
2545 flog = getfile(fn)
2551 try:
2546 try:
2552 fnode = ctx.filenode(fn)
2547 fnode = ctx.filenode(fn)
2553 except error.LookupError:
2548 except error.LookupError:
2554 continue
2549 continue
2555
2550
2556 copied = flog.renamed(fnode)
2551 copied = flog.renamed(fnode)
2557 copy = follow and copied and copied[0]
2552 copy = follow and copied and copied[0]
2558 if copy:
2553 if copy:
2559 copies.setdefault(rev, {})[fn] = copy
2554 copies.setdefault(rev, {})[fn] = copy
2560 if fn in skip:
2555 if fn in skip:
2561 if copy:
2556 if copy:
2562 skip[copy] = True
2557 skip[copy] = True
2563 continue
2558 continue
2564 files.append(fn)
2559 files.append(fn)
2565
2560
2566 if fn not in matches[rev]:
2561 if fn not in matches[rev]:
2567 grepbody(fn, rev, flog.read(fnode))
2562 grepbody(fn, rev, flog.read(fnode))
2568
2563
2569 pfn = copy or fn
2564 pfn = copy or fn
2570 if pfn not in matches[parent]:
2565 if pfn not in matches[parent]:
2571 try:
2566 try:
2572 fnode = pctx.filenode(pfn)
2567 fnode = pctx.filenode(pfn)
2573 grepbody(pfn, parent, flog.read(fnode))
2568 grepbody(pfn, parent, flog.read(fnode))
2574 except error.LookupError:
2569 except error.LookupError:
2575 pass
2570 pass
2576
2571
2577 ui.pager('grep')
2572 ui.pager('grep')
2578 fm = ui.formatter('grep', opts)
2573 fm = ui.formatter('grep', opts)
2579 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2574 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2580 rev = ctx.rev()
2575 rev = ctx.rev()
2581 parent = ctx.p1().rev()
2576 parent = ctx.p1().rev()
2582 for fn in sorted(revfiles.get(rev, [])):
2577 for fn in sorted(revfiles.get(rev, [])):
2583 states = matches[rev][fn]
2578 states = matches[rev][fn]
2584 copy = copies.get(rev, {}).get(fn)
2579 copy = copies.get(rev, {}).get(fn)
2585 if fn in skip:
2580 if fn in skip:
2586 if copy:
2581 if copy:
2587 skip[copy] = True
2582 skip[copy] = True
2588 continue
2583 continue
2589 pstates = matches.get(parent, {}).get(copy or fn, [])
2584 pstates = matches.get(parent, {}).get(copy or fn, [])
2590 if pstates or states:
2585 if pstates or states:
2591 r = display(fm, fn, ctx, pstates, states)
2586 r = display(fm, fn, ctx, pstates, states)
2592 found = found or r
2587 found = found or r
2593 if r and not opts.get('all'):
2588 if r and not opts.get('all'):
2594 skip[fn] = True
2589 skip[fn] = True
2595 if copy:
2590 if copy:
2596 skip[copy] = True
2591 skip[copy] = True
2597 del revfiles[rev]
2592 del revfiles[rev]
2598 # We will keep the matches dict for the duration of the window
2593 # We will keep the matches dict for the duration of the window
2599 # clear the matches dict once the window is over
2594 # clear the matches dict once the window is over
2600 if not revfiles:
2595 if not revfiles:
2601 matches.clear()
2596 matches.clear()
2602 fm.end()
2597 fm.end()
2603
2598
2604 return not found
2599 return not found
2605
2600
2606 @command('heads',
2601 @command('heads',
2607 [('r', 'rev', '',
2602 [('r', 'rev', '',
2608 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2603 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2609 ('t', 'topo', False, _('show topological heads only')),
2604 ('t', 'topo', False, _('show topological heads only')),
2610 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2605 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2611 ('c', 'closed', False, _('show normal and closed branch heads')),
2606 ('c', 'closed', False, _('show normal and closed branch heads')),
2612 ] + templateopts,
2607 ] + templateopts,
2613 _('[-ct] [-r STARTREV] [REV]...'), cmdtype=readonly)
2608 _('[-ct] [-r STARTREV] [REV]...'), cmdtype=readonly)
2614 def heads(ui, repo, *branchrevs, **opts):
2609 def heads(ui, repo, *branchrevs, **opts):
2615 """show branch heads
2610 """show branch heads
2616
2611
2617 With no arguments, show all open branch heads in the repository.
2612 With no arguments, show all open branch heads in the repository.
2618 Branch heads are changesets that have no descendants on the
2613 Branch heads are changesets that have no descendants on the
2619 same branch. They are where development generally takes place and
2614 same branch. They are where development generally takes place and
2620 are the usual targets for update and merge operations.
2615 are the usual targets for update and merge operations.
2621
2616
2622 If one or more REVs are given, only open branch heads on the
2617 If one or more REVs are given, only open branch heads on the
2623 branches associated with the specified changesets are shown. This
2618 branches associated with the specified changesets are shown. This
2624 means that you can use :hg:`heads .` to see the heads on the
2619 means that you can use :hg:`heads .` to see the heads on the
2625 currently checked-out branch.
2620 currently checked-out branch.
2626
2621
2627 If -c/--closed is specified, also show branch heads marked closed
2622 If -c/--closed is specified, also show branch heads marked closed
2628 (see :hg:`commit --close-branch`).
2623 (see :hg:`commit --close-branch`).
2629
2624
2630 If STARTREV is specified, only those heads that are descendants of
2625 If STARTREV is specified, only those heads that are descendants of
2631 STARTREV will be displayed.
2626 STARTREV will be displayed.
2632
2627
2633 If -t/--topo is specified, named branch mechanics will be ignored and only
2628 If -t/--topo is specified, named branch mechanics will be ignored and only
2634 topological heads (changesets with no children) will be shown.
2629 topological heads (changesets with no children) will be shown.
2635
2630
2636 Returns 0 if matching heads are found, 1 if not.
2631 Returns 0 if matching heads are found, 1 if not.
2637 """
2632 """
2638
2633
2639 opts = pycompat.byteskwargs(opts)
2634 opts = pycompat.byteskwargs(opts)
2640 start = None
2635 start = None
2641 rev = opts.get('rev')
2636 rev = opts.get('rev')
2642 if rev:
2637 if rev:
2643 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2638 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2644 start = scmutil.revsingle(repo, rev, None).node()
2639 start = scmutil.revsingle(repo, rev, None).node()
2645
2640
2646 if opts.get('topo'):
2641 if opts.get('topo'):
2647 heads = [repo[h] for h in repo.heads(start)]
2642 heads = [repo[h] for h in repo.heads(start)]
2648 else:
2643 else:
2649 heads = []
2644 heads = []
2650 for branch in repo.branchmap():
2645 for branch in repo.branchmap():
2651 heads += repo.branchheads(branch, start, opts.get('closed'))
2646 heads += repo.branchheads(branch, start, opts.get('closed'))
2652 heads = [repo[h] for h in heads]
2647 heads = [repo[h] for h in heads]
2653
2648
2654 if branchrevs:
2649 if branchrevs:
2655 branches = set(repo[br].branch() for br in branchrevs)
2650 branches = set(repo[br].branch() for br in branchrevs)
2656 heads = [h for h in heads if h.branch() in branches]
2651 heads = [h for h in heads if h.branch() in branches]
2657
2652
2658 if opts.get('active') and branchrevs:
2653 if opts.get('active') and branchrevs:
2659 dagheads = repo.heads(start)
2654 dagheads = repo.heads(start)
2660 heads = [h for h in heads if h.node() in dagheads]
2655 heads = [h for h in heads if h.node() in dagheads]
2661
2656
2662 if branchrevs:
2657 if branchrevs:
2663 haveheads = set(h.branch() for h in heads)
2658 haveheads = set(h.branch() for h in heads)
2664 if branches - haveheads:
2659 if branches - haveheads:
2665 headless = ', '.join(b for b in branches - haveheads)
2660 headless = ', '.join(b for b in branches - haveheads)
2666 msg = _('no open branch heads found on branches %s')
2661 msg = _('no open branch heads found on branches %s')
2667 if opts.get('rev'):
2662 if opts.get('rev'):
2668 msg += _(' (started at %s)') % opts['rev']
2663 msg += _(' (started at %s)') % opts['rev']
2669 ui.warn((msg + '\n') % headless)
2664 ui.warn((msg + '\n') % headless)
2670
2665
2671 if not heads:
2666 if not heads:
2672 return 1
2667 return 1
2673
2668
2674 ui.pager('heads')
2669 ui.pager('heads')
2675 heads = sorted(heads, key=lambda x: -x.rev())
2670 heads = sorted(heads, key=lambda x: -x.rev())
2676 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
2671 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
2677 for ctx in heads:
2672 for ctx in heads:
2678 displayer.show(ctx)
2673 displayer.show(ctx)
2679 displayer.close()
2674 displayer.close()
2680
2675
2681 @command('help',
2676 @command('help',
2682 [('e', 'extension', None, _('show only help for extensions')),
2677 [('e', 'extension', None, _('show only help for extensions')),
2683 ('c', 'command', None, _('show only help for commands')),
2678 ('c', 'command', None, _('show only help for commands')),
2684 ('k', 'keyword', None, _('show topics matching keyword')),
2679 ('k', 'keyword', None, _('show topics matching keyword')),
2685 ('s', 'system', [], _('show help for specific platform(s)')),
2680 ('s', 'system', [], _('show help for specific platform(s)')),
2686 ],
2681 ],
2687 _('[-ecks] [TOPIC]'),
2682 _('[-ecks] [TOPIC]'),
2688 norepo=True, cmdtype=readonly)
2683 norepo=True, cmdtype=readonly)
2689 def help_(ui, name=None, **opts):
2684 def help_(ui, name=None, **opts):
2690 """show help for a given topic or a help overview
2685 """show help for a given topic or a help overview
2691
2686
2692 With no arguments, print a list of commands with short help messages.
2687 With no arguments, print a list of commands with short help messages.
2693
2688
2694 Given a topic, extension, or command name, print help for that
2689 Given a topic, extension, or command name, print help for that
2695 topic.
2690 topic.
2696
2691
2697 Returns 0 if successful.
2692 Returns 0 if successful.
2698 """
2693 """
2699
2694
2700 keep = opts.get(r'system') or []
2695 keep = opts.get(r'system') or []
2701 if len(keep) == 0:
2696 if len(keep) == 0:
2702 if pycompat.sysplatform.startswith('win'):
2697 if pycompat.sysplatform.startswith('win'):
2703 keep.append('windows')
2698 keep.append('windows')
2704 elif pycompat.sysplatform == 'OpenVMS':
2699 elif pycompat.sysplatform == 'OpenVMS':
2705 keep.append('vms')
2700 keep.append('vms')
2706 elif pycompat.sysplatform == 'plan9':
2701 elif pycompat.sysplatform == 'plan9':
2707 keep.append('plan9')
2702 keep.append('plan9')
2708 else:
2703 else:
2709 keep.append('unix')
2704 keep.append('unix')
2710 keep.append(pycompat.sysplatform.lower())
2705 keep.append(pycompat.sysplatform.lower())
2711 if ui.verbose:
2706 if ui.verbose:
2712 keep.append('verbose')
2707 keep.append('verbose')
2713
2708
2714 commands = sys.modules[__name__]
2709 commands = sys.modules[__name__]
2715 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
2710 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
2716 ui.pager('help')
2711 ui.pager('help')
2717 ui.write(formatted)
2712 ui.write(formatted)
2718
2713
2719
2714
2720 @command('identify|id',
2715 @command('identify|id',
2721 [('r', 'rev', '',
2716 [('r', 'rev', '',
2722 _('identify the specified revision'), _('REV')),
2717 _('identify the specified revision'), _('REV')),
2723 ('n', 'num', None, _('show local revision number')),
2718 ('n', 'num', None, _('show local revision number')),
2724 ('i', 'id', None, _('show global revision id')),
2719 ('i', 'id', None, _('show global revision id')),
2725 ('b', 'branch', None, _('show branch')),
2720 ('b', 'branch', None, _('show branch')),
2726 ('t', 'tags', None, _('show tags')),
2721 ('t', 'tags', None, _('show tags')),
2727 ('B', 'bookmarks', None, _('show bookmarks')),
2722 ('B', 'bookmarks', None, _('show bookmarks')),
2728 ] + remoteopts + formatteropts,
2723 ] + remoteopts + formatteropts,
2729 _('[-nibtB] [-r REV] [SOURCE]'),
2724 _('[-nibtB] [-r REV] [SOURCE]'),
2730 optionalrepo=True, cmdtype=readonly)
2725 optionalrepo=True, cmdtype=readonly)
2731 def identify(ui, repo, source=None, rev=None,
2726 def identify(ui, repo, source=None, rev=None,
2732 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
2727 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
2733 """identify the working directory or specified revision
2728 """identify the working directory or specified revision
2734
2729
2735 Print a summary identifying the repository state at REV using one or
2730 Print a summary identifying the repository state at REV using one or
2736 two parent hash identifiers, followed by a "+" if the working
2731 two parent hash identifiers, followed by a "+" if the working
2737 directory has uncommitted changes, the branch name (if not default),
2732 directory has uncommitted changes, the branch name (if not default),
2738 a list of tags, and a list of bookmarks.
2733 a list of tags, and a list of bookmarks.
2739
2734
2740 When REV is not given, print a summary of the current state of the
2735 When REV is not given, print a summary of the current state of the
2741 repository including the working directory. Specify -r. to get information
2736 repository including the working directory. Specify -r. to get information
2742 of the working directory parent without scanning uncommitted changes.
2737 of the working directory parent without scanning uncommitted changes.
2743
2738
2744 Specifying a path to a repository root or Mercurial bundle will
2739 Specifying a path to a repository root or Mercurial bundle will
2745 cause lookup to operate on that repository/bundle.
2740 cause lookup to operate on that repository/bundle.
2746
2741
2747 .. container:: verbose
2742 .. container:: verbose
2748
2743
2749 Examples:
2744 Examples:
2750
2745
2751 - generate a build identifier for the working directory::
2746 - generate a build identifier for the working directory::
2752
2747
2753 hg id --id > build-id.dat
2748 hg id --id > build-id.dat
2754
2749
2755 - find the revision corresponding to a tag::
2750 - find the revision corresponding to a tag::
2756
2751
2757 hg id -n -r 1.3
2752 hg id -n -r 1.3
2758
2753
2759 - check the most recent revision of a remote repository::
2754 - check the most recent revision of a remote repository::
2760
2755
2761 hg id -r tip https://www.mercurial-scm.org/repo/hg/
2756 hg id -r tip https://www.mercurial-scm.org/repo/hg/
2762
2757
2763 See :hg:`log` for generating more information about specific revisions,
2758 See :hg:`log` for generating more information about specific revisions,
2764 including full hash identifiers.
2759 including full hash identifiers.
2765
2760
2766 Returns 0 if successful.
2761 Returns 0 if successful.
2767 """
2762 """
2768
2763
2769 opts = pycompat.byteskwargs(opts)
2764 opts = pycompat.byteskwargs(opts)
2770 if not repo and not source:
2765 if not repo and not source:
2771 raise error.Abort(_("there is no Mercurial repository here "
2766 raise error.Abort(_("there is no Mercurial repository here "
2772 "(.hg not found)"))
2767 "(.hg not found)"))
2773
2768
2774 if ui.debugflag:
2769 if ui.debugflag:
2775 hexfunc = hex
2770 hexfunc = hex
2776 else:
2771 else:
2777 hexfunc = short
2772 hexfunc = short
2778 default = not (num or id or branch or tags or bookmarks)
2773 default = not (num or id or branch or tags or bookmarks)
2779 output = []
2774 output = []
2780 revs = []
2775 revs = []
2781
2776
2782 if source:
2777 if source:
2783 source, branches = hg.parseurl(ui.expandpath(source))
2778 source, branches = hg.parseurl(ui.expandpath(source))
2784 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
2779 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
2785 repo = peer.local()
2780 repo = peer.local()
2786 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
2781 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
2787
2782
2788 fm = ui.formatter('identify', opts)
2783 fm = ui.formatter('identify', opts)
2789 fm.startitem()
2784 fm.startitem()
2790
2785
2791 if not repo:
2786 if not repo:
2792 if num or branch or tags:
2787 if num or branch or tags:
2793 raise error.Abort(
2788 raise error.Abort(
2794 _("can't query remote revision number, branch, or tags"))
2789 _("can't query remote revision number, branch, or tags"))
2795 if not rev and revs:
2790 if not rev and revs:
2796 rev = revs[0]
2791 rev = revs[0]
2797 if not rev:
2792 if not rev:
2798 rev = "tip"
2793 rev = "tip"
2799
2794
2800 remoterev = peer.lookup(rev)
2795 remoterev = peer.lookup(rev)
2801 hexrev = hexfunc(remoterev)
2796 hexrev = hexfunc(remoterev)
2802 if default or id:
2797 if default or id:
2803 output = [hexrev]
2798 output = [hexrev]
2804 fm.data(id=hexrev)
2799 fm.data(id=hexrev)
2805
2800
2806 def getbms():
2801 def getbms():
2807 bms = []
2802 bms = []
2808
2803
2809 if 'bookmarks' in peer.listkeys('namespaces'):
2804 if 'bookmarks' in peer.listkeys('namespaces'):
2810 hexremoterev = hex(remoterev)
2805 hexremoterev = hex(remoterev)
2811 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
2806 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
2812 if bmr == hexremoterev]
2807 if bmr == hexremoterev]
2813
2808
2814 return sorted(bms)
2809 return sorted(bms)
2815
2810
2816 bms = getbms()
2811 bms = getbms()
2817 if bookmarks:
2812 if bookmarks:
2818 output.extend(bms)
2813 output.extend(bms)
2819 elif default and not ui.quiet:
2814 elif default and not ui.quiet:
2820 # multiple bookmarks for a single parent separated by '/'
2815 # multiple bookmarks for a single parent separated by '/'
2821 bm = '/'.join(bms)
2816 bm = '/'.join(bms)
2822 if bm:
2817 if bm:
2823 output.append(bm)
2818 output.append(bm)
2824
2819
2825 fm.data(node=hex(remoterev))
2820 fm.data(node=hex(remoterev))
2826 fm.data(bookmarks=fm.formatlist(bms, name='bookmark'))
2821 fm.data(bookmarks=fm.formatlist(bms, name='bookmark'))
2827 else:
2822 else:
2828 if rev:
2823 if rev:
2829 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2824 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2830 ctx = scmutil.revsingle(repo, rev, None)
2825 ctx = scmutil.revsingle(repo, rev, None)
2831
2826
2832 if ctx.rev() is None:
2827 if ctx.rev() is None:
2833 ctx = repo[None]
2828 ctx = repo[None]
2834 parents = ctx.parents()
2829 parents = ctx.parents()
2835 taglist = []
2830 taglist = []
2836 for p in parents:
2831 for p in parents:
2837 taglist.extend(p.tags())
2832 taglist.extend(p.tags())
2838
2833
2839 dirty = ""
2834 dirty = ""
2840 if ctx.dirty(missing=True, merge=False, branch=False):
2835 if ctx.dirty(missing=True, merge=False, branch=False):
2841 dirty = '+'
2836 dirty = '+'
2842 fm.data(dirty=dirty)
2837 fm.data(dirty=dirty)
2843
2838
2844 hexoutput = [hexfunc(p.node()) for p in parents]
2839 hexoutput = [hexfunc(p.node()) for p in parents]
2845 if default or id:
2840 if default or id:
2846 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
2841 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
2847 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
2842 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
2848
2843
2849 if num:
2844 if num:
2850 numoutput = ["%d" % p.rev() for p in parents]
2845 numoutput = ["%d" % p.rev() for p in parents]
2851 output.append("%s%s" % ('+'.join(numoutput), dirty))
2846 output.append("%s%s" % ('+'.join(numoutput), dirty))
2852
2847
2853 fn = fm.nested('parents')
2848 fn = fm.nested('parents')
2854 for p in parents:
2849 for p in parents:
2855 fn.startitem()
2850 fn.startitem()
2856 fn.data(rev=p.rev())
2851 fn.data(rev=p.rev())
2857 fn.data(node=p.hex())
2852 fn.data(node=p.hex())
2858 fn.context(ctx=p)
2853 fn.context(ctx=p)
2859 fn.end()
2854 fn.end()
2860 else:
2855 else:
2861 hexoutput = hexfunc(ctx.node())
2856 hexoutput = hexfunc(ctx.node())
2862 if default or id:
2857 if default or id:
2863 output = [hexoutput]
2858 output = [hexoutput]
2864 fm.data(id=hexoutput)
2859 fm.data(id=hexoutput)
2865
2860
2866 if num:
2861 if num:
2867 output.append(pycompat.bytestr(ctx.rev()))
2862 output.append(pycompat.bytestr(ctx.rev()))
2868 taglist = ctx.tags()
2863 taglist = ctx.tags()
2869
2864
2870 if default and not ui.quiet:
2865 if default and not ui.quiet:
2871 b = ctx.branch()
2866 b = ctx.branch()
2872 if b != 'default':
2867 if b != 'default':
2873 output.append("(%s)" % b)
2868 output.append("(%s)" % b)
2874
2869
2875 # multiple tags for a single parent separated by '/'
2870 # multiple tags for a single parent separated by '/'
2876 t = '/'.join(taglist)
2871 t = '/'.join(taglist)
2877 if t:
2872 if t:
2878 output.append(t)
2873 output.append(t)
2879
2874
2880 # multiple bookmarks for a single parent separated by '/'
2875 # multiple bookmarks for a single parent separated by '/'
2881 bm = '/'.join(ctx.bookmarks())
2876 bm = '/'.join(ctx.bookmarks())
2882 if bm:
2877 if bm:
2883 output.append(bm)
2878 output.append(bm)
2884 else:
2879 else:
2885 if branch:
2880 if branch:
2886 output.append(ctx.branch())
2881 output.append(ctx.branch())
2887
2882
2888 if tags:
2883 if tags:
2889 output.extend(taglist)
2884 output.extend(taglist)
2890
2885
2891 if bookmarks:
2886 if bookmarks:
2892 output.extend(ctx.bookmarks())
2887 output.extend(ctx.bookmarks())
2893
2888
2894 fm.data(node=ctx.hex())
2889 fm.data(node=ctx.hex())
2895 fm.data(branch=ctx.branch())
2890 fm.data(branch=ctx.branch())
2896 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
2891 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
2897 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
2892 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
2898 fm.context(ctx=ctx)
2893 fm.context(ctx=ctx)
2899
2894
2900 fm.plain("%s\n" % ' '.join(output))
2895 fm.plain("%s\n" % ' '.join(output))
2901 fm.end()
2896 fm.end()
2902
2897
2903 @command('import|patch',
2898 @command('import|patch',
2904 [('p', 'strip', 1,
2899 [('p', 'strip', 1,
2905 _('directory strip option for patch. This has the same '
2900 _('directory strip option for patch. This has the same '
2906 'meaning as the corresponding patch option'), _('NUM')),
2901 'meaning as the corresponding patch option'), _('NUM')),
2907 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
2902 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
2908 ('e', 'edit', False, _('invoke editor on commit messages')),
2903 ('e', 'edit', False, _('invoke editor on commit messages')),
2909 ('f', 'force', None,
2904 ('f', 'force', None,
2910 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
2905 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
2911 ('', 'no-commit', None,
2906 ('', 'no-commit', None,
2912 _("don't commit, just update the working directory")),
2907 _("don't commit, just update the working directory")),
2913 ('', 'bypass', None,
2908 ('', 'bypass', None,
2914 _("apply patch without touching the working directory")),
2909 _("apply patch without touching the working directory")),
2915 ('', 'partial', None,
2910 ('', 'partial', None,
2916 _('commit even if some hunks fail')),
2911 _('commit even if some hunks fail')),
2917 ('', 'exact', None,
2912 ('', 'exact', None,
2918 _('abort if patch would apply lossily')),
2913 _('abort if patch would apply lossily')),
2919 ('', 'prefix', '',
2914 ('', 'prefix', '',
2920 _('apply patch to subdirectory'), _('DIR')),
2915 _('apply patch to subdirectory'), _('DIR')),
2921 ('', 'import-branch', None,
2916 ('', 'import-branch', None,
2922 _('use any branch information in patch (implied by --exact)'))] +
2917 _('use any branch information in patch (implied by --exact)'))] +
2923 commitopts + commitopts2 + similarityopts,
2918 commitopts + commitopts2 + similarityopts,
2924 _('[OPTION]... PATCH...'))
2919 _('[OPTION]... PATCH...'))
2925 def import_(ui, repo, patch1=None, *patches, **opts):
2920 def import_(ui, repo, patch1=None, *patches, **opts):
2926 """import an ordered set of patches
2921 """import an ordered set of patches
2927
2922
2928 Import a list of patches and commit them individually (unless
2923 Import a list of patches and commit them individually (unless
2929 --no-commit is specified).
2924 --no-commit is specified).
2930
2925
2931 To read a patch from standard input (stdin), use "-" as the patch
2926 To read a patch from standard input (stdin), use "-" as the patch
2932 name. If a URL is specified, the patch will be downloaded from
2927 name. If a URL is specified, the patch will be downloaded from
2933 there.
2928 there.
2934
2929
2935 Import first applies changes to the working directory (unless
2930 Import first applies changes to the working directory (unless
2936 --bypass is specified), import will abort if there are outstanding
2931 --bypass is specified), import will abort if there are outstanding
2937 changes.
2932 changes.
2938
2933
2939 Use --bypass to apply and commit patches directly to the
2934 Use --bypass to apply and commit patches directly to the
2940 repository, without affecting the working directory. Without
2935 repository, without affecting the working directory. Without
2941 --exact, patches will be applied on top of the working directory
2936 --exact, patches will be applied on top of the working directory
2942 parent revision.
2937 parent revision.
2943
2938
2944 You can import a patch straight from a mail message. Even patches
2939 You can import a patch straight from a mail message. Even patches
2945 as attachments work (to use the body part, it must have type
2940 as attachments work (to use the body part, it must have type
2946 text/plain or text/x-patch). From and Subject headers of email
2941 text/plain or text/x-patch). From and Subject headers of email
2947 message are used as default committer and commit message. All
2942 message are used as default committer and commit message. All
2948 text/plain body parts before first diff are added to the commit
2943 text/plain body parts before first diff are added to the commit
2949 message.
2944 message.
2950
2945
2951 If the imported patch was generated by :hg:`export`, user and
2946 If the imported patch was generated by :hg:`export`, user and
2952 description from patch override values from message headers and
2947 description from patch override values from message headers and
2953 body. Values given on command line with -m/--message and -u/--user
2948 body. Values given on command line with -m/--message and -u/--user
2954 override these.
2949 override these.
2955
2950
2956 If --exact is specified, import will set the working directory to
2951 If --exact is specified, import will set the working directory to
2957 the parent of each patch before applying it, and will abort if the
2952 the parent of each patch before applying it, and will abort if the
2958 resulting changeset has a different ID than the one recorded in
2953 resulting changeset has a different ID than the one recorded in
2959 the patch. This will guard against various ways that portable
2954 the patch. This will guard against various ways that portable
2960 patch formats and mail systems might fail to transfer Mercurial
2955 patch formats and mail systems might fail to transfer Mercurial
2961 data or metadata. See :hg:`bundle` for lossless transmission.
2956 data or metadata. See :hg:`bundle` for lossless transmission.
2962
2957
2963 Use --partial to ensure a changeset will be created from the patch
2958 Use --partial to ensure a changeset will be created from the patch
2964 even if some hunks fail to apply. Hunks that fail to apply will be
2959 even if some hunks fail to apply. Hunks that fail to apply will be
2965 written to a <target-file>.rej file. Conflicts can then be resolved
2960 written to a <target-file>.rej file. Conflicts can then be resolved
2966 by hand before :hg:`commit --amend` is run to update the created
2961 by hand before :hg:`commit --amend` is run to update the created
2967 changeset. This flag exists to let people import patches that
2962 changeset. This flag exists to let people import patches that
2968 partially apply without losing the associated metadata (author,
2963 partially apply without losing the associated metadata (author,
2969 date, description, ...).
2964 date, description, ...).
2970
2965
2971 .. note::
2966 .. note::
2972
2967
2973 When no hunks apply cleanly, :hg:`import --partial` will create
2968 When no hunks apply cleanly, :hg:`import --partial` will create
2974 an empty changeset, importing only the patch metadata.
2969 an empty changeset, importing only the patch metadata.
2975
2970
2976 With -s/--similarity, hg will attempt to discover renames and
2971 With -s/--similarity, hg will attempt to discover renames and
2977 copies in the patch in the same way as :hg:`addremove`.
2972 copies in the patch in the same way as :hg:`addremove`.
2978
2973
2979 It is possible to use external patch programs to perform the patch
2974 It is possible to use external patch programs to perform the patch
2980 by setting the ``ui.patch`` configuration option. For the default
2975 by setting the ``ui.patch`` configuration option. For the default
2981 internal tool, the fuzz can also be configured via ``patch.fuzz``.
2976 internal tool, the fuzz can also be configured via ``patch.fuzz``.
2982 See :hg:`help config` for more information about configuration
2977 See :hg:`help config` for more information about configuration
2983 files and how to use these options.
2978 files and how to use these options.
2984
2979
2985 See :hg:`help dates` for a list of formats valid for -d/--date.
2980 See :hg:`help dates` for a list of formats valid for -d/--date.
2986
2981
2987 .. container:: verbose
2982 .. container:: verbose
2988
2983
2989 Examples:
2984 Examples:
2990
2985
2991 - import a traditional patch from a website and detect renames::
2986 - import a traditional patch from a website and detect renames::
2992
2987
2993 hg import -s 80 http://example.com/bugfix.patch
2988 hg import -s 80 http://example.com/bugfix.patch
2994
2989
2995 - import a changeset from an hgweb server::
2990 - import a changeset from an hgweb server::
2996
2991
2997 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
2992 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
2998
2993
2999 - import all the patches in an Unix-style mbox::
2994 - import all the patches in an Unix-style mbox::
3000
2995
3001 hg import incoming-patches.mbox
2996 hg import incoming-patches.mbox
3002
2997
3003 - import patches from stdin::
2998 - import patches from stdin::
3004
2999
3005 hg import -
3000 hg import -
3006
3001
3007 - attempt to exactly restore an exported changeset (not always
3002 - attempt to exactly restore an exported changeset (not always
3008 possible)::
3003 possible)::
3009
3004
3010 hg import --exact proposed-fix.patch
3005 hg import --exact proposed-fix.patch
3011
3006
3012 - use an external tool to apply a patch which is too fuzzy for
3007 - use an external tool to apply a patch which is too fuzzy for
3013 the default internal tool.
3008 the default internal tool.
3014
3009
3015 hg import --config ui.patch="patch --merge" fuzzy.patch
3010 hg import --config ui.patch="patch --merge" fuzzy.patch
3016
3011
3017 - change the default fuzzing from 2 to a less strict 7
3012 - change the default fuzzing from 2 to a less strict 7
3018
3013
3019 hg import --config ui.fuzz=7 fuzz.patch
3014 hg import --config ui.fuzz=7 fuzz.patch
3020
3015
3021 Returns 0 on success, 1 on partial success (see --partial).
3016 Returns 0 on success, 1 on partial success (see --partial).
3022 """
3017 """
3023
3018
3024 opts = pycompat.byteskwargs(opts)
3019 opts = pycompat.byteskwargs(opts)
3025 if not patch1:
3020 if not patch1:
3026 raise error.Abort(_('need at least one patch to import'))
3021 raise error.Abort(_('need at least one patch to import'))
3027
3022
3028 patches = (patch1,) + patches
3023 patches = (patch1,) + patches
3029
3024
3030 date = opts.get('date')
3025 date = opts.get('date')
3031 if date:
3026 if date:
3032 opts['date'] = dateutil.parsedate(date)
3027 opts['date'] = dateutil.parsedate(date)
3033
3028
3034 exact = opts.get('exact')
3029 exact = opts.get('exact')
3035 update = not opts.get('bypass')
3030 update = not opts.get('bypass')
3036 if not update and opts.get('no_commit'):
3031 if not update and opts.get('no_commit'):
3037 raise error.Abort(_('cannot use --no-commit with --bypass'))
3032 raise error.Abort(_('cannot use --no-commit with --bypass'))
3038 try:
3033 try:
3039 sim = float(opts.get('similarity') or 0)
3034 sim = float(opts.get('similarity') or 0)
3040 except ValueError:
3035 except ValueError:
3041 raise error.Abort(_('similarity must be a number'))
3036 raise error.Abort(_('similarity must be a number'))
3042 if sim < 0 or sim > 100:
3037 if sim < 0 or sim > 100:
3043 raise error.Abort(_('similarity must be between 0 and 100'))
3038 raise error.Abort(_('similarity must be between 0 and 100'))
3044 if sim and not update:
3039 if sim and not update:
3045 raise error.Abort(_('cannot use --similarity with --bypass'))
3040 raise error.Abort(_('cannot use --similarity with --bypass'))
3046 if exact:
3041 if exact:
3047 if opts.get('edit'):
3042 if opts.get('edit'):
3048 raise error.Abort(_('cannot use --exact with --edit'))
3043 raise error.Abort(_('cannot use --exact with --edit'))
3049 if opts.get('prefix'):
3044 if opts.get('prefix'):
3050 raise error.Abort(_('cannot use --exact with --prefix'))
3045 raise error.Abort(_('cannot use --exact with --prefix'))
3051
3046
3052 base = opts["base"]
3047 base = opts["base"]
3053 wlock = dsguard = lock = tr = None
3048 wlock = dsguard = lock = tr = None
3054 msgs = []
3049 msgs = []
3055 ret = 0
3050 ret = 0
3056
3051
3057
3052
3058 try:
3053 try:
3059 wlock = repo.wlock()
3054 wlock = repo.wlock()
3060
3055
3061 if update:
3056 if update:
3062 cmdutil.checkunfinished(repo)
3057 cmdutil.checkunfinished(repo)
3063 if (exact or not opts.get('force')):
3058 if (exact or not opts.get('force')):
3064 cmdutil.bailifchanged(repo)
3059 cmdutil.bailifchanged(repo)
3065
3060
3066 if not opts.get('no_commit'):
3061 if not opts.get('no_commit'):
3067 lock = repo.lock()
3062 lock = repo.lock()
3068 tr = repo.transaction('import')
3063 tr = repo.transaction('import')
3069 else:
3064 else:
3070 dsguard = dirstateguard.dirstateguard(repo, 'import')
3065 dsguard = dirstateguard.dirstateguard(repo, 'import')
3071 parents = repo[None].parents()
3066 parents = repo[None].parents()
3072 for patchurl in patches:
3067 for patchurl in patches:
3073 if patchurl == '-':
3068 if patchurl == '-':
3074 ui.status(_('applying patch from stdin\n'))
3069 ui.status(_('applying patch from stdin\n'))
3075 patchfile = ui.fin
3070 patchfile = ui.fin
3076 patchurl = 'stdin' # for error message
3071 patchurl = 'stdin' # for error message
3077 else:
3072 else:
3078 patchurl = os.path.join(base, patchurl)
3073 patchurl = os.path.join(base, patchurl)
3079 ui.status(_('applying %s\n') % patchurl)
3074 ui.status(_('applying %s\n') % patchurl)
3080 patchfile = hg.openpath(ui, patchurl)
3075 patchfile = hg.openpath(ui, patchurl)
3081
3076
3082 haspatch = False
3077 haspatch = False
3083 for hunk in patch.split(patchfile):
3078 for hunk in patch.split(patchfile):
3084 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
3079 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
3085 parents, opts,
3080 parents, opts,
3086 msgs, hg.clean)
3081 msgs, hg.clean)
3087 if msg:
3082 if msg:
3088 haspatch = True
3083 haspatch = True
3089 ui.note(msg + '\n')
3084 ui.note(msg + '\n')
3090 if update or exact:
3085 if update or exact:
3091 parents = repo[None].parents()
3086 parents = repo[None].parents()
3092 else:
3087 else:
3093 parents = [repo[node]]
3088 parents = [repo[node]]
3094 if rej:
3089 if rej:
3095 ui.write_err(_("patch applied partially\n"))
3090 ui.write_err(_("patch applied partially\n"))
3096 ui.write_err(_("(fix the .rej files and run "
3091 ui.write_err(_("(fix the .rej files and run "
3097 "`hg commit --amend`)\n"))
3092 "`hg commit --amend`)\n"))
3098 ret = 1
3093 ret = 1
3099 break
3094 break
3100
3095
3101 if not haspatch:
3096 if not haspatch:
3102 raise error.Abort(_('%s: no diffs found') % patchurl)
3097 raise error.Abort(_('%s: no diffs found') % patchurl)
3103
3098
3104 if tr:
3099 if tr:
3105 tr.close()
3100 tr.close()
3106 if msgs:
3101 if msgs:
3107 repo.savecommitmessage('\n* * *\n'.join(msgs))
3102 repo.savecommitmessage('\n* * *\n'.join(msgs))
3108 if dsguard:
3103 if dsguard:
3109 dsguard.close()
3104 dsguard.close()
3110 return ret
3105 return ret
3111 finally:
3106 finally:
3112 if tr:
3107 if tr:
3113 tr.release()
3108 tr.release()
3114 release(lock, dsguard, wlock)
3109 release(lock, dsguard, wlock)
3115
3110
3116 @command('incoming|in',
3111 @command('incoming|in',
3117 [('f', 'force', None,
3112 [('f', 'force', None,
3118 _('run even if remote repository is unrelated')),
3113 _('run even if remote repository is unrelated')),
3119 ('n', 'newest-first', None, _('show newest record first')),
3114 ('n', 'newest-first', None, _('show newest record first')),
3120 ('', 'bundle', '',
3115 ('', 'bundle', '',
3121 _('file to store the bundles into'), _('FILE')),
3116 _('file to store the bundles into'), _('FILE')),
3122 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3117 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3123 ('B', 'bookmarks', False, _("compare bookmarks")),
3118 ('B', 'bookmarks', False, _("compare bookmarks")),
3124 ('b', 'branch', [],
3119 ('b', 'branch', [],
3125 _('a specific branch you would like to pull'), _('BRANCH')),
3120 _('a specific branch you would like to pull'), _('BRANCH')),
3126 ] + logopts + remoteopts + subrepoopts,
3121 ] + logopts + remoteopts + subrepoopts,
3127 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3122 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3128 def incoming(ui, repo, source="default", **opts):
3123 def incoming(ui, repo, source="default", **opts):
3129 """show new changesets found in source
3124 """show new changesets found in source
3130
3125
3131 Show new changesets found in the specified path/URL or the default
3126 Show new changesets found in the specified path/URL or the default
3132 pull location. These are the changesets that would have been pulled
3127 pull location. These are the changesets that would have been pulled
3133 by :hg:`pull` at the time you issued this command.
3128 by :hg:`pull` at the time you issued this command.
3134
3129
3135 See pull for valid source format details.
3130 See pull for valid source format details.
3136
3131
3137 .. container:: verbose
3132 .. container:: verbose
3138
3133
3139 With -B/--bookmarks, the result of bookmark comparison between
3134 With -B/--bookmarks, the result of bookmark comparison between
3140 local and remote repositories is displayed. With -v/--verbose,
3135 local and remote repositories is displayed. With -v/--verbose,
3141 status is also displayed for each bookmark like below::
3136 status is also displayed for each bookmark like below::
3142
3137
3143 BM1 01234567890a added
3138 BM1 01234567890a added
3144 BM2 1234567890ab advanced
3139 BM2 1234567890ab advanced
3145 BM3 234567890abc diverged
3140 BM3 234567890abc diverged
3146 BM4 34567890abcd changed
3141 BM4 34567890abcd changed
3147
3142
3148 The action taken locally when pulling depends on the
3143 The action taken locally when pulling depends on the
3149 status of each bookmark:
3144 status of each bookmark:
3150
3145
3151 :``added``: pull will create it
3146 :``added``: pull will create it
3152 :``advanced``: pull will update it
3147 :``advanced``: pull will update it
3153 :``diverged``: pull will create a divergent bookmark
3148 :``diverged``: pull will create a divergent bookmark
3154 :``changed``: result depends on remote changesets
3149 :``changed``: result depends on remote changesets
3155
3150
3156 From the point of view of pulling behavior, bookmark
3151 From the point of view of pulling behavior, bookmark
3157 existing only in the remote repository are treated as ``added``,
3152 existing only in the remote repository are treated as ``added``,
3158 even if it is in fact locally deleted.
3153 even if it is in fact locally deleted.
3159
3154
3160 .. container:: verbose
3155 .. container:: verbose
3161
3156
3162 For remote repository, using --bundle avoids downloading the
3157 For remote repository, using --bundle avoids downloading the
3163 changesets twice if the incoming is followed by a pull.
3158 changesets twice if the incoming is followed by a pull.
3164
3159
3165 Examples:
3160 Examples:
3166
3161
3167 - show incoming changes with patches and full description::
3162 - show incoming changes with patches and full description::
3168
3163
3169 hg incoming -vp
3164 hg incoming -vp
3170
3165
3171 - show incoming changes excluding merges, store a bundle::
3166 - show incoming changes excluding merges, store a bundle::
3172
3167
3173 hg in -vpM --bundle incoming.hg
3168 hg in -vpM --bundle incoming.hg
3174 hg pull incoming.hg
3169 hg pull incoming.hg
3175
3170
3176 - briefly list changes inside a bundle::
3171 - briefly list changes inside a bundle::
3177
3172
3178 hg in changes.hg -T "{desc|firstline}\\n"
3173 hg in changes.hg -T "{desc|firstline}\\n"
3179
3174
3180 Returns 0 if there are incoming changes, 1 otherwise.
3175 Returns 0 if there are incoming changes, 1 otherwise.
3181 """
3176 """
3182 opts = pycompat.byteskwargs(opts)
3177 opts = pycompat.byteskwargs(opts)
3183 if opts.get('graph'):
3178 if opts.get('graph'):
3184 logcmdutil.checkunsupportedgraphflags([], opts)
3179 logcmdutil.checkunsupportedgraphflags([], opts)
3185 def display(other, chlist, displayer):
3180 def display(other, chlist, displayer):
3186 revdag = logcmdutil.graphrevs(other, chlist, opts)
3181 revdag = logcmdutil.graphrevs(other, chlist, opts)
3187 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3182 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3188 graphmod.asciiedges)
3183 graphmod.asciiedges)
3189
3184
3190 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3185 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3191 return 0
3186 return 0
3192
3187
3193 if opts.get('bundle') and opts.get('subrepos'):
3188 if opts.get('bundle') and opts.get('subrepos'):
3194 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3189 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3195
3190
3196 if opts.get('bookmarks'):
3191 if opts.get('bookmarks'):
3197 source, branches = hg.parseurl(ui.expandpath(source),
3192 source, branches = hg.parseurl(ui.expandpath(source),
3198 opts.get('branch'))
3193 opts.get('branch'))
3199 other = hg.peer(repo, opts, source)
3194 other = hg.peer(repo, opts, source)
3200 if 'bookmarks' not in other.listkeys('namespaces'):
3195 if 'bookmarks' not in other.listkeys('namespaces'):
3201 ui.warn(_("remote doesn't support bookmarks\n"))
3196 ui.warn(_("remote doesn't support bookmarks\n"))
3202 return 0
3197 return 0
3203 ui.pager('incoming')
3198 ui.pager('incoming')
3204 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3199 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3205 return bookmarks.incoming(ui, repo, other)
3200 return bookmarks.incoming(ui, repo, other)
3206
3201
3207 repo._subtoppath = ui.expandpath(source)
3202 repo._subtoppath = ui.expandpath(source)
3208 try:
3203 try:
3209 return hg.incoming(ui, repo, source, opts)
3204 return hg.incoming(ui, repo, source, opts)
3210 finally:
3205 finally:
3211 del repo._subtoppath
3206 del repo._subtoppath
3212
3207
3213
3208
3214 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3209 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3215 norepo=True)
3210 norepo=True)
3216 def init(ui, dest=".", **opts):
3211 def init(ui, dest=".", **opts):
3217 """create a new repository in the given directory
3212 """create a new repository in the given directory
3218
3213
3219 Initialize a new repository in the given directory. If the given
3214 Initialize a new repository in the given directory. If the given
3220 directory does not exist, it will be created.
3215 directory does not exist, it will be created.
3221
3216
3222 If no directory is given, the current directory is used.
3217 If no directory is given, the current directory is used.
3223
3218
3224 It is possible to specify an ``ssh://`` URL as the destination.
3219 It is possible to specify an ``ssh://`` URL as the destination.
3225 See :hg:`help urls` for more information.
3220 See :hg:`help urls` for more information.
3226
3221
3227 Returns 0 on success.
3222 Returns 0 on success.
3228 """
3223 """
3229 opts = pycompat.byteskwargs(opts)
3224 opts = pycompat.byteskwargs(opts)
3230 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3225 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3231
3226
3232 @command('locate',
3227 @command('locate',
3233 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3228 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3234 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3229 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3235 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3230 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3236 ] + walkopts,
3231 ] + walkopts,
3237 _('[OPTION]... [PATTERN]...'))
3232 _('[OPTION]... [PATTERN]...'))
3238 def locate(ui, repo, *pats, **opts):
3233 def locate(ui, repo, *pats, **opts):
3239 """locate files matching specific patterns (DEPRECATED)
3234 """locate files matching specific patterns (DEPRECATED)
3240
3235
3241 Print files under Mercurial control in the working directory whose
3236 Print files under Mercurial control in the working directory whose
3242 names match the given patterns.
3237 names match the given patterns.
3243
3238
3244 By default, this command searches all directories in the working
3239 By default, this command searches all directories in the working
3245 directory. To search just the current directory and its
3240 directory. To search just the current directory and its
3246 subdirectories, use "--include .".
3241 subdirectories, use "--include .".
3247
3242
3248 If no patterns are given to match, this command prints the names
3243 If no patterns are given to match, this command prints the names
3249 of all files under Mercurial control in the working directory.
3244 of all files under Mercurial control in the working directory.
3250
3245
3251 If you want to feed the output of this command into the "xargs"
3246 If you want to feed the output of this command into the "xargs"
3252 command, use the -0 option to both this command and "xargs". This
3247 command, use the -0 option to both this command and "xargs". This
3253 will avoid the problem of "xargs" treating single filenames that
3248 will avoid the problem of "xargs" treating single filenames that
3254 contain whitespace as multiple filenames.
3249 contain whitespace as multiple filenames.
3255
3250
3256 See :hg:`help files` for a more versatile command.
3251 See :hg:`help files` for a more versatile command.
3257
3252
3258 Returns 0 if a match is found, 1 otherwise.
3253 Returns 0 if a match is found, 1 otherwise.
3259 """
3254 """
3260 opts = pycompat.byteskwargs(opts)
3255 opts = pycompat.byteskwargs(opts)
3261 if opts.get('print0'):
3256 if opts.get('print0'):
3262 end = '\0'
3257 end = '\0'
3263 else:
3258 else:
3264 end = '\n'
3259 end = '\n'
3265 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3260 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3266
3261
3267 ret = 1
3262 ret = 1
3268 m = scmutil.match(ctx, pats, opts, default='relglob',
3263 m = scmutil.match(ctx, pats, opts, default='relglob',
3269 badfn=lambda x, y: False)
3264 badfn=lambda x, y: False)
3270
3265
3271 ui.pager('locate')
3266 ui.pager('locate')
3272 for abs in ctx.matches(m):
3267 for abs in ctx.matches(m):
3273 if opts.get('fullpath'):
3268 if opts.get('fullpath'):
3274 ui.write(repo.wjoin(abs), end)
3269 ui.write(repo.wjoin(abs), end)
3275 else:
3270 else:
3276 ui.write(((pats and m.rel(abs)) or abs), end)
3271 ui.write(((pats and m.rel(abs)) or abs), end)
3277 ret = 0
3272 ret = 0
3278
3273
3279 return ret
3274 return ret
3280
3275
3281 @command('^log|history',
3276 @command('^log|history',
3282 [('f', 'follow', None,
3277 [('f', 'follow', None,
3283 _('follow changeset history, or file history across copies and renames')),
3278 _('follow changeset history, or file history across copies and renames')),
3284 ('', 'follow-first', None,
3279 ('', 'follow-first', None,
3285 _('only follow the first parent of merge changesets (DEPRECATED)')),
3280 _('only follow the first parent of merge changesets (DEPRECATED)')),
3286 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3281 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3287 ('C', 'copies', None, _('show copied files')),
3282 ('C', 'copies', None, _('show copied files')),
3288 ('k', 'keyword', [],
3283 ('k', 'keyword', [],
3289 _('do case-insensitive search for a given text'), _('TEXT')),
3284 _('do case-insensitive search for a given text'), _('TEXT')),
3290 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3285 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3291 ('L', 'line-range', [],
3286 ('L', 'line-range', [],
3292 _('follow line range of specified file (EXPERIMENTAL)'),
3287 _('follow line range of specified file (EXPERIMENTAL)'),
3293 _('FILE,RANGE')),
3288 _('FILE,RANGE')),
3294 ('', 'removed', None, _('include revisions where files were removed')),
3289 ('', 'removed', None, _('include revisions where files were removed')),
3295 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3290 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3296 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3291 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3297 ('', 'only-branch', [],
3292 ('', 'only-branch', [],
3298 _('show only changesets within the given named branch (DEPRECATED)'),
3293 _('show only changesets within the given named branch (DEPRECATED)'),
3299 _('BRANCH')),
3294 _('BRANCH')),
3300 ('b', 'branch', [],
3295 ('b', 'branch', [],
3301 _('show changesets within the given named branch'), _('BRANCH')),
3296 _('show changesets within the given named branch'), _('BRANCH')),
3302 ('P', 'prune', [],
3297 ('P', 'prune', [],
3303 _('do not display revision or any of its ancestors'), _('REV')),
3298 _('do not display revision or any of its ancestors'), _('REV')),
3304 ] + logopts + walkopts,
3299 ] + logopts + walkopts,
3305 _('[OPTION]... [FILE]'),
3300 _('[OPTION]... [FILE]'),
3306 inferrepo=True, cmdtype=readonly)
3301 inferrepo=True, cmdtype=readonly)
3307 def log(ui, repo, *pats, **opts):
3302 def log(ui, repo, *pats, **opts):
3308 """show revision history of entire repository or files
3303 """show revision history of entire repository or files
3309
3304
3310 Print the revision history of the specified files or the entire
3305 Print the revision history of the specified files or the entire
3311 project.
3306 project.
3312
3307
3313 If no revision range is specified, the default is ``tip:0`` unless
3308 If no revision range is specified, the default is ``tip:0`` unless
3314 --follow is set, in which case the working directory parent is
3309 --follow is set, in which case the working directory parent is
3315 used as the starting revision.
3310 used as the starting revision.
3316
3311
3317 File history is shown without following rename or copy history of
3312 File history is shown without following rename or copy history of
3318 files. Use -f/--follow with a filename to follow history across
3313 files. Use -f/--follow with a filename to follow history across
3319 renames and copies. --follow without a filename will only show
3314 renames and copies. --follow without a filename will only show
3320 ancestors of the starting revision.
3315 ancestors of the starting revision.
3321
3316
3322 By default this command prints revision number and changeset id,
3317 By default this command prints revision number and changeset id,
3323 tags, non-trivial parents, user, date and time, and a summary for
3318 tags, non-trivial parents, user, date and time, and a summary for
3324 each commit. When the -v/--verbose switch is used, the list of
3319 each commit. When the -v/--verbose switch is used, the list of
3325 changed files and full commit message are shown.
3320 changed files and full commit message are shown.
3326
3321
3327 With --graph the revisions are shown as an ASCII art DAG with the most
3322 With --graph the revisions are shown as an ASCII art DAG with the most
3328 recent changeset at the top.
3323 recent changeset at the top.
3329 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
3324 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
3330 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
3325 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
3331 changeset from the lines below is a parent of the 'o' merge on the same
3326 changeset from the lines below is a parent of the 'o' merge on the same
3332 line.
3327 line.
3333 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3328 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3334 of a '|' indicates one or more revisions in a path are omitted.
3329 of a '|' indicates one or more revisions in a path are omitted.
3335
3330
3336 .. container:: verbose
3331 .. container:: verbose
3337
3332
3338 Use -L/--line-range FILE,M:N options to follow the history of lines
3333 Use -L/--line-range FILE,M:N options to follow the history of lines
3339 from M to N in FILE. With -p/--patch only diff hunks affecting
3334 from M to N in FILE. With -p/--patch only diff hunks affecting
3340 specified line range will be shown. This option requires --follow;
3335 specified line range will be shown. This option requires --follow;
3341 it can be specified multiple times. Currently, this option is not
3336 it can be specified multiple times. Currently, this option is not
3342 compatible with --graph. This option is experimental.
3337 compatible with --graph. This option is experimental.
3343
3338
3344 .. note::
3339 .. note::
3345
3340
3346 :hg:`log --patch` may generate unexpected diff output for merge
3341 :hg:`log --patch` may generate unexpected diff output for merge
3347 changesets, as it will only compare the merge changeset against
3342 changesets, as it will only compare the merge changeset against
3348 its first parent. Also, only files different from BOTH parents
3343 its first parent. Also, only files different from BOTH parents
3349 will appear in files:.
3344 will appear in files:.
3350
3345
3351 .. note::
3346 .. note::
3352
3347
3353 For performance reasons, :hg:`log FILE` may omit duplicate changes
3348 For performance reasons, :hg:`log FILE` may omit duplicate changes
3354 made on branches and will not show removals or mode changes. To
3349 made on branches and will not show removals or mode changes. To
3355 see all such changes, use the --removed switch.
3350 see all such changes, use the --removed switch.
3356
3351
3357 .. container:: verbose
3352 .. container:: verbose
3358
3353
3359 .. note::
3354 .. note::
3360
3355
3361 The history resulting from -L/--line-range options depends on diff
3356 The history resulting from -L/--line-range options depends on diff
3362 options; for instance if white-spaces are ignored, respective changes
3357 options; for instance if white-spaces are ignored, respective changes
3363 with only white-spaces in specified line range will not be listed.
3358 with only white-spaces in specified line range will not be listed.
3364
3359
3365 .. container:: verbose
3360 .. container:: verbose
3366
3361
3367 Some examples:
3362 Some examples:
3368
3363
3369 - changesets with full descriptions and file lists::
3364 - changesets with full descriptions and file lists::
3370
3365
3371 hg log -v
3366 hg log -v
3372
3367
3373 - changesets ancestral to the working directory::
3368 - changesets ancestral to the working directory::
3374
3369
3375 hg log -f
3370 hg log -f
3376
3371
3377 - last 10 commits on the current branch::
3372 - last 10 commits on the current branch::
3378
3373
3379 hg log -l 10 -b .
3374 hg log -l 10 -b .
3380
3375
3381 - changesets showing all modifications of a file, including removals::
3376 - changesets showing all modifications of a file, including removals::
3382
3377
3383 hg log --removed file.c
3378 hg log --removed file.c
3384
3379
3385 - all changesets that touch a directory, with diffs, excluding merges::
3380 - all changesets that touch a directory, with diffs, excluding merges::
3386
3381
3387 hg log -Mp lib/
3382 hg log -Mp lib/
3388
3383
3389 - all revision numbers that match a keyword::
3384 - all revision numbers that match a keyword::
3390
3385
3391 hg log -k bug --template "{rev}\\n"
3386 hg log -k bug --template "{rev}\\n"
3392
3387
3393 - the full hash identifier of the working directory parent::
3388 - the full hash identifier of the working directory parent::
3394
3389
3395 hg log -r . --template "{node}\\n"
3390 hg log -r . --template "{node}\\n"
3396
3391
3397 - list available log templates::
3392 - list available log templates::
3398
3393
3399 hg log -T list
3394 hg log -T list
3400
3395
3401 - check if a given changeset is included in a tagged release::
3396 - check if a given changeset is included in a tagged release::
3402
3397
3403 hg log -r "a21ccf and ancestor(1.9)"
3398 hg log -r "a21ccf and ancestor(1.9)"
3404
3399
3405 - find all changesets by some user in a date range::
3400 - find all changesets by some user in a date range::
3406
3401
3407 hg log -k alice -d "may 2008 to jul 2008"
3402 hg log -k alice -d "may 2008 to jul 2008"
3408
3403
3409 - summary of all changesets after the last tag::
3404 - summary of all changesets after the last tag::
3410
3405
3411 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3406 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3412
3407
3413 - changesets touching lines 13 to 23 for file.c::
3408 - changesets touching lines 13 to 23 for file.c::
3414
3409
3415 hg log -L file.c,13:23
3410 hg log -L file.c,13:23
3416
3411
3417 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
3412 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
3418 main.c with patch::
3413 main.c with patch::
3419
3414
3420 hg log -L file.c,13:23 -L main.c,2:6 -p
3415 hg log -L file.c,13:23 -L main.c,2:6 -p
3421
3416
3422 See :hg:`help dates` for a list of formats valid for -d/--date.
3417 See :hg:`help dates` for a list of formats valid for -d/--date.
3423
3418
3424 See :hg:`help revisions` for more about specifying and ordering
3419 See :hg:`help revisions` for more about specifying and ordering
3425 revisions.
3420 revisions.
3426
3421
3427 See :hg:`help templates` for more about pre-packaged styles and
3422 See :hg:`help templates` for more about pre-packaged styles and
3428 specifying custom templates. The default template used by the log
3423 specifying custom templates. The default template used by the log
3429 command can be customized via the ``ui.logtemplate`` configuration
3424 command can be customized via the ``ui.logtemplate`` configuration
3430 setting.
3425 setting.
3431
3426
3432 Returns 0 on success.
3427 Returns 0 on success.
3433
3428
3434 """
3429 """
3435 opts = pycompat.byteskwargs(opts)
3430 opts = pycompat.byteskwargs(opts)
3436 linerange = opts.get('line_range')
3431 linerange = opts.get('line_range')
3437
3432
3438 if linerange and not opts.get('follow'):
3433 if linerange and not opts.get('follow'):
3439 raise error.Abort(_('--line-range requires --follow'))
3434 raise error.Abort(_('--line-range requires --follow'))
3440
3435
3441 if linerange and pats:
3436 if linerange and pats:
3442 # TODO: take pats as patterns with no line-range filter
3437 # TODO: take pats as patterns with no line-range filter
3443 raise error.Abort(
3438 raise error.Abort(
3444 _('FILE arguments are not compatible with --line-range option')
3439 _('FILE arguments are not compatible with --line-range option')
3445 )
3440 )
3446
3441
3447 repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
3442 repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
3448 revs, differ = logcmdutil.getrevs(repo, pats, opts)
3443 revs, differ = logcmdutil.getrevs(repo, pats, opts)
3449 if linerange:
3444 if linerange:
3450 # TODO: should follow file history from logcmdutil._initialrevs(),
3445 # TODO: should follow file history from logcmdutil._initialrevs(),
3451 # then filter the result by logcmdutil._makerevset() and --limit
3446 # then filter the result by logcmdutil._makerevset() and --limit
3452 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
3447 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
3453
3448
3454 getrenamed = None
3449 getrenamed = None
3455 if opts.get('copies'):
3450 if opts.get('copies'):
3456 endrev = None
3451 endrev = None
3457 if opts.get('rev'):
3452 if opts.get('rev'):
3458 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
3453 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
3459 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3454 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3460
3455
3461 ui.pager('log')
3456 ui.pager('log')
3462 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, differ,
3457 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, differ,
3463 buffered=True)
3458 buffered=True)
3464 if opts.get('graph'):
3459 if opts.get('graph'):
3465 displayfn = logcmdutil.displaygraphrevs
3460 displayfn = logcmdutil.displaygraphrevs
3466 else:
3461 else:
3467 displayfn = logcmdutil.displayrevs
3462 displayfn = logcmdutil.displayrevs
3468 displayfn(ui, repo, revs, displayer, getrenamed)
3463 displayfn(ui, repo, revs, displayer, getrenamed)
3469
3464
3470 @command('manifest',
3465 @command('manifest',
3471 [('r', 'rev', '', _('revision to display'), _('REV')),
3466 [('r', 'rev', '', _('revision to display'), _('REV')),
3472 ('', 'all', False, _("list files from all revisions"))]
3467 ('', 'all', False, _("list files from all revisions"))]
3473 + formatteropts,
3468 + formatteropts,
3474 _('[-r REV]'), cmdtype=readonly)
3469 _('[-r REV]'), cmdtype=readonly)
3475 def manifest(ui, repo, node=None, rev=None, **opts):
3470 def manifest(ui, repo, node=None, rev=None, **opts):
3476 """output the current or given revision of the project manifest
3471 """output the current or given revision of the project manifest
3477
3472
3478 Print a list of version controlled files for the given revision.
3473 Print a list of version controlled files for the given revision.
3479 If no revision is given, the first parent of the working directory
3474 If no revision is given, the first parent of the working directory
3480 is used, or the null revision if no revision is checked out.
3475 is used, or the null revision if no revision is checked out.
3481
3476
3482 With -v, print file permissions, symlink and executable bits.
3477 With -v, print file permissions, symlink and executable bits.
3483 With --debug, print file revision hashes.
3478 With --debug, print file revision hashes.
3484
3479
3485 If option --all is specified, the list of all files from all revisions
3480 If option --all is specified, the list of all files from all revisions
3486 is printed. This includes deleted and renamed files.
3481 is printed. This includes deleted and renamed files.
3487
3482
3488 Returns 0 on success.
3483 Returns 0 on success.
3489 """
3484 """
3490 opts = pycompat.byteskwargs(opts)
3485 opts = pycompat.byteskwargs(opts)
3491 fm = ui.formatter('manifest', opts)
3486 fm = ui.formatter('manifest', opts)
3492
3487
3493 if opts.get('all'):
3488 if opts.get('all'):
3494 if rev or node:
3489 if rev or node:
3495 raise error.Abort(_("can't specify a revision with --all"))
3490 raise error.Abort(_("can't specify a revision with --all"))
3496
3491
3497 res = []
3492 res = []
3498 prefix = "data/"
3493 prefix = "data/"
3499 suffix = ".i"
3494 suffix = ".i"
3500 plen = len(prefix)
3495 plen = len(prefix)
3501 slen = len(suffix)
3496 slen = len(suffix)
3502 with repo.lock():
3497 with repo.lock():
3503 for fn, b, size in repo.store.datafiles():
3498 for fn, b, size in repo.store.datafiles():
3504 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3499 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3505 res.append(fn[plen:-slen])
3500 res.append(fn[plen:-slen])
3506 ui.pager('manifest')
3501 ui.pager('manifest')
3507 for f in res:
3502 for f in res:
3508 fm.startitem()
3503 fm.startitem()
3509 fm.write("path", '%s\n', f)
3504 fm.write("path", '%s\n', f)
3510 fm.end()
3505 fm.end()
3511 return
3506 return
3512
3507
3513 if rev and node:
3508 if rev and node:
3514 raise error.Abort(_("please specify just one revision"))
3509 raise error.Abort(_("please specify just one revision"))
3515
3510
3516 if not node:
3511 if not node:
3517 node = rev
3512 node = rev
3518
3513
3519 char = {'l': '@', 'x': '*', '': '', 't': 'd'}
3514 char = {'l': '@', 'x': '*', '': '', 't': 'd'}
3520 mode = {'l': '644', 'x': '755', '': '644', 't': '755'}
3515 mode = {'l': '644', 'x': '755', '': '644', 't': '755'}
3521 if node:
3516 if node:
3522 repo = scmutil.unhidehashlikerevs(repo, [node], 'nowarn')
3517 repo = scmutil.unhidehashlikerevs(repo, [node], 'nowarn')
3523 ctx = scmutil.revsingle(repo, node)
3518 ctx = scmutil.revsingle(repo, node)
3524 mf = ctx.manifest()
3519 mf = ctx.manifest()
3525 ui.pager('manifest')
3520 ui.pager('manifest')
3526 for f in ctx:
3521 for f in ctx:
3527 fm.startitem()
3522 fm.startitem()
3528 fl = ctx[f].flags()
3523 fl = ctx[f].flags()
3529 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3524 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3530 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3525 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3531 fm.write('path', '%s\n', f)
3526 fm.write('path', '%s\n', f)
3532 fm.end()
3527 fm.end()
3533
3528
3534 @command('^merge',
3529 @command('^merge',
3535 [('f', 'force', None,
3530 [('f', 'force', None,
3536 _('force a merge including outstanding changes (DEPRECATED)')),
3531 _('force a merge including outstanding changes (DEPRECATED)')),
3537 ('r', 'rev', '', _('revision to merge'), _('REV')),
3532 ('r', 'rev', '', _('revision to merge'), _('REV')),
3538 ('P', 'preview', None,
3533 ('P', 'preview', None,
3539 _('review revisions to merge (no merge is performed)')),
3534 _('review revisions to merge (no merge is performed)')),
3540 ('', 'abort', None, _('abort the ongoing merge')),
3535 ('', 'abort', None, _('abort the ongoing merge')),
3541 ] + mergetoolopts,
3536 ] + mergetoolopts,
3542 _('[-P] [[-r] REV]'))
3537 _('[-P] [[-r] REV]'))
3543 def merge(ui, repo, node=None, **opts):
3538 def merge(ui, repo, node=None, **opts):
3544 """merge another revision into working directory
3539 """merge another revision into working directory
3545
3540
3546 The current working directory is updated with all changes made in
3541 The current working directory is updated with all changes made in
3547 the requested revision since the last common predecessor revision.
3542 the requested revision since the last common predecessor revision.
3548
3543
3549 Files that changed between either parent are marked as changed for
3544 Files that changed between either parent are marked as changed for
3550 the next commit and a commit must be performed before any further
3545 the next commit and a commit must be performed before any further
3551 updates to the repository are allowed. The next commit will have
3546 updates to the repository are allowed. The next commit will have
3552 two parents.
3547 two parents.
3553
3548
3554 ``--tool`` can be used to specify the merge tool used for file
3549 ``--tool`` can be used to specify the merge tool used for file
3555 merges. It overrides the HGMERGE environment variable and your
3550 merges. It overrides the HGMERGE environment variable and your
3556 configuration files. See :hg:`help merge-tools` for options.
3551 configuration files. See :hg:`help merge-tools` for options.
3557
3552
3558 If no revision is specified, the working directory's parent is a
3553 If no revision is specified, the working directory's parent is a
3559 head revision, and the current branch contains exactly one other
3554 head revision, and the current branch contains exactly one other
3560 head, the other head is merged with by default. Otherwise, an
3555 head, the other head is merged with by default. Otherwise, an
3561 explicit revision with which to merge with must be provided.
3556 explicit revision with which to merge with must be provided.
3562
3557
3563 See :hg:`help resolve` for information on handling file conflicts.
3558 See :hg:`help resolve` for information on handling file conflicts.
3564
3559
3565 To undo an uncommitted merge, use :hg:`merge --abort` which
3560 To undo an uncommitted merge, use :hg:`merge --abort` which
3566 will check out a clean copy of the original merge parent, losing
3561 will check out a clean copy of the original merge parent, losing
3567 all changes.
3562 all changes.
3568
3563
3569 Returns 0 on success, 1 if there are unresolved files.
3564 Returns 0 on success, 1 if there are unresolved files.
3570 """
3565 """
3571
3566
3572 opts = pycompat.byteskwargs(opts)
3567 opts = pycompat.byteskwargs(opts)
3573 abort = opts.get('abort')
3568 abort = opts.get('abort')
3574 if abort and repo.dirstate.p2() == nullid:
3569 if abort and repo.dirstate.p2() == nullid:
3575 cmdutil.wrongtooltocontinue(repo, _('merge'))
3570 cmdutil.wrongtooltocontinue(repo, _('merge'))
3576 if abort:
3571 if abort:
3577 if node:
3572 if node:
3578 raise error.Abort(_("cannot specify a node with --abort"))
3573 raise error.Abort(_("cannot specify a node with --abort"))
3579 if opts.get('rev'):
3574 if opts.get('rev'):
3580 raise error.Abort(_("cannot specify both --rev and --abort"))
3575 raise error.Abort(_("cannot specify both --rev and --abort"))
3581 if opts.get('preview'):
3576 if opts.get('preview'):
3582 raise error.Abort(_("cannot specify --preview with --abort"))
3577 raise error.Abort(_("cannot specify --preview with --abort"))
3583 if opts.get('rev') and node:
3578 if opts.get('rev') and node:
3584 raise error.Abort(_("please specify just one revision"))
3579 raise error.Abort(_("please specify just one revision"))
3585 if not node:
3580 if not node:
3586 node = opts.get('rev')
3581 node = opts.get('rev')
3587
3582
3588 if node:
3583 if node:
3589 node = scmutil.revsingle(repo, node).node()
3584 node = scmutil.revsingle(repo, node).node()
3590
3585
3591 if not node and not abort:
3586 if not node and not abort:
3592 node = repo[destutil.destmerge(repo)].node()
3587 node = repo[destutil.destmerge(repo)].node()
3593
3588
3594 if opts.get('preview'):
3589 if opts.get('preview'):
3595 # find nodes that are ancestors of p2 but not of p1
3590 # find nodes that are ancestors of p2 but not of p1
3596 p1 = repo.lookup('.')
3591 p1 = repo.lookup('.')
3597 p2 = repo.lookup(node)
3592 p2 = repo.lookup(node)
3598 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3593 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3599
3594
3600 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3595 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3601 for node in nodes:
3596 for node in nodes:
3602 displayer.show(repo[node])
3597 displayer.show(repo[node])
3603 displayer.close()
3598 displayer.close()
3604 return 0
3599 return 0
3605
3600
3606 try:
3601 try:
3607 # ui.forcemerge is an internal variable, do not document
3602 # ui.forcemerge is an internal variable, do not document
3608 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
3603 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
3609 force = opts.get('force')
3604 force = opts.get('force')
3610 labels = ['working copy', 'merge rev']
3605 labels = ['working copy', 'merge rev']
3611 return hg.merge(repo, node, force=force, mergeforce=force,
3606 return hg.merge(repo, node, force=force, mergeforce=force,
3612 labels=labels, abort=abort)
3607 labels=labels, abort=abort)
3613 finally:
3608 finally:
3614 ui.setconfig('ui', 'forcemerge', '', 'merge')
3609 ui.setconfig('ui', 'forcemerge', '', 'merge')
3615
3610
3616 @command('outgoing|out',
3611 @command('outgoing|out',
3617 [('f', 'force', None, _('run even when the destination is unrelated')),
3612 [('f', 'force', None, _('run even when the destination is unrelated')),
3618 ('r', 'rev', [],
3613 ('r', 'rev', [],
3619 _('a changeset intended to be included in the destination'), _('REV')),
3614 _('a changeset intended to be included in the destination'), _('REV')),
3620 ('n', 'newest-first', None, _('show newest record first')),
3615 ('n', 'newest-first', None, _('show newest record first')),
3621 ('B', 'bookmarks', False, _('compare bookmarks')),
3616 ('B', 'bookmarks', False, _('compare bookmarks')),
3622 ('b', 'branch', [], _('a specific branch you would like to push'),
3617 ('b', 'branch', [], _('a specific branch you would like to push'),
3623 _('BRANCH')),
3618 _('BRANCH')),
3624 ] + logopts + remoteopts + subrepoopts,
3619 ] + logopts + remoteopts + subrepoopts,
3625 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3620 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3626 def outgoing(ui, repo, dest=None, **opts):
3621 def outgoing(ui, repo, dest=None, **opts):
3627 """show changesets not found in the destination
3622 """show changesets not found in the destination
3628
3623
3629 Show changesets not found in the specified destination repository
3624 Show changesets not found in the specified destination repository
3630 or the default push location. These are the changesets that would
3625 or the default push location. These are the changesets that would
3631 be pushed if a push was requested.
3626 be pushed if a push was requested.
3632
3627
3633 See pull for details of valid destination formats.
3628 See pull for details of valid destination formats.
3634
3629
3635 .. container:: verbose
3630 .. container:: verbose
3636
3631
3637 With -B/--bookmarks, the result of bookmark comparison between
3632 With -B/--bookmarks, the result of bookmark comparison between
3638 local and remote repositories is displayed. With -v/--verbose,
3633 local and remote repositories is displayed. With -v/--verbose,
3639 status is also displayed for each bookmark like below::
3634 status is also displayed for each bookmark like below::
3640
3635
3641 BM1 01234567890a added
3636 BM1 01234567890a added
3642 BM2 deleted
3637 BM2 deleted
3643 BM3 234567890abc advanced
3638 BM3 234567890abc advanced
3644 BM4 34567890abcd diverged
3639 BM4 34567890abcd diverged
3645 BM5 4567890abcde changed
3640 BM5 4567890abcde changed
3646
3641
3647 The action taken when pushing depends on the
3642 The action taken when pushing depends on the
3648 status of each bookmark:
3643 status of each bookmark:
3649
3644
3650 :``added``: push with ``-B`` will create it
3645 :``added``: push with ``-B`` will create it
3651 :``deleted``: push with ``-B`` will delete it
3646 :``deleted``: push with ``-B`` will delete it
3652 :``advanced``: push will update it
3647 :``advanced``: push will update it
3653 :``diverged``: push with ``-B`` will update it
3648 :``diverged``: push with ``-B`` will update it
3654 :``changed``: push with ``-B`` will update it
3649 :``changed``: push with ``-B`` will update it
3655
3650
3656 From the point of view of pushing behavior, bookmarks
3651 From the point of view of pushing behavior, bookmarks
3657 existing only in the remote repository are treated as
3652 existing only in the remote repository are treated as
3658 ``deleted``, even if it is in fact added remotely.
3653 ``deleted``, even if it is in fact added remotely.
3659
3654
3660 Returns 0 if there are outgoing changes, 1 otherwise.
3655 Returns 0 if there are outgoing changes, 1 otherwise.
3661 """
3656 """
3662 opts = pycompat.byteskwargs(opts)
3657 opts = pycompat.byteskwargs(opts)
3663 if opts.get('graph'):
3658 if opts.get('graph'):
3664 logcmdutil.checkunsupportedgraphflags([], opts)
3659 logcmdutil.checkunsupportedgraphflags([], opts)
3665 o, other = hg._outgoing(ui, repo, dest, opts)
3660 o, other = hg._outgoing(ui, repo, dest, opts)
3666 if not o:
3661 if not o:
3667 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3662 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3668 return
3663 return
3669
3664
3670 revdag = logcmdutil.graphrevs(repo, o, opts)
3665 revdag = logcmdutil.graphrevs(repo, o, opts)
3671 ui.pager('outgoing')
3666 ui.pager('outgoing')
3672 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
3667 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
3673 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3668 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3674 graphmod.asciiedges)
3669 graphmod.asciiedges)
3675 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3670 cmdutil.outgoinghooks(ui, repo, other, opts, o)
3676 return 0
3671 return 0
3677
3672
3678 if opts.get('bookmarks'):
3673 if opts.get('bookmarks'):
3679 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3674 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3680 dest, branches = hg.parseurl(dest, opts.get('branch'))
3675 dest, branches = hg.parseurl(dest, opts.get('branch'))
3681 other = hg.peer(repo, opts, dest)
3676 other = hg.peer(repo, opts, dest)
3682 if 'bookmarks' not in other.listkeys('namespaces'):
3677 if 'bookmarks' not in other.listkeys('namespaces'):
3683 ui.warn(_("remote doesn't support bookmarks\n"))
3678 ui.warn(_("remote doesn't support bookmarks\n"))
3684 return 0
3679 return 0
3685 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3680 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3686 ui.pager('outgoing')
3681 ui.pager('outgoing')
3687 return bookmarks.outgoing(ui, repo, other)
3682 return bookmarks.outgoing(ui, repo, other)
3688
3683
3689 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3684 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3690 try:
3685 try:
3691 return hg.outgoing(ui, repo, dest, opts)
3686 return hg.outgoing(ui, repo, dest, opts)
3692 finally:
3687 finally:
3693 del repo._subtoppath
3688 del repo._subtoppath
3694
3689
3695 @command('parents',
3690 @command('parents',
3696 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3691 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3697 ] + templateopts,
3692 ] + templateopts,
3698 _('[-r REV] [FILE]'),
3693 _('[-r REV] [FILE]'),
3699 inferrepo=True)
3694 inferrepo=True)
3700 def parents(ui, repo, file_=None, **opts):
3695 def parents(ui, repo, file_=None, **opts):
3701 """show the parents of the working directory or revision (DEPRECATED)
3696 """show the parents of the working directory or revision (DEPRECATED)
3702
3697
3703 Print the working directory's parent revisions. If a revision is
3698 Print the working directory's parent revisions. If a revision is
3704 given via -r/--rev, the parent of that revision will be printed.
3699 given via -r/--rev, the parent of that revision will be printed.
3705 If a file argument is given, the revision in which the file was
3700 If a file argument is given, the revision in which the file was
3706 last changed (before the working directory revision or the
3701 last changed (before the working directory revision or the
3707 argument to --rev if given) is printed.
3702 argument to --rev if given) is printed.
3708
3703
3709 This command is equivalent to::
3704 This command is equivalent to::
3710
3705
3711 hg log -r "p1()+p2()" or
3706 hg log -r "p1()+p2()" or
3712 hg log -r "p1(REV)+p2(REV)" or
3707 hg log -r "p1(REV)+p2(REV)" or
3713 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
3708 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
3714 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
3709 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
3715
3710
3716 See :hg:`summary` and :hg:`help revsets` for related information.
3711 See :hg:`summary` and :hg:`help revsets` for related information.
3717
3712
3718 Returns 0 on success.
3713 Returns 0 on success.
3719 """
3714 """
3720
3715
3721 opts = pycompat.byteskwargs(opts)
3716 opts = pycompat.byteskwargs(opts)
3722 rev = opts.get('rev')
3717 rev = opts.get('rev')
3723 if rev:
3718 if rev:
3724 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3719 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3725 ctx = scmutil.revsingle(repo, rev, None)
3720 ctx = scmutil.revsingle(repo, rev, None)
3726
3721
3727 if file_:
3722 if file_:
3728 m = scmutil.match(ctx, (file_,), opts)
3723 m = scmutil.match(ctx, (file_,), opts)
3729 if m.anypats() or len(m.files()) != 1:
3724 if m.anypats() or len(m.files()) != 1:
3730 raise error.Abort(_('can only specify an explicit filename'))
3725 raise error.Abort(_('can only specify an explicit filename'))
3731 file_ = m.files()[0]
3726 file_ = m.files()[0]
3732 filenodes = []
3727 filenodes = []
3733 for cp in ctx.parents():
3728 for cp in ctx.parents():
3734 if not cp:
3729 if not cp:
3735 continue
3730 continue
3736 try:
3731 try:
3737 filenodes.append(cp.filenode(file_))
3732 filenodes.append(cp.filenode(file_))
3738 except error.LookupError:
3733 except error.LookupError:
3739 pass
3734 pass
3740 if not filenodes:
3735 if not filenodes:
3741 raise error.Abort(_("'%s' not found in manifest!") % file_)
3736 raise error.Abort(_("'%s' not found in manifest!") % file_)
3742 p = []
3737 p = []
3743 for fn in filenodes:
3738 for fn in filenodes:
3744 fctx = repo.filectx(file_, fileid=fn)
3739 fctx = repo.filectx(file_, fileid=fn)
3745 p.append(fctx.node())
3740 p.append(fctx.node())
3746 else:
3741 else:
3747 p = [cp.node() for cp in ctx.parents()]
3742 p = [cp.node() for cp in ctx.parents()]
3748
3743
3749 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3744 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3750 for n in p:
3745 for n in p:
3751 if n != nullid:
3746 if n != nullid:
3752 displayer.show(repo[n])
3747 displayer.show(repo[n])
3753 displayer.close()
3748 displayer.close()
3754
3749
3755 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True,
3750 @command('paths', formatteropts, _('[NAME]'), optionalrepo=True,
3756 cmdtype=readonly)
3751 cmdtype=readonly)
3757 def paths(ui, repo, search=None, **opts):
3752 def paths(ui, repo, search=None, **opts):
3758 """show aliases for remote repositories
3753 """show aliases for remote repositories
3759
3754
3760 Show definition of symbolic path name NAME. If no name is given,
3755 Show definition of symbolic path name NAME. If no name is given,
3761 show definition of all available names.
3756 show definition of all available names.
3762
3757
3763 Option -q/--quiet suppresses all output when searching for NAME
3758 Option -q/--quiet suppresses all output when searching for NAME
3764 and shows only the path names when listing all definitions.
3759 and shows only the path names when listing all definitions.
3765
3760
3766 Path names are defined in the [paths] section of your
3761 Path names are defined in the [paths] section of your
3767 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3762 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3768 repository, ``.hg/hgrc`` is used, too.
3763 repository, ``.hg/hgrc`` is used, too.
3769
3764
3770 The path names ``default`` and ``default-push`` have a special
3765 The path names ``default`` and ``default-push`` have a special
3771 meaning. When performing a push or pull operation, they are used
3766 meaning. When performing a push or pull operation, they are used
3772 as fallbacks if no location is specified on the command-line.
3767 as fallbacks if no location is specified on the command-line.
3773 When ``default-push`` is set, it will be used for push and
3768 When ``default-push`` is set, it will be used for push and
3774 ``default`` will be used for pull; otherwise ``default`` is used
3769 ``default`` will be used for pull; otherwise ``default`` is used
3775 as the fallback for both. When cloning a repository, the clone
3770 as the fallback for both. When cloning a repository, the clone
3776 source is written as ``default`` in ``.hg/hgrc``.
3771 source is written as ``default`` in ``.hg/hgrc``.
3777
3772
3778 .. note::
3773 .. note::
3779
3774
3780 ``default`` and ``default-push`` apply to all inbound (e.g.
3775 ``default`` and ``default-push`` apply to all inbound (e.g.
3781 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
3776 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
3782 and :hg:`bundle`) operations.
3777 and :hg:`bundle`) operations.
3783
3778
3784 See :hg:`help urls` for more information.
3779 See :hg:`help urls` for more information.
3785
3780
3786 Returns 0 on success.
3781 Returns 0 on success.
3787 """
3782 """
3788
3783
3789 opts = pycompat.byteskwargs(opts)
3784 opts = pycompat.byteskwargs(opts)
3790 ui.pager('paths')
3785 ui.pager('paths')
3791 if search:
3786 if search:
3792 pathitems = [(name, path) for name, path in ui.paths.iteritems()
3787 pathitems = [(name, path) for name, path in ui.paths.iteritems()
3793 if name == search]
3788 if name == search]
3794 else:
3789 else:
3795 pathitems = sorted(ui.paths.iteritems())
3790 pathitems = sorted(ui.paths.iteritems())
3796
3791
3797 fm = ui.formatter('paths', opts)
3792 fm = ui.formatter('paths', opts)
3798 if fm.isplain():
3793 if fm.isplain():
3799 hidepassword = util.hidepassword
3794 hidepassword = util.hidepassword
3800 else:
3795 else:
3801 hidepassword = bytes
3796 hidepassword = bytes
3802 if ui.quiet:
3797 if ui.quiet:
3803 namefmt = '%s\n'
3798 namefmt = '%s\n'
3804 else:
3799 else:
3805 namefmt = '%s = '
3800 namefmt = '%s = '
3806 showsubopts = not search and not ui.quiet
3801 showsubopts = not search and not ui.quiet
3807
3802
3808 for name, path in pathitems:
3803 for name, path in pathitems:
3809 fm.startitem()
3804 fm.startitem()
3810 fm.condwrite(not search, 'name', namefmt, name)
3805 fm.condwrite(not search, 'name', namefmt, name)
3811 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
3806 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
3812 for subopt, value in sorted(path.suboptions.items()):
3807 for subopt, value in sorted(path.suboptions.items()):
3813 assert subopt not in ('name', 'url')
3808 assert subopt not in ('name', 'url')
3814 if showsubopts:
3809 if showsubopts:
3815 fm.plain('%s:%s = ' % (name, subopt))
3810 fm.plain('%s:%s = ' % (name, subopt))
3816 fm.condwrite(showsubopts, subopt, '%s\n', value)
3811 fm.condwrite(showsubopts, subopt, '%s\n', value)
3817
3812
3818 fm.end()
3813 fm.end()
3819
3814
3820 if search and not pathitems:
3815 if search and not pathitems:
3821 if not ui.quiet:
3816 if not ui.quiet:
3822 ui.warn(_("not found!\n"))
3817 ui.warn(_("not found!\n"))
3823 return 1
3818 return 1
3824 else:
3819 else:
3825 return 0
3820 return 0
3826
3821
3827 @command('phase',
3822 @command('phase',
3828 [('p', 'public', False, _('set changeset phase to public')),
3823 [('p', 'public', False, _('set changeset phase to public')),
3829 ('d', 'draft', False, _('set changeset phase to draft')),
3824 ('d', 'draft', False, _('set changeset phase to draft')),
3830 ('s', 'secret', False, _('set changeset phase to secret')),
3825 ('s', 'secret', False, _('set changeset phase to secret')),
3831 ('f', 'force', False, _('allow to move boundary backward')),
3826 ('f', 'force', False, _('allow to move boundary backward')),
3832 ('r', 'rev', [], _('target revision'), _('REV')),
3827 ('r', 'rev', [], _('target revision'), _('REV')),
3833 ],
3828 ],
3834 _('[-p|-d|-s] [-f] [-r] [REV...]'))
3829 _('[-p|-d|-s] [-f] [-r] [REV...]'))
3835 def phase(ui, repo, *revs, **opts):
3830 def phase(ui, repo, *revs, **opts):
3836 """set or show the current phase name
3831 """set or show the current phase name
3837
3832
3838 With no argument, show the phase name of the current revision(s).
3833 With no argument, show the phase name of the current revision(s).
3839
3834
3840 With one of -p/--public, -d/--draft or -s/--secret, change the
3835 With one of -p/--public, -d/--draft or -s/--secret, change the
3841 phase value of the specified revisions.
3836 phase value of the specified revisions.
3842
3837
3843 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
3838 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
3844 lower phase to a higher phase. Phases are ordered as follows::
3839 lower phase to a higher phase. Phases are ordered as follows::
3845
3840
3846 public < draft < secret
3841 public < draft < secret
3847
3842
3848 Returns 0 on success, 1 if some phases could not be changed.
3843 Returns 0 on success, 1 if some phases could not be changed.
3849
3844
3850 (For more information about the phases concept, see :hg:`help phases`.)
3845 (For more information about the phases concept, see :hg:`help phases`.)
3851 """
3846 """
3852 opts = pycompat.byteskwargs(opts)
3847 opts = pycompat.byteskwargs(opts)
3853 # search for a unique phase argument
3848 # search for a unique phase argument
3854 targetphase = None
3849 targetphase = None
3855 for idx, name in enumerate(phases.phasenames):
3850 for idx, name in enumerate(phases.phasenames):
3856 if opts[name]:
3851 if opts[name]:
3857 if targetphase is not None:
3852 if targetphase is not None:
3858 raise error.Abort(_('only one phase can be specified'))
3853 raise error.Abort(_('only one phase can be specified'))
3859 targetphase = idx
3854 targetphase = idx
3860
3855
3861 # look for specified revision
3856 # look for specified revision
3862 revs = list(revs)
3857 revs = list(revs)
3863 revs.extend(opts['rev'])
3858 revs.extend(opts['rev'])
3864 if not revs:
3859 if not revs:
3865 # display both parents as the second parent phase can influence
3860 # display both parents as the second parent phase can influence
3866 # the phase of a merge commit
3861 # the phase of a merge commit
3867 revs = [c.rev() for c in repo[None].parents()]
3862 revs = [c.rev() for c in repo[None].parents()]
3868
3863
3869 revs = scmutil.revrange(repo, revs)
3864 revs = scmutil.revrange(repo, revs)
3870
3865
3871 ret = 0
3866 ret = 0
3872 if targetphase is None:
3867 if targetphase is None:
3873 # display
3868 # display
3874 for r in revs:
3869 for r in revs:
3875 ctx = repo[r]
3870 ctx = repo[r]
3876 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
3871 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
3877 else:
3872 else:
3878 with repo.lock(), repo.transaction("phase") as tr:
3873 with repo.lock(), repo.transaction("phase") as tr:
3879 # set phase
3874 # set phase
3880 if not revs:
3875 if not revs:
3881 raise error.Abort(_('empty revision set'))
3876 raise error.Abort(_('empty revision set'))
3882 nodes = [repo[r].node() for r in revs]
3877 nodes = [repo[r].node() for r in revs]
3883 # moving revision from public to draft may hide them
3878 # moving revision from public to draft may hide them
3884 # We have to check result on an unfiltered repository
3879 # We have to check result on an unfiltered repository
3885 unfi = repo.unfiltered()
3880 unfi = repo.unfiltered()
3886 getphase = unfi._phasecache.phase
3881 getphase = unfi._phasecache.phase
3887 olddata = [getphase(unfi, r) for r in unfi]
3882 olddata = [getphase(unfi, r) for r in unfi]
3888 phases.advanceboundary(repo, tr, targetphase, nodes)
3883 phases.advanceboundary(repo, tr, targetphase, nodes)
3889 if opts['force']:
3884 if opts['force']:
3890 phases.retractboundary(repo, tr, targetphase, nodes)
3885 phases.retractboundary(repo, tr, targetphase, nodes)
3891 getphase = unfi._phasecache.phase
3886 getphase = unfi._phasecache.phase
3892 newdata = [getphase(unfi, r) for r in unfi]
3887 newdata = [getphase(unfi, r) for r in unfi]
3893 changes = sum(newdata[r] != olddata[r] for r in unfi)
3888 changes = sum(newdata[r] != olddata[r] for r in unfi)
3894 cl = unfi.changelog
3889 cl = unfi.changelog
3895 rejected = [n for n in nodes
3890 rejected = [n for n in nodes
3896 if newdata[cl.rev(n)] < targetphase]
3891 if newdata[cl.rev(n)] < targetphase]
3897 if rejected:
3892 if rejected:
3898 ui.warn(_('cannot move %i changesets to a higher '
3893 ui.warn(_('cannot move %i changesets to a higher '
3899 'phase, use --force\n') % len(rejected))
3894 'phase, use --force\n') % len(rejected))
3900 ret = 1
3895 ret = 1
3901 if changes:
3896 if changes:
3902 msg = _('phase changed for %i changesets\n') % changes
3897 msg = _('phase changed for %i changesets\n') % changes
3903 if ret:
3898 if ret:
3904 ui.status(msg)
3899 ui.status(msg)
3905 else:
3900 else:
3906 ui.note(msg)
3901 ui.note(msg)
3907 else:
3902 else:
3908 ui.warn(_('no phases changed\n'))
3903 ui.warn(_('no phases changed\n'))
3909 return ret
3904 return ret
3910
3905
3911 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
3906 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
3912 """Run after a changegroup has been added via pull/unbundle
3907 """Run after a changegroup has been added via pull/unbundle
3913
3908
3914 This takes arguments below:
3909 This takes arguments below:
3915
3910
3916 :modheads: change of heads by pull/unbundle
3911 :modheads: change of heads by pull/unbundle
3917 :optupdate: updating working directory is needed or not
3912 :optupdate: updating working directory is needed or not
3918 :checkout: update destination revision (or None to default destination)
3913 :checkout: update destination revision (or None to default destination)
3919 :brev: a name, which might be a bookmark to be activated after updating
3914 :brev: a name, which might be a bookmark to be activated after updating
3920 """
3915 """
3921 if modheads == 0:
3916 if modheads == 0:
3922 return
3917 return
3923 if optupdate:
3918 if optupdate:
3924 try:
3919 try:
3925 return hg.updatetotally(ui, repo, checkout, brev)
3920 return hg.updatetotally(ui, repo, checkout, brev)
3926 except error.UpdateAbort as inst:
3921 except error.UpdateAbort as inst:
3927 msg = _("not updating: %s") % stringutil.forcebytestr(inst)
3922 msg = _("not updating: %s") % stringutil.forcebytestr(inst)
3928 hint = inst.hint
3923 hint = inst.hint
3929 raise error.UpdateAbort(msg, hint=hint)
3924 raise error.UpdateAbort(msg, hint=hint)
3930 if modheads > 1:
3925 if modheads > 1:
3931 currentbranchheads = len(repo.branchheads())
3926 currentbranchheads = len(repo.branchheads())
3932 if currentbranchheads == modheads:
3927 if currentbranchheads == modheads:
3933 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3928 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3934 elif currentbranchheads > 1:
3929 elif currentbranchheads > 1:
3935 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
3930 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
3936 "merge)\n"))
3931 "merge)\n"))
3937 else:
3932 else:
3938 ui.status(_("(run 'hg heads' to see heads)\n"))
3933 ui.status(_("(run 'hg heads' to see heads)\n"))
3939 elif not ui.configbool('commands', 'update.requiredest'):
3934 elif not ui.configbool('commands', 'update.requiredest'):
3940 ui.status(_("(run 'hg update' to get a working copy)\n"))
3935 ui.status(_("(run 'hg update' to get a working copy)\n"))
3941
3936
3942 @command('^pull',
3937 @command('^pull',
3943 [('u', 'update', None,
3938 [('u', 'update', None,
3944 _('update to new branch head if new descendants were pulled')),
3939 _('update to new branch head if new descendants were pulled')),
3945 ('f', 'force', None, _('run even when remote repository is unrelated')),
3940 ('f', 'force', None, _('run even when remote repository is unrelated')),
3946 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3941 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3947 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3942 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3948 ('b', 'branch', [], _('a specific branch you would like to pull'),
3943 ('b', 'branch', [], _('a specific branch you would like to pull'),
3949 _('BRANCH')),
3944 _('BRANCH')),
3950 ] + remoteopts,
3945 ] + remoteopts,
3951 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3946 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3952 def pull(ui, repo, source="default", **opts):
3947 def pull(ui, repo, source="default", **opts):
3953 """pull changes from the specified source
3948 """pull changes from the specified source
3954
3949
3955 Pull changes from a remote repository to a local one.
3950 Pull changes from a remote repository to a local one.
3956
3951
3957 This finds all changes from the repository at the specified path
3952 This finds all changes from the repository at the specified path
3958 or URL and adds them to a local repository (the current one unless
3953 or URL and adds them to a local repository (the current one unless
3959 -R is specified). By default, this does not update the copy of the
3954 -R is specified). By default, this does not update the copy of the
3960 project in the working directory.
3955 project in the working directory.
3961
3956
3962 Use :hg:`incoming` if you want to see what would have been added
3957 Use :hg:`incoming` if you want to see what would have been added
3963 by a pull at the time you issued this command. If you then decide
3958 by a pull at the time you issued this command. If you then decide
3964 to add those changes to the repository, you should use :hg:`pull
3959 to add those changes to the repository, you should use :hg:`pull
3965 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3960 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3966
3961
3967 If SOURCE is omitted, the 'default' path will be used.
3962 If SOURCE is omitted, the 'default' path will be used.
3968 See :hg:`help urls` for more information.
3963 See :hg:`help urls` for more information.
3969
3964
3970 Specifying bookmark as ``.`` is equivalent to specifying the active
3965 Specifying bookmark as ``.`` is equivalent to specifying the active
3971 bookmark's name.
3966 bookmark's name.
3972
3967
3973 Returns 0 on success, 1 if an update had unresolved files.
3968 Returns 0 on success, 1 if an update had unresolved files.
3974 """
3969 """
3975
3970
3976 opts = pycompat.byteskwargs(opts)
3971 opts = pycompat.byteskwargs(opts)
3977 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
3972 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
3978 msg = _('update destination required by configuration')
3973 msg = _('update destination required by configuration')
3979 hint = _('use hg pull followed by hg update DEST')
3974 hint = _('use hg pull followed by hg update DEST')
3980 raise error.Abort(msg, hint=hint)
3975 raise error.Abort(msg, hint=hint)
3981
3976
3982 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3977 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3983 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3978 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3984 other = hg.peer(repo, opts, source)
3979 other = hg.peer(repo, opts, source)
3985 try:
3980 try:
3986 revs, checkout = hg.addbranchrevs(repo, other, branches,
3981 revs, checkout = hg.addbranchrevs(repo, other, branches,
3987 opts.get('rev'))
3982 opts.get('rev'))
3988
3983
3989
3984
3990 pullopargs = {}
3985 pullopargs = {}
3991 if opts.get('bookmark'):
3986 if opts.get('bookmark'):
3992 if not revs:
3987 if not revs:
3993 revs = []
3988 revs = []
3994 # The list of bookmark used here is not the one used to actually
3989 # The list of bookmark used here is not the one used to actually
3995 # update the bookmark name. This can result in the revision pulled
3990 # update the bookmark name. This can result in the revision pulled
3996 # not ending up with the name of the bookmark because of a race
3991 # not ending up with the name of the bookmark because of a race
3997 # condition on the server. (See issue 4689 for details)
3992 # condition on the server. (See issue 4689 for details)
3998 remotebookmarks = other.listkeys('bookmarks')
3993 remotebookmarks = other.listkeys('bookmarks')
3999 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
3994 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
4000 pullopargs['remotebookmarks'] = remotebookmarks
3995 pullopargs['remotebookmarks'] = remotebookmarks
4001 for b in opts['bookmark']:
3996 for b in opts['bookmark']:
4002 b = repo._bookmarks.expandname(b)
3997 b = repo._bookmarks.expandname(b)
4003 if b not in remotebookmarks:
3998 if b not in remotebookmarks:
4004 raise error.Abort(_('remote bookmark %s not found!') % b)
3999 raise error.Abort(_('remote bookmark %s not found!') % b)
4005 revs.append(hex(remotebookmarks[b]))
4000 revs.append(hex(remotebookmarks[b]))
4006
4001
4007 if revs:
4002 if revs:
4008 try:
4003 try:
4009 # When 'rev' is a bookmark name, we cannot guarantee that it
4004 # When 'rev' is a bookmark name, we cannot guarantee that it
4010 # will be updated with that name because of a race condition
4005 # will be updated with that name because of a race condition
4011 # server side. (See issue 4689 for details)
4006 # server side. (See issue 4689 for details)
4012 oldrevs = revs
4007 oldrevs = revs
4013 revs = [] # actually, nodes
4008 revs = [] # actually, nodes
4014 for r in oldrevs:
4009 for r in oldrevs:
4015 node = other.lookup(r)
4010 node = other.lookup(r)
4016 revs.append(node)
4011 revs.append(node)
4017 if r == checkout:
4012 if r == checkout:
4018 checkout = node
4013 checkout = node
4019 except error.CapabilityError:
4014 except error.CapabilityError:
4020 err = _("other repository doesn't support revision lookup, "
4015 err = _("other repository doesn't support revision lookup, "
4021 "so a rev cannot be specified.")
4016 "so a rev cannot be specified.")
4022 raise error.Abort(err)
4017 raise error.Abort(err)
4023
4018
4024 wlock = util.nullcontextmanager()
4019 wlock = util.nullcontextmanager()
4025 if opts.get('update'):
4020 if opts.get('update'):
4026 wlock = repo.wlock()
4021 wlock = repo.wlock()
4027 with wlock:
4022 with wlock:
4028 pullopargs.update(opts.get('opargs', {}))
4023 pullopargs.update(opts.get('opargs', {}))
4029 modheads = exchange.pull(repo, other, heads=revs,
4024 modheads = exchange.pull(repo, other, heads=revs,
4030 force=opts.get('force'),
4025 force=opts.get('force'),
4031 bookmarks=opts.get('bookmark', ()),
4026 bookmarks=opts.get('bookmark', ()),
4032 opargs=pullopargs).cgresult
4027 opargs=pullopargs).cgresult
4033
4028
4034 # brev is a name, which might be a bookmark to be activated at
4029 # brev is a name, which might be a bookmark to be activated at
4035 # the end of the update. In other words, it is an explicit
4030 # the end of the update. In other words, it is an explicit
4036 # destination of the update
4031 # destination of the update
4037 brev = None
4032 brev = None
4038
4033
4039 if checkout:
4034 if checkout:
4040 checkout = "%d" % repo.changelog.rev(checkout)
4035 checkout = "%d" % repo.changelog.rev(checkout)
4041
4036
4042 # order below depends on implementation of
4037 # order below depends on implementation of
4043 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4038 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4044 # because 'checkout' is determined without it.
4039 # because 'checkout' is determined without it.
4045 if opts.get('rev'):
4040 if opts.get('rev'):
4046 brev = opts['rev'][0]
4041 brev = opts['rev'][0]
4047 elif opts.get('branch'):
4042 elif opts.get('branch'):
4048 brev = opts['branch'][0]
4043 brev = opts['branch'][0]
4049 else:
4044 else:
4050 brev = branches[0]
4045 brev = branches[0]
4051 repo._subtoppath = source
4046 repo._subtoppath = source
4052 try:
4047 try:
4053 ret = postincoming(ui, repo, modheads, opts.get('update'),
4048 ret = postincoming(ui, repo, modheads, opts.get('update'),
4054 checkout, brev)
4049 checkout, brev)
4055
4050
4056 finally:
4051 finally:
4057 del repo._subtoppath
4052 del repo._subtoppath
4058
4053
4059 finally:
4054 finally:
4060 other.close()
4055 other.close()
4061 return ret
4056 return ret
4062
4057
4063 @command('^push',
4058 @command('^push',
4064 [('f', 'force', None, _('force push')),
4059 [('f', 'force', None, _('force push')),
4065 ('r', 'rev', [],
4060 ('r', 'rev', [],
4066 _('a changeset intended to be included in the destination'),
4061 _('a changeset intended to be included in the destination'),
4067 _('REV')),
4062 _('REV')),
4068 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4063 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4069 ('b', 'branch', [],
4064 ('b', 'branch', [],
4070 _('a specific branch you would like to push'), _('BRANCH')),
4065 _('a specific branch you would like to push'), _('BRANCH')),
4071 ('', 'new-branch', False, _('allow pushing a new branch')),
4066 ('', 'new-branch', False, _('allow pushing a new branch')),
4072 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4067 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4073 ] + remoteopts,
4068 ] + remoteopts,
4074 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4069 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4075 def push(ui, repo, dest=None, **opts):
4070 def push(ui, repo, dest=None, **opts):
4076 """push changes to the specified destination
4071 """push changes to the specified destination
4077
4072
4078 Push changesets from the local repository to the specified
4073 Push changesets from the local repository to the specified
4079 destination.
4074 destination.
4080
4075
4081 This operation is symmetrical to pull: it is identical to a pull
4076 This operation is symmetrical to pull: it is identical to a pull
4082 in the destination repository from the current one.
4077 in the destination repository from the current one.
4083
4078
4084 By default, push will not allow creation of new heads at the
4079 By default, push will not allow creation of new heads at the
4085 destination, since multiple heads would make it unclear which head
4080 destination, since multiple heads would make it unclear which head
4086 to use. In this situation, it is recommended to pull and merge
4081 to use. In this situation, it is recommended to pull and merge
4087 before pushing.
4082 before pushing.
4088
4083
4089 Use --new-branch if you want to allow push to create a new named
4084 Use --new-branch if you want to allow push to create a new named
4090 branch that is not present at the destination. This allows you to
4085 branch that is not present at the destination. This allows you to
4091 only create a new branch without forcing other changes.
4086 only create a new branch without forcing other changes.
4092
4087
4093 .. note::
4088 .. note::
4094
4089
4095 Extra care should be taken with the -f/--force option,
4090 Extra care should be taken with the -f/--force option,
4096 which will push all new heads on all branches, an action which will
4091 which will push all new heads on all branches, an action which will
4097 almost always cause confusion for collaborators.
4092 almost always cause confusion for collaborators.
4098
4093
4099 If -r/--rev is used, the specified revision and all its ancestors
4094 If -r/--rev is used, the specified revision and all its ancestors
4100 will be pushed to the remote repository.
4095 will be pushed to the remote repository.
4101
4096
4102 If -B/--bookmark is used, the specified bookmarked revision, its
4097 If -B/--bookmark is used, the specified bookmarked revision, its
4103 ancestors, and the bookmark will be pushed to the remote
4098 ancestors, and the bookmark will be pushed to the remote
4104 repository. Specifying ``.`` is equivalent to specifying the active
4099 repository. Specifying ``.`` is equivalent to specifying the active
4105 bookmark's name.
4100 bookmark's name.
4106
4101
4107 Please see :hg:`help urls` for important details about ``ssh://``
4102 Please see :hg:`help urls` for important details about ``ssh://``
4108 URLs. If DESTINATION is omitted, a default path will be used.
4103 URLs. If DESTINATION is omitted, a default path will be used.
4109
4104
4110 .. container:: verbose
4105 .. container:: verbose
4111
4106
4112 The --pushvars option sends strings to the server that become
4107 The --pushvars option sends strings to the server that become
4113 environment variables prepended with ``HG_USERVAR_``. For example,
4108 environment variables prepended with ``HG_USERVAR_``. For example,
4114 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4109 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4115 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4110 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4116
4111
4117 pushvars can provide for user-overridable hooks as well as set debug
4112 pushvars can provide for user-overridable hooks as well as set debug
4118 levels. One example is having a hook that blocks commits containing
4113 levels. One example is having a hook that blocks commits containing
4119 conflict markers, but enables the user to override the hook if the file
4114 conflict markers, but enables the user to override the hook if the file
4120 is using conflict markers for testing purposes or the file format has
4115 is using conflict markers for testing purposes or the file format has
4121 strings that look like conflict markers.
4116 strings that look like conflict markers.
4122
4117
4123 By default, servers will ignore `--pushvars`. To enable it add the
4118 By default, servers will ignore `--pushvars`. To enable it add the
4124 following to your configuration file::
4119 following to your configuration file::
4125
4120
4126 [push]
4121 [push]
4127 pushvars.server = true
4122 pushvars.server = true
4128
4123
4129 Returns 0 if push was successful, 1 if nothing to push.
4124 Returns 0 if push was successful, 1 if nothing to push.
4130 """
4125 """
4131
4126
4132 opts = pycompat.byteskwargs(opts)
4127 opts = pycompat.byteskwargs(opts)
4133 if opts.get('bookmark'):
4128 if opts.get('bookmark'):
4134 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4129 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4135 for b in opts['bookmark']:
4130 for b in opts['bookmark']:
4136 # translate -B options to -r so changesets get pushed
4131 # translate -B options to -r so changesets get pushed
4137 b = repo._bookmarks.expandname(b)
4132 b = repo._bookmarks.expandname(b)
4138 if b in repo._bookmarks:
4133 if b in repo._bookmarks:
4139 opts.setdefault('rev', []).append(b)
4134 opts.setdefault('rev', []).append(b)
4140 else:
4135 else:
4141 # if we try to push a deleted bookmark, translate it to null
4136 # if we try to push a deleted bookmark, translate it to null
4142 # this lets simultaneous -r, -b options continue working
4137 # this lets simultaneous -r, -b options continue working
4143 opts.setdefault('rev', []).append("null")
4138 opts.setdefault('rev', []).append("null")
4144
4139
4145 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4140 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4146 if not path:
4141 if not path:
4147 raise error.Abort(_('default repository not configured!'),
4142 raise error.Abort(_('default repository not configured!'),
4148 hint=_("see 'hg help config.paths'"))
4143 hint=_("see 'hg help config.paths'"))
4149 dest = path.pushloc or path.loc
4144 dest = path.pushloc or path.loc
4150 branches = (path.branch, opts.get('branch') or [])
4145 branches = (path.branch, opts.get('branch') or [])
4151 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4146 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4152 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4147 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4153 other = hg.peer(repo, opts, dest)
4148 other = hg.peer(repo, opts, dest)
4154
4149
4155 if revs:
4150 if revs:
4156 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4151 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4157 if not revs:
4152 if not revs:
4158 raise error.Abort(_("specified revisions evaluate to an empty set"),
4153 raise error.Abort(_("specified revisions evaluate to an empty set"),
4159 hint=_("use different revision arguments"))
4154 hint=_("use different revision arguments"))
4160 elif path.pushrev:
4155 elif path.pushrev:
4161 # It doesn't make any sense to specify ancestor revisions. So limit
4156 # It doesn't make any sense to specify ancestor revisions. So limit
4162 # to DAG heads to make discovery simpler.
4157 # to DAG heads to make discovery simpler.
4163 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4158 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4164 revs = scmutil.revrange(repo, [expr])
4159 revs = scmutil.revrange(repo, [expr])
4165 revs = [repo[rev].node() for rev in revs]
4160 revs = [repo[rev].node() for rev in revs]
4166 if not revs:
4161 if not revs:
4167 raise error.Abort(_('default push revset for path evaluates to an '
4162 raise error.Abort(_('default push revset for path evaluates to an '
4168 'empty set'))
4163 'empty set'))
4169
4164
4170 repo._subtoppath = dest
4165 repo._subtoppath = dest
4171 try:
4166 try:
4172 # push subrepos depth-first for coherent ordering
4167 # push subrepos depth-first for coherent ordering
4173 c = repo['.']
4168 c = repo['.']
4174 subs = c.substate # only repos that are committed
4169 subs = c.substate # only repos that are committed
4175 for s in sorted(subs):
4170 for s in sorted(subs):
4176 result = c.sub(s).push(opts)
4171 result = c.sub(s).push(opts)
4177 if result == 0:
4172 if result == 0:
4178 return not result
4173 return not result
4179 finally:
4174 finally:
4180 del repo._subtoppath
4175 del repo._subtoppath
4181
4176
4182 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4177 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4183 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4178 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4184
4179
4185 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4180 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4186 newbranch=opts.get('new_branch'),
4181 newbranch=opts.get('new_branch'),
4187 bookmarks=opts.get('bookmark', ()),
4182 bookmarks=opts.get('bookmark', ()),
4188 opargs=opargs)
4183 opargs=opargs)
4189
4184
4190 result = not pushop.cgresult
4185 result = not pushop.cgresult
4191
4186
4192 if pushop.bkresult is not None:
4187 if pushop.bkresult is not None:
4193 if pushop.bkresult == 2:
4188 if pushop.bkresult == 2:
4194 result = 2
4189 result = 2
4195 elif not result and pushop.bkresult:
4190 elif not result and pushop.bkresult:
4196 result = 2
4191 result = 2
4197
4192
4198 return result
4193 return result
4199
4194
4200 @command('recover', [])
4195 @command('recover', [])
4201 def recover(ui, repo):
4196 def recover(ui, repo):
4202 """roll back an interrupted transaction
4197 """roll back an interrupted transaction
4203
4198
4204 Recover from an interrupted commit or pull.
4199 Recover from an interrupted commit or pull.
4205
4200
4206 This command tries to fix the repository status after an
4201 This command tries to fix the repository status after an
4207 interrupted operation. It should only be necessary when Mercurial
4202 interrupted operation. It should only be necessary when Mercurial
4208 suggests it.
4203 suggests it.
4209
4204
4210 Returns 0 if successful, 1 if nothing to recover or verify fails.
4205 Returns 0 if successful, 1 if nothing to recover or verify fails.
4211 """
4206 """
4212 if repo.recover():
4207 if repo.recover():
4213 return hg.verify(repo)
4208 return hg.verify(repo)
4214 return 1
4209 return 1
4215
4210
4216 @command('^remove|rm',
4211 @command('^remove|rm',
4217 [('A', 'after', None, _('record delete for missing files')),
4212 [('A', 'after', None, _('record delete for missing files')),
4218 ('f', 'force', None,
4213 ('f', 'force', None,
4219 _('forget added files, delete modified files')),
4214 _('forget added files, delete modified files')),
4220 ] + subrepoopts + walkopts + dryrunopts,
4215 ] + subrepoopts + walkopts + dryrunopts,
4221 _('[OPTION]... FILE...'),
4216 _('[OPTION]... FILE...'),
4222 inferrepo=True)
4217 inferrepo=True)
4223 def remove(ui, repo, *pats, **opts):
4218 def remove(ui, repo, *pats, **opts):
4224 """remove the specified files on the next commit
4219 """remove the specified files on the next commit
4225
4220
4226 Schedule the indicated files for removal from the current branch.
4221 Schedule the indicated files for removal from the current branch.
4227
4222
4228 This command schedules the files to be removed at the next commit.
4223 This command schedules the files to be removed at the next commit.
4229 To undo a remove before that, see :hg:`revert`. To undo added
4224 To undo a remove before that, see :hg:`revert`. To undo added
4230 files, see :hg:`forget`.
4225 files, see :hg:`forget`.
4231
4226
4232 .. container:: verbose
4227 .. container:: verbose
4233
4228
4234 -A/--after can be used to remove only files that have already
4229 -A/--after can be used to remove only files that have already
4235 been deleted, -f/--force can be used to force deletion, and -Af
4230 been deleted, -f/--force can be used to force deletion, and -Af
4236 can be used to remove files from the next revision without
4231 can be used to remove files from the next revision without
4237 deleting them from the working directory.
4232 deleting them from the working directory.
4238
4233
4239 The following table details the behavior of remove for different
4234 The following table details the behavior of remove for different
4240 file states (columns) and option combinations (rows). The file
4235 file states (columns) and option combinations (rows). The file
4241 states are Added [A], Clean [C], Modified [M] and Missing [!]
4236 states are Added [A], Clean [C], Modified [M] and Missing [!]
4242 (as reported by :hg:`status`). The actions are Warn, Remove
4237 (as reported by :hg:`status`). The actions are Warn, Remove
4243 (from branch) and Delete (from disk):
4238 (from branch) and Delete (from disk):
4244
4239
4245 ========= == == == ==
4240 ========= == == == ==
4246 opt/state A C M !
4241 opt/state A C M !
4247 ========= == == == ==
4242 ========= == == == ==
4248 none W RD W R
4243 none W RD W R
4249 -f R RD RD R
4244 -f R RD RD R
4250 -A W W W R
4245 -A W W W R
4251 -Af R R R R
4246 -Af R R R R
4252 ========= == == == ==
4247 ========= == == == ==
4253
4248
4254 .. note::
4249 .. note::
4255
4250
4256 :hg:`remove` never deletes files in Added [A] state from the
4251 :hg:`remove` never deletes files in Added [A] state from the
4257 working directory, not even if ``--force`` is specified.
4252 working directory, not even if ``--force`` is specified.
4258
4253
4259 Returns 0 on success, 1 if any warnings encountered.
4254 Returns 0 on success, 1 if any warnings encountered.
4260 """
4255 """
4261
4256
4262 opts = pycompat.byteskwargs(opts)
4257 opts = pycompat.byteskwargs(opts)
4263 after, force = opts.get('after'), opts.get('force')
4258 after, force = opts.get('after'), opts.get('force')
4264 dryrun = opts.get('dry_run')
4259 dryrun = opts.get('dry_run')
4265 if not pats and not after:
4260 if not pats and not after:
4266 raise error.Abort(_('no files specified'))
4261 raise error.Abort(_('no files specified'))
4267
4262
4268 m = scmutil.match(repo[None], pats, opts)
4263 m = scmutil.match(repo[None], pats, opts)
4269 subrepos = opts.get('subrepos')
4264 subrepos = opts.get('subrepos')
4270 return cmdutil.remove(ui, repo, m, "", after, force, subrepos,
4265 return cmdutil.remove(ui, repo, m, "", after, force, subrepos,
4271 dryrun=dryrun)
4266 dryrun=dryrun)
4272
4267
4273 @command('rename|move|mv',
4268 @command('rename|move|mv',
4274 [('A', 'after', None, _('record a rename that has already occurred')),
4269 [('A', 'after', None, _('record a rename that has already occurred')),
4275 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4270 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4276 ] + walkopts + dryrunopts,
4271 ] + walkopts + dryrunopts,
4277 _('[OPTION]... SOURCE... DEST'))
4272 _('[OPTION]... SOURCE... DEST'))
4278 def rename(ui, repo, *pats, **opts):
4273 def rename(ui, repo, *pats, **opts):
4279 """rename files; equivalent of copy + remove
4274 """rename files; equivalent of copy + remove
4280
4275
4281 Mark dest as copies of sources; mark sources for deletion. If dest
4276 Mark dest as copies of sources; mark sources for deletion. If dest
4282 is a directory, copies are put in that directory. If dest is a
4277 is a directory, copies are put in that directory. If dest is a
4283 file, there can only be one source.
4278 file, there can only be one source.
4284
4279
4285 By default, this command copies the contents of files as they
4280 By default, this command copies the contents of files as they
4286 exist in the working directory. If invoked with -A/--after, the
4281 exist in the working directory. If invoked with -A/--after, the
4287 operation is recorded, but no copying is performed.
4282 operation is recorded, but no copying is performed.
4288
4283
4289 This command takes effect at the next commit. To undo a rename
4284 This command takes effect at the next commit. To undo a rename
4290 before that, see :hg:`revert`.
4285 before that, see :hg:`revert`.
4291
4286
4292 Returns 0 on success, 1 if errors are encountered.
4287 Returns 0 on success, 1 if errors are encountered.
4293 """
4288 """
4294 opts = pycompat.byteskwargs(opts)
4289 opts = pycompat.byteskwargs(opts)
4295 with repo.wlock(False):
4290 with repo.wlock(False):
4296 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4291 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4297
4292
4298 @command('resolve',
4293 @command('resolve',
4299 [('a', 'all', None, _('select all unresolved files')),
4294 [('a', 'all', None, _('select all unresolved files')),
4300 ('l', 'list', None, _('list state of files needing merge')),
4295 ('l', 'list', None, _('list state of files needing merge')),
4301 ('m', 'mark', None, _('mark files as resolved')),
4296 ('m', 'mark', None, _('mark files as resolved')),
4302 ('u', 'unmark', None, _('mark files as unresolved')),
4297 ('u', 'unmark', None, _('mark files as unresolved')),
4303 ('n', 'no-status', None, _('hide status prefix'))]
4298 ('n', 'no-status', None, _('hide status prefix'))]
4304 + mergetoolopts + walkopts + formatteropts,
4299 + mergetoolopts + walkopts + formatteropts,
4305 _('[OPTION]... [FILE]...'),
4300 _('[OPTION]... [FILE]...'),
4306 inferrepo=True)
4301 inferrepo=True)
4307 def resolve(ui, repo, *pats, **opts):
4302 def resolve(ui, repo, *pats, **opts):
4308 """redo merges or set/view the merge status of files
4303 """redo merges or set/view the merge status of files
4309
4304
4310 Merges with unresolved conflicts are often the result of
4305 Merges with unresolved conflicts are often the result of
4311 non-interactive merging using the ``internal:merge`` configuration
4306 non-interactive merging using the ``internal:merge`` configuration
4312 setting, or a command-line merge tool like ``diff3``. The resolve
4307 setting, or a command-line merge tool like ``diff3``. The resolve
4313 command is used to manage the files involved in a merge, after
4308 command is used to manage the files involved in a merge, after
4314 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4309 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4315 working directory must have two parents). See :hg:`help
4310 working directory must have two parents). See :hg:`help
4316 merge-tools` for information on configuring merge tools.
4311 merge-tools` for information on configuring merge tools.
4317
4312
4318 The resolve command can be used in the following ways:
4313 The resolve command can be used in the following ways:
4319
4314
4320 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4315 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4321 files, discarding any previous merge attempts. Re-merging is not
4316 files, discarding any previous merge attempts. Re-merging is not
4322 performed for files already marked as resolved. Use ``--all/-a``
4317 performed for files already marked as resolved. Use ``--all/-a``
4323 to select all unresolved files. ``--tool`` can be used to specify
4318 to select all unresolved files. ``--tool`` can be used to specify
4324 the merge tool used for the given files. It overrides the HGMERGE
4319 the merge tool used for the given files. It overrides the HGMERGE
4325 environment variable and your configuration files. Previous file
4320 environment variable and your configuration files. Previous file
4326 contents are saved with a ``.orig`` suffix.
4321 contents are saved with a ``.orig`` suffix.
4327
4322
4328 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4323 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4329 (e.g. after having manually fixed-up the files). The default is
4324 (e.g. after having manually fixed-up the files). The default is
4330 to mark all unresolved files.
4325 to mark all unresolved files.
4331
4326
4332 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4327 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4333 default is to mark all resolved files.
4328 default is to mark all resolved files.
4334
4329
4335 - :hg:`resolve -l`: list files which had or still have conflicts.
4330 - :hg:`resolve -l`: list files which had or still have conflicts.
4336 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4331 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4337 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4332 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4338 the list. See :hg:`help filesets` for details.
4333 the list. See :hg:`help filesets` for details.
4339
4334
4340 .. note::
4335 .. note::
4341
4336
4342 Mercurial will not let you commit files with unresolved merge
4337 Mercurial will not let you commit files with unresolved merge
4343 conflicts. You must use :hg:`resolve -m ...` before you can
4338 conflicts. You must use :hg:`resolve -m ...` before you can
4344 commit after a conflicting merge.
4339 commit after a conflicting merge.
4345
4340
4346 Returns 0 on success, 1 if any files fail a resolve attempt.
4341 Returns 0 on success, 1 if any files fail a resolve attempt.
4347 """
4342 """
4348
4343
4349 opts = pycompat.byteskwargs(opts)
4344 opts = pycompat.byteskwargs(opts)
4350 flaglist = 'all mark unmark list no_status'.split()
4345 flaglist = 'all mark unmark list no_status'.split()
4351 all, mark, unmark, show, nostatus = \
4346 all, mark, unmark, show, nostatus = \
4352 [opts.get(o) for o in flaglist]
4347 [opts.get(o) for o in flaglist]
4353
4348
4354 if (show and (mark or unmark)) or (mark and unmark):
4349 if (show and (mark or unmark)) or (mark and unmark):
4355 raise error.Abort(_("too many options specified"))
4350 raise error.Abort(_("too many options specified"))
4356 if pats and all:
4351 if pats and all:
4357 raise error.Abort(_("can't specify --all and patterns"))
4352 raise error.Abort(_("can't specify --all and patterns"))
4358 if not (all or pats or show or mark or unmark):
4353 if not (all or pats or show or mark or unmark):
4359 raise error.Abort(_('no files or directories specified'),
4354 raise error.Abort(_('no files or directories specified'),
4360 hint=('use --all to re-merge all unresolved files'))
4355 hint=('use --all to re-merge all unresolved files'))
4361
4356
4362 if show:
4357 if show:
4363 ui.pager('resolve')
4358 ui.pager('resolve')
4364 fm = ui.formatter('resolve', opts)
4359 fm = ui.formatter('resolve', opts)
4365 ms = mergemod.mergestate.read(repo)
4360 ms = mergemod.mergestate.read(repo)
4366 m = scmutil.match(repo[None], pats, opts)
4361 m = scmutil.match(repo[None], pats, opts)
4367
4362
4368 # Labels and keys based on merge state. Unresolved path conflicts show
4363 # Labels and keys based on merge state. Unresolved path conflicts show
4369 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4364 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4370 # resolved conflicts.
4365 # resolved conflicts.
4371 mergestateinfo = {
4366 mergestateinfo = {
4372 mergemod.MERGE_RECORD_UNRESOLVED: ('resolve.unresolved', 'U'),
4367 mergemod.MERGE_RECORD_UNRESOLVED: ('resolve.unresolved', 'U'),
4373 mergemod.MERGE_RECORD_RESOLVED: ('resolve.resolved', 'R'),
4368 mergemod.MERGE_RECORD_RESOLVED: ('resolve.resolved', 'R'),
4374 mergemod.MERGE_RECORD_UNRESOLVED_PATH: ('resolve.unresolved', 'P'),
4369 mergemod.MERGE_RECORD_UNRESOLVED_PATH: ('resolve.unresolved', 'P'),
4375 mergemod.MERGE_RECORD_RESOLVED_PATH: ('resolve.resolved', 'R'),
4370 mergemod.MERGE_RECORD_RESOLVED_PATH: ('resolve.resolved', 'R'),
4376 mergemod.MERGE_RECORD_DRIVER_RESOLVED: ('resolve.driverresolved',
4371 mergemod.MERGE_RECORD_DRIVER_RESOLVED: ('resolve.driverresolved',
4377 'D'),
4372 'D'),
4378 }
4373 }
4379
4374
4380 for f in ms:
4375 for f in ms:
4381 if not m(f):
4376 if not m(f):
4382 continue
4377 continue
4383
4378
4384 label, key = mergestateinfo[ms[f]]
4379 label, key = mergestateinfo[ms[f]]
4385 fm.startitem()
4380 fm.startitem()
4386 fm.condwrite(not nostatus, 'status', '%s ', key, label=label)
4381 fm.condwrite(not nostatus, 'status', '%s ', key, label=label)
4387 fm.write('path', '%s\n', f, label=label)
4382 fm.write('path', '%s\n', f, label=label)
4388 fm.end()
4383 fm.end()
4389 return 0
4384 return 0
4390
4385
4391 with repo.wlock():
4386 with repo.wlock():
4392 ms = mergemod.mergestate.read(repo)
4387 ms = mergemod.mergestate.read(repo)
4393
4388
4394 if not (ms.active() or repo.dirstate.p2() != nullid):
4389 if not (ms.active() or repo.dirstate.p2() != nullid):
4395 raise error.Abort(
4390 raise error.Abort(
4396 _('resolve command not applicable when not merging'))
4391 _('resolve command not applicable when not merging'))
4397
4392
4398 wctx = repo[None]
4393 wctx = repo[None]
4399
4394
4400 if (ms.mergedriver
4395 if (ms.mergedriver
4401 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED):
4396 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED):
4402 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4397 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4403 ms.commit()
4398 ms.commit()
4404 # allow mark and unmark to go through
4399 # allow mark and unmark to go through
4405 if not mark and not unmark and not proceed:
4400 if not mark and not unmark and not proceed:
4406 return 1
4401 return 1
4407
4402
4408 m = scmutil.match(wctx, pats, opts)
4403 m = scmutil.match(wctx, pats, opts)
4409 ret = 0
4404 ret = 0
4410 didwork = False
4405 didwork = False
4411 runconclude = False
4406 runconclude = False
4412
4407
4413 tocomplete = []
4408 tocomplete = []
4414 for f in ms:
4409 for f in ms:
4415 if not m(f):
4410 if not m(f):
4416 continue
4411 continue
4417
4412
4418 didwork = True
4413 didwork = True
4419
4414
4420 # don't let driver-resolved files be marked, and run the conclude
4415 # don't let driver-resolved files be marked, and run the conclude
4421 # step if asked to resolve
4416 # step if asked to resolve
4422 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
4417 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
4423 exact = m.exact(f)
4418 exact = m.exact(f)
4424 if mark:
4419 if mark:
4425 if exact:
4420 if exact:
4426 ui.warn(_('not marking %s as it is driver-resolved\n')
4421 ui.warn(_('not marking %s as it is driver-resolved\n')
4427 % f)
4422 % f)
4428 elif unmark:
4423 elif unmark:
4429 if exact:
4424 if exact:
4430 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4425 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4431 % f)
4426 % f)
4432 else:
4427 else:
4433 runconclude = True
4428 runconclude = True
4434 continue
4429 continue
4435
4430
4436 # path conflicts must be resolved manually
4431 # path conflicts must be resolved manually
4437 if ms[f] in (mergemod.MERGE_RECORD_UNRESOLVED_PATH,
4432 if ms[f] in (mergemod.MERGE_RECORD_UNRESOLVED_PATH,
4438 mergemod.MERGE_RECORD_RESOLVED_PATH):
4433 mergemod.MERGE_RECORD_RESOLVED_PATH):
4439 if mark:
4434 if mark:
4440 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
4435 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
4441 elif unmark:
4436 elif unmark:
4442 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
4437 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
4443 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
4438 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
4444 ui.warn(_('%s: path conflict must be resolved manually\n')
4439 ui.warn(_('%s: path conflict must be resolved manually\n')
4445 % f)
4440 % f)
4446 continue
4441 continue
4447
4442
4448 if mark:
4443 if mark:
4449 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
4444 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
4450 elif unmark:
4445 elif unmark:
4451 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
4446 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
4452 else:
4447 else:
4453 # backup pre-resolve (merge uses .orig for its own purposes)
4448 # backup pre-resolve (merge uses .orig for its own purposes)
4454 a = repo.wjoin(f)
4449 a = repo.wjoin(f)
4455 try:
4450 try:
4456 util.copyfile(a, a + ".resolve")
4451 util.copyfile(a, a + ".resolve")
4457 except (IOError, OSError) as inst:
4452 except (IOError, OSError) as inst:
4458 if inst.errno != errno.ENOENT:
4453 if inst.errno != errno.ENOENT:
4459 raise
4454 raise
4460
4455
4461 try:
4456 try:
4462 # preresolve file
4457 # preresolve file
4463 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4458 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4464 'resolve')
4459 'resolve')
4465 complete, r = ms.preresolve(f, wctx)
4460 complete, r = ms.preresolve(f, wctx)
4466 if not complete:
4461 if not complete:
4467 tocomplete.append(f)
4462 tocomplete.append(f)
4468 elif r:
4463 elif r:
4469 ret = 1
4464 ret = 1
4470 finally:
4465 finally:
4471 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4466 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4472 ms.commit()
4467 ms.commit()
4473
4468
4474 # replace filemerge's .orig file with our resolve file, but only
4469 # replace filemerge's .orig file with our resolve file, but only
4475 # for merges that are complete
4470 # for merges that are complete
4476 if complete:
4471 if complete:
4477 try:
4472 try:
4478 util.rename(a + ".resolve",
4473 util.rename(a + ".resolve",
4479 scmutil.origpath(ui, repo, a))
4474 scmutil.origpath(ui, repo, a))
4480 except OSError as inst:
4475 except OSError as inst:
4481 if inst.errno != errno.ENOENT:
4476 if inst.errno != errno.ENOENT:
4482 raise
4477 raise
4483
4478
4484 for f in tocomplete:
4479 for f in tocomplete:
4485 try:
4480 try:
4486 # resolve file
4481 # resolve file
4487 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4482 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
4488 'resolve')
4483 'resolve')
4489 r = ms.resolve(f, wctx)
4484 r = ms.resolve(f, wctx)
4490 if r:
4485 if r:
4491 ret = 1
4486 ret = 1
4492 finally:
4487 finally:
4493 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4488 ui.setconfig('ui', 'forcemerge', '', 'resolve')
4494 ms.commit()
4489 ms.commit()
4495
4490
4496 # replace filemerge's .orig file with our resolve file
4491 # replace filemerge's .orig file with our resolve file
4497 a = repo.wjoin(f)
4492 a = repo.wjoin(f)
4498 try:
4493 try:
4499 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4494 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4500 except OSError as inst:
4495 except OSError as inst:
4501 if inst.errno != errno.ENOENT:
4496 if inst.errno != errno.ENOENT:
4502 raise
4497 raise
4503
4498
4504 ms.commit()
4499 ms.commit()
4505 ms.recordactions()
4500 ms.recordactions()
4506
4501
4507 if not didwork and pats:
4502 if not didwork and pats:
4508 hint = None
4503 hint = None
4509 if not any([p for p in pats if p.find(':') >= 0]):
4504 if not any([p for p in pats if p.find(':') >= 0]):
4510 pats = ['path:%s' % p for p in pats]
4505 pats = ['path:%s' % p for p in pats]
4511 m = scmutil.match(wctx, pats, opts)
4506 m = scmutil.match(wctx, pats, opts)
4512 for f in ms:
4507 for f in ms:
4513 if not m(f):
4508 if not m(f):
4514 continue
4509 continue
4515 flags = ''.join(['-%s ' % o[0:1] for o in flaglist
4510 flags = ''.join(['-%s ' % o[0:1] for o in flaglist
4516 if opts.get(o)])
4511 if opts.get(o)])
4517 hint = _("(try: hg resolve %s%s)\n") % (
4512 hint = _("(try: hg resolve %s%s)\n") % (
4518 flags,
4513 flags,
4519 ' '.join(pats))
4514 ' '.join(pats))
4520 break
4515 break
4521 ui.warn(_("arguments do not match paths that need resolving\n"))
4516 ui.warn(_("arguments do not match paths that need resolving\n"))
4522 if hint:
4517 if hint:
4523 ui.warn(hint)
4518 ui.warn(hint)
4524 elif ms.mergedriver and ms.mdstate() != 's':
4519 elif ms.mergedriver and ms.mdstate() != 's':
4525 # run conclude step when either a driver-resolved file is requested
4520 # run conclude step when either a driver-resolved file is requested
4526 # or there are no driver-resolved files
4521 # or there are no driver-resolved files
4527 # we can't use 'ret' to determine whether any files are unresolved
4522 # we can't use 'ret' to determine whether any files are unresolved
4528 # because we might not have tried to resolve some
4523 # because we might not have tried to resolve some
4529 if ((runconclude or not list(ms.driverresolved()))
4524 if ((runconclude or not list(ms.driverresolved()))
4530 and not list(ms.unresolved())):
4525 and not list(ms.unresolved())):
4531 proceed = mergemod.driverconclude(repo, ms, wctx)
4526 proceed = mergemod.driverconclude(repo, ms, wctx)
4532 ms.commit()
4527 ms.commit()
4533 if not proceed:
4528 if not proceed:
4534 return 1
4529 return 1
4535
4530
4536 # Nudge users into finishing an unfinished operation
4531 # Nudge users into finishing an unfinished operation
4537 unresolvedf = list(ms.unresolved())
4532 unresolvedf = list(ms.unresolved())
4538 driverresolvedf = list(ms.driverresolved())
4533 driverresolvedf = list(ms.driverresolved())
4539 if not unresolvedf and not driverresolvedf:
4534 if not unresolvedf and not driverresolvedf:
4540 ui.status(_('(no more unresolved files)\n'))
4535 ui.status(_('(no more unresolved files)\n'))
4541 cmdutil.checkafterresolved(repo)
4536 cmdutil.checkafterresolved(repo)
4542 elif not unresolvedf:
4537 elif not unresolvedf:
4543 ui.status(_('(no more unresolved files -- '
4538 ui.status(_('(no more unresolved files -- '
4544 'run "hg resolve --all" to conclude)\n'))
4539 'run "hg resolve --all" to conclude)\n'))
4545
4540
4546 return ret
4541 return ret
4547
4542
4548 @command('revert',
4543 @command('revert',
4549 [('a', 'all', None, _('revert all changes when no arguments given')),
4544 [('a', 'all', None, _('revert all changes when no arguments given')),
4550 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4545 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4551 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4546 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4552 ('C', 'no-backup', None, _('do not save backup copies of files')),
4547 ('C', 'no-backup', None, _('do not save backup copies of files')),
4553 ('i', 'interactive', None, _('interactively select the changes')),
4548 ('i', 'interactive', None, _('interactively select the changes')),
4554 ] + walkopts + dryrunopts,
4549 ] + walkopts + dryrunopts,
4555 _('[OPTION]... [-r REV] [NAME]...'))
4550 _('[OPTION]... [-r REV] [NAME]...'))
4556 def revert(ui, repo, *pats, **opts):
4551 def revert(ui, repo, *pats, **opts):
4557 """restore files to their checkout state
4552 """restore files to their checkout state
4558
4553
4559 .. note::
4554 .. note::
4560
4555
4561 To check out earlier revisions, you should use :hg:`update REV`.
4556 To check out earlier revisions, you should use :hg:`update REV`.
4562 To cancel an uncommitted merge (and lose your changes),
4557 To cancel an uncommitted merge (and lose your changes),
4563 use :hg:`merge --abort`.
4558 use :hg:`merge --abort`.
4564
4559
4565 With no revision specified, revert the specified files or directories
4560 With no revision specified, revert the specified files or directories
4566 to the contents they had in the parent of the working directory.
4561 to the contents they had in the parent of the working directory.
4567 This restores the contents of files to an unmodified
4562 This restores the contents of files to an unmodified
4568 state and unschedules adds, removes, copies, and renames. If the
4563 state and unschedules adds, removes, copies, and renames. If the
4569 working directory has two parents, you must explicitly specify a
4564 working directory has two parents, you must explicitly specify a
4570 revision.
4565 revision.
4571
4566
4572 Using the -r/--rev or -d/--date options, revert the given files or
4567 Using the -r/--rev or -d/--date options, revert the given files or
4573 directories to their states as of a specific revision. Because
4568 directories to their states as of a specific revision. Because
4574 revert does not change the working directory parents, this will
4569 revert does not change the working directory parents, this will
4575 cause these files to appear modified. This can be helpful to "back
4570 cause these files to appear modified. This can be helpful to "back
4576 out" some or all of an earlier change. See :hg:`backout` for a
4571 out" some or all of an earlier change. See :hg:`backout` for a
4577 related method.
4572 related method.
4578
4573
4579 Modified files are saved with a .orig suffix before reverting.
4574 Modified files are saved with a .orig suffix before reverting.
4580 To disable these backups, use --no-backup. It is possible to store
4575 To disable these backups, use --no-backup. It is possible to store
4581 the backup files in a custom directory relative to the root of the
4576 the backup files in a custom directory relative to the root of the
4582 repository by setting the ``ui.origbackuppath`` configuration
4577 repository by setting the ``ui.origbackuppath`` configuration
4583 option.
4578 option.
4584
4579
4585 See :hg:`help dates` for a list of formats valid for -d/--date.
4580 See :hg:`help dates` for a list of formats valid for -d/--date.
4586
4581
4587 See :hg:`help backout` for a way to reverse the effect of an
4582 See :hg:`help backout` for a way to reverse the effect of an
4588 earlier changeset.
4583 earlier changeset.
4589
4584
4590 Returns 0 on success.
4585 Returns 0 on success.
4591 """
4586 """
4592
4587
4593 opts = pycompat.byteskwargs(opts)
4588 opts = pycompat.byteskwargs(opts)
4594 if opts.get("date"):
4589 if opts.get("date"):
4595 if opts.get("rev"):
4590 if opts.get("rev"):
4596 raise error.Abort(_("you can't specify a revision and a date"))
4591 raise error.Abort(_("you can't specify a revision and a date"))
4597 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4592 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4598
4593
4599 parent, p2 = repo.dirstate.parents()
4594 parent, p2 = repo.dirstate.parents()
4600 if not opts.get('rev') and p2 != nullid:
4595 if not opts.get('rev') and p2 != nullid:
4601 # revert after merge is a trap for new users (issue2915)
4596 # revert after merge is a trap for new users (issue2915)
4602 raise error.Abort(_('uncommitted merge with no revision specified'),
4597 raise error.Abort(_('uncommitted merge with no revision specified'),
4603 hint=_("use 'hg update' or see 'hg help revert'"))
4598 hint=_("use 'hg update' or see 'hg help revert'"))
4604
4599
4605 rev = opts.get('rev')
4600 rev = opts.get('rev')
4606 if rev:
4601 if rev:
4607 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
4602 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
4608 ctx = scmutil.revsingle(repo, rev)
4603 ctx = scmutil.revsingle(repo, rev)
4609
4604
4610 if (not (pats or opts.get('include') or opts.get('exclude') or
4605 if (not (pats or opts.get('include') or opts.get('exclude') or
4611 opts.get('all') or opts.get('interactive'))):
4606 opts.get('all') or opts.get('interactive'))):
4612 msg = _("no files or directories specified")
4607 msg = _("no files or directories specified")
4613 if p2 != nullid:
4608 if p2 != nullid:
4614 hint = _("uncommitted merge, use --all to discard all changes,"
4609 hint = _("uncommitted merge, use --all to discard all changes,"
4615 " or 'hg update -C .' to abort the merge")
4610 " or 'hg update -C .' to abort the merge")
4616 raise error.Abort(msg, hint=hint)
4611 raise error.Abort(msg, hint=hint)
4617 dirty = any(repo.status())
4612 dirty = any(repo.status())
4618 node = ctx.node()
4613 node = ctx.node()
4619 if node != parent:
4614 if node != parent:
4620 if dirty:
4615 if dirty:
4621 hint = _("uncommitted changes, use --all to discard all"
4616 hint = _("uncommitted changes, use --all to discard all"
4622 " changes, or 'hg update %s' to update") % ctx.rev()
4617 " changes, or 'hg update %s' to update") % ctx.rev()
4623 else:
4618 else:
4624 hint = _("use --all to revert all files,"
4619 hint = _("use --all to revert all files,"
4625 " or 'hg update %s' to update") % ctx.rev()
4620 " or 'hg update %s' to update") % ctx.rev()
4626 elif dirty:
4621 elif dirty:
4627 hint = _("uncommitted changes, use --all to discard all changes")
4622 hint = _("uncommitted changes, use --all to discard all changes")
4628 else:
4623 else:
4629 hint = _("use --all to revert all files")
4624 hint = _("use --all to revert all files")
4630 raise error.Abort(msg, hint=hint)
4625 raise error.Abort(msg, hint=hint)
4631
4626
4632 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats,
4627 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats,
4633 **pycompat.strkwargs(opts))
4628 **pycompat.strkwargs(opts))
4634
4629
4635 @command('rollback', dryrunopts +
4630 @command('rollback', dryrunopts +
4636 [('f', 'force', False, _('ignore safety measures'))])
4631 [('f', 'force', False, _('ignore safety measures'))])
4637 def rollback(ui, repo, **opts):
4632 def rollback(ui, repo, **opts):
4638 """roll back the last transaction (DANGEROUS) (DEPRECATED)
4633 """roll back the last transaction (DANGEROUS) (DEPRECATED)
4639
4634
4640 Please use :hg:`commit --amend` instead of rollback to correct
4635 Please use :hg:`commit --amend` instead of rollback to correct
4641 mistakes in the last commit.
4636 mistakes in the last commit.
4642
4637
4643 This command should be used with care. There is only one level of
4638 This command should be used with care. There is only one level of
4644 rollback, and there is no way to undo a rollback. It will also
4639 rollback, and there is no way to undo a rollback. It will also
4645 restore the dirstate at the time of the last transaction, losing
4640 restore the dirstate at the time of the last transaction, losing
4646 any dirstate changes since that time. This command does not alter
4641 any dirstate changes since that time. This command does not alter
4647 the working directory.
4642 the working directory.
4648
4643
4649 Transactions are used to encapsulate the effects of all commands
4644 Transactions are used to encapsulate the effects of all commands
4650 that create new changesets or propagate existing changesets into a
4645 that create new changesets or propagate existing changesets into a
4651 repository.
4646 repository.
4652
4647
4653 .. container:: verbose
4648 .. container:: verbose
4654
4649
4655 For example, the following commands are transactional, and their
4650 For example, the following commands are transactional, and their
4656 effects can be rolled back:
4651 effects can be rolled back:
4657
4652
4658 - commit
4653 - commit
4659 - import
4654 - import
4660 - pull
4655 - pull
4661 - push (with this repository as the destination)
4656 - push (with this repository as the destination)
4662 - unbundle
4657 - unbundle
4663
4658
4664 To avoid permanent data loss, rollback will refuse to rollback a
4659 To avoid permanent data loss, rollback will refuse to rollback a
4665 commit transaction if it isn't checked out. Use --force to
4660 commit transaction if it isn't checked out. Use --force to
4666 override this protection.
4661 override this protection.
4667
4662
4668 The rollback command can be entirely disabled by setting the
4663 The rollback command can be entirely disabled by setting the
4669 ``ui.rollback`` configuration setting to false. If you're here
4664 ``ui.rollback`` configuration setting to false. If you're here
4670 because you want to use rollback and it's disabled, you can
4665 because you want to use rollback and it's disabled, you can
4671 re-enable the command by setting ``ui.rollback`` to true.
4666 re-enable the command by setting ``ui.rollback`` to true.
4672
4667
4673 This command is not intended for use on public repositories. Once
4668 This command is not intended for use on public repositories. Once
4674 changes are visible for pull by other users, rolling a transaction
4669 changes are visible for pull by other users, rolling a transaction
4675 back locally is ineffective (someone else may already have pulled
4670 back locally is ineffective (someone else may already have pulled
4676 the changes). Furthermore, a race is possible with readers of the
4671 the changes). Furthermore, a race is possible with readers of the
4677 repository; for example an in-progress pull from the repository
4672 repository; for example an in-progress pull from the repository
4678 may fail if a rollback is performed.
4673 may fail if a rollback is performed.
4679
4674
4680 Returns 0 on success, 1 if no rollback data is available.
4675 Returns 0 on success, 1 if no rollback data is available.
4681 """
4676 """
4682 if not ui.configbool('ui', 'rollback'):
4677 if not ui.configbool('ui', 'rollback'):
4683 raise error.Abort(_('rollback is disabled because it is unsafe'),
4678 raise error.Abort(_('rollback is disabled because it is unsafe'),
4684 hint=('see `hg help -v rollback` for information'))
4679 hint=('see `hg help -v rollback` for information'))
4685 return repo.rollback(dryrun=opts.get(r'dry_run'),
4680 return repo.rollback(dryrun=opts.get(r'dry_run'),
4686 force=opts.get(r'force'))
4681 force=opts.get(r'force'))
4687
4682
4688 @command('root', [], cmdtype=readonly)
4683 @command('root', [], cmdtype=readonly)
4689 def root(ui, repo):
4684 def root(ui, repo):
4690 """print the root (top) of the current working directory
4685 """print the root (top) of the current working directory
4691
4686
4692 Print the root directory of the current repository.
4687 Print the root directory of the current repository.
4693
4688
4694 Returns 0 on success.
4689 Returns 0 on success.
4695 """
4690 """
4696 ui.write(repo.root + "\n")
4691 ui.write(repo.root + "\n")
4697
4692
4698 @command('^serve',
4693 @command('^serve',
4699 [('A', 'accesslog', '', _('name of access log file to write to'),
4694 [('A', 'accesslog', '', _('name of access log file to write to'),
4700 _('FILE')),
4695 _('FILE')),
4701 ('d', 'daemon', None, _('run server in background')),
4696 ('d', 'daemon', None, _('run server in background')),
4702 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
4697 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
4703 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4698 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4704 # use string type, then we can check if something was passed
4699 # use string type, then we can check if something was passed
4705 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4700 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4706 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4701 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4707 _('ADDR')),
4702 _('ADDR')),
4708 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4703 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4709 _('PREFIX')),
4704 _('PREFIX')),
4710 ('n', 'name', '',
4705 ('n', 'name', '',
4711 _('name to show in web pages (default: working directory)'), _('NAME')),
4706 _('name to show in web pages (default: working directory)'), _('NAME')),
4712 ('', 'web-conf', '',
4707 ('', 'web-conf', '',
4713 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
4708 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
4714 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4709 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4715 _('FILE')),
4710 _('FILE')),
4716 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4711 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4717 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
4712 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
4718 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
4713 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
4719 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4714 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4720 ('', 'style', '', _('template style to use'), _('STYLE')),
4715 ('', 'style', '', _('template style to use'), _('STYLE')),
4721 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4716 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4722 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))]
4717 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))]
4723 + subrepoopts,
4718 + subrepoopts,
4724 _('[OPTION]...'),
4719 _('[OPTION]...'),
4725 optionalrepo=True)
4720 optionalrepo=True)
4726 def serve(ui, repo, **opts):
4721 def serve(ui, repo, **opts):
4727 """start stand-alone webserver
4722 """start stand-alone webserver
4728
4723
4729 Start a local HTTP repository browser and pull server. You can use
4724 Start a local HTTP repository browser and pull server. You can use
4730 this for ad-hoc sharing and browsing of repositories. It is
4725 this for ad-hoc sharing and browsing of repositories. It is
4731 recommended to use a real web server to serve a repository for
4726 recommended to use a real web server to serve a repository for
4732 longer periods of time.
4727 longer periods of time.
4733
4728
4734 Please note that the server does not implement access control.
4729 Please note that the server does not implement access control.
4735 This means that, by default, anybody can read from the server and
4730 This means that, by default, anybody can read from the server and
4736 nobody can write to it by default. Set the ``web.allow-push``
4731 nobody can write to it by default. Set the ``web.allow-push``
4737 option to ``*`` to allow everybody to push to the server. You
4732 option to ``*`` to allow everybody to push to the server. You
4738 should use a real web server if you need to authenticate users.
4733 should use a real web server if you need to authenticate users.
4739
4734
4740 By default, the server logs accesses to stdout and errors to
4735 By default, the server logs accesses to stdout and errors to
4741 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4736 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4742 files.
4737 files.
4743
4738
4744 To have the server choose a free port number to listen on, specify
4739 To have the server choose a free port number to listen on, specify
4745 a port number of 0; in this case, the server will print the port
4740 a port number of 0; in this case, the server will print the port
4746 number it uses.
4741 number it uses.
4747
4742
4748 Returns 0 on success.
4743 Returns 0 on success.
4749 """
4744 """
4750
4745
4751 opts = pycompat.byteskwargs(opts)
4746 opts = pycompat.byteskwargs(opts)
4752 if opts["stdio"] and opts["cmdserver"]:
4747 if opts["stdio"] and opts["cmdserver"]:
4753 raise error.Abort(_("cannot use --stdio with --cmdserver"))
4748 raise error.Abort(_("cannot use --stdio with --cmdserver"))
4754
4749
4755 if opts["stdio"]:
4750 if opts["stdio"]:
4756 if repo is None:
4751 if repo is None:
4757 raise error.RepoError(_("there is no Mercurial repository here"
4752 raise error.RepoError(_("there is no Mercurial repository here"
4758 " (.hg not found)"))
4753 " (.hg not found)"))
4759 s = wireprotoserver.sshserver(ui, repo)
4754 s = wireprotoserver.sshserver(ui, repo)
4760 s.serve_forever()
4755 s.serve_forever()
4761
4756
4762 service = server.createservice(ui, repo, opts)
4757 service = server.createservice(ui, repo, opts)
4763 return server.runservice(opts, initfn=service.init, runfn=service.run)
4758 return server.runservice(opts, initfn=service.init, runfn=service.run)
4764
4759
4765 @command('^status|st',
4760 @command('^status|st',
4766 [('A', 'all', None, _('show status of all files')),
4761 [('A', 'all', None, _('show status of all files')),
4767 ('m', 'modified', None, _('show only modified files')),
4762 ('m', 'modified', None, _('show only modified files')),
4768 ('a', 'added', None, _('show only added files')),
4763 ('a', 'added', None, _('show only added files')),
4769 ('r', 'removed', None, _('show only removed files')),
4764 ('r', 'removed', None, _('show only removed files')),
4770 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4765 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4771 ('c', 'clean', None, _('show only files without changes')),
4766 ('c', 'clean', None, _('show only files without changes')),
4772 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4767 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4773 ('i', 'ignored', None, _('show only ignored files')),
4768 ('i', 'ignored', None, _('show only ignored files')),
4774 ('n', 'no-status', None, _('hide status prefix')),
4769 ('n', 'no-status', None, _('hide status prefix')),
4775 ('t', 'terse', '', _('show the terse output (EXPERIMENTAL)')),
4770 ('t', 'terse', '', _('show the terse output (EXPERIMENTAL)')),
4776 ('C', 'copies', None, _('show source of copied files')),
4771 ('C', 'copies', None, _('show source of copied files')),
4777 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4772 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4778 ('', 'rev', [], _('show difference from revision'), _('REV')),
4773 ('', 'rev', [], _('show difference from revision'), _('REV')),
4779 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4774 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4780 ] + walkopts + subrepoopts + formatteropts,
4775 ] + walkopts + subrepoopts + formatteropts,
4781 _('[OPTION]... [FILE]...'),
4776 _('[OPTION]... [FILE]...'),
4782 inferrepo=True, cmdtype=readonly)
4777 inferrepo=True, cmdtype=readonly)
4783 def status(ui, repo, *pats, **opts):
4778 def status(ui, repo, *pats, **opts):
4784 """show changed files in the working directory
4779 """show changed files in the working directory
4785
4780
4786 Show status of files in the repository. If names are given, only
4781 Show status of files in the repository. If names are given, only
4787 files that match are shown. Files that are clean or ignored or
4782 files that match are shown. Files that are clean or ignored or
4788 the source of a copy/move operation, are not listed unless
4783 the source of a copy/move operation, are not listed unless
4789 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4784 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4790 Unless options described with "show only ..." are given, the
4785 Unless options described with "show only ..." are given, the
4791 options -mardu are used.
4786 options -mardu are used.
4792
4787
4793 Option -q/--quiet hides untracked (unknown and ignored) files
4788 Option -q/--quiet hides untracked (unknown and ignored) files
4794 unless explicitly requested with -u/--unknown or -i/--ignored.
4789 unless explicitly requested with -u/--unknown or -i/--ignored.
4795
4790
4796 .. note::
4791 .. note::
4797
4792
4798 :hg:`status` may appear to disagree with diff if permissions have
4793 :hg:`status` may appear to disagree with diff if permissions have
4799 changed or a merge has occurred. The standard diff format does
4794 changed or a merge has occurred. The standard diff format does
4800 not report permission changes and diff only reports changes
4795 not report permission changes and diff only reports changes
4801 relative to one merge parent.
4796 relative to one merge parent.
4802
4797
4803 If one revision is given, it is used as the base revision.
4798 If one revision is given, it is used as the base revision.
4804 If two revisions are given, the differences between them are
4799 If two revisions are given, the differences between them are
4805 shown. The --change option can also be used as a shortcut to list
4800 shown. The --change option can also be used as a shortcut to list
4806 the changed files of a revision from its first parent.
4801 the changed files of a revision from its first parent.
4807
4802
4808 The codes used to show the status of files are::
4803 The codes used to show the status of files are::
4809
4804
4810 M = modified
4805 M = modified
4811 A = added
4806 A = added
4812 R = removed
4807 R = removed
4813 C = clean
4808 C = clean
4814 ! = missing (deleted by non-hg command, but still tracked)
4809 ! = missing (deleted by non-hg command, but still tracked)
4815 ? = not tracked
4810 ? = not tracked
4816 I = ignored
4811 I = ignored
4817 = origin of the previous file (with --copies)
4812 = origin of the previous file (with --copies)
4818
4813
4819 .. container:: verbose
4814 .. container:: verbose
4820
4815
4821 The -t/--terse option abbreviates the output by showing only the directory
4816 The -t/--terse option abbreviates the output by showing only the directory
4822 name if all the files in it share the same status. The option takes an
4817 name if all the files in it share the same status. The option takes an
4823 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
4818 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
4824 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
4819 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
4825 for 'ignored' and 'c' for clean.
4820 for 'ignored' and 'c' for clean.
4826
4821
4827 It abbreviates only those statuses which are passed. Note that clean and
4822 It abbreviates only those statuses which are passed. Note that clean and
4828 ignored files are not displayed with '--terse ic' unless the -c/--clean
4823 ignored files are not displayed with '--terse ic' unless the -c/--clean
4829 and -i/--ignored options are also used.
4824 and -i/--ignored options are also used.
4830
4825
4831 The -v/--verbose option shows information when the repository is in an
4826 The -v/--verbose option shows information when the repository is in an
4832 unfinished merge, shelve, rebase state etc. You can have this behavior
4827 unfinished merge, shelve, rebase state etc. You can have this behavior
4833 turned on by default by enabling the ``commands.status.verbose`` option.
4828 turned on by default by enabling the ``commands.status.verbose`` option.
4834
4829
4835 You can skip displaying some of these states by setting
4830 You can skip displaying some of these states by setting
4836 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
4831 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
4837 'histedit', 'merge', 'rebase', or 'unshelve'.
4832 'histedit', 'merge', 'rebase', or 'unshelve'.
4838
4833
4839 Examples:
4834 Examples:
4840
4835
4841 - show changes in the working directory relative to a
4836 - show changes in the working directory relative to a
4842 changeset::
4837 changeset::
4843
4838
4844 hg status --rev 9353
4839 hg status --rev 9353
4845
4840
4846 - show changes in the working directory relative to the
4841 - show changes in the working directory relative to the
4847 current directory (see :hg:`help patterns` for more information)::
4842 current directory (see :hg:`help patterns` for more information)::
4848
4843
4849 hg status re:
4844 hg status re:
4850
4845
4851 - show all changes including copies in an existing changeset::
4846 - show all changes including copies in an existing changeset::
4852
4847
4853 hg status --copies --change 9353
4848 hg status --copies --change 9353
4854
4849
4855 - get a NUL separated list of added files, suitable for xargs::
4850 - get a NUL separated list of added files, suitable for xargs::
4856
4851
4857 hg status -an0
4852 hg status -an0
4858
4853
4859 - show more information about the repository status, abbreviating
4854 - show more information about the repository status, abbreviating
4860 added, removed, modified, deleted, and untracked paths::
4855 added, removed, modified, deleted, and untracked paths::
4861
4856
4862 hg status -v -t mardu
4857 hg status -v -t mardu
4863
4858
4864 Returns 0 on success.
4859 Returns 0 on success.
4865
4860
4866 """
4861 """
4867
4862
4868 opts = pycompat.byteskwargs(opts)
4863 opts = pycompat.byteskwargs(opts)
4869 revs = opts.get('rev')
4864 revs = opts.get('rev')
4870 change = opts.get('change')
4865 change = opts.get('change')
4871 terse = opts.get('terse')
4866 terse = opts.get('terse')
4872
4867
4873 if revs and change:
4868 if revs and change:
4874 msg = _('cannot specify --rev and --change at the same time')
4869 msg = _('cannot specify --rev and --change at the same time')
4875 raise error.Abort(msg)
4870 raise error.Abort(msg)
4876 elif revs and terse:
4871 elif revs and terse:
4877 msg = _('cannot use --terse with --rev')
4872 msg = _('cannot use --terse with --rev')
4878 raise error.Abort(msg)
4873 raise error.Abort(msg)
4879 elif change:
4874 elif change:
4880 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
4875 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
4881 ctx2 = scmutil.revsingle(repo, change, None)
4876 ctx2 = scmutil.revsingle(repo, change, None)
4882 ctx1 = ctx2.p1()
4877 ctx1 = ctx2.p1()
4883 else:
4878 else:
4884 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
4879 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
4885 ctx1, ctx2 = scmutil.revpair(repo, revs)
4880 ctx1, ctx2 = scmutil.revpair(repo, revs)
4886
4881
4887 if pats or ui.configbool('commands', 'status.relative'):
4882 if pats or ui.configbool('commands', 'status.relative'):
4888 cwd = repo.getcwd()
4883 cwd = repo.getcwd()
4889 else:
4884 else:
4890 cwd = ''
4885 cwd = ''
4891
4886
4892 if opts.get('print0'):
4887 if opts.get('print0'):
4893 end = '\0'
4888 end = '\0'
4894 else:
4889 else:
4895 end = '\n'
4890 end = '\n'
4896 copy = {}
4891 copy = {}
4897 states = 'modified added removed deleted unknown ignored clean'.split()
4892 states = 'modified added removed deleted unknown ignored clean'.split()
4898 show = [k for k in states if opts.get(k)]
4893 show = [k for k in states if opts.get(k)]
4899 if opts.get('all'):
4894 if opts.get('all'):
4900 show += ui.quiet and (states[:4] + ['clean']) or states
4895 show += ui.quiet and (states[:4] + ['clean']) or states
4901
4896
4902 if not show:
4897 if not show:
4903 if ui.quiet:
4898 if ui.quiet:
4904 show = states[:4]
4899 show = states[:4]
4905 else:
4900 else:
4906 show = states[:5]
4901 show = states[:5]
4907
4902
4908 m = scmutil.match(ctx2, pats, opts)
4903 m = scmutil.match(ctx2, pats, opts)
4909 if terse:
4904 if terse:
4910 # we need to compute clean and unknown to terse
4905 # we need to compute clean and unknown to terse
4911 stat = repo.status(ctx1.node(), ctx2.node(), m,
4906 stat = repo.status(ctx1.node(), ctx2.node(), m,
4912 'ignored' in show or 'i' in terse,
4907 'ignored' in show or 'i' in terse,
4913 True, True, opts.get('subrepos'))
4908 True, True, opts.get('subrepos'))
4914
4909
4915 stat = cmdutil.tersedir(stat, terse)
4910 stat = cmdutil.tersedir(stat, terse)
4916 else:
4911 else:
4917 stat = repo.status(ctx1.node(), ctx2.node(), m,
4912 stat = repo.status(ctx1.node(), ctx2.node(), m,
4918 'ignored' in show, 'clean' in show,
4913 'ignored' in show, 'clean' in show,
4919 'unknown' in show, opts.get('subrepos'))
4914 'unknown' in show, opts.get('subrepos'))
4920
4915
4921 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
4916 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
4922
4917
4923 if (opts.get('all') or opts.get('copies')
4918 if (opts.get('all') or opts.get('copies')
4924 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
4919 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
4925 copy = copies.pathcopies(ctx1, ctx2, m)
4920 copy = copies.pathcopies(ctx1, ctx2, m)
4926
4921
4927 ui.pager('status')
4922 ui.pager('status')
4928 fm = ui.formatter('status', opts)
4923 fm = ui.formatter('status', opts)
4929 fmt = '%s' + end
4924 fmt = '%s' + end
4930 showchar = not opts.get('no_status')
4925 showchar = not opts.get('no_status')
4931
4926
4932 for state, char, files in changestates:
4927 for state, char, files in changestates:
4933 if state in show:
4928 if state in show:
4934 label = 'status.' + state
4929 label = 'status.' + state
4935 for f in files:
4930 for f in files:
4936 fm.startitem()
4931 fm.startitem()
4937 fm.condwrite(showchar, 'status', '%s ', char, label=label)
4932 fm.condwrite(showchar, 'status', '%s ', char, label=label)
4938 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
4933 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
4939 if f in copy:
4934 if f in copy:
4940 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
4935 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
4941 label='status.copied')
4936 label='status.copied')
4942
4937
4943 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
4938 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
4944 and not ui.plain()):
4939 and not ui.plain()):
4945 cmdutil.morestatus(repo, fm)
4940 cmdutil.morestatus(repo, fm)
4946 fm.end()
4941 fm.end()
4947
4942
4948 @command('^summary|sum',
4943 @command('^summary|sum',
4949 [('', 'remote', None, _('check for push and pull'))],
4944 [('', 'remote', None, _('check for push and pull'))],
4950 '[--remote]', cmdtype=readonly)
4945 '[--remote]', cmdtype=readonly)
4951 def summary(ui, repo, **opts):
4946 def summary(ui, repo, **opts):
4952 """summarize working directory state
4947 """summarize working directory state
4953
4948
4954 This generates a brief summary of the working directory state,
4949 This generates a brief summary of the working directory state,
4955 including parents, branch, commit status, phase and available updates.
4950 including parents, branch, commit status, phase and available updates.
4956
4951
4957 With the --remote option, this will check the default paths for
4952 With the --remote option, this will check the default paths for
4958 incoming and outgoing changes. This can be time-consuming.
4953 incoming and outgoing changes. This can be time-consuming.
4959
4954
4960 Returns 0 on success.
4955 Returns 0 on success.
4961 """
4956 """
4962
4957
4963 opts = pycompat.byteskwargs(opts)
4958 opts = pycompat.byteskwargs(opts)
4964 ui.pager('summary')
4959 ui.pager('summary')
4965 ctx = repo[None]
4960 ctx = repo[None]
4966 parents = ctx.parents()
4961 parents = ctx.parents()
4967 pnode = parents[0].node()
4962 pnode = parents[0].node()
4968 marks = []
4963 marks = []
4969
4964
4970 ms = None
4965 ms = None
4971 try:
4966 try:
4972 ms = mergemod.mergestate.read(repo)
4967 ms = mergemod.mergestate.read(repo)
4973 except error.UnsupportedMergeRecords as e:
4968 except error.UnsupportedMergeRecords as e:
4974 s = ' '.join(e.recordtypes)
4969 s = ' '.join(e.recordtypes)
4975 ui.warn(
4970 ui.warn(
4976 _('warning: merge state has unsupported record types: %s\n') % s)
4971 _('warning: merge state has unsupported record types: %s\n') % s)
4977 unresolved = []
4972 unresolved = []
4978 else:
4973 else:
4979 unresolved = list(ms.unresolved())
4974 unresolved = list(ms.unresolved())
4980
4975
4981 for p in parents:
4976 for p in parents:
4982 # label with log.changeset (instead of log.parent) since this
4977 # label with log.changeset (instead of log.parent) since this
4983 # shows a working directory parent *changeset*:
4978 # shows a working directory parent *changeset*:
4984 # i18n: column positioning for "hg summary"
4979 # i18n: column positioning for "hg summary"
4985 ui.write(_('parent: %d:%s ') % (p.rev(), p),
4980 ui.write(_('parent: %d:%s ') % (p.rev(), p),
4986 label=logcmdutil.changesetlabels(p))
4981 label=logcmdutil.changesetlabels(p))
4987 ui.write(' '.join(p.tags()), label='log.tag')
4982 ui.write(' '.join(p.tags()), label='log.tag')
4988 if p.bookmarks():
4983 if p.bookmarks():
4989 marks.extend(p.bookmarks())
4984 marks.extend(p.bookmarks())
4990 if p.rev() == -1:
4985 if p.rev() == -1:
4991 if not len(repo):
4986 if not len(repo):
4992 ui.write(_(' (empty repository)'))
4987 ui.write(_(' (empty repository)'))
4993 else:
4988 else:
4994 ui.write(_(' (no revision checked out)'))
4989 ui.write(_(' (no revision checked out)'))
4995 if p.obsolete():
4990 if p.obsolete():
4996 ui.write(_(' (obsolete)'))
4991 ui.write(_(' (obsolete)'))
4997 if p.isunstable():
4992 if p.isunstable():
4998 instabilities = (ui.label(instability, 'trouble.%s' % instability)
4993 instabilities = (ui.label(instability, 'trouble.%s' % instability)
4999 for instability in p.instabilities())
4994 for instability in p.instabilities())
5000 ui.write(' ('
4995 ui.write(' ('
5001 + ', '.join(instabilities)
4996 + ', '.join(instabilities)
5002 + ')')
4997 + ')')
5003 ui.write('\n')
4998 ui.write('\n')
5004 if p.description():
4999 if p.description():
5005 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5000 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5006 label='log.summary')
5001 label='log.summary')
5007
5002
5008 branch = ctx.branch()
5003 branch = ctx.branch()
5009 bheads = repo.branchheads(branch)
5004 bheads = repo.branchheads(branch)
5010 # i18n: column positioning for "hg summary"
5005 # i18n: column positioning for "hg summary"
5011 m = _('branch: %s\n') % branch
5006 m = _('branch: %s\n') % branch
5012 if branch != 'default':
5007 if branch != 'default':
5013 ui.write(m, label='log.branch')
5008 ui.write(m, label='log.branch')
5014 else:
5009 else:
5015 ui.status(m, label='log.branch')
5010 ui.status(m, label='log.branch')
5016
5011
5017 if marks:
5012 if marks:
5018 active = repo._activebookmark
5013 active = repo._activebookmark
5019 # i18n: column positioning for "hg summary"
5014 # i18n: column positioning for "hg summary"
5020 ui.write(_('bookmarks:'), label='log.bookmark')
5015 ui.write(_('bookmarks:'), label='log.bookmark')
5021 if active is not None:
5016 if active is not None:
5022 if active in marks:
5017 if active in marks:
5023 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
5018 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
5024 marks.remove(active)
5019 marks.remove(active)
5025 else:
5020 else:
5026 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
5021 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
5027 for m in marks:
5022 for m in marks:
5028 ui.write(' ' + m, label='log.bookmark')
5023 ui.write(' ' + m, label='log.bookmark')
5029 ui.write('\n', label='log.bookmark')
5024 ui.write('\n', label='log.bookmark')
5030
5025
5031 status = repo.status(unknown=True)
5026 status = repo.status(unknown=True)
5032
5027
5033 c = repo.dirstate.copies()
5028 c = repo.dirstate.copies()
5034 copied, renamed = [], []
5029 copied, renamed = [], []
5035 for d, s in c.iteritems():
5030 for d, s in c.iteritems():
5036 if s in status.removed:
5031 if s in status.removed:
5037 status.removed.remove(s)
5032 status.removed.remove(s)
5038 renamed.append(d)
5033 renamed.append(d)
5039 else:
5034 else:
5040 copied.append(d)
5035 copied.append(d)
5041 if d in status.added:
5036 if d in status.added:
5042 status.added.remove(d)
5037 status.added.remove(d)
5043
5038
5044 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5039 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5045
5040
5046 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5041 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5047 (ui.label(_('%d added'), 'status.added'), status.added),
5042 (ui.label(_('%d added'), 'status.added'), status.added),
5048 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5043 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5049 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5044 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5050 (ui.label(_('%d copied'), 'status.copied'), copied),
5045 (ui.label(_('%d copied'), 'status.copied'), copied),
5051 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5046 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5052 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5047 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5053 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5048 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5054 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5049 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5055 t = []
5050 t = []
5056 for l, s in labels:
5051 for l, s in labels:
5057 if s:
5052 if s:
5058 t.append(l % len(s))
5053 t.append(l % len(s))
5059
5054
5060 t = ', '.join(t)
5055 t = ', '.join(t)
5061 cleanworkdir = False
5056 cleanworkdir = False
5062
5057
5063 if repo.vfs.exists('graftstate'):
5058 if repo.vfs.exists('graftstate'):
5064 t += _(' (graft in progress)')
5059 t += _(' (graft in progress)')
5065 if repo.vfs.exists('updatestate'):
5060 if repo.vfs.exists('updatestate'):
5066 t += _(' (interrupted update)')
5061 t += _(' (interrupted update)')
5067 elif len(parents) > 1:
5062 elif len(parents) > 1:
5068 t += _(' (merge)')
5063 t += _(' (merge)')
5069 elif branch != parents[0].branch():
5064 elif branch != parents[0].branch():
5070 t += _(' (new branch)')
5065 t += _(' (new branch)')
5071 elif (parents[0].closesbranch() and
5066 elif (parents[0].closesbranch() and
5072 pnode in repo.branchheads(branch, closed=True)):
5067 pnode in repo.branchheads(branch, closed=True)):
5073 t += _(' (head closed)')
5068 t += _(' (head closed)')
5074 elif not (status.modified or status.added or status.removed or renamed or
5069 elif not (status.modified or status.added or status.removed or renamed or
5075 copied or subs):
5070 copied or subs):
5076 t += _(' (clean)')
5071 t += _(' (clean)')
5077 cleanworkdir = True
5072 cleanworkdir = True
5078 elif pnode not in bheads:
5073 elif pnode not in bheads:
5079 t += _(' (new branch head)')
5074 t += _(' (new branch head)')
5080
5075
5081 if parents:
5076 if parents:
5082 pendingphase = max(p.phase() for p in parents)
5077 pendingphase = max(p.phase() for p in parents)
5083 else:
5078 else:
5084 pendingphase = phases.public
5079 pendingphase = phases.public
5085
5080
5086 if pendingphase > phases.newcommitphase(ui):
5081 if pendingphase > phases.newcommitphase(ui):
5087 t += ' (%s)' % phases.phasenames[pendingphase]
5082 t += ' (%s)' % phases.phasenames[pendingphase]
5088
5083
5089 if cleanworkdir:
5084 if cleanworkdir:
5090 # i18n: column positioning for "hg summary"
5085 # i18n: column positioning for "hg summary"
5091 ui.status(_('commit: %s\n') % t.strip())
5086 ui.status(_('commit: %s\n') % t.strip())
5092 else:
5087 else:
5093 # i18n: column positioning for "hg summary"
5088 # i18n: column positioning for "hg summary"
5094 ui.write(_('commit: %s\n') % t.strip())
5089 ui.write(_('commit: %s\n') % t.strip())
5095
5090
5096 # all ancestors of branch heads - all ancestors of parent = new csets
5091 # all ancestors of branch heads - all ancestors of parent = new csets
5097 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5092 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5098 bheads))
5093 bheads))
5099
5094
5100 if new == 0:
5095 if new == 0:
5101 # i18n: column positioning for "hg summary"
5096 # i18n: column positioning for "hg summary"
5102 ui.status(_('update: (current)\n'))
5097 ui.status(_('update: (current)\n'))
5103 elif pnode not in bheads:
5098 elif pnode not in bheads:
5104 # i18n: column positioning for "hg summary"
5099 # i18n: column positioning for "hg summary"
5105 ui.write(_('update: %d new changesets (update)\n') % new)
5100 ui.write(_('update: %d new changesets (update)\n') % new)
5106 else:
5101 else:
5107 # i18n: column positioning for "hg summary"
5102 # i18n: column positioning for "hg summary"
5108 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5103 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5109 (new, len(bheads)))
5104 (new, len(bheads)))
5110
5105
5111 t = []
5106 t = []
5112 draft = len(repo.revs('draft()'))
5107 draft = len(repo.revs('draft()'))
5113 if draft:
5108 if draft:
5114 t.append(_('%d draft') % draft)
5109 t.append(_('%d draft') % draft)
5115 secret = len(repo.revs('secret()'))
5110 secret = len(repo.revs('secret()'))
5116 if secret:
5111 if secret:
5117 t.append(_('%d secret') % secret)
5112 t.append(_('%d secret') % secret)
5118
5113
5119 if draft or secret:
5114 if draft or secret:
5120 ui.status(_('phases: %s\n') % ', '.join(t))
5115 ui.status(_('phases: %s\n') % ', '.join(t))
5121
5116
5122 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5117 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5123 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5118 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5124 numtrouble = len(repo.revs(trouble + "()"))
5119 numtrouble = len(repo.revs(trouble + "()"))
5125 # We write all the possibilities to ease translation
5120 # We write all the possibilities to ease translation
5126 troublemsg = {
5121 troublemsg = {
5127 "orphan": _("orphan: %d changesets"),
5122 "orphan": _("orphan: %d changesets"),
5128 "contentdivergent": _("content-divergent: %d changesets"),
5123 "contentdivergent": _("content-divergent: %d changesets"),
5129 "phasedivergent": _("phase-divergent: %d changesets"),
5124 "phasedivergent": _("phase-divergent: %d changesets"),
5130 }
5125 }
5131 if numtrouble > 0:
5126 if numtrouble > 0:
5132 ui.status(troublemsg[trouble] % numtrouble + "\n")
5127 ui.status(troublemsg[trouble] % numtrouble + "\n")
5133
5128
5134 cmdutil.summaryhooks(ui, repo)
5129 cmdutil.summaryhooks(ui, repo)
5135
5130
5136 if opts.get('remote'):
5131 if opts.get('remote'):
5137 needsincoming, needsoutgoing = True, True
5132 needsincoming, needsoutgoing = True, True
5138 else:
5133 else:
5139 needsincoming, needsoutgoing = False, False
5134 needsincoming, needsoutgoing = False, False
5140 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5135 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5141 if i:
5136 if i:
5142 needsincoming = True
5137 needsincoming = True
5143 if o:
5138 if o:
5144 needsoutgoing = True
5139 needsoutgoing = True
5145 if not needsincoming and not needsoutgoing:
5140 if not needsincoming and not needsoutgoing:
5146 return
5141 return
5147
5142
5148 def getincoming():
5143 def getincoming():
5149 source, branches = hg.parseurl(ui.expandpath('default'))
5144 source, branches = hg.parseurl(ui.expandpath('default'))
5150 sbranch = branches[0]
5145 sbranch = branches[0]
5151 try:
5146 try:
5152 other = hg.peer(repo, {}, source)
5147 other = hg.peer(repo, {}, source)
5153 except error.RepoError:
5148 except error.RepoError:
5154 if opts.get('remote'):
5149 if opts.get('remote'):
5155 raise
5150 raise
5156 return source, sbranch, None, None, None
5151 return source, sbranch, None, None, None
5157 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5152 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5158 if revs:
5153 if revs:
5159 revs = [other.lookup(rev) for rev in revs]
5154 revs = [other.lookup(rev) for rev in revs]
5160 ui.debug('comparing with %s\n' % util.hidepassword(source))
5155 ui.debug('comparing with %s\n' % util.hidepassword(source))
5161 repo.ui.pushbuffer()
5156 repo.ui.pushbuffer()
5162 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5157 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5163 repo.ui.popbuffer()
5158 repo.ui.popbuffer()
5164 return source, sbranch, other, commoninc, commoninc[1]
5159 return source, sbranch, other, commoninc, commoninc[1]
5165
5160
5166 if needsincoming:
5161 if needsincoming:
5167 source, sbranch, sother, commoninc, incoming = getincoming()
5162 source, sbranch, sother, commoninc, incoming = getincoming()
5168 else:
5163 else:
5169 source = sbranch = sother = commoninc = incoming = None
5164 source = sbranch = sother = commoninc = incoming = None
5170
5165
5171 def getoutgoing():
5166 def getoutgoing():
5172 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5167 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5173 dbranch = branches[0]
5168 dbranch = branches[0]
5174 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5169 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5175 if source != dest:
5170 if source != dest:
5176 try:
5171 try:
5177 dother = hg.peer(repo, {}, dest)
5172 dother = hg.peer(repo, {}, dest)
5178 except error.RepoError:
5173 except error.RepoError:
5179 if opts.get('remote'):
5174 if opts.get('remote'):
5180 raise
5175 raise
5181 return dest, dbranch, None, None
5176 return dest, dbranch, None, None
5182 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5177 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5183 elif sother is None:
5178 elif sother is None:
5184 # there is no explicit destination peer, but source one is invalid
5179 # there is no explicit destination peer, but source one is invalid
5185 return dest, dbranch, None, None
5180 return dest, dbranch, None, None
5186 else:
5181 else:
5187 dother = sother
5182 dother = sother
5188 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5183 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5189 common = None
5184 common = None
5190 else:
5185 else:
5191 common = commoninc
5186 common = commoninc
5192 if revs:
5187 if revs:
5193 revs = [repo.lookup(rev) for rev in revs]
5188 revs = [repo.lookup(rev) for rev in revs]
5194 repo.ui.pushbuffer()
5189 repo.ui.pushbuffer()
5195 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5190 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5196 commoninc=common)
5191 commoninc=common)
5197 repo.ui.popbuffer()
5192 repo.ui.popbuffer()
5198 return dest, dbranch, dother, outgoing
5193 return dest, dbranch, dother, outgoing
5199
5194
5200 if needsoutgoing:
5195 if needsoutgoing:
5201 dest, dbranch, dother, outgoing = getoutgoing()
5196 dest, dbranch, dother, outgoing = getoutgoing()
5202 else:
5197 else:
5203 dest = dbranch = dother = outgoing = None
5198 dest = dbranch = dother = outgoing = None
5204
5199
5205 if opts.get('remote'):
5200 if opts.get('remote'):
5206 t = []
5201 t = []
5207 if incoming:
5202 if incoming:
5208 t.append(_('1 or more incoming'))
5203 t.append(_('1 or more incoming'))
5209 o = outgoing.missing
5204 o = outgoing.missing
5210 if o:
5205 if o:
5211 t.append(_('%d outgoing') % len(o))
5206 t.append(_('%d outgoing') % len(o))
5212 other = dother or sother
5207 other = dother or sother
5213 if 'bookmarks' in other.listkeys('namespaces'):
5208 if 'bookmarks' in other.listkeys('namespaces'):
5214 counts = bookmarks.summary(repo, other)
5209 counts = bookmarks.summary(repo, other)
5215 if counts[0] > 0:
5210 if counts[0] > 0:
5216 t.append(_('%d incoming bookmarks') % counts[0])
5211 t.append(_('%d incoming bookmarks') % counts[0])
5217 if counts[1] > 0:
5212 if counts[1] > 0:
5218 t.append(_('%d outgoing bookmarks') % counts[1])
5213 t.append(_('%d outgoing bookmarks') % counts[1])
5219
5214
5220 if t:
5215 if t:
5221 # i18n: column positioning for "hg summary"
5216 # i18n: column positioning for "hg summary"
5222 ui.write(_('remote: %s\n') % (', '.join(t)))
5217 ui.write(_('remote: %s\n') % (', '.join(t)))
5223 else:
5218 else:
5224 # i18n: column positioning for "hg summary"
5219 # i18n: column positioning for "hg summary"
5225 ui.status(_('remote: (synced)\n'))
5220 ui.status(_('remote: (synced)\n'))
5226
5221
5227 cmdutil.summaryremotehooks(ui, repo, opts,
5222 cmdutil.summaryremotehooks(ui, repo, opts,
5228 ((source, sbranch, sother, commoninc),
5223 ((source, sbranch, sother, commoninc),
5229 (dest, dbranch, dother, outgoing)))
5224 (dest, dbranch, dother, outgoing)))
5230
5225
5231 @command('tag',
5226 @command('tag',
5232 [('f', 'force', None, _('force tag')),
5227 [('f', 'force', None, _('force tag')),
5233 ('l', 'local', None, _('make the tag local')),
5228 ('l', 'local', None, _('make the tag local')),
5234 ('r', 'rev', '', _('revision to tag'), _('REV')),
5229 ('r', 'rev', '', _('revision to tag'), _('REV')),
5235 ('', 'remove', None, _('remove a tag')),
5230 ('', 'remove', None, _('remove a tag')),
5236 # -l/--local is already there, commitopts cannot be used
5231 # -l/--local is already there, commitopts cannot be used
5237 ('e', 'edit', None, _('invoke editor on commit messages')),
5232 ('e', 'edit', None, _('invoke editor on commit messages')),
5238 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5233 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5239 ] + commitopts2,
5234 ] + commitopts2,
5240 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5235 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5241 def tag(ui, repo, name1, *names, **opts):
5236 def tag(ui, repo, name1, *names, **opts):
5242 """add one or more tags for the current or given revision
5237 """add one or more tags for the current or given revision
5243
5238
5244 Name a particular revision using <name>.
5239 Name a particular revision using <name>.
5245
5240
5246 Tags are used to name particular revisions of the repository and are
5241 Tags are used to name particular revisions of the repository and are
5247 very useful to compare different revisions, to go back to significant
5242 very useful to compare different revisions, to go back to significant
5248 earlier versions or to mark branch points as releases, etc. Changing
5243 earlier versions or to mark branch points as releases, etc. Changing
5249 an existing tag is normally disallowed; use -f/--force to override.
5244 an existing tag is normally disallowed; use -f/--force to override.
5250
5245
5251 If no revision is given, the parent of the working directory is
5246 If no revision is given, the parent of the working directory is
5252 used.
5247 used.
5253
5248
5254 To facilitate version control, distribution, and merging of tags,
5249 To facilitate version control, distribution, and merging of tags,
5255 they are stored as a file named ".hgtags" which is managed similarly
5250 they are stored as a file named ".hgtags" which is managed similarly
5256 to other project files and can be hand-edited if necessary. This
5251 to other project files and can be hand-edited if necessary. This
5257 also means that tagging creates a new commit. The file
5252 also means that tagging creates a new commit. The file
5258 ".hg/localtags" is used for local tags (not shared among
5253 ".hg/localtags" is used for local tags (not shared among
5259 repositories).
5254 repositories).
5260
5255
5261 Tag commits are usually made at the head of a branch. If the parent
5256 Tag commits are usually made at the head of a branch. If the parent
5262 of the working directory is not a branch head, :hg:`tag` aborts; use
5257 of the working directory is not a branch head, :hg:`tag` aborts; use
5263 -f/--force to force the tag commit to be based on a non-head
5258 -f/--force to force the tag commit to be based on a non-head
5264 changeset.
5259 changeset.
5265
5260
5266 See :hg:`help dates` for a list of formats valid for -d/--date.
5261 See :hg:`help dates` for a list of formats valid for -d/--date.
5267
5262
5268 Since tag names have priority over branch names during revision
5263 Since tag names have priority over branch names during revision
5269 lookup, using an existing branch name as a tag name is discouraged.
5264 lookup, using an existing branch name as a tag name is discouraged.
5270
5265
5271 Returns 0 on success.
5266 Returns 0 on success.
5272 """
5267 """
5273 opts = pycompat.byteskwargs(opts)
5268 opts = pycompat.byteskwargs(opts)
5274 wlock = lock = None
5269 wlock = lock = None
5275 try:
5270 try:
5276 wlock = repo.wlock()
5271 wlock = repo.wlock()
5277 lock = repo.lock()
5272 lock = repo.lock()
5278 rev_ = "."
5273 rev_ = "."
5279 names = [t.strip() for t in (name1,) + names]
5274 names = [t.strip() for t in (name1,) + names]
5280 if len(names) != len(set(names)):
5275 if len(names) != len(set(names)):
5281 raise error.Abort(_('tag names must be unique'))
5276 raise error.Abort(_('tag names must be unique'))
5282 for n in names:
5277 for n in names:
5283 scmutil.checknewlabel(repo, n, 'tag')
5278 scmutil.checknewlabel(repo, n, 'tag')
5284 if not n:
5279 if not n:
5285 raise error.Abort(_('tag names cannot consist entirely of '
5280 raise error.Abort(_('tag names cannot consist entirely of '
5286 'whitespace'))
5281 'whitespace'))
5287 if opts.get('rev') and opts.get('remove'):
5282 if opts.get('rev') and opts.get('remove'):
5288 raise error.Abort(_("--rev and --remove are incompatible"))
5283 raise error.Abort(_("--rev and --remove are incompatible"))
5289 if opts.get('rev'):
5284 if opts.get('rev'):
5290 rev_ = opts['rev']
5285 rev_ = opts['rev']
5291 message = opts.get('message')
5286 message = opts.get('message')
5292 if opts.get('remove'):
5287 if opts.get('remove'):
5293 if opts.get('local'):
5288 if opts.get('local'):
5294 expectedtype = 'local'
5289 expectedtype = 'local'
5295 else:
5290 else:
5296 expectedtype = 'global'
5291 expectedtype = 'global'
5297
5292
5298 for n in names:
5293 for n in names:
5299 if not repo.tagtype(n):
5294 if not repo.tagtype(n):
5300 raise error.Abort(_("tag '%s' does not exist") % n)
5295 raise error.Abort(_("tag '%s' does not exist") % n)
5301 if repo.tagtype(n) != expectedtype:
5296 if repo.tagtype(n) != expectedtype:
5302 if expectedtype == 'global':
5297 if expectedtype == 'global':
5303 raise error.Abort(_("tag '%s' is not a global tag") % n)
5298 raise error.Abort(_("tag '%s' is not a global tag") % n)
5304 else:
5299 else:
5305 raise error.Abort(_("tag '%s' is not a local tag") % n)
5300 raise error.Abort(_("tag '%s' is not a local tag") % n)
5306 rev_ = 'null'
5301 rev_ = 'null'
5307 if not message:
5302 if not message:
5308 # we don't translate commit messages
5303 # we don't translate commit messages
5309 message = 'Removed tag %s' % ', '.join(names)
5304 message = 'Removed tag %s' % ', '.join(names)
5310 elif not opts.get('force'):
5305 elif not opts.get('force'):
5311 for n in names:
5306 for n in names:
5312 if n in repo.tags():
5307 if n in repo.tags():
5313 raise error.Abort(_("tag '%s' already exists "
5308 raise error.Abort(_("tag '%s' already exists "
5314 "(use -f to force)") % n)
5309 "(use -f to force)") % n)
5315 if not opts.get('local'):
5310 if not opts.get('local'):
5316 p1, p2 = repo.dirstate.parents()
5311 p1, p2 = repo.dirstate.parents()
5317 if p2 != nullid:
5312 if p2 != nullid:
5318 raise error.Abort(_('uncommitted merge'))
5313 raise error.Abort(_('uncommitted merge'))
5319 bheads = repo.branchheads()
5314 bheads = repo.branchheads()
5320 if not opts.get('force') and bheads and p1 not in bheads:
5315 if not opts.get('force') and bheads and p1 not in bheads:
5321 raise error.Abort(_('working directory is not at a branch head '
5316 raise error.Abort(_('working directory is not at a branch head '
5322 '(use -f to force)'))
5317 '(use -f to force)'))
5323 node = scmutil.revsingle(repo, rev_).node()
5318 node = scmutil.revsingle(repo, rev_).node()
5324
5319
5325 if not message:
5320 if not message:
5326 # we don't translate commit messages
5321 # we don't translate commit messages
5327 message = ('Added tag %s for changeset %s' %
5322 message = ('Added tag %s for changeset %s' %
5328 (', '.join(names), short(node)))
5323 (', '.join(names), short(node)))
5329
5324
5330 date = opts.get('date')
5325 date = opts.get('date')
5331 if date:
5326 if date:
5332 date = dateutil.parsedate(date)
5327 date = dateutil.parsedate(date)
5333
5328
5334 if opts.get('remove'):
5329 if opts.get('remove'):
5335 editform = 'tag.remove'
5330 editform = 'tag.remove'
5336 else:
5331 else:
5337 editform = 'tag.add'
5332 editform = 'tag.add'
5338 editor = cmdutil.getcommiteditor(editform=editform,
5333 editor = cmdutil.getcommiteditor(editform=editform,
5339 **pycompat.strkwargs(opts))
5334 **pycompat.strkwargs(opts))
5340
5335
5341 # don't allow tagging the null rev
5336 # don't allow tagging the null rev
5342 if (not opts.get('remove') and
5337 if (not opts.get('remove') and
5343 scmutil.revsingle(repo, rev_).rev() == nullrev):
5338 scmutil.revsingle(repo, rev_).rev() == nullrev):
5344 raise error.Abort(_("cannot tag null revision"))
5339 raise error.Abort(_("cannot tag null revision"))
5345
5340
5346 tagsmod.tag(repo, names, node, message, opts.get('local'),
5341 tagsmod.tag(repo, names, node, message, opts.get('local'),
5347 opts.get('user'), date, editor=editor)
5342 opts.get('user'), date, editor=editor)
5348 finally:
5343 finally:
5349 release(lock, wlock)
5344 release(lock, wlock)
5350
5345
5351 @command('tags', formatteropts, '', cmdtype=readonly)
5346 @command('tags', formatteropts, '', cmdtype=readonly)
5352 def tags(ui, repo, **opts):
5347 def tags(ui, repo, **opts):
5353 """list repository tags
5348 """list repository tags
5354
5349
5355 This lists both regular and local tags. When the -v/--verbose
5350 This lists both regular and local tags. When the -v/--verbose
5356 switch is used, a third column "local" is printed for local tags.
5351 switch is used, a third column "local" is printed for local tags.
5357 When the -q/--quiet switch is used, only the tag name is printed.
5352 When the -q/--quiet switch is used, only the tag name is printed.
5358
5353
5359 Returns 0 on success.
5354 Returns 0 on success.
5360 """
5355 """
5361
5356
5362 opts = pycompat.byteskwargs(opts)
5357 opts = pycompat.byteskwargs(opts)
5363 ui.pager('tags')
5358 ui.pager('tags')
5364 fm = ui.formatter('tags', opts)
5359 fm = ui.formatter('tags', opts)
5365 hexfunc = fm.hexfunc
5360 hexfunc = fm.hexfunc
5366 tagtype = ""
5361 tagtype = ""
5367
5362
5368 for t, n in reversed(repo.tagslist()):
5363 for t, n in reversed(repo.tagslist()):
5369 hn = hexfunc(n)
5364 hn = hexfunc(n)
5370 label = 'tags.normal'
5365 label = 'tags.normal'
5371 tagtype = ''
5366 tagtype = ''
5372 if repo.tagtype(t) == 'local':
5367 if repo.tagtype(t) == 'local':
5373 label = 'tags.local'
5368 label = 'tags.local'
5374 tagtype = 'local'
5369 tagtype = 'local'
5375
5370
5376 fm.startitem()
5371 fm.startitem()
5377 fm.write('tag', '%s', t, label=label)
5372 fm.write('tag', '%s', t, label=label)
5378 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5373 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5379 fm.condwrite(not ui.quiet, 'rev node', fmt,
5374 fm.condwrite(not ui.quiet, 'rev node', fmt,
5380 repo.changelog.rev(n), hn, label=label)
5375 repo.changelog.rev(n), hn, label=label)
5381 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5376 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5382 tagtype, label=label)
5377 tagtype, label=label)
5383 fm.plain('\n')
5378 fm.plain('\n')
5384 fm.end()
5379 fm.end()
5385
5380
5386 @command('tip',
5381 @command('tip',
5387 [('p', 'patch', None, _('show patch')),
5382 [('p', 'patch', None, _('show patch')),
5388 ('g', 'git', None, _('use git extended diff format')),
5383 ('g', 'git', None, _('use git extended diff format')),
5389 ] + templateopts,
5384 ] + templateopts,
5390 _('[-p] [-g]'))
5385 _('[-p] [-g]'))
5391 def tip(ui, repo, **opts):
5386 def tip(ui, repo, **opts):
5392 """show the tip revision (DEPRECATED)
5387 """show the tip revision (DEPRECATED)
5393
5388
5394 The tip revision (usually just called the tip) is the changeset
5389 The tip revision (usually just called the tip) is the changeset
5395 most recently added to the repository (and therefore the most
5390 most recently added to the repository (and therefore the most
5396 recently changed head).
5391 recently changed head).
5397
5392
5398 If you have just made a commit, that commit will be the tip. If
5393 If you have just made a commit, that commit will be the tip. If
5399 you have just pulled changes from another repository, the tip of
5394 you have just pulled changes from another repository, the tip of
5400 that repository becomes the current tip. The "tip" tag is special
5395 that repository becomes the current tip. The "tip" tag is special
5401 and cannot be renamed or assigned to a different changeset.
5396 and cannot be renamed or assigned to a different changeset.
5402
5397
5403 This command is deprecated, please use :hg:`heads` instead.
5398 This command is deprecated, please use :hg:`heads` instead.
5404
5399
5405 Returns 0 on success.
5400 Returns 0 on success.
5406 """
5401 """
5407 opts = pycompat.byteskwargs(opts)
5402 opts = pycompat.byteskwargs(opts)
5408 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5403 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5409 displayer.show(repo['tip'])
5404 displayer.show(repo['tip'])
5410 displayer.close()
5405 displayer.close()
5411
5406
5412 @command('unbundle',
5407 @command('unbundle',
5413 [('u', 'update', None,
5408 [('u', 'update', None,
5414 _('update to new branch head if changesets were unbundled'))],
5409 _('update to new branch head if changesets were unbundled'))],
5415 _('[-u] FILE...'))
5410 _('[-u] FILE...'))
5416 def unbundle(ui, repo, fname1, *fnames, **opts):
5411 def unbundle(ui, repo, fname1, *fnames, **opts):
5417 """apply one or more bundle files
5412 """apply one or more bundle files
5418
5413
5419 Apply one or more bundle files generated by :hg:`bundle`.
5414 Apply one or more bundle files generated by :hg:`bundle`.
5420
5415
5421 Returns 0 on success, 1 if an update has unresolved files.
5416 Returns 0 on success, 1 if an update has unresolved files.
5422 """
5417 """
5423 fnames = (fname1,) + fnames
5418 fnames = (fname1,) + fnames
5424
5419
5425 with repo.lock():
5420 with repo.lock():
5426 for fname in fnames:
5421 for fname in fnames:
5427 f = hg.openpath(ui, fname)
5422 f = hg.openpath(ui, fname)
5428 gen = exchange.readbundle(ui, f, fname)
5423 gen = exchange.readbundle(ui, f, fname)
5429 if isinstance(gen, streamclone.streamcloneapplier):
5424 if isinstance(gen, streamclone.streamcloneapplier):
5430 raise error.Abort(
5425 raise error.Abort(
5431 _('packed bundles cannot be applied with '
5426 _('packed bundles cannot be applied with '
5432 '"hg unbundle"'),
5427 '"hg unbundle"'),
5433 hint=_('use "hg debugapplystreamclonebundle"'))
5428 hint=_('use "hg debugapplystreamclonebundle"'))
5434 url = 'bundle:' + fname
5429 url = 'bundle:' + fname
5435 try:
5430 try:
5436 txnname = 'unbundle'
5431 txnname = 'unbundle'
5437 if not isinstance(gen, bundle2.unbundle20):
5432 if not isinstance(gen, bundle2.unbundle20):
5438 txnname = 'unbundle\n%s' % util.hidepassword(url)
5433 txnname = 'unbundle\n%s' % util.hidepassword(url)
5439 with repo.transaction(txnname) as tr:
5434 with repo.transaction(txnname) as tr:
5440 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5435 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5441 url=url)
5436 url=url)
5442 except error.BundleUnknownFeatureError as exc:
5437 except error.BundleUnknownFeatureError as exc:
5443 raise error.Abort(
5438 raise error.Abort(
5444 _('%s: unknown bundle feature, %s') % (fname, exc),
5439 _('%s: unknown bundle feature, %s') % (fname, exc),
5445 hint=_("see https://mercurial-scm.org/"
5440 hint=_("see https://mercurial-scm.org/"
5446 "wiki/BundleFeature for more "
5441 "wiki/BundleFeature for more "
5447 "information"))
5442 "information"))
5448 modheads = bundle2.combinechangegroupresults(op)
5443 modheads = bundle2.combinechangegroupresults(op)
5449
5444
5450 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5445 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5451
5446
5452 @command('^update|up|checkout|co',
5447 @command('^update|up|checkout|co',
5453 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5448 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5454 ('c', 'check', None, _('require clean working directory')),
5449 ('c', 'check', None, _('require clean working directory')),
5455 ('m', 'merge', None, _('merge uncommitted changes')),
5450 ('m', 'merge', None, _('merge uncommitted changes')),
5456 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5451 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5457 ('r', 'rev', '', _('revision'), _('REV'))
5452 ('r', 'rev', '', _('revision'), _('REV'))
5458 ] + mergetoolopts,
5453 ] + mergetoolopts,
5459 _('[-C|-c|-m] [-d DATE] [[-r] REV]'))
5454 _('[-C|-c|-m] [-d DATE] [[-r] REV]'))
5460 def update(ui, repo, node=None, **opts):
5455 def update(ui, repo, node=None, **opts):
5461 """update working directory (or switch revisions)
5456 """update working directory (or switch revisions)
5462
5457
5463 Update the repository's working directory to the specified
5458 Update the repository's working directory to the specified
5464 changeset. If no changeset is specified, update to the tip of the
5459 changeset. If no changeset is specified, update to the tip of the
5465 current named branch and move the active bookmark (see :hg:`help
5460 current named branch and move the active bookmark (see :hg:`help
5466 bookmarks`).
5461 bookmarks`).
5467
5462
5468 Update sets the working directory's parent revision to the specified
5463 Update sets the working directory's parent revision to the specified
5469 changeset (see :hg:`help parents`).
5464 changeset (see :hg:`help parents`).
5470
5465
5471 If the changeset is not a descendant or ancestor of the working
5466 If the changeset is not a descendant or ancestor of the working
5472 directory's parent and there are uncommitted changes, the update is
5467 directory's parent and there are uncommitted changes, the update is
5473 aborted. With the -c/--check option, the working directory is checked
5468 aborted. With the -c/--check option, the working directory is checked
5474 for uncommitted changes; if none are found, the working directory is
5469 for uncommitted changes; if none are found, the working directory is
5475 updated to the specified changeset.
5470 updated to the specified changeset.
5476
5471
5477 .. container:: verbose
5472 .. container:: verbose
5478
5473
5479 The -C/--clean, -c/--check, and -m/--merge options control what
5474 The -C/--clean, -c/--check, and -m/--merge options control what
5480 happens if the working directory contains uncommitted changes.
5475 happens if the working directory contains uncommitted changes.
5481 At most of one of them can be specified.
5476 At most of one of them can be specified.
5482
5477
5483 1. If no option is specified, and if
5478 1. If no option is specified, and if
5484 the requested changeset is an ancestor or descendant of
5479 the requested changeset is an ancestor or descendant of
5485 the working directory's parent, the uncommitted changes
5480 the working directory's parent, the uncommitted changes
5486 are merged into the requested changeset and the merged
5481 are merged into the requested changeset and the merged
5487 result is left uncommitted. If the requested changeset is
5482 result is left uncommitted. If the requested changeset is
5488 not an ancestor or descendant (that is, it is on another
5483 not an ancestor or descendant (that is, it is on another
5489 branch), the update is aborted and the uncommitted changes
5484 branch), the update is aborted and the uncommitted changes
5490 are preserved.
5485 are preserved.
5491
5486
5492 2. With the -m/--merge option, the update is allowed even if the
5487 2. With the -m/--merge option, the update is allowed even if the
5493 requested changeset is not an ancestor or descendant of
5488 requested changeset is not an ancestor or descendant of
5494 the working directory's parent.
5489 the working directory's parent.
5495
5490
5496 3. With the -c/--check option, the update is aborted and the
5491 3. With the -c/--check option, the update is aborted and the
5497 uncommitted changes are preserved.
5492 uncommitted changes are preserved.
5498
5493
5499 4. With the -C/--clean option, uncommitted changes are discarded and
5494 4. With the -C/--clean option, uncommitted changes are discarded and
5500 the working directory is updated to the requested changeset.
5495 the working directory is updated to the requested changeset.
5501
5496
5502 To cancel an uncommitted merge (and lose your changes), use
5497 To cancel an uncommitted merge (and lose your changes), use
5503 :hg:`merge --abort`.
5498 :hg:`merge --abort`.
5504
5499
5505 Use null as the changeset to remove the working directory (like
5500 Use null as the changeset to remove the working directory (like
5506 :hg:`clone -U`).
5501 :hg:`clone -U`).
5507
5502
5508 If you want to revert just one file to an older revision, use
5503 If you want to revert just one file to an older revision, use
5509 :hg:`revert [-r REV] NAME`.
5504 :hg:`revert [-r REV] NAME`.
5510
5505
5511 See :hg:`help dates` for a list of formats valid for -d/--date.
5506 See :hg:`help dates` for a list of formats valid for -d/--date.
5512
5507
5513 Returns 0 on success, 1 if there are unresolved files.
5508 Returns 0 on success, 1 if there are unresolved files.
5514 """
5509 """
5515 rev = opts.get(r'rev')
5510 rev = opts.get(r'rev')
5516 date = opts.get(r'date')
5511 date = opts.get(r'date')
5517 clean = opts.get(r'clean')
5512 clean = opts.get(r'clean')
5518 check = opts.get(r'check')
5513 check = opts.get(r'check')
5519 merge = opts.get(r'merge')
5514 merge = opts.get(r'merge')
5520 if rev and node:
5515 if rev and node:
5521 raise error.Abort(_("please specify just one revision"))
5516 raise error.Abort(_("please specify just one revision"))
5522
5517
5523 if ui.configbool('commands', 'update.requiredest'):
5518 if ui.configbool('commands', 'update.requiredest'):
5524 if not node and not rev and not date:
5519 if not node and not rev and not date:
5525 raise error.Abort(_('you must specify a destination'),
5520 raise error.Abort(_('you must specify a destination'),
5526 hint=_('for example: hg update ".::"'))
5521 hint=_('for example: hg update ".::"'))
5527
5522
5528 if rev is None or rev == '':
5523 if rev is None or rev == '':
5529 rev = node
5524 rev = node
5530
5525
5531 if date and rev is not None:
5526 if date and rev is not None:
5532 raise error.Abort(_("you can't specify a revision and a date"))
5527 raise error.Abort(_("you can't specify a revision and a date"))
5533
5528
5534 if len([x for x in (clean, check, merge) if x]) > 1:
5529 if len([x for x in (clean, check, merge) if x]) > 1:
5535 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
5530 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
5536 "or -m/--merge"))
5531 "or -m/--merge"))
5537
5532
5538 updatecheck = None
5533 updatecheck = None
5539 if check:
5534 if check:
5540 updatecheck = 'abort'
5535 updatecheck = 'abort'
5541 elif merge:
5536 elif merge:
5542 updatecheck = 'none'
5537 updatecheck = 'none'
5543
5538
5544 with repo.wlock():
5539 with repo.wlock():
5545 cmdutil.clearunfinished(repo)
5540 cmdutil.clearunfinished(repo)
5546
5541
5547 if date:
5542 if date:
5548 rev = cmdutil.finddate(ui, repo, date)
5543 rev = cmdutil.finddate(ui, repo, date)
5549
5544
5550 # if we defined a bookmark, we have to remember the original name
5545 # if we defined a bookmark, we have to remember the original name
5551 brev = rev
5546 brev = rev
5552 if rev:
5547 if rev:
5553 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
5548 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
5554 ctx = scmutil.revsingle(repo, rev, rev)
5549 ctx = scmutil.revsingle(repo, rev, rev)
5555 rev = ctx.rev()
5550 rev = ctx.rev()
5556 if ctx.hidden():
5551 if ctx.hidden():
5557 ctxstr = ctx.hex()[:12]
5552 ctxstr = ctx.hex()[:12]
5558 ui.warn(_("updating to a hidden changeset %s\n") % ctxstr)
5553 ui.warn(_("updating to a hidden changeset %s\n") % ctxstr)
5559
5554
5560 if ctx.obsolete():
5555 if ctx.obsolete():
5561 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
5556 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
5562 ui.warn("(%s)\n" % obsfatemsg)
5557 ui.warn("(%s)\n" % obsfatemsg)
5563
5558
5564 repo.ui.setconfig('ui', 'forcemerge', opts.get(r'tool'), 'update')
5559 repo.ui.setconfig('ui', 'forcemerge', opts.get(r'tool'), 'update')
5565
5560
5566 return hg.updatetotally(ui, repo, rev, brev, clean=clean,
5561 return hg.updatetotally(ui, repo, rev, brev, clean=clean,
5567 updatecheck=updatecheck)
5562 updatecheck=updatecheck)
5568
5563
5569 @command('verify', [])
5564 @command('verify', [])
5570 def verify(ui, repo):
5565 def verify(ui, repo):
5571 """verify the integrity of the repository
5566 """verify the integrity of the repository
5572
5567
5573 Verify the integrity of the current repository.
5568 Verify the integrity of the current repository.
5574
5569
5575 This will perform an extensive check of the repository's
5570 This will perform an extensive check of the repository's
5576 integrity, validating the hashes and checksums of each entry in
5571 integrity, validating the hashes and checksums of each entry in
5577 the changelog, manifest, and tracked files, as well as the
5572 the changelog, manifest, and tracked files, as well as the
5578 integrity of their crosslinks and indices.
5573 integrity of their crosslinks and indices.
5579
5574
5580 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
5575 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
5581 for more information about recovery from corruption of the
5576 for more information about recovery from corruption of the
5582 repository.
5577 repository.
5583
5578
5584 Returns 0 on success, 1 if errors are encountered.
5579 Returns 0 on success, 1 if errors are encountered.
5585 """
5580 """
5586 return hg.verify(repo)
5581 return hg.verify(repo)
5587
5582
5588 @command('version', [] + formatteropts, norepo=True, cmdtype=readonly)
5583 @command('version', [] + formatteropts, norepo=True, cmdtype=readonly)
5589 def version_(ui, **opts):
5584 def version_(ui, **opts):
5590 """output version and copyright information"""
5585 """output version and copyright information"""
5591 opts = pycompat.byteskwargs(opts)
5586 opts = pycompat.byteskwargs(opts)
5592 if ui.verbose:
5587 if ui.verbose:
5593 ui.pager('version')
5588 ui.pager('version')
5594 fm = ui.formatter("version", opts)
5589 fm = ui.formatter("version", opts)
5595 fm.startitem()
5590 fm.startitem()
5596 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
5591 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
5597 util.version())
5592 util.version())
5598 license = _(
5593 license = _(
5599 "(see https://mercurial-scm.org for more information)\n"
5594 "(see https://mercurial-scm.org for more information)\n"
5600 "\nCopyright (C) 2005-2018 Matt Mackall and others\n"
5595 "\nCopyright (C) 2005-2018 Matt Mackall and others\n"
5601 "This is free software; see the source for copying conditions. "
5596 "This is free software; see the source for copying conditions. "
5602 "There is NO\nwarranty; "
5597 "There is NO\nwarranty; "
5603 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5598 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5604 )
5599 )
5605 if not ui.quiet:
5600 if not ui.quiet:
5606 fm.plain(license)
5601 fm.plain(license)
5607
5602
5608 if ui.verbose:
5603 if ui.verbose:
5609 fm.plain(_("\nEnabled extensions:\n\n"))
5604 fm.plain(_("\nEnabled extensions:\n\n"))
5610 # format names and versions into columns
5605 # format names and versions into columns
5611 names = []
5606 names = []
5612 vers = []
5607 vers = []
5613 isinternals = []
5608 isinternals = []
5614 for name, module in extensions.extensions():
5609 for name, module in extensions.extensions():
5615 names.append(name)
5610 names.append(name)
5616 vers.append(extensions.moduleversion(module) or None)
5611 vers.append(extensions.moduleversion(module) or None)
5617 isinternals.append(extensions.ismoduleinternal(module))
5612 isinternals.append(extensions.ismoduleinternal(module))
5618 fn = fm.nested("extensions")
5613 fn = fm.nested("extensions")
5619 if names:
5614 if names:
5620 namefmt = " %%-%ds " % max(len(n) for n in names)
5615 namefmt = " %%-%ds " % max(len(n) for n in names)
5621 places = [_("external"), _("internal")]
5616 places = [_("external"), _("internal")]
5622 for n, v, p in zip(names, vers, isinternals):
5617 for n, v, p in zip(names, vers, isinternals):
5623 fn.startitem()
5618 fn.startitem()
5624 fn.condwrite(ui.verbose, "name", namefmt, n)
5619 fn.condwrite(ui.verbose, "name", namefmt, n)
5625 if ui.verbose:
5620 if ui.verbose:
5626 fn.plain("%s " % places[p])
5621 fn.plain("%s " % places[p])
5627 fn.data(bundled=p)
5622 fn.data(bundled=p)
5628 fn.condwrite(ui.verbose and v, "ver", "%s", v)
5623 fn.condwrite(ui.verbose and v, "ver", "%s", v)
5629 if ui.verbose:
5624 if ui.verbose:
5630 fn.plain("\n")
5625 fn.plain("\n")
5631 fn.end()
5626 fn.end()
5632 fm.end()
5627 fm.end()
5633
5628
5634 def loadcmdtable(ui, name, cmdtable):
5629 def loadcmdtable(ui, name, cmdtable):
5635 """Load command functions from specified cmdtable
5630 """Load command functions from specified cmdtable
5636 """
5631 """
5637 overrides = [cmd for cmd in cmdtable if cmd in table]
5632 overrides = [cmd for cmd in cmdtable if cmd in table]
5638 if overrides:
5633 if overrides:
5639 ui.warn(_("extension '%s' overrides commands: %s\n")
5634 ui.warn(_("extension '%s' overrides commands: %s\n")
5640 % (name, " ".join(overrides)))
5635 % (name, " ".join(overrides)))
5641 table.update(cmdtable)
5636 table.update(cmdtable)
@@ -1,1447 +1,1453
1 # scmutil.py - Mercurial core utility functions
1 # scmutil.py - Mercurial core utility functions
2 #
2 #
3 # Copyright Matt Mackall <mpm@selenic.com>
3 # Copyright 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 errno
10 import errno
11 import glob
11 import glob
12 import hashlib
12 import hashlib
13 import os
13 import os
14 import re
14 import re
15 import socket
15 import socket
16 import subprocess
16 import subprocess
17 import weakref
17 import weakref
18
18
19 from .i18n import _
19 from .i18n import _
20 from .node import (
20 from .node import (
21 hex,
21 hex,
22 nullid,
22 nullid,
23 short,
23 short,
24 wdirid,
24 wdirid,
25 wdirrev,
25 wdirrev,
26 )
26 )
27
27
28 from . import (
28 from . import (
29 encoding,
29 encoding,
30 error,
30 error,
31 match as matchmod,
31 match as matchmod,
32 obsolete,
32 obsolete,
33 obsutil,
33 obsutil,
34 pathutil,
34 pathutil,
35 phases,
35 phases,
36 pycompat,
36 pycompat,
37 revsetlang,
37 revsetlang,
38 similar,
38 similar,
39 url,
39 url,
40 util,
40 util,
41 vfs,
41 vfs,
42 )
42 )
43
43
44 from .utils import (
44 from .utils import (
45 procutil,
45 procutil,
46 stringutil,
46 stringutil,
47 )
47 )
48
48
49 if pycompat.iswindows:
49 if pycompat.iswindows:
50 from . import scmwindows as scmplatform
50 from . import scmwindows as scmplatform
51 else:
51 else:
52 from . import scmposix as scmplatform
52 from . import scmposix as scmplatform
53
53
54 termsize = scmplatform.termsize
54 termsize = scmplatform.termsize
55
55
56 class status(tuple):
56 class status(tuple):
57 '''Named tuple with a list of files per status. The 'deleted', 'unknown'
57 '''Named tuple with a list of files per status. The 'deleted', 'unknown'
58 and 'ignored' properties are only relevant to the working copy.
58 and 'ignored' properties are only relevant to the working copy.
59 '''
59 '''
60
60
61 __slots__ = ()
61 __slots__ = ()
62
62
63 def __new__(cls, modified, added, removed, deleted, unknown, ignored,
63 def __new__(cls, modified, added, removed, deleted, unknown, ignored,
64 clean):
64 clean):
65 return tuple.__new__(cls, (modified, added, removed, deleted, unknown,
65 return tuple.__new__(cls, (modified, added, removed, deleted, unknown,
66 ignored, clean))
66 ignored, clean))
67
67
68 @property
68 @property
69 def modified(self):
69 def modified(self):
70 '''files that have been modified'''
70 '''files that have been modified'''
71 return self[0]
71 return self[0]
72
72
73 @property
73 @property
74 def added(self):
74 def added(self):
75 '''files that have been added'''
75 '''files that have been added'''
76 return self[1]
76 return self[1]
77
77
78 @property
78 @property
79 def removed(self):
79 def removed(self):
80 '''files that have been removed'''
80 '''files that have been removed'''
81 return self[2]
81 return self[2]
82
82
83 @property
83 @property
84 def deleted(self):
84 def deleted(self):
85 '''files that are in the dirstate, but have been deleted from the
85 '''files that are in the dirstate, but have been deleted from the
86 working copy (aka "missing")
86 working copy (aka "missing")
87 '''
87 '''
88 return self[3]
88 return self[3]
89
89
90 @property
90 @property
91 def unknown(self):
91 def unknown(self):
92 '''files not in the dirstate that are not ignored'''
92 '''files not in the dirstate that are not ignored'''
93 return self[4]
93 return self[4]
94
94
95 @property
95 @property
96 def ignored(self):
96 def ignored(self):
97 '''files not in the dirstate that are ignored (by _dirignore())'''
97 '''files not in the dirstate that are ignored (by _dirignore())'''
98 return self[5]
98 return self[5]
99
99
100 @property
100 @property
101 def clean(self):
101 def clean(self):
102 '''files that have not been modified'''
102 '''files that have not been modified'''
103 return self[6]
103 return self[6]
104
104
105 def __repr__(self, *args, **kwargs):
105 def __repr__(self, *args, **kwargs):
106 return (('<status modified=%r, added=%r, removed=%r, deleted=%r, '
106 return (('<status modified=%r, added=%r, removed=%r, deleted=%r, '
107 'unknown=%r, ignored=%r, clean=%r>') % self)
107 'unknown=%r, ignored=%r, clean=%r>') % self)
108
108
109 def itersubrepos(ctx1, ctx2):
109 def itersubrepos(ctx1, ctx2):
110 """find subrepos in ctx1 or ctx2"""
110 """find subrepos in ctx1 or ctx2"""
111 # Create a (subpath, ctx) mapping where we prefer subpaths from
111 # Create a (subpath, ctx) mapping where we prefer subpaths from
112 # ctx1. The subpaths from ctx2 are important when the .hgsub file
112 # ctx1. The subpaths from ctx2 are important when the .hgsub file
113 # has been modified (in ctx2) but not yet committed (in ctx1).
113 # has been modified (in ctx2) but not yet committed (in ctx1).
114 subpaths = dict.fromkeys(ctx2.substate, ctx2)
114 subpaths = dict.fromkeys(ctx2.substate, ctx2)
115 subpaths.update(dict.fromkeys(ctx1.substate, ctx1))
115 subpaths.update(dict.fromkeys(ctx1.substate, ctx1))
116
116
117 missing = set()
117 missing = set()
118
118
119 for subpath in ctx2.substate:
119 for subpath in ctx2.substate:
120 if subpath not in ctx1.substate:
120 if subpath not in ctx1.substate:
121 del subpaths[subpath]
121 del subpaths[subpath]
122 missing.add(subpath)
122 missing.add(subpath)
123
123
124 for subpath, ctx in sorted(subpaths.iteritems()):
124 for subpath, ctx in sorted(subpaths.iteritems()):
125 yield subpath, ctx.sub(subpath)
125 yield subpath, ctx.sub(subpath)
126
126
127 # Yield an empty subrepo based on ctx1 for anything only in ctx2. That way,
127 # Yield an empty subrepo based on ctx1 for anything only in ctx2. That way,
128 # status and diff will have an accurate result when it does
128 # status and diff will have an accurate result when it does
129 # 'sub.{status|diff}(rev2)'. Otherwise, the ctx2 subrepo is compared
129 # 'sub.{status|diff}(rev2)'. Otherwise, the ctx2 subrepo is compared
130 # against itself.
130 # against itself.
131 for subpath in missing:
131 for subpath in missing:
132 yield subpath, ctx2.nullsub(subpath, ctx1)
132 yield subpath, ctx2.nullsub(subpath, ctx1)
133
133
134 def nochangesfound(ui, repo, excluded=None):
134 def nochangesfound(ui, repo, excluded=None):
135 '''Report no changes for push/pull, excluded is None or a list of
135 '''Report no changes for push/pull, excluded is None or a list of
136 nodes excluded from the push/pull.
136 nodes excluded from the push/pull.
137 '''
137 '''
138 secretlist = []
138 secretlist = []
139 if excluded:
139 if excluded:
140 for n in excluded:
140 for n in excluded:
141 ctx = repo[n]
141 ctx = repo[n]
142 if ctx.phase() >= phases.secret and not ctx.extinct():
142 if ctx.phase() >= phases.secret and not ctx.extinct():
143 secretlist.append(n)
143 secretlist.append(n)
144
144
145 if secretlist:
145 if secretlist:
146 ui.status(_("no changes found (ignored %d secret changesets)\n")
146 ui.status(_("no changes found (ignored %d secret changesets)\n")
147 % len(secretlist))
147 % len(secretlist))
148 else:
148 else:
149 ui.status(_("no changes found\n"))
149 ui.status(_("no changes found\n"))
150
150
151 def callcatch(ui, func):
151 def callcatch(ui, func):
152 """call func() with global exception handling
152 """call func() with global exception handling
153
153
154 return func() if no exception happens. otherwise do some error handling
154 return func() if no exception happens. otherwise do some error handling
155 and return an exit code accordingly. does not handle all exceptions.
155 and return an exit code accordingly. does not handle all exceptions.
156 """
156 """
157 try:
157 try:
158 try:
158 try:
159 return func()
159 return func()
160 except: # re-raises
160 except: # re-raises
161 ui.traceback()
161 ui.traceback()
162 raise
162 raise
163 # Global exception handling, alphabetically
163 # Global exception handling, alphabetically
164 # Mercurial-specific first, followed by built-in and library exceptions
164 # Mercurial-specific first, followed by built-in and library exceptions
165 except error.LockHeld as inst:
165 except error.LockHeld as inst:
166 if inst.errno == errno.ETIMEDOUT:
166 if inst.errno == errno.ETIMEDOUT:
167 reason = _('timed out waiting for lock held by %r') % inst.locker
167 reason = _('timed out waiting for lock held by %r') % inst.locker
168 else:
168 else:
169 reason = _('lock held by %r') % inst.locker
169 reason = _('lock held by %r') % inst.locker
170 ui.warn(_("abort: %s: %s\n")
170 ui.warn(_("abort: %s: %s\n")
171 % (inst.desc or stringutil.forcebytestr(inst.filename), reason))
171 % (inst.desc or stringutil.forcebytestr(inst.filename), reason))
172 if not inst.locker:
172 if not inst.locker:
173 ui.warn(_("(lock might be very busy)\n"))
173 ui.warn(_("(lock might be very busy)\n"))
174 except error.LockUnavailable as inst:
174 except error.LockUnavailable as inst:
175 ui.warn(_("abort: could not lock %s: %s\n") %
175 ui.warn(_("abort: could not lock %s: %s\n") %
176 (inst.desc or stringutil.forcebytestr(inst.filename),
176 (inst.desc or stringutil.forcebytestr(inst.filename),
177 encoding.strtolocal(inst.strerror)))
177 encoding.strtolocal(inst.strerror)))
178 except error.OutOfBandError as inst:
178 except error.OutOfBandError as inst:
179 if inst.args:
179 if inst.args:
180 msg = _("abort: remote error:\n")
180 msg = _("abort: remote error:\n")
181 else:
181 else:
182 msg = _("abort: remote error\n")
182 msg = _("abort: remote error\n")
183 ui.warn(msg)
183 ui.warn(msg)
184 if inst.args:
184 if inst.args:
185 ui.warn(''.join(inst.args))
185 ui.warn(''.join(inst.args))
186 if inst.hint:
186 if inst.hint:
187 ui.warn('(%s)\n' % inst.hint)
187 ui.warn('(%s)\n' % inst.hint)
188 except error.RepoError as inst:
188 except error.RepoError as inst:
189 ui.warn(_("abort: %s!\n") % inst)
189 ui.warn(_("abort: %s!\n") % inst)
190 if inst.hint:
190 if inst.hint:
191 ui.warn(_("(%s)\n") % inst.hint)
191 ui.warn(_("(%s)\n") % inst.hint)
192 except error.ResponseError as inst:
192 except error.ResponseError as inst:
193 ui.warn(_("abort: %s") % inst.args[0])
193 ui.warn(_("abort: %s") % inst.args[0])
194 msg = inst.args[1]
194 msg = inst.args[1]
195 if isinstance(msg, type(u'')):
195 if isinstance(msg, type(u'')):
196 msg = pycompat.sysbytes(msg)
196 msg = pycompat.sysbytes(msg)
197 if not isinstance(msg, bytes):
197 if not isinstance(msg, bytes):
198 ui.warn(" %r\n" % (msg,))
198 ui.warn(" %r\n" % (msg,))
199 elif not msg:
199 elif not msg:
200 ui.warn(_(" empty string\n"))
200 ui.warn(_(" empty string\n"))
201 else:
201 else:
202 ui.warn("\n%r\n" % stringutil.ellipsis(msg))
202 ui.warn("\n%r\n" % stringutil.ellipsis(msg))
203 except error.CensoredNodeError as inst:
203 except error.CensoredNodeError as inst:
204 ui.warn(_("abort: file censored %s!\n") % inst)
204 ui.warn(_("abort: file censored %s!\n") % inst)
205 except error.RevlogError as inst:
205 except error.RevlogError as inst:
206 ui.warn(_("abort: %s!\n") % inst)
206 ui.warn(_("abort: %s!\n") % inst)
207 except error.InterventionRequired as inst:
207 except error.InterventionRequired as inst:
208 ui.warn("%s\n" % inst)
208 ui.warn("%s\n" % inst)
209 if inst.hint:
209 if inst.hint:
210 ui.warn(_("(%s)\n") % inst.hint)
210 ui.warn(_("(%s)\n") % inst.hint)
211 return 1
211 return 1
212 except error.WdirUnsupported:
212 except error.WdirUnsupported:
213 ui.warn(_("abort: working directory revision cannot be specified\n"))
213 ui.warn(_("abort: working directory revision cannot be specified\n"))
214 except error.Abort as inst:
214 except error.Abort as inst:
215 ui.warn(_("abort: %s\n") % inst)
215 ui.warn(_("abort: %s\n") % inst)
216 if inst.hint:
216 if inst.hint:
217 ui.warn(_("(%s)\n") % inst.hint)
217 ui.warn(_("(%s)\n") % inst.hint)
218 except ImportError as inst:
218 except ImportError as inst:
219 ui.warn(_("abort: %s!\n") % stringutil.forcebytestr(inst))
219 ui.warn(_("abort: %s!\n") % stringutil.forcebytestr(inst))
220 m = stringutil.forcebytestr(inst).split()[-1]
220 m = stringutil.forcebytestr(inst).split()[-1]
221 if m in "mpatch bdiff".split():
221 if m in "mpatch bdiff".split():
222 ui.warn(_("(did you forget to compile extensions?)\n"))
222 ui.warn(_("(did you forget to compile extensions?)\n"))
223 elif m in "zlib".split():
223 elif m in "zlib".split():
224 ui.warn(_("(is your Python install correct?)\n"))
224 ui.warn(_("(is your Python install correct?)\n"))
225 except IOError as inst:
225 except IOError as inst:
226 if util.safehasattr(inst, "code"):
226 if util.safehasattr(inst, "code"):
227 ui.warn(_("abort: %s\n") % stringutil.forcebytestr(inst))
227 ui.warn(_("abort: %s\n") % stringutil.forcebytestr(inst))
228 elif util.safehasattr(inst, "reason"):
228 elif util.safehasattr(inst, "reason"):
229 try: # usually it is in the form (errno, strerror)
229 try: # usually it is in the form (errno, strerror)
230 reason = inst.reason.args[1]
230 reason = inst.reason.args[1]
231 except (AttributeError, IndexError):
231 except (AttributeError, IndexError):
232 # it might be anything, for example a string
232 # it might be anything, for example a string
233 reason = inst.reason
233 reason = inst.reason
234 if isinstance(reason, unicode):
234 if isinstance(reason, unicode):
235 # SSLError of Python 2.7.9 contains a unicode
235 # SSLError of Python 2.7.9 contains a unicode
236 reason = encoding.unitolocal(reason)
236 reason = encoding.unitolocal(reason)
237 ui.warn(_("abort: error: %s\n") % reason)
237 ui.warn(_("abort: error: %s\n") % reason)
238 elif (util.safehasattr(inst, "args")
238 elif (util.safehasattr(inst, "args")
239 and inst.args and inst.args[0] == errno.EPIPE):
239 and inst.args and inst.args[0] == errno.EPIPE):
240 pass
240 pass
241 elif getattr(inst, "strerror", None):
241 elif getattr(inst, "strerror", None):
242 if getattr(inst, "filename", None):
242 if getattr(inst, "filename", None):
243 ui.warn(_("abort: %s: %s\n") % (
243 ui.warn(_("abort: %s: %s\n") % (
244 encoding.strtolocal(inst.strerror),
244 encoding.strtolocal(inst.strerror),
245 stringutil.forcebytestr(inst.filename)))
245 stringutil.forcebytestr(inst.filename)))
246 else:
246 else:
247 ui.warn(_("abort: %s\n") % encoding.strtolocal(inst.strerror))
247 ui.warn(_("abort: %s\n") % encoding.strtolocal(inst.strerror))
248 else:
248 else:
249 raise
249 raise
250 except OSError as inst:
250 except OSError as inst:
251 if getattr(inst, "filename", None) is not None:
251 if getattr(inst, "filename", None) is not None:
252 ui.warn(_("abort: %s: '%s'\n") % (
252 ui.warn(_("abort: %s: '%s'\n") % (
253 encoding.strtolocal(inst.strerror),
253 encoding.strtolocal(inst.strerror),
254 stringutil.forcebytestr(inst.filename)))
254 stringutil.forcebytestr(inst.filename)))
255 else:
255 else:
256 ui.warn(_("abort: %s\n") % encoding.strtolocal(inst.strerror))
256 ui.warn(_("abort: %s\n") % encoding.strtolocal(inst.strerror))
257 except MemoryError:
257 except MemoryError:
258 ui.warn(_("abort: out of memory\n"))
258 ui.warn(_("abort: out of memory\n"))
259 except SystemExit as inst:
259 except SystemExit as inst:
260 # Commands shouldn't sys.exit directly, but give a return code.
260 # Commands shouldn't sys.exit directly, but give a return code.
261 # Just in case catch this and and pass exit code to caller.
261 # Just in case catch this and and pass exit code to caller.
262 return inst.code
262 return inst.code
263 except socket.error as inst:
263 except socket.error as inst:
264 ui.warn(_("abort: %s\n") % stringutil.forcebytestr(inst.args[-1]))
264 ui.warn(_("abort: %s\n") % stringutil.forcebytestr(inst.args[-1]))
265
265
266 return -1
266 return -1
267
267
268 def checknewlabel(repo, lbl, kind):
268 def checknewlabel(repo, lbl, kind):
269 # Do not use the "kind" parameter in ui output.
269 # Do not use the "kind" parameter in ui output.
270 # It makes strings difficult to translate.
270 # It makes strings difficult to translate.
271 if lbl in ['tip', '.', 'null']:
271 if lbl in ['tip', '.', 'null']:
272 raise error.Abort(_("the name '%s' is reserved") % lbl)
272 raise error.Abort(_("the name '%s' is reserved") % lbl)
273 for c in (':', '\0', '\n', '\r'):
273 for c in (':', '\0', '\n', '\r'):
274 if c in lbl:
274 if c in lbl:
275 raise error.Abort(
275 raise error.Abort(
276 _("%r cannot be used in a name") % pycompat.bytestr(c))
276 _("%r cannot be used in a name") % pycompat.bytestr(c))
277 try:
277 try:
278 int(lbl)
278 int(lbl)
279 raise error.Abort(_("cannot use an integer as a name"))
279 raise error.Abort(_("cannot use an integer as a name"))
280 except ValueError:
280 except ValueError:
281 pass
281 pass
282 if lbl.strip() != lbl:
282 if lbl.strip() != lbl:
283 raise error.Abort(_("leading or trailing whitespace in name %r") % lbl)
283 raise error.Abort(_("leading or trailing whitespace in name %r") % lbl)
284
284
285 def checkfilename(f):
285 def checkfilename(f):
286 '''Check that the filename f is an acceptable filename for a tracked file'''
286 '''Check that the filename f is an acceptable filename for a tracked file'''
287 if '\r' in f or '\n' in f:
287 if '\r' in f or '\n' in f:
288 raise error.Abort(_("'\\n' and '\\r' disallowed in filenames: %r") % f)
288 raise error.Abort(_("'\\n' and '\\r' disallowed in filenames: %r") % f)
289
289
290 def checkportable(ui, f):
290 def checkportable(ui, f):
291 '''Check if filename f is portable and warn or abort depending on config'''
291 '''Check if filename f is portable and warn or abort depending on config'''
292 checkfilename(f)
292 checkfilename(f)
293 abort, warn = checkportabilityalert(ui)
293 abort, warn = checkportabilityalert(ui)
294 if abort or warn:
294 if abort or warn:
295 msg = util.checkwinfilename(f)
295 msg = util.checkwinfilename(f)
296 if msg:
296 if msg:
297 msg = "%s: %s" % (msg, procutil.shellquote(f))
297 msg = "%s: %s" % (msg, procutil.shellquote(f))
298 if abort:
298 if abort:
299 raise error.Abort(msg)
299 raise error.Abort(msg)
300 ui.warn(_("warning: %s\n") % msg)
300 ui.warn(_("warning: %s\n") % msg)
301
301
302 def checkportabilityalert(ui):
302 def checkportabilityalert(ui):
303 '''check if the user's config requests nothing, a warning, or abort for
303 '''check if the user's config requests nothing, a warning, or abort for
304 non-portable filenames'''
304 non-portable filenames'''
305 val = ui.config('ui', 'portablefilenames')
305 val = ui.config('ui', 'portablefilenames')
306 lval = val.lower()
306 lval = val.lower()
307 bval = stringutil.parsebool(val)
307 bval = stringutil.parsebool(val)
308 abort = pycompat.iswindows or lval == 'abort'
308 abort = pycompat.iswindows or lval == 'abort'
309 warn = bval or lval == 'warn'
309 warn = bval or lval == 'warn'
310 if bval is None and not (warn or abort or lval == 'ignore'):
310 if bval is None and not (warn or abort or lval == 'ignore'):
311 raise error.ConfigError(
311 raise error.ConfigError(
312 _("ui.portablefilenames value is invalid ('%s')") % val)
312 _("ui.portablefilenames value is invalid ('%s')") % val)
313 return abort, warn
313 return abort, warn
314
314
315 class casecollisionauditor(object):
315 class casecollisionauditor(object):
316 def __init__(self, ui, abort, dirstate):
316 def __init__(self, ui, abort, dirstate):
317 self._ui = ui
317 self._ui = ui
318 self._abort = abort
318 self._abort = abort
319 allfiles = '\0'.join(dirstate._map)
319 allfiles = '\0'.join(dirstate._map)
320 self._loweredfiles = set(encoding.lower(allfiles).split('\0'))
320 self._loweredfiles = set(encoding.lower(allfiles).split('\0'))
321 self._dirstate = dirstate
321 self._dirstate = dirstate
322 # The purpose of _newfiles is so that we don't complain about
322 # The purpose of _newfiles is so that we don't complain about
323 # case collisions if someone were to call this object with the
323 # case collisions if someone were to call this object with the
324 # same filename twice.
324 # same filename twice.
325 self._newfiles = set()
325 self._newfiles = set()
326
326
327 def __call__(self, f):
327 def __call__(self, f):
328 if f in self._newfiles:
328 if f in self._newfiles:
329 return
329 return
330 fl = encoding.lower(f)
330 fl = encoding.lower(f)
331 if fl in self._loweredfiles and f not in self._dirstate:
331 if fl in self._loweredfiles and f not in self._dirstate:
332 msg = _('possible case-folding collision for %s') % f
332 msg = _('possible case-folding collision for %s') % f
333 if self._abort:
333 if self._abort:
334 raise error.Abort(msg)
334 raise error.Abort(msg)
335 self._ui.warn(_("warning: %s\n") % msg)
335 self._ui.warn(_("warning: %s\n") % msg)
336 self._loweredfiles.add(fl)
336 self._loweredfiles.add(fl)
337 self._newfiles.add(f)
337 self._newfiles.add(f)
338
338
339 def filteredhash(repo, maxrev):
339 def filteredhash(repo, maxrev):
340 """build hash of filtered revisions in the current repoview.
340 """build hash of filtered revisions in the current repoview.
341
341
342 Multiple caches perform up-to-date validation by checking that the
342 Multiple caches perform up-to-date validation by checking that the
343 tiprev and tipnode stored in the cache file match the current repository.
343 tiprev and tipnode stored in the cache file match the current repository.
344 However, this is not sufficient for validating repoviews because the set
344 However, this is not sufficient for validating repoviews because the set
345 of revisions in the view may change without the repository tiprev and
345 of revisions in the view may change without the repository tiprev and
346 tipnode changing.
346 tipnode changing.
347
347
348 This function hashes all the revs filtered from the view and returns
348 This function hashes all the revs filtered from the view and returns
349 that SHA-1 digest.
349 that SHA-1 digest.
350 """
350 """
351 cl = repo.changelog
351 cl = repo.changelog
352 if not cl.filteredrevs:
352 if not cl.filteredrevs:
353 return None
353 return None
354 key = None
354 key = None
355 revs = sorted(r for r in cl.filteredrevs if r <= maxrev)
355 revs = sorted(r for r in cl.filteredrevs if r <= maxrev)
356 if revs:
356 if revs:
357 s = hashlib.sha1()
357 s = hashlib.sha1()
358 for rev in revs:
358 for rev in revs:
359 s.update('%d;' % rev)
359 s.update('%d;' % rev)
360 key = s.digest()
360 key = s.digest()
361 return key
361 return key
362
362
363 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
363 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
364 '''yield every hg repository under path, always recursively.
364 '''yield every hg repository under path, always recursively.
365 The recurse flag will only control recursion into repo working dirs'''
365 The recurse flag will only control recursion into repo working dirs'''
366 def errhandler(err):
366 def errhandler(err):
367 if err.filename == path:
367 if err.filename == path:
368 raise err
368 raise err
369 samestat = getattr(os.path, 'samestat', None)
369 samestat = getattr(os.path, 'samestat', None)
370 if followsym and samestat is not None:
370 if followsym and samestat is not None:
371 def adddir(dirlst, dirname):
371 def adddir(dirlst, dirname):
372 dirstat = os.stat(dirname)
372 dirstat = os.stat(dirname)
373 match = any(samestat(dirstat, lstdirstat) for lstdirstat in dirlst)
373 match = any(samestat(dirstat, lstdirstat) for lstdirstat in dirlst)
374 if not match:
374 if not match:
375 dirlst.append(dirstat)
375 dirlst.append(dirstat)
376 return not match
376 return not match
377 else:
377 else:
378 followsym = False
378 followsym = False
379
379
380 if (seen_dirs is None) and followsym:
380 if (seen_dirs is None) and followsym:
381 seen_dirs = []
381 seen_dirs = []
382 adddir(seen_dirs, path)
382 adddir(seen_dirs, path)
383 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
383 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
384 dirs.sort()
384 dirs.sort()
385 if '.hg' in dirs:
385 if '.hg' in dirs:
386 yield root # found a repository
386 yield root # found a repository
387 qroot = os.path.join(root, '.hg', 'patches')
387 qroot = os.path.join(root, '.hg', 'patches')
388 if os.path.isdir(os.path.join(qroot, '.hg')):
388 if os.path.isdir(os.path.join(qroot, '.hg')):
389 yield qroot # we have a patch queue repo here
389 yield qroot # we have a patch queue repo here
390 if recurse:
390 if recurse:
391 # avoid recursing inside the .hg directory
391 # avoid recursing inside the .hg directory
392 dirs.remove('.hg')
392 dirs.remove('.hg')
393 else:
393 else:
394 dirs[:] = [] # don't descend further
394 dirs[:] = [] # don't descend further
395 elif followsym:
395 elif followsym:
396 newdirs = []
396 newdirs = []
397 for d in dirs:
397 for d in dirs:
398 fname = os.path.join(root, d)
398 fname = os.path.join(root, d)
399 if adddir(seen_dirs, fname):
399 if adddir(seen_dirs, fname):
400 if os.path.islink(fname):
400 if os.path.islink(fname):
401 for hgname in walkrepos(fname, True, seen_dirs):
401 for hgname in walkrepos(fname, True, seen_dirs):
402 yield hgname
402 yield hgname
403 else:
403 else:
404 newdirs.append(d)
404 newdirs.append(d)
405 dirs[:] = newdirs
405 dirs[:] = newdirs
406
406
407 def binnode(ctx):
407 def binnode(ctx):
408 """Return binary node id for a given basectx"""
408 """Return binary node id for a given basectx"""
409 node = ctx.node()
409 node = ctx.node()
410 if node is None:
410 if node is None:
411 return wdirid
411 return wdirid
412 return node
412 return node
413
413
414 def intrev(ctx):
414 def intrev(ctx):
415 """Return integer for a given basectx that can be used in comparison or
415 """Return integer for a given basectx that can be used in comparison or
416 arithmetic operation"""
416 arithmetic operation"""
417 rev = ctx.rev()
417 rev = ctx.rev()
418 if rev is None:
418 if rev is None:
419 return wdirrev
419 return wdirrev
420 return rev
420 return rev
421
421
422 def formatchangeid(ctx):
422 def formatchangeid(ctx):
423 """Format changectx as '{rev}:{node|formatnode}', which is the default
423 """Format changectx as '{rev}:{node|formatnode}', which is the default
424 template provided by logcmdutil.changesettemplater"""
424 template provided by logcmdutil.changesettemplater"""
425 repo = ctx.repo()
425 repo = ctx.repo()
426 return formatrevnode(repo.ui, intrev(ctx), binnode(ctx))
426 return formatrevnode(repo.ui, intrev(ctx), binnode(ctx))
427
427
428 def formatrevnode(ui, rev, node):
428 def formatrevnode(ui, rev, node):
429 """Format given revision and node depending on the current verbosity"""
429 """Format given revision and node depending on the current verbosity"""
430 if ui.debugflag:
430 if ui.debugflag:
431 hexfunc = hex
431 hexfunc = hex
432 else:
432 else:
433 hexfunc = short
433 hexfunc = short
434 return '%d:%s' % (rev, hexfunc(node))
434 return '%d:%s' % (rev, hexfunc(node))
435
435
436 def revsymbol(repo, symbol):
436 def revsymbol(repo, symbol):
437 """Returns a context given a single revision symbol (as string).
437 """Returns a context given a single revision symbol (as string).
438
438
439 This is similar to revsingle(), but accepts only a single revision symbol,
439 This is similar to revsingle(), but accepts only a single revision symbol,
440 i.e. things like ".", "tip", "1234", "deadbeef", "my-bookmark" work, but
440 i.e. things like ".", "tip", "1234", "deadbeef", "my-bookmark" work, but
441 not "max(public())".
441 not "max(public())".
442 """
442 """
443 if not isinstance(symbol, bytes):
443 if not isinstance(symbol, bytes):
444 msg = ("symbol (%s of type %s) was not a string, did you mean "
444 msg = ("symbol (%s of type %s) was not a string, did you mean "
445 "repo[symbol]?" % (symbol, type(symbol)))
445 "repo[symbol]?" % (symbol, type(symbol)))
446 raise error.ProgrammingError(msg)
446 raise error.ProgrammingError(msg)
447 return repo[symbol]
447 return repo[symbol]
448
448
449 def revsingle(repo, revspec, default='.', localalias=None):
449 def revsingle(repo, revspec, default='.', localalias=None):
450 if not revspec and revspec != 0:
450 if not revspec and revspec != 0:
451 return repo[default]
451 return repo[default]
452
452
453 l = revrange(repo, [revspec], localalias=localalias)
453 l = revrange(repo, [revspec], localalias=localalias)
454 if not l:
454 if not l:
455 raise error.Abort(_('empty revision set'))
455 raise error.Abort(_('empty revision set'))
456 return repo[l.last()]
456 return repo[l.last()]
457
457
458 def _pairspec(revspec):
458 def _pairspec(revspec):
459 tree = revsetlang.parse(revspec)
459 tree = revsetlang.parse(revspec)
460 return tree and tree[0] in ('range', 'rangepre', 'rangepost', 'rangeall')
460 return tree and tree[0] in ('range', 'rangepre', 'rangepost', 'rangeall')
461
461
462 def revpairnodes(repo, revs):
462 def revpairnodes(repo, revs):
463 repo.ui.deprecwarn("revpairnodes is deprecated, please use revpair", "4.6")
463 repo.ui.deprecwarn("revpairnodes is deprecated, please use revpair", "4.6")
464 ctx1, ctx2 = revpair(repo, revs)
464 ctx1, ctx2 = revpair(repo, revs)
465 return ctx1.node(), ctx2.node()
465 return ctx1.node(), ctx2.node()
466
466
467 def revpair(repo, revs):
467 def revpair(repo, revs):
468 if not revs:
468 if not revs:
469 return repo['.'], repo[None]
469 return repo['.'], repo[None]
470
470
471 l = revrange(repo, revs)
471 l = revrange(repo, revs)
472
472
473 if not l:
473 if not l:
474 first = second = None
474 first = second = None
475 elif l.isascending():
475 elif l.isascending():
476 first = l.min()
476 first = l.min()
477 second = l.max()
477 second = l.max()
478 elif l.isdescending():
478 elif l.isdescending():
479 first = l.max()
479 first = l.max()
480 second = l.min()
480 second = l.min()
481 else:
481 else:
482 first = l.first()
482 first = l.first()
483 second = l.last()
483 second = l.last()
484
484
485 if first is None:
485 if first is None:
486 raise error.Abort(_('empty revision range'))
486 raise error.Abort(_('empty revision range'))
487 if (first == second and len(revs) >= 2
487 if (first == second and len(revs) >= 2
488 and not all(revrange(repo, [r]) for r in revs)):
488 and not all(revrange(repo, [r]) for r in revs)):
489 raise error.Abort(_('empty revision on one side of range'))
489 raise error.Abort(_('empty revision on one side of range'))
490
490
491 # if top-level is range expression, the result must always be a pair
491 # if top-level is range expression, the result must always be a pair
492 if first == second and len(revs) == 1 and not _pairspec(revs[0]):
492 if first == second and len(revs) == 1 and not _pairspec(revs[0]):
493 return repo[first], repo[None]
493 return repo[first], repo[None]
494
494
495 return repo[first], repo[second]
495 return repo[first], repo[second]
496
496
497 def revrange(repo, specs, localalias=None):
497 def revrange(repo, specs, localalias=None):
498 """Execute 1 to many revsets and return the union.
498 """Execute 1 to many revsets and return the union.
499
499
500 This is the preferred mechanism for executing revsets using user-specified
500 This is the preferred mechanism for executing revsets using user-specified
501 config options, such as revset aliases.
501 config options, such as revset aliases.
502
502
503 The revsets specified by ``specs`` will be executed via a chained ``OR``
503 The revsets specified by ``specs`` will be executed via a chained ``OR``
504 expression. If ``specs`` is empty, an empty result is returned.
504 expression. If ``specs`` is empty, an empty result is returned.
505
505
506 ``specs`` can contain integers, in which case they are assumed to be
506 ``specs`` can contain integers, in which case they are assumed to be
507 revision numbers.
507 revision numbers.
508
508
509 It is assumed the revsets are already formatted. If you have arguments
509 It is assumed the revsets are already formatted. If you have arguments
510 that need to be expanded in the revset, call ``revsetlang.formatspec()``
510 that need to be expanded in the revset, call ``revsetlang.formatspec()``
511 and pass the result as an element of ``specs``.
511 and pass the result as an element of ``specs``.
512
512
513 Specifying a single revset is allowed.
513 Specifying a single revset is allowed.
514
514
515 Returns a ``revset.abstractsmartset`` which is a list-like interface over
515 Returns a ``revset.abstractsmartset`` which is a list-like interface over
516 integer revisions.
516 integer revisions.
517 """
517 """
518 allspecs = []
518 allspecs = []
519 for spec in specs:
519 for spec in specs:
520 if isinstance(spec, int):
520 if isinstance(spec, int):
521 spec = revsetlang.formatspec('rev(%d)', spec)
521 spec = revsetlang.formatspec('rev(%d)', spec)
522 allspecs.append(spec)
522 allspecs.append(spec)
523 return repo.anyrevs(allspecs, user=True, localalias=localalias)
523 return repo.anyrevs(allspecs, user=True, localalias=localalias)
524
524
525 def meaningfulparents(repo, ctx):
525 def meaningfulparents(repo, ctx):
526 """Return list of meaningful (or all if debug) parentrevs for rev.
526 """Return list of meaningful (or all if debug) parentrevs for rev.
527
527
528 For merges (two non-nullrev revisions) both parents are meaningful.
528 For merges (two non-nullrev revisions) both parents are meaningful.
529 Otherwise the first parent revision is considered meaningful if it
529 Otherwise the first parent revision is considered meaningful if it
530 is not the preceding revision.
530 is not the preceding revision.
531 """
531 """
532 parents = ctx.parents()
532 parents = ctx.parents()
533 if len(parents) > 1:
533 if len(parents) > 1:
534 return parents
534 return parents
535 if repo.ui.debugflag:
535 if repo.ui.debugflag:
536 return [parents[0], repo['null']]
536 return [parents[0], repo['null']]
537 if parents[0].rev() >= intrev(ctx) - 1:
537 if parents[0].rev() >= intrev(ctx) - 1:
538 return []
538 return []
539 return parents
539 return parents
540
540
541 def expandpats(pats):
541 def expandpats(pats):
542 '''Expand bare globs when running on windows.
542 '''Expand bare globs when running on windows.
543 On posix we assume it already has already been done by sh.'''
543 On posix we assume it already has already been done by sh.'''
544 if not util.expandglobs:
544 if not util.expandglobs:
545 return list(pats)
545 return list(pats)
546 ret = []
546 ret = []
547 for kindpat in pats:
547 for kindpat in pats:
548 kind, pat = matchmod._patsplit(kindpat, None)
548 kind, pat = matchmod._patsplit(kindpat, None)
549 if kind is None:
549 if kind is None:
550 try:
550 try:
551 globbed = glob.glob(pat)
551 globbed = glob.glob(pat)
552 except re.error:
552 except re.error:
553 globbed = [pat]
553 globbed = [pat]
554 if globbed:
554 if globbed:
555 ret.extend(globbed)
555 ret.extend(globbed)
556 continue
556 continue
557 ret.append(kindpat)
557 ret.append(kindpat)
558 return ret
558 return ret
559
559
560 def matchandpats(ctx, pats=(), opts=None, globbed=False, default='relpath',
560 def matchandpats(ctx, pats=(), opts=None, globbed=False, default='relpath',
561 badfn=None):
561 badfn=None):
562 '''Return a matcher and the patterns that were used.
562 '''Return a matcher and the patterns that were used.
563 The matcher will warn about bad matches, unless an alternate badfn callback
563 The matcher will warn about bad matches, unless an alternate badfn callback
564 is provided.'''
564 is provided.'''
565 if pats == ("",):
565 if pats == ("",):
566 pats = []
566 pats = []
567 if opts is None:
567 if opts is None:
568 opts = {}
568 opts = {}
569 if not globbed and default == 'relpath':
569 if not globbed and default == 'relpath':
570 pats = expandpats(pats or [])
570 pats = expandpats(pats or [])
571
571
572 def bad(f, msg):
572 def bad(f, msg):
573 ctx.repo().ui.warn("%s: %s\n" % (m.rel(f), msg))
573 ctx.repo().ui.warn("%s: %s\n" % (m.rel(f), msg))
574
574
575 if badfn is None:
575 if badfn is None:
576 badfn = bad
576 badfn = bad
577
577
578 m = ctx.match(pats, opts.get('include'), opts.get('exclude'),
578 m = ctx.match(pats, opts.get('include'), opts.get('exclude'),
579 default, listsubrepos=opts.get('subrepos'), badfn=badfn)
579 default, listsubrepos=opts.get('subrepos'), badfn=badfn)
580
580
581 if m.always():
581 if m.always():
582 pats = []
582 pats = []
583 return m, pats
583 return m, pats
584
584
585 def match(ctx, pats=(), opts=None, globbed=False, default='relpath',
585 def match(ctx, pats=(), opts=None, globbed=False, default='relpath',
586 badfn=None):
586 badfn=None):
587 '''Return a matcher that will warn about bad matches.'''
587 '''Return a matcher that will warn about bad matches.'''
588 return matchandpats(ctx, pats, opts, globbed, default, badfn=badfn)[0]
588 return matchandpats(ctx, pats, opts, globbed, default, badfn=badfn)[0]
589
589
590 def matchall(repo):
590 def matchall(repo):
591 '''Return a matcher that will efficiently match everything.'''
591 '''Return a matcher that will efficiently match everything.'''
592 return matchmod.always(repo.root, repo.getcwd())
592 return matchmod.always(repo.root, repo.getcwd())
593
593
594 def matchfiles(repo, files, badfn=None):
594 def matchfiles(repo, files, badfn=None):
595 '''Return a matcher that will efficiently match exactly these files.'''
595 '''Return a matcher that will efficiently match exactly these files.'''
596 return matchmod.exact(repo.root, repo.getcwd(), files, badfn=badfn)
596 return matchmod.exact(repo.root, repo.getcwd(), files, badfn=badfn)
597
597
598 def parsefollowlinespattern(repo, rev, pat, msg):
598 def parsefollowlinespattern(repo, rev, pat, msg):
599 """Return a file name from `pat` pattern suitable for usage in followlines
599 """Return a file name from `pat` pattern suitable for usage in followlines
600 logic.
600 logic.
601 """
601 """
602 if not matchmod.patkind(pat):
602 if not matchmod.patkind(pat):
603 return pathutil.canonpath(repo.root, repo.getcwd(), pat)
603 return pathutil.canonpath(repo.root, repo.getcwd(), pat)
604 else:
604 else:
605 ctx = repo[rev]
605 ctx = repo[rev]
606 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=ctx)
606 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=ctx)
607 files = [f for f in ctx if m(f)]
607 files = [f for f in ctx if m(f)]
608 if len(files) != 1:
608 if len(files) != 1:
609 raise error.ParseError(msg)
609 raise error.ParseError(msg)
610 return files[0]
610 return files[0]
611
611
612 def origpath(ui, repo, filepath):
612 def origpath(ui, repo, filepath):
613 '''customize where .orig files are created
613 '''customize where .orig files are created
614
614
615 Fetch user defined path from config file: [ui] origbackuppath = <path>
615 Fetch user defined path from config file: [ui] origbackuppath = <path>
616 Fall back to default (filepath with .orig suffix) if not specified
616 Fall back to default (filepath with .orig suffix) if not specified
617 '''
617 '''
618 origbackuppath = ui.config('ui', 'origbackuppath')
618 origbackuppath = ui.config('ui', 'origbackuppath')
619 if not origbackuppath:
619 if not origbackuppath:
620 return filepath + ".orig"
620 return filepath + ".orig"
621
621
622 # Convert filepath from an absolute path into a path inside the repo.
622 # Convert filepath from an absolute path into a path inside the repo.
623 filepathfromroot = util.normpath(os.path.relpath(filepath,
623 filepathfromroot = util.normpath(os.path.relpath(filepath,
624 start=repo.root))
624 start=repo.root))
625
625
626 origvfs = vfs.vfs(repo.wjoin(origbackuppath))
626 origvfs = vfs.vfs(repo.wjoin(origbackuppath))
627 origbackupdir = origvfs.dirname(filepathfromroot)
627 origbackupdir = origvfs.dirname(filepathfromroot)
628 if not origvfs.isdir(origbackupdir) or origvfs.islink(origbackupdir):
628 if not origvfs.isdir(origbackupdir) or origvfs.islink(origbackupdir):
629 ui.note(_('creating directory: %s\n') % origvfs.join(origbackupdir))
629 ui.note(_('creating directory: %s\n') % origvfs.join(origbackupdir))
630
630
631 # Remove any files that conflict with the backup file's path
631 # Remove any files that conflict with the backup file's path
632 for f in reversed(list(util.finddirs(filepathfromroot))):
632 for f in reversed(list(util.finddirs(filepathfromroot))):
633 if origvfs.isfileorlink(f):
633 if origvfs.isfileorlink(f):
634 ui.note(_('removing conflicting file: %s\n')
634 ui.note(_('removing conflicting file: %s\n')
635 % origvfs.join(f))
635 % origvfs.join(f))
636 origvfs.unlink(f)
636 origvfs.unlink(f)
637 break
637 break
638
638
639 origvfs.makedirs(origbackupdir)
639 origvfs.makedirs(origbackupdir)
640
640
641 if origvfs.isdir(filepathfromroot) and not origvfs.islink(filepathfromroot):
641 if origvfs.isdir(filepathfromroot) and not origvfs.islink(filepathfromroot):
642 ui.note(_('removing conflicting directory: %s\n')
642 ui.note(_('removing conflicting directory: %s\n')
643 % origvfs.join(filepathfromroot))
643 % origvfs.join(filepathfromroot))
644 origvfs.rmtree(filepathfromroot, forcibly=True)
644 origvfs.rmtree(filepathfromroot, forcibly=True)
645
645
646 return origvfs.join(filepathfromroot)
646 return origvfs.join(filepathfromroot)
647
647
648 class _containsnode(object):
648 class _containsnode(object):
649 """proxy __contains__(node) to container.__contains__ which accepts revs"""
649 """proxy __contains__(node) to container.__contains__ which accepts revs"""
650
650
651 def __init__(self, repo, revcontainer):
651 def __init__(self, repo, revcontainer):
652 self._torev = repo.changelog.rev
652 self._torev = repo.changelog.rev
653 self._revcontains = revcontainer.__contains__
653 self._revcontains = revcontainer.__contains__
654
654
655 def __contains__(self, node):
655 def __contains__(self, node):
656 return self._revcontains(self._torev(node))
656 return self._revcontains(self._torev(node))
657
657
658 def cleanupnodes(repo, replacements, operation, moves=None, metadata=None):
658 def cleanupnodes(repo, replacements, operation, moves=None, metadata=None):
659 """do common cleanups when old nodes are replaced by new nodes
659 """do common cleanups when old nodes are replaced by new nodes
660
660
661 That includes writing obsmarkers or stripping nodes, and moving bookmarks.
661 That includes writing obsmarkers or stripping nodes, and moving bookmarks.
662 (we might also want to move working directory parent in the future)
662 (we might also want to move working directory parent in the future)
663
663
664 By default, bookmark moves are calculated automatically from 'replacements',
664 By default, bookmark moves are calculated automatically from 'replacements',
665 but 'moves' can be used to override that. Also, 'moves' may include
665 but 'moves' can be used to override that. Also, 'moves' may include
666 additional bookmark moves that should not have associated obsmarkers.
666 additional bookmark moves that should not have associated obsmarkers.
667
667
668 replacements is {oldnode: [newnode]} or a iterable of nodes if they do not
668 replacements is {oldnode: [newnode]} or a iterable of nodes if they do not
669 have replacements. operation is a string, like "rebase".
669 have replacements. operation is a string, like "rebase".
670
670
671 metadata is dictionary containing metadata to be stored in obsmarker if
671 metadata is dictionary containing metadata to be stored in obsmarker if
672 obsolescence is enabled.
672 obsolescence is enabled.
673 """
673 """
674 if not replacements and not moves:
674 if not replacements and not moves:
675 return
675 return
676
676
677 # translate mapping's other forms
677 # translate mapping's other forms
678 if not util.safehasattr(replacements, 'items'):
678 if not util.safehasattr(replacements, 'items'):
679 replacements = {n: () for n in replacements}
679 replacements = {n: () for n in replacements}
680
680
681 # Calculate bookmark movements
681 # Calculate bookmark movements
682 if moves is None:
682 if moves is None:
683 moves = {}
683 moves = {}
684 # Unfiltered repo is needed since nodes in replacements might be hidden.
684 # Unfiltered repo is needed since nodes in replacements might be hidden.
685 unfi = repo.unfiltered()
685 unfi = repo.unfiltered()
686 for oldnode, newnodes in replacements.items():
686 for oldnode, newnodes in replacements.items():
687 if oldnode in moves:
687 if oldnode in moves:
688 continue
688 continue
689 if len(newnodes) > 1:
689 if len(newnodes) > 1:
690 # usually a split, take the one with biggest rev number
690 # usually a split, take the one with biggest rev number
691 newnode = next(unfi.set('max(%ln)', newnodes)).node()
691 newnode = next(unfi.set('max(%ln)', newnodes)).node()
692 elif len(newnodes) == 0:
692 elif len(newnodes) == 0:
693 # move bookmark backwards
693 # move bookmark backwards
694 roots = list(unfi.set('max((::%n) - %ln)', oldnode,
694 roots = list(unfi.set('max((::%n) - %ln)', oldnode,
695 list(replacements)))
695 list(replacements)))
696 if roots:
696 if roots:
697 newnode = roots[0].node()
697 newnode = roots[0].node()
698 else:
698 else:
699 newnode = nullid
699 newnode = nullid
700 else:
700 else:
701 newnode = newnodes[0]
701 newnode = newnodes[0]
702 moves[oldnode] = newnode
702 moves[oldnode] = newnode
703
703
704 with repo.transaction('cleanup') as tr:
704 with repo.transaction('cleanup') as tr:
705 # Move bookmarks
705 # Move bookmarks
706 bmarks = repo._bookmarks
706 bmarks = repo._bookmarks
707 bmarkchanges = []
707 bmarkchanges = []
708 allnewnodes = [n for ns in replacements.values() for n in ns]
708 allnewnodes = [n for ns in replacements.values() for n in ns]
709 for oldnode, newnode in moves.items():
709 for oldnode, newnode in moves.items():
710 oldbmarks = repo.nodebookmarks(oldnode)
710 oldbmarks = repo.nodebookmarks(oldnode)
711 if not oldbmarks:
711 if not oldbmarks:
712 continue
712 continue
713 from . import bookmarks # avoid import cycle
713 from . import bookmarks # avoid import cycle
714 repo.ui.debug('moving bookmarks %r from %s to %s\n' %
714 repo.ui.debug('moving bookmarks %r from %s to %s\n' %
715 (util.rapply(pycompat.maybebytestr, oldbmarks),
715 (util.rapply(pycompat.maybebytestr, oldbmarks),
716 hex(oldnode), hex(newnode)))
716 hex(oldnode), hex(newnode)))
717 # Delete divergent bookmarks being parents of related newnodes
717 # Delete divergent bookmarks being parents of related newnodes
718 deleterevs = repo.revs('parents(roots(%ln & (::%n))) - parents(%n)',
718 deleterevs = repo.revs('parents(roots(%ln & (::%n))) - parents(%n)',
719 allnewnodes, newnode, oldnode)
719 allnewnodes, newnode, oldnode)
720 deletenodes = _containsnode(repo, deleterevs)
720 deletenodes = _containsnode(repo, deleterevs)
721 for name in oldbmarks:
721 for name in oldbmarks:
722 bmarkchanges.append((name, newnode))
722 bmarkchanges.append((name, newnode))
723 for b in bookmarks.divergent2delete(repo, deletenodes, name):
723 for b in bookmarks.divergent2delete(repo, deletenodes, name):
724 bmarkchanges.append((b, None))
724 bmarkchanges.append((b, None))
725
725
726 if bmarkchanges:
726 if bmarkchanges:
727 bmarks.applychanges(repo, tr, bmarkchanges)
727 bmarks.applychanges(repo, tr, bmarkchanges)
728
728
729 # Obsolete or strip nodes
729 # Obsolete or strip nodes
730 if obsolete.isenabled(repo, obsolete.createmarkersopt):
730 if obsolete.isenabled(repo, obsolete.createmarkersopt):
731 # If a node is already obsoleted, and we want to obsolete it
731 # If a node is already obsoleted, and we want to obsolete it
732 # without a successor, skip that obssolete request since it's
732 # without a successor, skip that obssolete request since it's
733 # unnecessary. That's the "if s or not isobs(n)" check below.
733 # unnecessary. That's the "if s or not isobs(n)" check below.
734 # Also sort the node in topology order, that might be useful for
734 # Also sort the node in topology order, that might be useful for
735 # some obsstore logic.
735 # some obsstore logic.
736 # NOTE: the filtering and sorting might belong to createmarkers.
736 # NOTE: the filtering and sorting might belong to createmarkers.
737 isobs = unfi.obsstore.successors.__contains__
737 isobs = unfi.obsstore.successors.__contains__
738 torev = unfi.changelog.rev
738 torev = unfi.changelog.rev
739 sortfunc = lambda ns: torev(ns[0])
739 sortfunc = lambda ns: torev(ns[0])
740 rels = [(unfi[n], tuple(unfi[m] for m in s))
740 rels = [(unfi[n], tuple(unfi[m] for m in s))
741 for n, s in sorted(replacements.items(), key=sortfunc)
741 for n, s in sorted(replacements.items(), key=sortfunc)
742 if s or not isobs(n)]
742 if s or not isobs(n)]
743 if rels:
743 if rels:
744 obsolete.createmarkers(repo, rels, operation=operation,
744 obsolete.createmarkers(repo, rels, operation=operation,
745 metadata=metadata)
745 metadata=metadata)
746 else:
746 else:
747 from . import repair # avoid import cycle
747 from . import repair # avoid import cycle
748 tostrip = list(replacements)
748 tostrip = list(replacements)
749 if tostrip:
749 if tostrip:
750 repair.delayedstrip(repo.ui, repo, tostrip, operation)
750 repair.delayedstrip(repo.ui, repo, tostrip, operation)
751
751
752 def addremove(repo, matcher, prefix, opts=None):
752 def addremove(repo, matcher, prefix, opts=None):
753 if opts is None:
753 if opts is None:
754 opts = {}
754 opts = {}
755 m = matcher
755 m = matcher
756 dry_run = opts.get('dry_run')
756 dry_run = opts.get('dry_run')
757 try:
757 similarity = float(opts.get('similarity') or 0)
758 similarity = float(opts.get('similarity') or 0)
759 except ValueError:
760 raise error.Abort(_('similarity must be a number'))
761 if similarity < 0 or similarity > 100:
762 raise error.Abort(_('similarity must be between 0 and 100'))
763 similarity /= 100.0
758
764
759 ret = 0
765 ret = 0
760 join = lambda f: os.path.join(prefix, f)
766 join = lambda f: os.path.join(prefix, f)
761
767
762 wctx = repo[None]
768 wctx = repo[None]
763 for subpath in sorted(wctx.substate):
769 for subpath in sorted(wctx.substate):
764 submatch = matchmod.subdirmatcher(subpath, m)
770 submatch = matchmod.subdirmatcher(subpath, m)
765 if opts.get('subrepos') or m.exact(subpath) or any(submatch.files()):
771 if opts.get('subrepos') or m.exact(subpath) or any(submatch.files()):
766 sub = wctx.sub(subpath)
772 sub = wctx.sub(subpath)
767 try:
773 try:
768 if sub.addremove(submatch, prefix, opts):
774 if sub.addremove(submatch, prefix, opts):
769 ret = 1
775 ret = 1
770 except error.LookupError:
776 except error.LookupError:
771 repo.ui.status(_("skipping missing subrepository: %s\n")
777 repo.ui.status(_("skipping missing subrepository: %s\n")
772 % join(subpath))
778 % join(subpath))
773
779
774 rejected = []
780 rejected = []
775 def badfn(f, msg):
781 def badfn(f, msg):
776 if f in m.files():
782 if f in m.files():
777 m.bad(f, msg)
783 m.bad(f, msg)
778 rejected.append(f)
784 rejected.append(f)
779
785
780 badmatch = matchmod.badmatch(m, badfn)
786 badmatch = matchmod.badmatch(m, badfn)
781 added, unknown, deleted, removed, forgotten = _interestingfiles(repo,
787 added, unknown, deleted, removed, forgotten = _interestingfiles(repo,
782 badmatch)
788 badmatch)
783
789
784 unknownset = set(unknown + forgotten)
790 unknownset = set(unknown + forgotten)
785 toprint = unknownset.copy()
791 toprint = unknownset.copy()
786 toprint.update(deleted)
792 toprint.update(deleted)
787 for abs in sorted(toprint):
793 for abs in sorted(toprint):
788 if repo.ui.verbose or not m.exact(abs):
794 if repo.ui.verbose or not m.exact(abs):
789 if abs in unknownset:
795 if abs in unknownset:
790 status = _('adding %s\n') % m.uipath(abs)
796 status = _('adding %s\n') % m.uipath(abs)
791 else:
797 else:
792 status = _('removing %s\n') % m.uipath(abs)
798 status = _('removing %s\n') % m.uipath(abs)
793 repo.ui.status(status)
799 repo.ui.status(status)
794
800
795 renames = _findrenames(repo, m, added + unknown, removed + deleted,
801 renames = _findrenames(repo, m, added + unknown, removed + deleted,
796 similarity)
802 similarity)
797
803
798 if not dry_run:
804 if not dry_run:
799 _markchanges(repo, unknown + forgotten, deleted, renames)
805 _markchanges(repo, unknown + forgotten, deleted, renames)
800
806
801 for f in rejected:
807 for f in rejected:
802 if f in m.files():
808 if f in m.files():
803 return 1
809 return 1
804 return ret
810 return ret
805
811
806 def marktouched(repo, files, similarity=0.0):
812 def marktouched(repo, files, similarity=0.0):
807 '''Assert that files have somehow been operated upon. files are relative to
813 '''Assert that files have somehow been operated upon. files are relative to
808 the repo root.'''
814 the repo root.'''
809 m = matchfiles(repo, files, badfn=lambda x, y: rejected.append(x))
815 m = matchfiles(repo, files, badfn=lambda x, y: rejected.append(x))
810 rejected = []
816 rejected = []
811
817
812 added, unknown, deleted, removed, forgotten = _interestingfiles(repo, m)
818 added, unknown, deleted, removed, forgotten = _interestingfiles(repo, m)
813
819
814 if repo.ui.verbose:
820 if repo.ui.verbose:
815 unknownset = set(unknown + forgotten)
821 unknownset = set(unknown + forgotten)
816 toprint = unknownset.copy()
822 toprint = unknownset.copy()
817 toprint.update(deleted)
823 toprint.update(deleted)
818 for abs in sorted(toprint):
824 for abs in sorted(toprint):
819 if abs in unknownset:
825 if abs in unknownset:
820 status = _('adding %s\n') % abs
826 status = _('adding %s\n') % abs
821 else:
827 else:
822 status = _('removing %s\n') % abs
828 status = _('removing %s\n') % abs
823 repo.ui.status(status)
829 repo.ui.status(status)
824
830
825 renames = _findrenames(repo, m, added + unknown, removed + deleted,
831 renames = _findrenames(repo, m, added + unknown, removed + deleted,
826 similarity)
832 similarity)
827
833
828 _markchanges(repo, unknown + forgotten, deleted, renames)
834 _markchanges(repo, unknown + forgotten, deleted, renames)
829
835
830 for f in rejected:
836 for f in rejected:
831 if f in m.files():
837 if f in m.files():
832 return 1
838 return 1
833 return 0
839 return 0
834
840
835 def _interestingfiles(repo, matcher):
841 def _interestingfiles(repo, matcher):
836 '''Walk dirstate with matcher, looking for files that addremove would care
842 '''Walk dirstate with matcher, looking for files that addremove would care
837 about.
843 about.
838
844
839 This is different from dirstate.status because it doesn't care about
845 This is different from dirstate.status because it doesn't care about
840 whether files are modified or clean.'''
846 whether files are modified or clean.'''
841 added, unknown, deleted, removed, forgotten = [], [], [], [], []
847 added, unknown, deleted, removed, forgotten = [], [], [], [], []
842 audit_path = pathutil.pathauditor(repo.root, cached=True)
848 audit_path = pathutil.pathauditor(repo.root, cached=True)
843
849
844 ctx = repo[None]
850 ctx = repo[None]
845 dirstate = repo.dirstate
851 dirstate = repo.dirstate
846 walkresults = dirstate.walk(matcher, subrepos=sorted(ctx.substate),
852 walkresults = dirstate.walk(matcher, subrepos=sorted(ctx.substate),
847 unknown=True, ignored=False, full=False)
853 unknown=True, ignored=False, full=False)
848 for abs, st in walkresults.iteritems():
854 for abs, st in walkresults.iteritems():
849 dstate = dirstate[abs]
855 dstate = dirstate[abs]
850 if dstate == '?' and audit_path.check(abs):
856 if dstate == '?' and audit_path.check(abs):
851 unknown.append(abs)
857 unknown.append(abs)
852 elif dstate != 'r' and not st:
858 elif dstate != 'r' and not st:
853 deleted.append(abs)
859 deleted.append(abs)
854 elif dstate == 'r' and st:
860 elif dstate == 'r' and st:
855 forgotten.append(abs)
861 forgotten.append(abs)
856 # for finding renames
862 # for finding renames
857 elif dstate == 'r' and not st:
863 elif dstate == 'r' and not st:
858 removed.append(abs)
864 removed.append(abs)
859 elif dstate == 'a':
865 elif dstate == 'a':
860 added.append(abs)
866 added.append(abs)
861
867
862 return added, unknown, deleted, removed, forgotten
868 return added, unknown, deleted, removed, forgotten
863
869
864 def _findrenames(repo, matcher, added, removed, similarity):
870 def _findrenames(repo, matcher, added, removed, similarity):
865 '''Find renames from removed files to added ones.'''
871 '''Find renames from removed files to added ones.'''
866 renames = {}
872 renames = {}
867 if similarity > 0:
873 if similarity > 0:
868 for old, new, score in similar.findrenames(repo, added, removed,
874 for old, new, score in similar.findrenames(repo, added, removed,
869 similarity):
875 similarity):
870 if (repo.ui.verbose or not matcher.exact(old)
876 if (repo.ui.verbose or not matcher.exact(old)
871 or not matcher.exact(new)):
877 or not matcher.exact(new)):
872 repo.ui.status(_('recording removal of %s as rename to %s '
878 repo.ui.status(_('recording removal of %s as rename to %s '
873 '(%d%% similar)\n') %
879 '(%d%% similar)\n') %
874 (matcher.rel(old), matcher.rel(new),
880 (matcher.rel(old), matcher.rel(new),
875 score * 100))
881 score * 100))
876 renames[new] = old
882 renames[new] = old
877 return renames
883 return renames
878
884
879 def _markchanges(repo, unknown, deleted, renames):
885 def _markchanges(repo, unknown, deleted, renames):
880 '''Marks the files in unknown as added, the files in deleted as removed,
886 '''Marks the files in unknown as added, the files in deleted as removed,
881 and the files in renames as copied.'''
887 and the files in renames as copied.'''
882 wctx = repo[None]
888 wctx = repo[None]
883 with repo.wlock():
889 with repo.wlock():
884 wctx.forget(deleted)
890 wctx.forget(deleted)
885 wctx.add(unknown)
891 wctx.add(unknown)
886 for new, old in renames.iteritems():
892 for new, old in renames.iteritems():
887 wctx.copy(old, new)
893 wctx.copy(old, new)
888
894
889 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
895 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
890 """Update the dirstate to reflect the intent of copying src to dst. For
896 """Update the dirstate to reflect the intent of copying src to dst. For
891 different reasons it might not end with dst being marked as copied from src.
897 different reasons it might not end with dst being marked as copied from src.
892 """
898 """
893 origsrc = repo.dirstate.copied(src) or src
899 origsrc = repo.dirstate.copied(src) or src
894 if dst == origsrc: # copying back a copy?
900 if dst == origsrc: # copying back a copy?
895 if repo.dirstate[dst] not in 'mn' and not dryrun:
901 if repo.dirstate[dst] not in 'mn' and not dryrun:
896 repo.dirstate.normallookup(dst)
902 repo.dirstate.normallookup(dst)
897 else:
903 else:
898 if repo.dirstate[origsrc] == 'a' and origsrc == src:
904 if repo.dirstate[origsrc] == 'a' and origsrc == src:
899 if not ui.quiet:
905 if not ui.quiet:
900 ui.warn(_("%s has not been committed yet, so no copy "
906 ui.warn(_("%s has not been committed yet, so no copy "
901 "data will be stored for %s.\n")
907 "data will be stored for %s.\n")
902 % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd)))
908 % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd)))
903 if repo.dirstate[dst] in '?r' and not dryrun:
909 if repo.dirstate[dst] in '?r' and not dryrun:
904 wctx.add([dst])
910 wctx.add([dst])
905 elif not dryrun:
911 elif not dryrun:
906 wctx.copy(origsrc, dst)
912 wctx.copy(origsrc, dst)
907
913
908 def readrequires(opener, supported):
914 def readrequires(opener, supported):
909 '''Reads and parses .hg/requires and checks if all entries found
915 '''Reads and parses .hg/requires and checks if all entries found
910 are in the list of supported features.'''
916 are in the list of supported features.'''
911 requirements = set(opener.read("requires").splitlines())
917 requirements = set(opener.read("requires").splitlines())
912 missings = []
918 missings = []
913 for r in requirements:
919 for r in requirements:
914 if r not in supported:
920 if r not in supported:
915 if not r or not r[0:1].isalnum():
921 if not r or not r[0:1].isalnum():
916 raise error.RequirementError(_(".hg/requires file is corrupt"))
922 raise error.RequirementError(_(".hg/requires file is corrupt"))
917 missings.append(r)
923 missings.append(r)
918 missings.sort()
924 missings.sort()
919 if missings:
925 if missings:
920 raise error.RequirementError(
926 raise error.RequirementError(
921 _("repository requires features unknown to this Mercurial: %s")
927 _("repository requires features unknown to this Mercurial: %s")
922 % " ".join(missings),
928 % " ".join(missings),
923 hint=_("see https://mercurial-scm.org/wiki/MissingRequirement"
929 hint=_("see https://mercurial-scm.org/wiki/MissingRequirement"
924 " for more information"))
930 " for more information"))
925 return requirements
931 return requirements
926
932
927 def writerequires(opener, requirements):
933 def writerequires(opener, requirements):
928 with opener('requires', 'w') as fp:
934 with opener('requires', 'w') as fp:
929 for r in sorted(requirements):
935 for r in sorted(requirements):
930 fp.write("%s\n" % r)
936 fp.write("%s\n" % r)
931
937
932 class filecachesubentry(object):
938 class filecachesubentry(object):
933 def __init__(self, path, stat):
939 def __init__(self, path, stat):
934 self.path = path
940 self.path = path
935 self.cachestat = None
941 self.cachestat = None
936 self._cacheable = None
942 self._cacheable = None
937
943
938 if stat:
944 if stat:
939 self.cachestat = filecachesubentry.stat(self.path)
945 self.cachestat = filecachesubentry.stat(self.path)
940
946
941 if self.cachestat:
947 if self.cachestat:
942 self._cacheable = self.cachestat.cacheable()
948 self._cacheable = self.cachestat.cacheable()
943 else:
949 else:
944 # None means we don't know yet
950 # None means we don't know yet
945 self._cacheable = None
951 self._cacheable = None
946
952
947 def refresh(self):
953 def refresh(self):
948 if self.cacheable():
954 if self.cacheable():
949 self.cachestat = filecachesubentry.stat(self.path)
955 self.cachestat = filecachesubentry.stat(self.path)
950
956
951 def cacheable(self):
957 def cacheable(self):
952 if self._cacheable is not None:
958 if self._cacheable is not None:
953 return self._cacheable
959 return self._cacheable
954
960
955 # we don't know yet, assume it is for now
961 # we don't know yet, assume it is for now
956 return True
962 return True
957
963
958 def changed(self):
964 def changed(self):
959 # no point in going further if we can't cache it
965 # no point in going further if we can't cache it
960 if not self.cacheable():
966 if not self.cacheable():
961 return True
967 return True
962
968
963 newstat = filecachesubentry.stat(self.path)
969 newstat = filecachesubentry.stat(self.path)
964
970
965 # we may not know if it's cacheable yet, check again now
971 # we may not know if it's cacheable yet, check again now
966 if newstat and self._cacheable is None:
972 if newstat and self._cacheable is None:
967 self._cacheable = newstat.cacheable()
973 self._cacheable = newstat.cacheable()
968
974
969 # check again
975 # check again
970 if not self._cacheable:
976 if not self._cacheable:
971 return True
977 return True
972
978
973 if self.cachestat != newstat:
979 if self.cachestat != newstat:
974 self.cachestat = newstat
980 self.cachestat = newstat
975 return True
981 return True
976 else:
982 else:
977 return False
983 return False
978
984
979 @staticmethod
985 @staticmethod
980 def stat(path):
986 def stat(path):
981 try:
987 try:
982 return util.cachestat(path)
988 return util.cachestat(path)
983 except OSError as e:
989 except OSError as e:
984 if e.errno != errno.ENOENT:
990 if e.errno != errno.ENOENT:
985 raise
991 raise
986
992
987 class filecacheentry(object):
993 class filecacheentry(object):
988 def __init__(self, paths, stat=True):
994 def __init__(self, paths, stat=True):
989 self._entries = []
995 self._entries = []
990 for path in paths:
996 for path in paths:
991 self._entries.append(filecachesubentry(path, stat))
997 self._entries.append(filecachesubentry(path, stat))
992
998
993 def changed(self):
999 def changed(self):
994 '''true if any entry has changed'''
1000 '''true if any entry has changed'''
995 for entry in self._entries:
1001 for entry in self._entries:
996 if entry.changed():
1002 if entry.changed():
997 return True
1003 return True
998 return False
1004 return False
999
1005
1000 def refresh(self):
1006 def refresh(self):
1001 for entry in self._entries:
1007 for entry in self._entries:
1002 entry.refresh()
1008 entry.refresh()
1003
1009
1004 class filecache(object):
1010 class filecache(object):
1005 '''A property like decorator that tracks files under .hg/ for updates.
1011 '''A property like decorator that tracks files under .hg/ for updates.
1006
1012
1007 Records stat info when called in _filecache.
1013 Records stat info when called in _filecache.
1008
1014
1009 On subsequent calls, compares old stat info with new info, and recreates the
1015 On subsequent calls, compares old stat info with new info, and recreates the
1010 object when any of the files changes, updating the new stat info in
1016 object when any of the files changes, updating the new stat info in
1011 _filecache.
1017 _filecache.
1012
1018
1013 Mercurial either atomic renames or appends for files under .hg,
1019 Mercurial either atomic renames or appends for files under .hg,
1014 so to ensure the cache is reliable we need the filesystem to be able
1020 so to ensure the cache is reliable we need the filesystem to be able
1015 to tell us if a file has been replaced. If it can't, we fallback to
1021 to tell us if a file has been replaced. If it can't, we fallback to
1016 recreating the object on every call (essentially the same behavior as
1022 recreating the object on every call (essentially the same behavior as
1017 propertycache).
1023 propertycache).
1018
1024
1019 '''
1025 '''
1020 def __init__(self, *paths):
1026 def __init__(self, *paths):
1021 self.paths = paths
1027 self.paths = paths
1022
1028
1023 def join(self, obj, fname):
1029 def join(self, obj, fname):
1024 """Used to compute the runtime path of a cached file.
1030 """Used to compute the runtime path of a cached file.
1025
1031
1026 Users should subclass filecache and provide their own version of this
1032 Users should subclass filecache and provide their own version of this
1027 function to call the appropriate join function on 'obj' (an instance
1033 function to call the appropriate join function on 'obj' (an instance
1028 of the class that its member function was decorated).
1034 of the class that its member function was decorated).
1029 """
1035 """
1030 raise NotImplementedError
1036 raise NotImplementedError
1031
1037
1032 def __call__(self, func):
1038 def __call__(self, func):
1033 self.func = func
1039 self.func = func
1034 self.name = func.__name__.encode('ascii')
1040 self.name = func.__name__.encode('ascii')
1035 return self
1041 return self
1036
1042
1037 def __get__(self, obj, type=None):
1043 def __get__(self, obj, type=None):
1038 # if accessed on the class, return the descriptor itself.
1044 # if accessed on the class, return the descriptor itself.
1039 if obj is None:
1045 if obj is None:
1040 return self
1046 return self
1041 # do we need to check if the file changed?
1047 # do we need to check if the file changed?
1042 if self.name in obj.__dict__:
1048 if self.name in obj.__dict__:
1043 assert self.name in obj._filecache, self.name
1049 assert self.name in obj._filecache, self.name
1044 return obj.__dict__[self.name]
1050 return obj.__dict__[self.name]
1045
1051
1046 entry = obj._filecache.get(self.name)
1052 entry = obj._filecache.get(self.name)
1047
1053
1048 if entry:
1054 if entry:
1049 if entry.changed():
1055 if entry.changed():
1050 entry.obj = self.func(obj)
1056 entry.obj = self.func(obj)
1051 else:
1057 else:
1052 paths = [self.join(obj, path) for path in self.paths]
1058 paths = [self.join(obj, path) for path in self.paths]
1053
1059
1054 # We stat -before- creating the object so our cache doesn't lie if
1060 # We stat -before- creating the object so our cache doesn't lie if
1055 # a writer modified between the time we read and stat
1061 # a writer modified between the time we read and stat
1056 entry = filecacheentry(paths, True)
1062 entry = filecacheentry(paths, True)
1057 entry.obj = self.func(obj)
1063 entry.obj = self.func(obj)
1058
1064
1059 obj._filecache[self.name] = entry
1065 obj._filecache[self.name] = entry
1060
1066
1061 obj.__dict__[self.name] = entry.obj
1067 obj.__dict__[self.name] = entry.obj
1062 return entry.obj
1068 return entry.obj
1063
1069
1064 def __set__(self, obj, value):
1070 def __set__(self, obj, value):
1065 if self.name not in obj._filecache:
1071 if self.name not in obj._filecache:
1066 # we add an entry for the missing value because X in __dict__
1072 # we add an entry for the missing value because X in __dict__
1067 # implies X in _filecache
1073 # implies X in _filecache
1068 paths = [self.join(obj, path) for path in self.paths]
1074 paths = [self.join(obj, path) for path in self.paths]
1069 ce = filecacheentry(paths, False)
1075 ce = filecacheentry(paths, False)
1070 obj._filecache[self.name] = ce
1076 obj._filecache[self.name] = ce
1071 else:
1077 else:
1072 ce = obj._filecache[self.name]
1078 ce = obj._filecache[self.name]
1073
1079
1074 ce.obj = value # update cached copy
1080 ce.obj = value # update cached copy
1075 obj.__dict__[self.name] = value # update copy returned by obj.x
1081 obj.__dict__[self.name] = value # update copy returned by obj.x
1076
1082
1077 def __delete__(self, obj):
1083 def __delete__(self, obj):
1078 try:
1084 try:
1079 del obj.__dict__[self.name]
1085 del obj.__dict__[self.name]
1080 except KeyError:
1086 except KeyError:
1081 raise AttributeError(self.name)
1087 raise AttributeError(self.name)
1082
1088
1083 def extdatasource(repo, source):
1089 def extdatasource(repo, source):
1084 """Gather a map of rev -> value dict from the specified source
1090 """Gather a map of rev -> value dict from the specified source
1085
1091
1086 A source spec is treated as a URL, with a special case shell: type
1092 A source spec is treated as a URL, with a special case shell: type
1087 for parsing the output from a shell command.
1093 for parsing the output from a shell command.
1088
1094
1089 The data is parsed as a series of newline-separated records where
1095 The data is parsed as a series of newline-separated records where
1090 each record is a revision specifier optionally followed by a space
1096 each record is a revision specifier optionally followed by a space
1091 and a freeform string value. If the revision is known locally, it
1097 and a freeform string value. If the revision is known locally, it
1092 is converted to a rev, otherwise the record is skipped.
1098 is converted to a rev, otherwise the record is skipped.
1093
1099
1094 Note that both key and value are treated as UTF-8 and converted to
1100 Note that both key and value are treated as UTF-8 and converted to
1095 the local encoding. This allows uniformity between local and
1101 the local encoding. This allows uniformity between local and
1096 remote data sources.
1102 remote data sources.
1097 """
1103 """
1098
1104
1099 spec = repo.ui.config("extdata", source)
1105 spec = repo.ui.config("extdata", source)
1100 if not spec:
1106 if not spec:
1101 raise error.Abort(_("unknown extdata source '%s'") % source)
1107 raise error.Abort(_("unknown extdata source '%s'") % source)
1102
1108
1103 data = {}
1109 data = {}
1104 src = proc = None
1110 src = proc = None
1105 try:
1111 try:
1106 if spec.startswith("shell:"):
1112 if spec.startswith("shell:"):
1107 # external commands should be run relative to the repo root
1113 # external commands should be run relative to the repo root
1108 cmd = spec[6:]
1114 cmd = spec[6:]
1109 proc = subprocess.Popen(cmd, shell=True, bufsize=-1,
1115 proc = subprocess.Popen(cmd, shell=True, bufsize=-1,
1110 close_fds=procutil.closefds,
1116 close_fds=procutil.closefds,
1111 stdout=subprocess.PIPE, cwd=repo.root)
1117 stdout=subprocess.PIPE, cwd=repo.root)
1112 src = proc.stdout
1118 src = proc.stdout
1113 else:
1119 else:
1114 # treat as a URL or file
1120 # treat as a URL or file
1115 src = url.open(repo.ui, spec)
1121 src = url.open(repo.ui, spec)
1116 for l in src:
1122 for l in src:
1117 if " " in l:
1123 if " " in l:
1118 k, v = l.strip().split(" ", 1)
1124 k, v = l.strip().split(" ", 1)
1119 else:
1125 else:
1120 k, v = l.strip(), ""
1126 k, v = l.strip(), ""
1121
1127
1122 k = encoding.tolocal(k)
1128 k = encoding.tolocal(k)
1123 try:
1129 try:
1124 data[repo[k].rev()] = encoding.tolocal(v)
1130 data[repo[k].rev()] = encoding.tolocal(v)
1125 except (error.LookupError, error.RepoLookupError):
1131 except (error.LookupError, error.RepoLookupError):
1126 pass # we ignore data for nodes that don't exist locally
1132 pass # we ignore data for nodes that don't exist locally
1127 finally:
1133 finally:
1128 if proc:
1134 if proc:
1129 proc.communicate()
1135 proc.communicate()
1130 if src:
1136 if src:
1131 src.close()
1137 src.close()
1132 if proc and proc.returncode != 0:
1138 if proc and proc.returncode != 0:
1133 raise error.Abort(_("extdata command '%s' failed: %s")
1139 raise error.Abort(_("extdata command '%s' failed: %s")
1134 % (cmd, procutil.explainexit(proc.returncode)[0]))
1140 % (cmd, procutil.explainexit(proc.returncode)[0]))
1135
1141
1136 return data
1142 return data
1137
1143
1138 def _locksub(repo, lock, envvar, cmd, environ=None, *args, **kwargs):
1144 def _locksub(repo, lock, envvar, cmd, environ=None, *args, **kwargs):
1139 if lock is None:
1145 if lock is None:
1140 raise error.LockInheritanceContractViolation(
1146 raise error.LockInheritanceContractViolation(
1141 'lock can only be inherited while held')
1147 'lock can only be inherited while held')
1142 if environ is None:
1148 if environ is None:
1143 environ = {}
1149 environ = {}
1144 with lock.inherit() as locker:
1150 with lock.inherit() as locker:
1145 environ[envvar] = locker
1151 environ[envvar] = locker
1146 return repo.ui.system(cmd, environ=environ, *args, **kwargs)
1152 return repo.ui.system(cmd, environ=environ, *args, **kwargs)
1147
1153
1148 def wlocksub(repo, cmd, *args, **kwargs):
1154 def wlocksub(repo, cmd, *args, **kwargs):
1149 """run cmd as a subprocess that allows inheriting repo's wlock
1155 """run cmd as a subprocess that allows inheriting repo's wlock
1150
1156
1151 This can only be called while the wlock is held. This takes all the
1157 This can only be called while the wlock is held. This takes all the
1152 arguments that ui.system does, and returns the exit code of the
1158 arguments that ui.system does, and returns the exit code of the
1153 subprocess."""
1159 subprocess."""
1154 return _locksub(repo, repo.currentwlock(), 'HG_WLOCK_LOCKER', cmd, *args,
1160 return _locksub(repo, repo.currentwlock(), 'HG_WLOCK_LOCKER', cmd, *args,
1155 **kwargs)
1161 **kwargs)
1156
1162
1157 def gdinitconfig(ui):
1163 def gdinitconfig(ui):
1158 """helper function to know if a repo should be created as general delta
1164 """helper function to know if a repo should be created as general delta
1159 """
1165 """
1160 # experimental config: format.generaldelta
1166 # experimental config: format.generaldelta
1161 return (ui.configbool('format', 'generaldelta')
1167 return (ui.configbool('format', 'generaldelta')
1162 or ui.configbool('format', 'usegeneraldelta'))
1168 or ui.configbool('format', 'usegeneraldelta'))
1163
1169
1164 def gddeltaconfig(ui):
1170 def gddeltaconfig(ui):
1165 """helper function to know if incoming delta should be optimised
1171 """helper function to know if incoming delta should be optimised
1166 """
1172 """
1167 # experimental config: format.generaldelta
1173 # experimental config: format.generaldelta
1168 return ui.configbool('format', 'generaldelta')
1174 return ui.configbool('format', 'generaldelta')
1169
1175
1170 class simplekeyvaluefile(object):
1176 class simplekeyvaluefile(object):
1171 """A simple file with key=value lines
1177 """A simple file with key=value lines
1172
1178
1173 Keys must be alphanumerics and start with a letter, values must not
1179 Keys must be alphanumerics and start with a letter, values must not
1174 contain '\n' characters"""
1180 contain '\n' characters"""
1175 firstlinekey = '__firstline'
1181 firstlinekey = '__firstline'
1176
1182
1177 def __init__(self, vfs, path, keys=None):
1183 def __init__(self, vfs, path, keys=None):
1178 self.vfs = vfs
1184 self.vfs = vfs
1179 self.path = path
1185 self.path = path
1180
1186
1181 def read(self, firstlinenonkeyval=False):
1187 def read(self, firstlinenonkeyval=False):
1182 """Read the contents of a simple key-value file
1188 """Read the contents of a simple key-value file
1183
1189
1184 'firstlinenonkeyval' indicates whether the first line of file should
1190 'firstlinenonkeyval' indicates whether the first line of file should
1185 be treated as a key-value pair or reuturned fully under the
1191 be treated as a key-value pair or reuturned fully under the
1186 __firstline key."""
1192 __firstline key."""
1187 lines = self.vfs.readlines(self.path)
1193 lines = self.vfs.readlines(self.path)
1188 d = {}
1194 d = {}
1189 if firstlinenonkeyval:
1195 if firstlinenonkeyval:
1190 if not lines:
1196 if not lines:
1191 e = _("empty simplekeyvalue file")
1197 e = _("empty simplekeyvalue file")
1192 raise error.CorruptedState(e)
1198 raise error.CorruptedState(e)
1193 # we don't want to include '\n' in the __firstline
1199 # we don't want to include '\n' in the __firstline
1194 d[self.firstlinekey] = lines[0][:-1]
1200 d[self.firstlinekey] = lines[0][:-1]
1195 del lines[0]
1201 del lines[0]
1196
1202
1197 try:
1203 try:
1198 # the 'if line.strip()' part prevents us from failing on empty
1204 # the 'if line.strip()' part prevents us from failing on empty
1199 # lines which only contain '\n' therefore are not skipped
1205 # lines which only contain '\n' therefore are not skipped
1200 # by 'if line'
1206 # by 'if line'
1201 updatedict = dict(line[:-1].split('=', 1) for line in lines
1207 updatedict = dict(line[:-1].split('=', 1) for line in lines
1202 if line.strip())
1208 if line.strip())
1203 if self.firstlinekey in updatedict:
1209 if self.firstlinekey in updatedict:
1204 e = _("%r can't be used as a key")
1210 e = _("%r can't be used as a key")
1205 raise error.CorruptedState(e % self.firstlinekey)
1211 raise error.CorruptedState(e % self.firstlinekey)
1206 d.update(updatedict)
1212 d.update(updatedict)
1207 except ValueError as e:
1213 except ValueError as e:
1208 raise error.CorruptedState(str(e))
1214 raise error.CorruptedState(str(e))
1209 return d
1215 return d
1210
1216
1211 def write(self, data, firstline=None):
1217 def write(self, data, firstline=None):
1212 """Write key=>value mapping to a file
1218 """Write key=>value mapping to a file
1213 data is a dict. Keys must be alphanumerical and start with a letter.
1219 data is a dict. Keys must be alphanumerical and start with a letter.
1214 Values must not contain newline characters.
1220 Values must not contain newline characters.
1215
1221
1216 If 'firstline' is not None, it is written to file before
1222 If 'firstline' is not None, it is written to file before
1217 everything else, as it is, not in a key=value form"""
1223 everything else, as it is, not in a key=value form"""
1218 lines = []
1224 lines = []
1219 if firstline is not None:
1225 if firstline is not None:
1220 lines.append('%s\n' % firstline)
1226 lines.append('%s\n' % firstline)
1221
1227
1222 for k, v in data.items():
1228 for k, v in data.items():
1223 if k == self.firstlinekey:
1229 if k == self.firstlinekey:
1224 e = "key name '%s' is reserved" % self.firstlinekey
1230 e = "key name '%s' is reserved" % self.firstlinekey
1225 raise error.ProgrammingError(e)
1231 raise error.ProgrammingError(e)
1226 if not k[0:1].isalpha():
1232 if not k[0:1].isalpha():
1227 e = "keys must start with a letter in a key-value file"
1233 e = "keys must start with a letter in a key-value file"
1228 raise error.ProgrammingError(e)
1234 raise error.ProgrammingError(e)
1229 if not k.isalnum():
1235 if not k.isalnum():
1230 e = "invalid key name in a simple key-value file"
1236 e = "invalid key name in a simple key-value file"
1231 raise error.ProgrammingError(e)
1237 raise error.ProgrammingError(e)
1232 if '\n' in v:
1238 if '\n' in v:
1233 e = "invalid value in a simple key-value file"
1239 e = "invalid value in a simple key-value file"
1234 raise error.ProgrammingError(e)
1240 raise error.ProgrammingError(e)
1235 lines.append("%s=%s\n" % (k, v))
1241 lines.append("%s=%s\n" % (k, v))
1236 with self.vfs(self.path, mode='wb', atomictemp=True) as fp:
1242 with self.vfs(self.path, mode='wb', atomictemp=True) as fp:
1237 fp.write(''.join(lines))
1243 fp.write(''.join(lines))
1238
1244
1239 _reportobsoletedsource = [
1245 _reportobsoletedsource = [
1240 'debugobsolete',
1246 'debugobsolete',
1241 'pull',
1247 'pull',
1242 'push',
1248 'push',
1243 'serve',
1249 'serve',
1244 'unbundle',
1250 'unbundle',
1245 ]
1251 ]
1246
1252
1247 _reportnewcssource = [
1253 _reportnewcssource = [
1248 'pull',
1254 'pull',
1249 'unbundle',
1255 'unbundle',
1250 ]
1256 ]
1251
1257
1252 # a list of (repo, ctx, files) functions called by various commands to allow
1258 # a list of (repo, ctx, files) functions called by various commands to allow
1253 # extensions to ensure the corresponding files are available locally, before the
1259 # extensions to ensure the corresponding files are available locally, before the
1254 # command uses them.
1260 # command uses them.
1255 fileprefetchhooks = util.hooks()
1261 fileprefetchhooks = util.hooks()
1256
1262
1257 # A marker that tells the evolve extension to suppress its own reporting
1263 # A marker that tells the evolve extension to suppress its own reporting
1258 _reportstroubledchangesets = True
1264 _reportstroubledchangesets = True
1259
1265
1260 def registersummarycallback(repo, otr, txnname=''):
1266 def registersummarycallback(repo, otr, txnname=''):
1261 """register a callback to issue a summary after the transaction is closed
1267 """register a callback to issue a summary after the transaction is closed
1262 """
1268 """
1263 def txmatch(sources):
1269 def txmatch(sources):
1264 return any(txnname.startswith(source) for source in sources)
1270 return any(txnname.startswith(source) for source in sources)
1265
1271
1266 categories = []
1272 categories = []
1267
1273
1268 def reportsummary(func):
1274 def reportsummary(func):
1269 """decorator for report callbacks."""
1275 """decorator for report callbacks."""
1270 # The repoview life cycle is shorter than the one of the actual
1276 # The repoview life cycle is shorter than the one of the actual
1271 # underlying repository. So the filtered object can die before the
1277 # underlying repository. So the filtered object can die before the
1272 # weakref is used leading to troubles. We keep a reference to the
1278 # weakref is used leading to troubles. We keep a reference to the
1273 # unfiltered object and restore the filtering when retrieving the
1279 # unfiltered object and restore the filtering when retrieving the
1274 # repository through the weakref.
1280 # repository through the weakref.
1275 filtername = repo.filtername
1281 filtername = repo.filtername
1276 reporef = weakref.ref(repo.unfiltered())
1282 reporef = weakref.ref(repo.unfiltered())
1277 def wrapped(tr):
1283 def wrapped(tr):
1278 repo = reporef()
1284 repo = reporef()
1279 if filtername:
1285 if filtername:
1280 repo = repo.filtered(filtername)
1286 repo = repo.filtered(filtername)
1281 func(repo, tr)
1287 func(repo, tr)
1282 newcat = '%02i-txnreport' % len(categories)
1288 newcat = '%02i-txnreport' % len(categories)
1283 otr.addpostclose(newcat, wrapped)
1289 otr.addpostclose(newcat, wrapped)
1284 categories.append(newcat)
1290 categories.append(newcat)
1285 return wrapped
1291 return wrapped
1286
1292
1287 if txmatch(_reportobsoletedsource):
1293 if txmatch(_reportobsoletedsource):
1288 @reportsummary
1294 @reportsummary
1289 def reportobsoleted(repo, tr):
1295 def reportobsoleted(repo, tr):
1290 obsoleted = obsutil.getobsoleted(repo, tr)
1296 obsoleted = obsutil.getobsoleted(repo, tr)
1291 if obsoleted:
1297 if obsoleted:
1292 repo.ui.status(_('obsoleted %i changesets\n')
1298 repo.ui.status(_('obsoleted %i changesets\n')
1293 % len(obsoleted))
1299 % len(obsoleted))
1294
1300
1295 if (obsolete.isenabled(repo, obsolete.createmarkersopt) and
1301 if (obsolete.isenabled(repo, obsolete.createmarkersopt) and
1296 repo.ui.configbool('experimental', 'evolution.report-instabilities')):
1302 repo.ui.configbool('experimental', 'evolution.report-instabilities')):
1297 instabilitytypes = [
1303 instabilitytypes = [
1298 ('orphan', 'orphan'),
1304 ('orphan', 'orphan'),
1299 ('phase-divergent', 'phasedivergent'),
1305 ('phase-divergent', 'phasedivergent'),
1300 ('content-divergent', 'contentdivergent'),
1306 ('content-divergent', 'contentdivergent'),
1301 ]
1307 ]
1302
1308
1303 def getinstabilitycounts(repo):
1309 def getinstabilitycounts(repo):
1304 filtered = repo.changelog.filteredrevs
1310 filtered = repo.changelog.filteredrevs
1305 counts = {}
1311 counts = {}
1306 for instability, revset in instabilitytypes:
1312 for instability, revset in instabilitytypes:
1307 counts[instability] = len(set(obsolete.getrevs(repo, revset)) -
1313 counts[instability] = len(set(obsolete.getrevs(repo, revset)) -
1308 filtered)
1314 filtered)
1309 return counts
1315 return counts
1310
1316
1311 oldinstabilitycounts = getinstabilitycounts(repo)
1317 oldinstabilitycounts = getinstabilitycounts(repo)
1312 @reportsummary
1318 @reportsummary
1313 def reportnewinstabilities(repo, tr):
1319 def reportnewinstabilities(repo, tr):
1314 newinstabilitycounts = getinstabilitycounts(repo)
1320 newinstabilitycounts = getinstabilitycounts(repo)
1315 for instability, revset in instabilitytypes:
1321 for instability, revset in instabilitytypes:
1316 delta = (newinstabilitycounts[instability] -
1322 delta = (newinstabilitycounts[instability] -
1317 oldinstabilitycounts[instability])
1323 oldinstabilitycounts[instability])
1318 if delta > 0:
1324 if delta > 0:
1319 repo.ui.warn(_('%i new %s changesets\n') %
1325 repo.ui.warn(_('%i new %s changesets\n') %
1320 (delta, instability))
1326 (delta, instability))
1321
1327
1322 if txmatch(_reportnewcssource):
1328 if txmatch(_reportnewcssource):
1323 @reportsummary
1329 @reportsummary
1324 def reportnewcs(repo, tr):
1330 def reportnewcs(repo, tr):
1325 """Report the range of new revisions pulled/unbundled."""
1331 """Report the range of new revisions pulled/unbundled."""
1326 newrevs = tr.changes.get('revs', xrange(0, 0))
1332 newrevs = tr.changes.get('revs', xrange(0, 0))
1327 if not newrevs:
1333 if not newrevs:
1328 return
1334 return
1329
1335
1330 # Compute the bounds of new revisions' range, excluding obsoletes.
1336 # Compute the bounds of new revisions' range, excluding obsoletes.
1331 unfi = repo.unfiltered()
1337 unfi = repo.unfiltered()
1332 revs = unfi.revs('%ld and not obsolete()', newrevs)
1338 revs = unfi.revs('%ld and not obsolete()', newrevs)
1333 if not revs:
1339 if not revs:
1334 # Got only obsoletes.
1340 # Got only obsoletes.
1335 return
1341 return
1336 minrev, maxrev = repo[revs.min()], repo[revs.max()]
1342 minrev, maxrev = repo[revs.min()], repo[revs.max()]
1337
1343
1338 if minrev == maxrev:
1344 if minrev == maxrev:
1339 revrange = minrev
1345 revrange = minrev
1340 else:
1346 else:
1341 revrange = '%s:%s' % (minrev, maxrev)
1347 revrange = '%s:%s' % (minrev, maxrev)
1342 repo.ui.status(_('new changesets %s\n') % revrange)
1348 repo.ui.status(_('new changesets %s\n') % revrange)
1343
1349
1344 def nodesummaries(repo, nodes, maxnumnodes=4):
1350 def nodesummaries(repo, nodes, maxnumnodes=4):
1345 if len(nodes) <= maxnumnodes or repo.ui.verbose:
1351 if len(nodes) <= maxnumnodes or repo.ui.verbose:
1346 return ' '.join(short(h) for h in nodes)
1352 return ' '.join(short(h) for h in nodes)
1347 first = ' '.join(short(h) for h in nodes[:maxnumnodes])
1353 first = ' '.join(short(h) for h in nodes[:maxnumnodes])
1348 return _("%s and %d others") % (first, len(nodes) - maxnumnodes)
1354 return _("%s and %d others") % (first, len(nodes) - maxnumnodes)
1349
1355
1350 def enforcesinglehead(repo, tr, desc):
1356 def enforcesinglehead(repo, tr, desc):
1351 """check that no named branch has multiple heads"""
1357 """check that no named branch has multiple heads"""
1352 if desc in ('strip', 'repair'):
1358 if desc in ('strip', 'repair'):
1353 # skip the logic during strip
1359 # skip the logic during strip
1354 return
1360 return
1355 visible = repo.filtered('visible')
1361 visible = repo.filtered('visible')
1356 # possible improvement: we could restrict the check to affected branch
1362 # possible improvement: we could restrict the check to affected branch
1357 for name, heads in visible.branchmap().iteritems():
1363 for name, heads in visible.branchmap().iteritems():
1358 if len(heads) > 1:
1364 if len(heads) > 1:
1359 msg = _('rejecting multiple heads on branch "%s"')
1365 msg = _('rejecting multiple heads on branch "%s"')
1360 msg %= name
1366 msg %= name
1361 hint = _('%d heads: %s')
1367 hint = _('%d heads: %s')
1362 hint %= (len(heads), nodesummaries(repo, heads))
1368 hint %= (len(heads), nodesummaries(repo, heads))
1363 raise error.Abort(msg, hint=hint)
1369 raise error.Abort(msg, hint=hint)
1364
1370
1365 def wrapconvertsink(sink):
1371 def wrapconvertsink(sink):
1366 """Allow extensions to wrap the sink returned by convcmd.convertsink()
1372 """Allow extensions to wrap the sink returned by convcmd.convertsink()
1367 before it is used, whether or not the convert extension was formally loaded.
1373 before it is used, whether or not the convert extension was formally loaded.
1368 """
1374 """
1369 return sink
1375 return sink
1370
1376
1371 def unhidehashlikerevs(repo, specs, hiddentype):
1377 def unhidehashlikerevs(repo, specs, hiddentype):
1372 """parse the user specs and unhide changesets whose hash or revision number
1378 """parse the user specs and unhide changesets whose hash or revision number
1373 is passed.
1379 is passed.
1374
1380
1375 hiddentype can be: 1) 'warn': warn while unhiding changesets
1381 hiddentype can be: 1) 'warn': warn while unhiding changesets
1376 2) 'nowarn': don't warn while unhiding changesets
1382 2) 'nowarn': don't warn while unhiding changesets
1377
1383
1378 returns a repo object with the required changesets unhidden
1384 returns a repo object with the required changesets unhidden
1379 """
1385 """
1380 if not repo.filtername or not repo.ui.configbool('experimental',
1386 if not repo.filtername or not repo.ui.configbool('experimental',
1381 'directaccess'):
1387 'directaccess'):
1382 return repo
1388 return repo
1383
1389
1384 if repo.filtername not in ('visible', 'visible-hidden'):
1390 if repo.filtername not in ('visible', 'visible-hidden'):
1385 return repo
1391 return repo
1386
1392
1387 symbols = set()
1393 symbols = set()
1388 for spec in specs:
1394 for spec in specs:
1389 try:
1395 try:
1390 tree = revsetlang.parse(spec)
1396 tree = revsetlang.parse(spec)
1391 except error.ParseError: # will be reported by scmutil.revrange()
1397 except error.ParseError: # will be reported by scmutil.revrange()
1392 continue
1398 continue
1393
1399
1394 symbols.update(revsetlang.gethashlikesymbols(tree))
1400 symbols.update(revsetlang.gethashlikesymbols(tree))
1395
1401
1396 if not symbols:
1402 if not symbols:
1397 return repo
1403 return repo
1398
1404
1399 revs = _getrevsfromsymbols(repo, symbols)
1405 revs = _getrevsfromsymbols(repo, symbols)
1400
1406
1401 if not revs:
1407 if not revs:
1402 return repo
1408 return repo
1403
1409
1404 if hiddentype == 'warn':
1410 if hiddentype == 'warn':
1405 unfi = repo.unfiltered()
1411 unfi = repo.unfiltered()
1406 revstr = ", ".join([pycompat.bytestr(unfi[l]) for l in revs])
1412 revstr = ", ".join([pycompat.bytestr(unfi[l]) for l in revs])
1407 repo.ui.warn(_("warning: accessing hidden changesets for write "
1413 repo.ui.warn(_("warning: accessing hidden changesets for write "
1408 "operation: %s\n") % revstr)
1414 "operation: %s\n") % revstr)
1409
1415
1410 # we have to use new filtername to separate branch/tags cache until we can
1416 # we have to use new filtername to separate branch/tags cache until we can
1411 # disbale these cache when revisions are dynamically pinned.
1417 # disbale these cache when revisions are dynamically pinned.
1412 return repo.filtered('visible-hidden', revs)
1418 return repo.filtered('visible-hidden', revs)
1413
1419
1414 def _getrevsfromsymbols(repo, symbols):
1420 def _getrevsfromsymbols(repo, symbols):
1415 """parse the list of symbols and returns a set of revision numbers of hidden
1421 """parse the list of symbols and returns a set of revision numbers of hidden
1416 changesets present in symbols"""
1422 changesets present in symbols"""
1417 revs = set()
1423 revs = set()
1418 unfi = repo.unfiltered()
1424 unfi = repo.unfiltered()
1419 unficl = unfi.changelog
1425 unficl = unfi.changelog
1420 cl = repo.changelog
1426 cl = repo.changelog
1421 tiprev = len(unficl)
1427 tiprev = len(unficl)
1422 pmatch = unficl._partialmatch
1428 pmatch = unficl._partialmatch
1423 allowrevnums = repo.ui.configbool('experimental', 'directaccess.revnums')
1429 allowrevnums = repo.ui.configbool('experimental', 'directaccess.revnums')
1424 for s in symbols:
1430 for s in symbols:
1425 try:
1431 try:
1426 n = int(s)
1432 n = int(s)
1427 if n <= tiprev:
1433 if n <= tiprev:
1428 if not allowrevnums:
1434 if not allowrevnums:
1429 continue
1435 continue
1430 else:
1436 else:
1431 if n not in cl:
1437 if n not in cl:
1432 revs.add(n)
1438 revs.add(n)
1433 continue
1439 continue
1434 except ValueError:
1440 except ValueError:
1435 pass
1441 pass
1436
1442
1437 try:
1443 try:
1438 s = pmatch(s)
1444 s = pmatch(s)
1439 except (error.LookupError, error.WdirUnsupported):
1445 except (error.LookupError, error.WdirUnsupported):
1440 s = None
1446 s = None
1441
1447
1442 if s is not None:
1448 if s is not None:
1443 rev = unficl.rev(s)
1449 rev = unficl.rev(s)
1444 if rev not in cl:
1450 if rev not in cl:
1445 revs.add(rev)
1451 revs.add(rev)
1446
1452
1447 return revs
1453 return revs
General Comments 0
You need to be logged in to leave comments. Login now