##// END OF EJS Templates
pull: fix inconsistent view of bookmarks during pull (issue4700)...
Valentin Gatien-Baron -
r41081:bad05a6a default
parent child Browse files
Show More
@@ -1,6209 +1,6207 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import difflib
10 import difflib
11 import errno
11 import errno
12 import os
12 import os
13 import re
13 import re
14 import sys
14 import sys
15
15
16 from .i18n import _
16 from .i18n import _
17 from .node import (
17 from .node import (
18 hex,
18 hex,
19 nullid,
19 nullid,
20 nullrev,
20 nullrev,
21 short,
21 short,
22 wdirhex,
22 wdirhex,
23 wdirrev,
23 wdirrev,
24 )
24 )
25 from . import (
25 from . import (
26 archival,
26 archival,
27 bookmarks,
27 bookmarks,
28 bundle2,
28 bundle2,
29 changegroup,
29 changegroup,
30 cmdutil,
30 cmdutil,
31 copies,
31 copies,
32 debugcommands as debugcommandsmod,
32 debugcommands as debugcommandsmod,
33 destutil,
33 destutil,
34 dirstateguard,
34 dirstateguard,
35 discovery,
35 discovery,
36 encoding,
36 encoding,
37 error,
37 error,
38 exchange,
38 exchange,
39 extensions,
39 extensions,
40 filemerge,
40 filemerge,
41 formatter,
41 formatter,
42 graphmod,
42 graphmod,
43 hbisect,
43 hbisect,
44 help,
44 help,
45 hg,
45 hg,
46 logcmdutil,
46 logcmdutil,
47 merge as mergemod,
47 merge as mergemod,
48 narrowspec,
48 narrowspec,
49 obsolete,
49 obsolete,
50 obsutil,
50 obsutil,
51 patch,
51 patch,
52 phases,
52 phases,
53 pycompat,
53 pycompat,
54 rcutil,
54 rcutil,
55 registrar,
55 registrar,
56 repair,
56 repair,
57 revsetlang,
57 revsetlang,
58 rewriteutil,
58 rewriteutil,
59 scmutil,
59 scmutil,
60 server,
60 server,
61 state as statemod,
61 state as statemod,
62 streamclone,
62 streamclone,
63 tags as tagsmod,
63 tags as tagsmod,
64 templatekw,
64 templatekw,
65 ui as uimod,
65 ui as uimod,
66 util,
66 util,
67 wireprotoserver,
67 wireprotoserver,
68 )
68 )
69 from .utils import (
69 from .utils import (
70 dateutil,
70 dateutil,
71 stringutil,
71 stringutil,
72 )
72 )
73
73
74 table = {}
74 table = {}
75 table.update(debugcommandsmod.command._table)
75 table.update(debugcommandsmod.command._table)
76
76
77 command = registrar.command(table)
77 command = registrar.command(table)
78 INTENT_READONLY = registrar.INTENT_READONLY
78 INTENT_READONLY = registrar.INTENT_READONLY
79
79
80 # common command options
80 # common command options
81
81
82 globalopts = [
82 globalopts = [
83 ('R', 'repository', '',
83 ('R', 'repository', '',
84 _('repository root directory or name of overlay bundle file'),
84 _('repository root directory or name of overlay bundle file'),
85 _('REPO')),
85 _('REPO')),
86 ('', 'cwd', '',
86 ('', 'cwd', '',
87 _('change working directory'), _('DIR')),
87 _('change working directory'), _('DIR')),
88 ('y', 'noninteractive', None,
88 ('y', 'noninteractive', None,
89 _('do not prompt, automatically pick the first choice for all prompts')),
89 _('do not prompt, automatically pick the first choice for all prompts')),
90 ('q', 'quiet', None, _('suppress output')),
90 ('q', 'quiet', None, _('suppress output')),
91 ('v', 'verbose', None, _('enable additional output')),
91 ('v', 'verbose', None, _('enable additional output')),
92 ('', 'color', '',
92 ('', 'color', '',
93 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
93 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
94 # and should not be translated
94 # and should not be translated
95 _("when to colorize (boolean, always, auto, never, or debug)"),
95 _("when to colorize (boolean, always, auto, never, or debug)"),
96 _('TYPE')),
96 _('TYPE')),
97 ('', 'config', [],
97 ('', 'config', [],
98 _('set/override config option (use \'section.name=value\')'),
98 _('set/override config option (use \'section.name=value\')'),
99 _('CONFIG')),
99 _('CONFIG')),
100 ('', 'debug', None, _('enable debugging output')),
100 ('', 'debug', None, _('enable debugging output')),
101 ('', 'debugger', None, _('start debugger')),
101 ('', 'debugger', None, _('start debugger')),
102 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
102 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
103 _('ENCODE')),
103 _('ENCODE')),
104 ('', 'encodingmode', encoding.encodingmode,
104 ('', 'encodingmode', encoding.encodingmode,
105 _('set the charset encoding mode'), _('MODE')),
105 _('set the charset encoding mode'), _('MODE')),
106 ('', 'traceback', None, _('always print a traceback on exception')),
106 ('', 'traceback', None, _('always print a traceback on exception')),
107 ('', 'time', None, _('time how long the command takes')),
107 ('', 'time', None, _('time how long the command takes')),
108 ('', 'profile', None, _('print command execution profile')),
108 ('', 'profile', None, _('print command execution profile')),
109 ('', 'version', None, _('output version information and exit')),
109 ('', 'version', None, _('output version information and exit')),
110 ('h', 'help', None, _('display help and exit')),
110 ('h', 'help', None, _('display help and exit')),
111 ('', 'hidden', False, _('consider hidden changesets')),
111 ('', 'hidden', False, _('consider hidden changesets')),
112 ('', 'pager', 'auto',
112 ('', 'pager', 'auto',
113 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
113 _("when to paginate (boolean, always, auto, or never)"), _('TYPE')),
114 ]
114 ]
115
115
116 dryrunopts = cmdutil.dryrunopts
116 dryrunopts = cmdutil.dryrunopts
117 remoteopts = cmdutil.remoteopts
117 remoteopts = cmdutil.remoteopts
118 walkopts = cmdutil.walkopts
118 walkopts = cmdutil.walkopts
119 commitopts = cmdutil.commitopts
119 commitopts = cmdutil.commitopts
120 commitopts2 = cmdutil.commitopts2
120 commitopts2 = cmdutil.commitopts2
121 formatteropts = cmdutil.formatteropts
121 formatteropts = cmdutil.formatteropts
122 templateopts = cmdutil.templateopts
122 templateopts = cmdutil.templateopts
123 logopts = cmdutil.logopts
123 logopts = cmdutil.logopts
124 diffopts = cmdutil.diffopts
124 diffopts = cmdutil.diffopts
125 diffwsopts = cmdutil.diffwsopts
125 diffwsopts = cmdutil.diffwsopts
126 diffopts2 = cmdutil.diffopts2
126 diffopts2 = cmdutil.diffopts2
127 mergetoolopts = cmdutil.mergetoolopts
127 mergetoolopts = cmdutil.mergetoolopts
128 similarityopts = cmdutil.similarityopts
128 similarityopts = cmdutil.similarityopts
129 subrepoopts = cmdutil.subrepoopts
129 subrepoopts = cmdutil.subrepoopts
130 debugrevlogopts = cmdutil.debugrevlogopts
130 debugrevlogopts = cmdutil.debugrevlogopts
131
131
132 # Commands start here, listed alphabetically
132 # Commands start here, listed alphabetically
133
133
134 @command('add',
134 @command('add',
135 walkopts + subrepoopts + dryrunopts,
135 walkopts + subrepoopts + dryrunopts,
136 _('[OPTION]... [FILE]...'),
136 _('[OPTION]... [FILE]...'),
137 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
137 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
138 helpbasic=True, inferrepo=True)
138 helpbasic=True, inferrepo=True)
139 def add(ui, repo, *pats, **opts):
139 def add(ui, repo, *pats, **opts):
140 """add the specified files on the next commit
140 """add the specified files on the next commit
141
141
142 Schedule files to be version controlled and added to the
142 Schedule files to be version controlled and added to the
143 repository.
143 repository.
144
144
145 The files will be added to the repository at the next commit. To
145 The files will be added to the repository at the next commit. To
146 undo an add before that, see :hg:`forget`.
146 undo an add before that, see :hg:`forget`.
147
147
148 If no names are given, add all files to the repository (except
148 If no names are given, add all files to the repository (except
149 files matching ``.hgignore``).
149 files matching ``.hgignore``).
150
150
151 .. container:: verbose
151 .. container:: verbose
152
152
153 Examples:
153 Examples:
154
154
155 - New (unknown) files are added
155 - New (unknown) files are added
156 automatically by :hg:`add`::
156 automatically by :hg:`add`::
157
157
158 $ ls
158 $ ls
159 foo.c
159 foo.c
160 $ hg status
160 $ hg status
161 ? foo.c
161 ? foo.c
162 $ hg add
162 $ hg add
163 adding foo.c
163 adding foo.c
164 $ hg status
164 $ hg status
165 A foo.c
165 A foo.c
166
166
167 - Specific files to be added can be specified::
167 - Specific files to be added can be specified::
168
168
169 $ ls
169 $ ls
170 bar.c foo.c
170 bar.c foo.c
171 $ hg status
171 $ hg status
172 ? bar.c
172 ? bar.c
173 ? foo.c
173 ? foo.c
174 $ hg add bar.c
174 $ hg add bar.c
175 $ hg status
175 $ hg status
176 A bar.c
176 A bar.c
177 ? foo.c
177 ? foo.c
178
178
179 Returns 0 if all files are successfully added.
179 Returns 0 if all files are successfully added.
180 """
180 """
181
181
182 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
182 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
183 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
183 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
184 return rejected and 1 or 0
184 return rejected and 1 or 0
185
185
186 @command('addremove',
186 @command('addremove',
187 similarityopts + subrepoopts + walkopts + dryrunopts,
187 similarityopts + subrepoopts + walkopts + dryrunopts,
188 _('[OPTION]... [FILE]...'),
188 _('[OPTION]... [FILE]...'),
189 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
189 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
190 inferrepo=True)
190 inferrepo=True)
191 def addremove(ui, repo, *pats, **opts):
191 def addremove(ui, repo, *pats, **opts):
192 """add all new files, delete all missing files
192 """add all new files, delete all missing files
193
193
194 Add all new files and remove all missing files from the
194 Add all new files and remove all missing files from the
195 repository.
195 repository.
196
196
197 Unless names are given, new files are ignored if they match any of
197 Unless names are given, new files are ignored if they match any of
198 the patterns in ``.hgignore``. As with add, these changes take
198 the patterns in ``.hgignore``. As with add, these changes take
199 effect at the next commit.
199 effect at the next commit.
200
200
201 Use the -s/--similarity option to detect renamed files. This
201 Use the -s/--similarity option to detect renamed files. This
202 option takes a percentage between 0 (disabled) and 100 (files must
202 option takes a percentage between 0 (disabled) and 100 (files must
203 be identical) as its parameter. With a parameter greater than 0,
203 be identical) as its parameter. With a parameter greater than 0,
204 this compares every removed file with every added file and records
204 this compares every removed file with every added file and records
205 those similar enough as renames. Detecting renamed files this way
205 those similar enough as renames. Detecting renamed files this way
206 can be expensive. After using this option, :hg:`status -C` can be
206 can be expensive. After using this option, :hg:`status -C` can be
207 used to check which files were identified as moved or renamed. If
207 used to check which files were identified as moved or renamed. If
208 not specified, -s/--similarity defaults to 100 and only renames of
208 not specified, -s/--similarity defaults to 100 and only renames of
209 identical files are detected.
209 identical files are detected.
210
210
211 .. container:: verbose
211 .. container:: verbose
212
212
213 Examples:
213 Examples:
214
214
215 - A number of files (bar.c and foo.c) are new,
215 - A number of files (bar.c and foo.c) are new,
216 while foobar.c has been removed (without using :hg:`remove`)
216 while foobar.c has been removed (without using :hg:`remove`)
217 from the repository::
217 from the repository::
218
218
219 $ ls
219 $ ls
220 bar.c foo.c
220 bar.c foo.c
221 $ hg status
221 $ hg status
222 ! foobar.c
222 ! foobar.c
223 ? bar.c
223 ? bar.c
224 ? foo.c
224 ? foo.c
225 $ hg addremove
225 $ hg addremove
226 adding bar.c
226 adding bar.c
227 adding foo.c
227 adding foo.c
228 removing foobar.c
228 removing foobar.c
229 $ hg status
229 $ hg status
230 A bar.c
230 A bar.c
231 A foo.c
231 A foo.c
232 R foobar.c
232 R foobar.c
233
233
234 - A file foobar.c was moved to foo.c without using :hg:`rename`.
234 - A file foobar.c was moved to foo.c without using :hg:`rename`.
235 Afterwards, it was edited slightly::
235 Afterwards, it was edited slightly::
236
236
237 $ ls
237 $ ls
238 foo.c
238 foo.c
239 $ hg status
239 $ hg status
240 ! foobar.c
240 ! foobar.c
241 ? foo.c
241 ? foo.c
242 $ hg addremove --similarity 90
242 $ hg addremove --similarity 90
243 removing foobar.c
243 removing foobar.c
244 adding foo.c
244 adding foo.c
245 recording removal of foobar.c as rename to foo.c (94% similar)
245 recording removal of foobar.c as rename to foo.c (94% similar)
246 $ hg status -C
246 $ hg status -C
247 A foo.c
247 A foo.c
248 foobar.c
248 foobar.c
249 R foobar.c
249 R foobar.c
250
250
251 Returns 0 if all files are successfully added.
251 Returns 0 if all files are successfully added.
252 """
252 """
253 opts = pycompat.byteskwargs(opts)
253 opts = pycompat.byteskwargs(opts)
254 if not opts.get('similarity'):
254 if not opts.get('similarity'):
255 opts['similarity'] = '100'
255 opts['similarity'] = '100'
256 matcher = scmutil.match(repo[None], pats, opts)
256 matcher = scmutil.match(repo[None], pats, opts)
257 return scmutil.addremove(repo, matcher, "", opts)
257 return scmutil.addremove(repo, matcher, "", opts)
258
258
259 @command('annotate|blame',
259 @command('annotate|blame',
260 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
260 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
261 ('', 'follow', None,
261 ('', 'follow', None,
262 _('follow copies/renames and list the filename (DEPRECATED)')),
262 _('follow copies/renames and list the filename (DEPRECATED)')),
263 ('', 'no-follow', None, _("don't follow copies and renames")),
263 ('', 'no-follow', None, _("don't follow copies and renames")),
264 ('a', 'text', None, _('treat all files as text')),
264 ('a', 'text', None, _('treat all files as text')),
265 ('u', 'user', None, _('list the author (long with -v)')),
265 ('u', 'user', None, _('list the author (long with -v)')),
266 ('f', 'file', None, _('list the filename')),
266 ('f', 'file', None, _('list the filename')),
267 ('d', 'date', None, _('list the date (short with -q)')),
267 ('d', 'date', None, _('list the date (short with -q)')),
268 ('n', 'number', None, _('list the revision number (default)')),
268 ('n', 'number', None, _('list the revision number (default)')),
269 ('c', 'changeset', None, _('list the changeset')),
269 ('c', 'changeset', None, _('list the changeset')),
270 ('l', 'line-number', None, _('show line number at the first appearance')),
270 ('l', 'line-number', None, _('show line number at the first appearance')),
271 ('', 'skip', [], _('revision to not display (EXPERIMENTAL)'), _('REV')),
271 ('', 'skip', [], _('revision to not display (EXPERIMENTAL)'), _('REV')),
272 ] + diffwsopts + walkopts + formatteropts,
272 ] + diffwsopts + walkopts + formatteropts,
273 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
273 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
274 helpcategory=command.CATEGORY_FILE_CONTENTS,
274 helpcategory=command.CATEGORY_FILE_CONTENTS,
275 helpbasic=True, inferrepo=True)
275 helpbasic=True, inferrepo=True)
276 def annotate(ui, repo, *pats, **opts):
276 def annotate(ui, repo, *pats, **opts):
277 """show changeset information by line for each file
277 """show changeset information by line for each file
278
278
279 List changes in files, showing the revision id responsible for
279 List changes in files, showing the revision id responsible for
280 each line.
280 each line.
281
281
282 This command is useful for discovering when a change was made and
282 This command is useful for discovering when a change was made and
283 by whom.
283 by whom.
284
284
285 If you include --file, --user, or --date, the revision number is
285 If you include --file, --user, or --date, the revision number is
286 suppressed unless you also include --number.
286 suppressed unless you also include --number.
287
287
288 Without the -a/--text option, annotate will avoid processing files
288 Without the -a/--text option, annotate will avoid processing files
289 it detects as binary. With -a, annotate will annotate the file
289 it detects as binary. With -a, annotate will annotate the file
290 anyway, although the results will probably be neither useful
290 anyway, although the results will probably be neither useful
291 nor desirable.
291 nor desirable.
292
292
293 .. container:: verbose
293 .. container:: verbose
294
294
295 Template:
295 Template:
296
296
297 The following keywords are supported in addition to the common template
297 The following keywords are supported in addition to the common template
298 keywords and functions. See also :hg:`help templates`.
298 keywords and functions. See also :hg:`help templates`.
299
299
300 :lines: List of lines with annotation data.
300 :lines: List of lines with annotation data.
301 :path: String. Repository-absolute path of the specified file.
301 :path: String. Repository-absolute path of the specified file.
302
302
303 And each entry of ``{lines}`` provides the following sub-keywords in
303 And each entry of ``{lines}`` provides the following sub-keywords in
304 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
304 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
305
305
306 :line: String. Line content.
306 :line: String. Line content.
307 :lineno: Integer. Line number at that revision.
307 :lineno: Integer. Line number at that revision.
308 :path: String. Repository-absolute path of the file at that revision.
308 :path: String. Repository-absolute path of the file at that revision.
309
309
310 See :hg:`help templates.operators` for the list expansion syntax.
310 See :hg:`help templates.operators` for the list expansion syntax.
311
311
312 Returns 0 on success.
312 Returns 0 on success.
313 """
313 """
314 opts = pycompat.byteskwargs(opts)
314 opts = pycompat.byteskwargs(opts)
315 if not pats:
315 if not pats:
316 raise error.Abort(_('at least one filename or pattern is required'))
316 raise error.Abort(_('at least one filename or pattern is required'))
317
317
318 if opts.get('follow'):
318 if opts.get('follow'):
319 # --follow is deprecated and now just an alias for -f/--file
319 # --follow is deprecated and now just an alias for -f/--file
320 # to mimic the behavior of Mercurial before version 1.5
320 # to mimic the behavior of Mercurial before version 1.5
321 opts['file'] = True
321 opts['file'] = True
322
322
323 if (not opts.get('user') and not opts.get('changeset')
323 if (not opts.get('user') and not opts.get('changeset')
324 and not opts.get('date') and not opts.get('file')):
324 and not opts.get('date') and not opts.get('file')):
325 opts['number'] = True
325 opts['number'] = True
326
326
327 linenumber = opts.get('line_number') is not None
327 linenumber = opts.get('line_number') is not None
328 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
328 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
329 raise error.Abort(_('at least one of -n/-c is required for -l'))
329 raise error.Abort(_('at least one of -n/-c is required for -l'))
330
330
331 rev = opts.get('rev')
331 rev = opts.get('rev')
332 if rev:
332 if rev:
333 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
333 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
334 ctx = scmutil.revsingle(repo, rev)
334 ctx = scmutil.revsingle(repo, rev)
335
335
336 ui.pager('annotate')
336 ui.pager('annotate')
337 rootfm = ui.formatter('annotate', opts)
337 rootfm = ui.formatter('annotate', opts)
338 if ui.debugflag:
338 if ui.debugflag:
339 shorthex = pycompat.identity
339 shorthex = pycompat.identity
340 else:
340 else:
341 def shorthex(h):
341 def shorthex(h):
342 return h[:12]
342 return h[:12]
343 if ui.quiet:
343 if ui.quiet:
344 datefunc = dateutil.shortdate
344 datefunc = dateutil.shortdate
345 else:
345 else:
346 datefunc = dateutil.datestr
346 datefunc = dateutil.datestr
347 if ctx.rev() is None:
347 if ctx.rev() is None:
348 if opts.get('changeset'):
348 if opts.get('changeset'):
349 # omit "+" suffix which is appended to node hex
349 # omit "+" suffix which is appended to node hex
350 def formatrev(rev):
350 def formatrev(rev):
351 if rev == wdirrev:
351 if rev == wdirrev:
352 return '%d' % ctx.p1().rev()
352 return '%d' % ctx.p1().rev()
353 else:
353 else:
354 return '%d' % rev
354 return '%d' % rev
355 else:
355 else:
356 def formatrev(rev):
356 def formatrev(rev):
357 if rev == wdirrev:
357 if rev == wdirrev:
358 return '%d+' % ctx.p1().rev()
358 return '%d+' % ctx.p1().rev()
359 else:
359 else:
360 return '%d ' % rev
360 return '%d ' % rev
361 def formathex(h):
361 def formathex(h):
362 if h == wdirhex:
362 if h == wdirhex:
363 return '%s+' % shorthex(hex(ctx.p1().node()))
363 return '%s+' % shorthex(hex(ctx.p1().node()))
364 else:
364 else:
365 return '%s ' % shorthex(h)
365 return '%s ' % shorthex(h)
366 else:
366 else:
367 formatrev = b'%d'.__mod__
367 formatrev = b'%d'.__mod__
368 formathex = shorthex
368 formathex = shorthex
369
369
370 opmap = [
370 opmap = [
371 ('user', ' ', lambda x: x.fctx.user(), ui.shortuser),
371 ('user', ' ', lambda x: x.fctx.user(), ui.shortuser),
372 ('rev', ' ', lambda x: scmutil.intrev(x.fctx), formatrev),
372 ('rev', ' ', lambda x: scmutil.intrev(x.fctx), formatrev),
373 ('node', ' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
373 ('node', ' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
374 ('date', ' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
374 ('date', ' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
375 ('path', ' ', lambda x: x.fctx.path(), pycompat.bytestr),
375 ('path', ' ', lambda x: x.fctx.path(), pycompat.bytestr),
376 ('lineno', ':', lambda x: x.lineno, pycompat.bytestr),
376 ('lineno', ':', lambda x: x.lineno, pycompat.bytestr),
377 ]
377 ]
378 opnamemap = {
378 opnamemap = {
379 'rev': 'number',
379 'rev': 'number',
380 'node': 'changeset',
380 'node': 'changeset',
381 'path': 'file',
381 'path': 'file',
382 'lineno': 'line_number',
382 'lineno': 'line_number',
383 }
383 }
384
384
385 if rootfm.isplain():
385 if rootfm.isplain():
386 def makefunc(get, fmt):
386 def makefunc(get, fmt):
387 return lambda x: fmt(get(x))
387 return lambda x: fmt(get(x))
388 else:
388 else:
389 def makefunc(get, fmt):
389 def makefunc(get, fmt):
390 return get
390 return get
391 datahint = rootfm.datahint()
391 datahint = rootfm.datahint()
392 funcmap = [(makefunc(get, fmt), sep) for fn, sep, get, fmt in opmap
392 funcmap = [(makefunc(get, fmt), sep) for fn, sep, get, fmt in opmap
393 if opts.get(opnamemap.get(fn, fn)) or fn in datahint]
393 if opts.get(opnamemap.get(fn, fn)) or fn in datahint]
394 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
394 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
395 fields = ' '.join(fn for fn, sep, get, fmt in opmap
395 fields = ' '.join(fn for fn, sep, get, fmt in opmap
396 if opts.get(opnamemap.get(fn, fn)) or fn in datahint)
396 if opts.get(opnamemap.get(fn, fn)) or fn in datahint)
397
397
398 def bad(x, y):
398 def bad(x, y):
399 raise error.Abort("%s: %s" % (x, y))
399 raise error.Abort("%s: %s" % (x, y))
400
400
401 m = scmutil.match(ctx, pats, opts, badfn=bad)
401 m = scmutil.match(ctx, pats, opts, badfn=bad)
402
402
403 follow = not opts.get('no_follow')
403 follow = not opts.get('no_follow')
404 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
404 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
405 whitespace=True)
405 whitespace=True)
406 skiprevs = opts.get('skip')
406 skiprevs = opts.get('skip')
407 if skiprevs:
407 if skiprevs:
408 skiprevs = scmutil.revrange(repo, skiprevs)
408 skiprevs = scmutil.revrange(repo, skiprevs)
409
409
410 for abs in ctx.walk(m):
410 for abs in ctx.walk(m):
411 fctx = ctx[abs]
411 fctx = ctx[abs]
412 rootfm.startitem()
412 rootfm.startitem()
413 rootfm.data(path=abs)
413 rootfm.data(path=abs)
414 if not opts.get('text') and fctx.isbinary():
414 if not opts.get('text') and fctx.isbinary():
415 rootfm.plain(_("%s: binary file\n") % m.rel(abs))
415 rootfm.plain(_("%s: binary file\n") % m.rel(abs))
416 continue
416 continue
417
417
418 fm = rootfm.nested('lines', tmpl='{rev}: {line}')
418 fm = rootfm.nested('lines', tmpl='{rev}: {line}')
419 lines = fctx.annotate(follow=follow, skiprevs=skiprevs,
419 lines = fctx.annotate(follow=follow, skiprevs=skiprevs,
420 diffopts=diffopts)
420 diffopts=diffopts)
421 if not lines:
421 if not lines:
422 fm.end()
422 fm.end()
423 continue
423 continue
424 formats = []
424 formats = []
425 pieces = []
425 pieces = []
426
426
427 for f, sep in funcmap:
427 for f, sep in funcmap:
428 l = [f(n) for n in lines]
428 l = [f(n) for n in lines]
429 if fm.isplain():
429 if fm.isplain():
430 sizes = [encoding.colwidth(x) for x in l]
430 sizes = [encoding.colwidth(x) for x in l]
431 ml = max(sizes)
431 ml = max(sizes)
432 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
432 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
433 else:
433 else:
434 formats.append(['%s' for x in l])
434 formats.append(['%s' for x in l])
435 pieces.append(l)
435 pieces.append(l)
436
436
437 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
437 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
438 fm.startitem()
438 fm.startitem()
439 fm.context(fctx=n.fctx)
439 fm.context(fctx=n.fctx)
440 fm.write(fields, "".join(f), *p)
440 fm.write(fields, "".join(f), *p)
441 if n.skip:
441 if n.skip:
442 fmt = "* %s"
442 fmt = "* %s"
443 else:
443 else:
444 fmt = ": %s"
444 fmt = ": %s"
445 fm.write('line', fmt, n.text)
445 fm.write('line', fmt, n.text)
446
446
447 if not lines[-1].text.endswith('\n'):
447 if not lines[-1].text.endswith('\n'):
448 fm.plain('\n')
448 fm.plain('\n')
449 fm.end()
449 fm.end()
450
450
451 rootfm.end()
451 rootfm.end()
452
452
453 @command('archive',
453 @command('archive',
454 [('', 'no-decode', None, _('do not pass files through decoders')),
454 [('', 'no-decode', None, _('do not pass files through decoders')),
455 ('p', 'prefix', '', _('directory prefix for files in archive'),
455 ('p', 'prefix', '', _('directory prefix for files in archive'),
456 _('PREFIX')),
456 _('PREFIX')),
457 ('r', 'rev', '', _('revision to distribute'), _('REV')),
457 ('r', 'rev', '', _('revision to distribute'), _('REV')),
458 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
458 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
459 ] + subrepoopts + walkopts,
459 ] + subrepoopts + walkopts,
460 _('[OPTION]... DEST'),
460 _('[OPTION]... DEST'),
461 helpcategory=command.CATEGORY_IMPORT_EXPORT)
461 helpcategory=command.CATEGORY_IMPORT_EXPORT)
462 def archive(ui, repo, dest, **opts):
462 def archive(ui, repo, dest, **opts):
463 '''create an unversioned archive of a repository revision
463 '''create an unversioned archive of a repository revision
464
464
465 By default, the revision used is the parent of the working
465 By default, the revision used is the parent of the working
466 directory; use -r/--rev to specify a different revision.
466 directory; use -r/--rev to specify a different revision.
467
467
468 The archive type is automatically detected based on file
468 The archive type is automatically detected based on file
469 extension (to override, use -t/--type).
469 extension (to override, use -t/--type).
470
470
471 .. container:: verbose
471 .. container:: verbose
472
472
473 Examples:
473 Examples:
474
474
475 - create a zip file containing the 1.0 release::
475 - create a zip file containing the 1.0 release::
476
476
477 hg archive -r 1.0 project-1.0.zip
477 hg archive -r 1.0 project-1.0.zip
478
478
479 - create a tarball excluding .hg files::
479 - create a tarball excluding .hg files::
480
480
481 hg archive project.tar.gz -X ".hg*"
481 hg archive project.tar.gz -X ".hg*"
482
482
483 Valid types are:
483 Valid types are:
484
484
485 :``files``: a directory full of files (default)
485 :``files``: a directory full of files (default)
486 :``tar``: tar archive, uncompressed
486 :``tar``: tar archive, uncompressed
487 :``tbz2``: tar archive, compressed using bzip2
487 :``tbz2``: tar archive, compressed using bzip2
488 :``tgz``: tar archive, compressed using gzip
488 :``tgz``: tar archive, compressed using gzip
489 :``uzip``: zip archive, uncompressed
489 :``uzip``: zip archive, uncompressed
490 :``zip``: zip archive, compressed using deflate
490 :``zip``: zip archive, compressed using deflate
491
491
492 The exact name of the destination archive or directory is given
492 The exact name of the destination archive or directory is given
493 using a format string; see :hg:`help export` for details.
493 using a format string; see :hg:`help export` for details.
494
494
495 Each member added to an archive file has a directory prefix
495 Each member added to an archive file has a directory prefix
496 prepended. Use -p/--prefix to specify a format string for the
496 prepended. Use -p/--prefix to specify a format string for the
497 prefix. The default is the basename of the archive, with suffixes
497 prefix. The default is the basename of the archive, with suffixes
498 removed.
498 removed.
499
499
500 Returns 0 on success.
500 Returns 0 on success.
501 '''
501 '''
502
502
503 opts = pycompat.byteskwargs(opts)
503 opts = pycompat.byteskwargs(opts)
504 rev = opts.get('rev')
504 rev = opts.get('rev')
505 if rev:
505 if rev:
506 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
506 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
507 ctx = scmutil.revsingle(repo, rev)
507 ctx = scmutil.revsingle(repo, rev)
508 if not ctx:
508 if not ctx:
509 raise error.Abort(_('no working directory: please specify a revision'))
509 raise error.Abort(_('no working directory: please specify a revision'))
510 node = ctx.node()
510 node = ctx.node()
511 dest = cmdutil.makefilename(ctx, dest)
511 dest = cmdutil.makefilename(ctx, dest)
512 if os.path.realpath(dest) == repo.root:
512 if os.path.realpath(dest) == repo.root:
513 raise error.Abort(_('repository root cannot be destination'))
513 raise error.Abort(_('repository root cannot be destination'))
514
514
515 kind = opts.get('type') or archival.guesskind(dest) or 'files'
515 kind = opts.get('type') or archival.guesskind(dest) or 'files'
516 prefix = opts.get('prefix')
516 prefix = opts.get('prefix')
517
517
518 if dest == '-':
518 if dest == '-':
519 if kind == 'files':
519 if kind == 'files':
520 raise error.Abort(_('cannot archive plain files to stdout'))
520 raise error.Abort(_('cannot archive plain files to stdout'))
521 dest = cmdutil.makefileobj(ctx, dest)
521 dest = cmdutil.makefileobj(ctx, dest)
522 if not prefix:
522 if not prefix:
523 prefix = os.path.basename(repo.root) + '-%h'
523 prefix = os.path.basename(repo.root) + '-%h'
524
524
525 prefix = cmdutil.makefilename(ctx, prefix)
525 prefix = cmdutil.makefilename(ctx, prefix)
526 match = scmutil.match(ctx, [], opts)
526 match = scmutil.match(ctx, [], opts)
527 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
527 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
528 match, prefix, subrepos=opts.get('subrepos'))
528 match, prefix, subrepos=opts.get('subrepos'))
529
529
530 @command('backout',
530 @command('backout',
531 [('', 'merge', None, _('merge with old dirstate parent after backout')),
531 [('', 'merge', None, _('merge with old dirstate parent after backout')),
532 ('', 'commit', None,
532 ('', 'commit', None,
533 _('commit if no conflicts were encountered (DEPRECATED)')),
533 _('commit if no conflicts were encountered (DEPRECATED)')),
534 ('', 'no-commit', None, _('do not commit')),
534 ('', 'no-commit', None, _('do not commit')),
535 ('', 'parent', '',
535 ('', 'parent', '',
536 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
536 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
537 ('r', 'rev', '', _('revision to backout'), _('REV')),
537 ('r', 'rev', '', _('revision to backout'), _('REV')),
538 ('e', 'edit', False, _('invoke editor on commit messages')),
538 ('e', 'edit', False, _('invoke editor on commit messages')),
539 ] + mergetoolopts + walkopts + commitopts + commitopts2,
539 ] + mergetoolopts + walkopts + commitopts + commitopts2,
540 _('[OPTION]... [-r] REV'),
540 _('[OPTION]... [-r] REV'),
541 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
541 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
542 def backout(ui, repo, node=None, rev=None, **opts):
542 def backout(ui, repo, node=None, rev=None, **opts):
543 '''reverse effect of earlier changeset
543 '''reverse effect of earlier changeset
544
544
545 Prepare a new changeset with the effect of REV undone in the
545 Prepare a new changeset with the effect of REV undone in the
546 current working directory. If no conflicts were encountered,
546 current working directory. If no conflicts were encountered,
547 it will be committed immediately.
547 it will be committed immediately.
548
548
549 If REV is the parent of the working directory, then this new changeset
549 If REV is the parent of the working directory, then this new changeset
550 is committed automatically (unless --no-commit is specified).
550 is committed automatically (unless --no-commit is specified).
551
551
552 .. note::
552 .. note::
553
553
554 :hg:`backout` cannot be used to fix either an unwanted or
554 :hg:`backout` cannot be used to fix either an unwanted or
555 incorrect merge.
555 incorrect merge.
556
556
557 .. container:: verbose
557 .. container:: verbose
558
558
559 Examples:
559 Examples:
560
560
561 - Reverse the effect of the parent of the working directory.
561 - Reverse the effect of the parent of the working directory.
562 This backout will be committed immediately::
562 This backout will be committed immediately::
563
563
564 hg backout -r .
564 hg backout -r .
565
565
566 - Reverse the effect of previous bad revision 23::
566 - Reverse the effect of previous bad revision 23::
567
567
568 hg backout -r 23
568 hg backout -r 23
569
569
570 - Reverse the effect of previous bad revision 23 and
570 - Reverse the effect of previous bad revision 23 and
571 leave changes uncommitted::
571 leave changes uncommitted::
572
572
573 hg backout -r 23 --no-commit
573 hg backout -r 23 --no-commit
574 hg commit -m "Backout revision 23"
574 hg commit -m "Backout revision 23"
575
575
576 By default, the pending changeset will have one parent,
576 By default, the pending changeset will have one parent,
577 maintaining a linear history. With --merge, the pending
577 maintaining a linear history. With --merge, the pending
578 changeset will instead have two parents: the old parent of the
578 changeset will instead have two parents: the old parent of the
579 working directory and a new child of REV that simply undoes REV.
579 working directory and a new child of REV that simply undoes REV.
580
580
581 Before version 1.7, the behavior without --merge was equivalent
581 Before version 1.7, the behavior without --merge was equivalent
582 to specifying --merge followed by :hg:`update --clean .` to
582 to specifying --merge followed by :hg:`update --clean .` to
583 cancel the merge and leave the child of REV as a head to be
583 cancel the merge and leave the child of REV as a head to be
584 merged separately.
584 merged separately.
585
585
586 See :hg:`help dates` for a list of formats valid for -d/--date.
586 See :hg:`help dates` for a list of formats valid for -d/--date.
587
587
588 See :hg:`help revert` for a way to restore files to the state
588 See :hg:`help revert` for a way to restore files to the state
589 of another revision.
589 of another revision.
590
590
591 Returns 0 on success, 1 if nothing to backout or there are unresolved
591 Returns 0 on success, 1 if nothing to backout or there are unresolved
592 files.
592 files.
593 '''
593 '''
594 with repo.wlock(), repo.lock():
594 with repo.wlock(), repo.lock():
595 return _dobackout(ui, repo, node, rev, **opts)
595 return _dobackout(ui, repo, node, rev, **opts)
596
596
597 def _dobackout(ui, repo, node=None, rev=None, **opts):
597 def _dobackout(ui, repo, node=None, rev=None, **opts):
598 opts = pycompat.byteskwargs(opts)
598 opts = pycompat.byteskwargs(opts)
599 if opts.get('commit') and opts.get('no_commit'):
599 if opts.get('commit') and opts.get('no_commit'):
600 raise error.Abort(_("cannot use --commit with --no-commit"))
600 raise error.Abort(_("cannot use --commit with --no-commit"))
601 if opts.get('merge') and opts.get('no_commit'):
601 if opts.get('merge') and opts.get('no_commit'):
602 raise error.Abort(_("cannot use --merge with --no-commit"))
602 raise error.Abort(_("cannot use --merge with --no-commit"))
603
603
604 if rev and node:
604 if rev and node:
605 raise error.Abort(_("please specify just one revision"))
605 raise error.Abort(_("please specify just one revision"))
606
606
607 if not rev:
607 if not rev:
608 rev = node
608 rev = node
609
609
610 if not rev:
610 if not rev:
611 raise error.Abort(_("please specify a revision to backout"))
611 raise error.Abort(_("please specify a revision to backout"))
612
612
613 date = opts.get('date')
613 date = opts.get('date')
614 if date:
614 if date:
615 opts['date'] = dateutil.parsedate(date)
615 opts['date'] = dateutil.parsedate(date)
616
616
617 cmdutil.checkunfinished(repo)
617 cmdutil.checkunfinished(repo)
618 cmdutil.bailifchanged(repo)
618 cmdutil.bailifchanged(repo)
619 node = scmutil.revsingle(repo, rev).node()
619 node = scmutil.revsingle(repo, rev).node()
620
620
621 op1, op2 = repo.dirstate.parents()
621 op1, op2 = repo.dirstate.parents()
622 if not repo.changelog.isancestor(node, op1):
622 if not repo.changelog.isancestor(node, op1):
623 raise error.Abort(_('cannot backout change that is not an ancestor'))
623 raise error.Abort(_('cannot backout change that is not an ancestor'))
624
624
625 p1, p2 = repo.changelog.parents(node)
625 p1, p2 = repo.changelog.parents(node)
626 if p1 == nullid:
626 if p1 == nullid:
627 raise error.Abort(_('cannot backout a change with no parents'))
627 raise error.Abort(_('cannot backout a change with no parents'))
628 if p2 != nullid:
628 if p2 != nullid:
629 if not opts.get('parent'):
629 if not opts.get('parent'):
630 raise error.Abort(_('cannot backout a merge changeset'))
630 raise error.Abort(_('cannot backout a merge changeset'))
631 p = repo.lookup(opts['parent'])
631 p = repo.lookup(opts['parent'])
632 if p not in (p1, p2):
632 if p not in (p1, p2):
633 raise error.Abort(_('%s is not a parent of %s') %
633 raise error.Abort(_('%s is not a parent of %s') %
634 (short(p), short(node)))
634 (short(p), short(node)))
635 parent = p
635 parent = p
636 else:
636 else:
637 if opts.get('parent'):
637 if opts.get('parent'):
638 raise error.Abort(_('cannot use --parent on non-merge changeset'))
638 raise error.Abort(_('cannot use --parent on non-merge changeset'))
639 parent = p1
639 parent = p1
640
640
641 # the backout should appear on the same branch
641 # the backout should appear on the same branch
642 branch = repo.dirstate.branch()
642 branch = repo.dirstate.branch()
643 bheads = repo.branchheads(branch)
643 bheads = repo.branchheads(branch)
644 rctx = scmutil.revsingle(repo, hex(parent))
644 rctx = scmutil.revsingle(repo, hex(parent))
645 if not opts.get('merge') and op1 != node:
645 if not opts.get('merge') and op1 != node:
646 with dirstateguard.dirstateguard(repo, 'backout'):
646 with dirstateguard.dirstateguard(repo, 'backout'):
647 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
647 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
648 with ui.configoverride(overrides, 'backout'):
648 with ui.configoverride(overrides, 'backout'):
649 stats = mergemod.update(repo, parent, branchmerge=True,
649 stats = mergemod.update(repo, parent, branchmerge=True,
650 force=True, ancestor=node,
650 force=True, ancestor=node,
651 mergeancestor=False)
651 mergeancestor=False)
652 repo.setparents(op1, op2)
652 repo.setparents(op1, op2)
653 hg._showstats(repo, stats)
653 hg._showstats(repo, stats)
654 if stats.unresolvedcount:
654 if stats.unresolvedcount:
655 repo.ui.status(_("use 'hg resolve' to retry unresolved "
655 repo.ui.status(_("use 'hg resolve' to retry unresolved "
656 "file merges\n"))
656 "file merges\n"))
657 return 1
657 return 1
658 else:
658 else:
659 hg.clean(repo, node, show_stats=False)
659 hg.clean(repo, node, show_stats=False)
660 repo.dirstate.setbranch(branch)
660 repo.dirstate.setbranch(branch)
661 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
661 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
662
662
663 if opts.get('no_commit'):
663 if opts.get('no_commit'):
664 msg = _("changeset %s backed out, "
664 msg = _("changeset %s backed out, "
665 "don't forget to commit.\n")
665 "don't forget to commit.\n")
666 ui.status(msg % short(node))
666 ui.status(msg % short(node))
667 return 0
667 return 0
668
668
669 def commitfunc(ui, repo, message, match, opts):
669 def commitfunc(ui, repo, message, match, opts):
670 editform = 'backout'
670 editform = 'backout'
671 e = cmdutil.getcommiteditor(editform=editform,
671 e = cmdutil.getcommiteditor(editform=editform,
672 **pycompat.strkwargs(opts))
672 **pycompat.strkwargs(opts))
673 if not message:
673 if not message:
674 # we don't translate commit messages
674 # we don't translate commit messages
675 message = "Backed out changeset %s" % short(node)
675 message = "Backed out changeset %s" % short(node)
676 e = cmdutil.getcommiteditor(edit=True, editform=editform)
676 e = cmdutil.getcommiteditor(edit=True, editform=editform)
677 return repo.commit(message, opts.get('user'), opts.get('date'),
677 return repo.commit(message, opts.get('user'), opts.get('date'),
678 match, editor=e)
678 match, editor=e)
679 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
679 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
680 if not newnode:
680 if not newnode:
681 ui.status(_("nothing changed\n"))
681 ui.status(_("nothing changed\n"))
682 return 1
682 return 1
683 cmdutil.commitstatus(repo, newnode, branch, bheads)
683 cmdutil.commitstatus(repo, newnode, branch, bheads)
684
684
685 def nice(node):
685 def nice(node):
686 return '%d:%s' % (repo.changelog.rev(node), short(node))
686 return '%d:%s' % (repo.changelog.rev(node), short(node))
687 ui.status(_('changeset %s backs out changeset %s\n') %
687 ui.status(_('changeset %s backs out changeset %s\n') %
688 (nice(repo.changelog.tip()), nice(node)))
688 (nice(repo.changelog.tip()), nice(node)))
689 if opts.get('merge') and op1 != node:
689 if opts.get('merge') and op1 != node:
690 hg.clean(repo, op1, show_stats=False)
690 hg.clean(repo, op1, show_stats=False)
691 ui.status(_('merging with changeset %s\n')
691 ui.status(_('merging with changeset %s\n')
692 % nice(repo.changelog.tip()))
692 % nice(repo.changelog.tip()))
693 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
693 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
694 with ui.configoverride(overrides, 'backout'):
694 with ui.configoverride(overrides, 'backout'):
695 return hg.merge(repo, hex(repo.changelog.tip()))
695 return hg.merge(repo, hex(repo.changelog.tip()))
696 return 0
696 return 0
697
697
698 @command('bisect',
698 @command('bisect',
699 [('r', 'reset', False, _('reset bisect state')),
699 [('r', 'reset', False, _('reset bisect state')),
700 ('g', 'good', False, _('mark changeset good')),
700 ('g', 'good', False, _('mark changeset good')),
701 ('b', 'bad', False, _('mark changeset bad')),
701 ('b', 'bad', False, _('mark changeset bad')),
702 ('s', 'skip', False, _('skip testing changeset')),
702 ('s', 'skip', False, _('skip testing changeset')),
703 ('e', 'extend', False, _('extend the bisect range')),
703 ('e', 'extend', False, _('extend the bisect range')),
704 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
704 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
705 ('U', 'noupdate', False, _('do not update to target'))],
705 ('U', 'noupdate', False, _('do not update to target'))],
706 _("[-gbsr] [-U] [-c CMD] [REV]"),
706 _("[-gbsr] [-U] [-c CMD] [REV]"),
707 helpcategory=command.CATEGORY_CHANGE_NAVIGATION)
707 helpcategory=command.CATEGORY_CHANGE_NAVIGATION)
708 def bisect(ui, repo, rev=None, extra=None, command=None,
708 def bisect(ui, repo, rev=None, extra=None, command=None,
709 reset=None, good=None, bad=None, skip=None, extend=None,
709 reset=None, good=None, bad=None, skip=None, extend=None,
710 noupdate=None):
710 noupdate=None):
711 """subdivision search of changesets
711 """subdivision search of changesets
712
712
713 This command helps to find changesets which introduce problems. To
713 This command helps to find changesets which introduce problems. To
714 use, mark the earliest changeset you know exhibits the problem as
714 use, mark the earliest changeset you know exhibits the problem as
715 bad, then mark the latest changeset which is free from the problem
715 bad, then mark the latest changeset which is free from the problem
716 as good. Bisect will update your working directory to a revision
716 as good. Bisect will update your working directory to a revision
717 for testing (unless the -U/--noupdate option is specified). Once
717 for testing (unless the -U/--noupdate option is specified). Once
718 you have performed tests, mark the working directory as good or
718 you have performed tests, mark the working directory as good or
719 bad, and bisect will either update to another candidate changeset
719 bad, and bisect will either update to another candidate changeset
720 or announce that it has found the bad revision.
720 or announce that it has found the bad revision.
721
721
722 As a shortcut, you can also use the revision argument to mark a
722 As a shortcut, you can also use the revision argument to mark a
723 revision as good or bad without checking it out first.
723 revision as good or bad without checking it out first.
724
724
725 If you supply a command, it will be used for automatic bisection.
725 If you supply a command, it will be used for automatic bisection.
726 The environment variable HG_NODE will contain the ID of the
726 The environment variable HG_NODE will contain the ID of the
727 changeset being tested. The exit status of the command will be
727 changeset being tested. The exit status of the command will be
728 used to mark revisions as good or bad: status 0 means good, 125
728 used to mark revisions as good or bad: status 0 means good, 125
729 means to skip the revision, 127 (command not found) will abort the
729 means to skip the revision, 127 (command not found) will abort the
730 bisection, and any other non-zero exit status means the revision
730 bisection, and any other non-zero exit status means the revision
731 is bad.
731 is bad.
732
732
733 .. container:: verbose
733 .. container:: verbose
734
734
735 Some examples:
735 Some examples:
736
736
737 - start a bisection with known bad revision 34, and good revision 12::
737 - start a bisection with known bad revision 34, and good revision 12::
738
738
739 hg bisect --bad 34
739 hg bisect --bad 34
740 hg bisect --good 12
740 hg bisect --good 12
741
741
742 - advance the current bisection by marking current revision as good or
742 - advance the current bisection by marking current revision as good or
743 bad::
743 bad::
744
744
745 hg bisect --good
745 hg bisect --good
746 hg bisect --bad
746 hg bisect --bad
747
747
748 - mark the current revision, or a known revision, to be skipped (e.g. if
748 - mark the current revision, or a known revision, to be skipped (e.g. if
749 that revision is not usable because of another issue)::
749 that revision is not usable because of another issue)::
750
750
751 hg bisect --skip
751 hg bisect --skip
752 hg bisect --skip 23
752 hg bisect --skip 23
753
753
754 - skip all revisions that do not touch directories ``foo`` or ``bar``::
754 - skip all revisions that do not touch directories ``foo`` or ``bar``::
755
755
756 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
756 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
757
757
758 - forget the current bisection::
758 - forget the current bisection::
759
759
760 hg bisect --reset
760 hg bisect --reset
761
761
762 - use 'make && make tests' to automatically find the first broken
762 - use 'make && make tests' to automatically find the first broken
763 revision::
763 revision::
764
764
765 hg bisect --reset
765 hg bisect --reset
766 hg bisect --bad 34
766 hg bisect --bad 34
767 hg bisect --good 12
767 hg bisect --good 12
768 hg bisect --command "make && make tests"
768 hg bisect --command "make && make tests"
769
769
770 - see all changesets whose states are already known in the current
770 - see all changesets whose states are already known in the current
771 bisection::
771 bisection::
772
772
773 hg log -r "bisect(pruned)"
773 hg log -r "bisect(pruned)"
774
774
775 - see the changeset currently being bisected (especially useful
775 - see the changeset currently being bisected (especially useful
776 if running with -U/--noupdate)::
776 if running with -U/--noupdate)::
777
777
778 hg log -r "bisect(current)"
778 hg log -r "bisect(current)"
779
779
780 - see all changesets that took part in the current bisection::
780 - see all changesets that took part in the current bisection::
781
781
782 hg log -r "bisect(range)"
782 hg log -r "bisect(range)"
783
783
784 - you can even get a nice graph::
784 - you can even get a nice graph::
785
785
786 hg log --graph -r "bisect(range)"
786 hg log --graph -r "bisect(range)"
787
787
788 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
788 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
789
789
790 Returns 0 on success.
790 Returns 0 on success.
791 """
791 """
792 # backward compatibility
792 # backward compatibility
793 if rev in "good bad reset init".split():
793 if rev in "good bad reset init".split():
794 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
794 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
795 cmd, rev, extra = rev, extra, None
795 cmd, rev, extra = rev, extra, None
796 if cmd == "good":
796 if cmd == "good":
797 good = True
797 good = True
798 elif cmd == "bad":
798 elif cmd == "bad":
799 bad = True
799 bad = True
800 else:
800 else:
801 reset = True
801 reset = True
802 elif extra:
802 elif extra:
803 raise error.Abort(_('incompatible arguments'))
803 raise error.Abort(_('incompatible arguments'))
804
804
805 incompatibles = {
805 incompatibles = {
806 '--bad': bad,
806 '--bad': bad,
807 '--command': bool(command),
807 '--command': bool(command),
808 '--extend': extend,
808 '--extend': extend,
809 '--good': good,
809 '--good': good,
810 '--reset': reset,
810 '--reset': reset,
811 '--skip': skip,
811 '--skip': skip,
812 }
812 }
813
813
814 enabled = [x for x in incompatibles if incompatibles[x]]
814 enabled = [x for x in incompatibles if incompatibles[x]]
815
815
816 if len(enabled) > 1:
816 if len(enabled) > 1:
817 raise error.Abort(_('%s and %s are incompatible') %
817 raise error.Abort(_('%s and %s are incompatible') %
818 tuple(sorted(enabled)[0:2]))
818 tuple(sorted(enabled)[0:2]))
819
819
820 if reset:
820 if reset:
821 hbisect.resetstate(repo)
821 hbisect.resetstate(repo)
822 return
822 return
823
823
824 state = hbisect.load_state(repo)
824 state = hbisect.load_state(repo)
825
825
826 # update state
826 # update state
827 if good or bad or skip:
827 if good or bad or skip:
828 if rev:
828 if rev:
829 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
829 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
830 else:
830 else:
831 nodes = [repo.lookup('.')]
831 nodes = [repo.lookup('.')]
832 if good:
832 if good:
833 state['good'] += nodes
833 state['good'] += nodes
834 elif bad:
834 elif bad:
835 state['bad'] += nodes
835 state['bad'] += nodes
836 elif skip:
836 elif skip:
837 state['skip'] += nodes
837 state['skip'] += nodes
838 hbisect.save_state(repo, state)
838 hbisect.save_state(repo, state)
839 if not (state['good'] and state['bad']):
839 if not (state['good'] and state['bad']):
840 return
840 return
841
841
842 def mayupdate(repo, node, show_stats=True):
842 def mayupdate(repo, node, show_stats=True):
843 """common used update sequence"""
843 """common used update sequence"""
844 if noupdate:
844 if noupdate:
845 return
845 return
846 cmdutil.checkunfinished(repo)
846 cmdutil.checkunfinished(repo)
847 cmdutil.bailifchanged(repo)
847 cmdutil.bailifchanged(repo)
848 return hg.clean(repo, node, show_stats=show_stats)
848 return hg.clean(repo, node, show_stats=show_stats)
849
849
850 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
850 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
851
851
852 if command:
852 if command:
853 changesets = 1
853 changesets = 1
854 if noupdate:
854 if noupdate:
855 try:
855 try:
856 node = state['current'][0]
856 node = state['current'][0]
857 except LookupError:
857 except LookupError:
858 raise error.Abort(_('current bisect revision is unknown - '
858 raise error.Abort(_('current bisect revision is unknown - '
859 'start a new bisect to fix'))
859 'start a new bisect to fix'))
860 else:
860 else:
861 node, p2 = repo.dirstate.parents()
861 node, p2 = repo.dirstate.parents()
862 if p2 != nullid:
862 if p2 != nullid:
863 raise error.Abort(_('current bisect revision is a merge'))
863 raise error.Abort(_('current bisect revision is a merge'))
864 if rev:
864 if rev:
865 node = repo[scmutil.revsingle(repo, rev, node)].node()
865 node = repo[scmutil.revsingle(repo, rev, node)].node()
866 try:
866 try:
867 while changesets:
867 while changesets:
868 # update state
868 # update state
869 state['current'] = [node]
869 state['current'] = [node]
870 hbisect.save_state(repo, state)
870 hbisect.save_state(repo, state)
871 status = ui.system(command, environ={'HG_NODE': hex(node)},
871 status = ui.system(command, environ={'HG_NODE': hex(node)},
872 blockedtag='bisect_check')
872 blockedtag='bisect_check')
873 if status == 125:
873 if status == 125:
874 transition = "skip"
874 transition = "skip"
875 elif status == 0:
875 elif status == 0:
876 transition = "good"
876 transition = "good"
877 # status < 0 means process was killed
877 # status < 0 means process was killed
878 elif status == 127:
878 elif status == 127:
879 raise error.Abort(_("failed to execute %s") % command)
879 raise error.Abort(_("failed to execute %s") % command)
880 elif status < 0:
880 elif status < 0:
881 raise error.Abort(_("%s killed") % command)
881 raise error.Abort(_("%s killed") % command)
882 else:
882 else:
883 transition = "bad"
883 transition = "bad"
884 state[transition].append(node)
884 state[transition].append(node)
885 ctx = repo[node]
885 ctx = repo[node]
886 ui.status(_('changeset %d:%s: %s\n') % (ctx.rev(), ctx,
886 ui.status(_('changeset %d:%s: %s\n') % (ctx.rev(), ctx,
887 transition))
887 transition))
888 hbisect.checkstate(state)
888 hbisect.checkstate(state)
889 # bisect
889 # bisect
890 nodes, changesets, bgood = hbisect.bisect(repo, state)
890 nodes, changesets, bgood = hbisect.bisect(repo, state)
891 # update to next check
891 # update to next check
892 node = nodes[0]
892 node = nodes[0]
893 mayupdate(repo, node, show_stats=False)
893 mayupdate(repo, node, show_stats=False)
894 finally:
894 finally:
895 state['current'] = [node]
895 state['current'] = [node]
896 hbisect.save_state(repo, state)
896 hbisect.save_state(repo, state)
897 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
897 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
898 return
898 return
899
899
900 hbisect.checkstate(state)
900 hbisect.checkstate(state)
901
901
902 # actually bisect
902 # actually bisect
903 nodes, changesets, good = hbisect.bisect(repo, state)
903 nodes, changesets, good = hbisect.bisect(repo, state)
904 if extend:
904 if extend:
905 if not changesets:
905 if not changesets:
906 extendnode = hbisect.extendrange(repo, state, nodes, good)
906 extendnode = hbisect.extendrange(repo, state, nodes, good)
907 if extendnode is not None:
907 if extendnode is not None:
908 ui.write(_("Extending search to changeset %d:%s\n")
908 ui.write(_("Extending search to changeset %d:%s\n")
909 % (extendnode.rev(), extendnode))
909 % (extendnode.rev(), extendnode))
910 state['current'] = [extendnode.node()]
910 state['current'] = [extendnode.node()]
911 hbisect.save_state(repo, state)
911 hbisect.save_state(repo, state)
912 return mayupdate(repo, extendnode.node())
912 return mayupdate(repo, extendnode.node())
913 raise error.Abort(_("nothing to extend"))
913 raise error.Abort(_("nothing to extend"))
914
914
915 if changesets == 0:
915 if changesets == 0:
916 hbisect.printresult(ui, repo, state, displayer, nodes, good)
916 hbisect.printresult(ui, repo, state, displayer, nodes, good)
917 else:
917 else:
918 assert len(nodes) == 1 # only a single node can be tested next
918 assert len(nodes) == 1 # only a single node can be tested next
919 node = nodes[0]
919 node = nodes[0]
920 # compute the approximate number of remaining tests
920 # compute the approximate number of remaining tests
921 tests, size = 0, 2
921 tests, size = 0, 2
922 while size <= changesets:
922 while size <= changesets:
923 tests, size = tests + 1, size * 2
923 tests, size = tests + 1, size * 2
924 rev = repo.changelog.rev(node)
924 rev = repo.changelog.rev(node)
925 ui.write(_("Testing changeset %d:%s "
925 ui.write(_("Testing changeset %d:%s "
926 "(%d changesets remaining, ~%d tests)\n")
926 "(%d changesets remaining, ~%d tests)\n")
927 % (rev, short(node), changesets, tests))
927 % (rev, short(node), changesets, tests))
928 state['current'] = [node]
928 state['current'] = [node]
929 hbisect.save_state(repo, state)
929 hbisect.save_state(repo, state)
930 return mayupdate(repo, node)
930 return mayupdate(repo, node)
931
931
932 @command('bookmarks|bookmark',
932 @command('bookmarks|bookmark',
933 [('f', 'force', False, _('force')),
933 [('f', 'force', False, _('force')),
934 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
934 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
935 ('d', 'delete', False, _('delete a given bookmark')),
935 ('d', 'delete', False, _('delete a given bookmark')),
936 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
936 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
937 ('i', 'inactive', False, _('mark a bookmark inactive')),
937 ('i', 'inactive', False, _('mark a bookmark inactive')),
938 ('l', 'list', False, _('list existing bookmarks')),
938 ('l', 'list', False, _('list existing bookmarks')),
939 ] + formatteropts,
939 ] + formatteropts,
940 _('hg bookmarks [OPTIONS]... [NAME]...'),
940 _('hg bookmarks [OPTIONS]... [NAME]...'),
941 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
941 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
942 def bookmark(ui, repo, *names, **opts):
942 def bookmark(ui, repo, *names, **opts):
943 '''create a new bookmark or list existing bookmarks
943 '''create a new bookmark or list existing bookmarks
944
944
945 Bookmarks are labels on changesets to help track lines of development.
945 Bookmarks are labels on changesets to help track lines of development.
946 Bookmarks are unversioned and can be moved, renamed and deleted.
946 Bookmarks are unversioned and can be moved, renamed and deleted.
947 Deleting or moving a bookmark has no effect on the associated changesets.
947 Deleting or moving a bookmark has no effect on the associated changesets.
948
948
949 Creating or updating to a bookmark causes it to be marked as 'active'.
949 Creating or updating to a bookmark causes it to be marked as 'active'.
950 The active bookmark is indicated with a '*'.
950 The active bookmark is indicated with a '*'.
951 When a commit is made, the active bookmark will advance to the new commit.
951 When a commit is made, the active bookmark will advance to the new commit.
952 A plain :hg:`update` will also advance an active bookmark, if possible.
952 A plain :hg:`update` will also advance an active bookmark, if possible.
953 Updating away from a bookmark will cause it to be deactivated.
953 Updating away from a bookmark will cause it to be deactivated.
954
954
955 Bookmarks can be pushed and pulled between repositories (see
955 Bookmarks can be pushed and pulled between repositories (see
956 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
956 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
957 diverged, a new 'divergent bookmark' of the form 'name@path' will
957 diverged, a new 'divergent bookmark' of the form 'name@path' will
958 be created. Using :hg:`merge` will resolve the divergence.
958 be created. Using :hg:`merge` will resolve the divergence.
959
959
960 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
960 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
961 the active bookmark's name.
961 the active bookmark's name.
962
962
963 A bookmark named '@' has the special property that :hg:`clone` will
963 A bookmark named '@' has the special property that :hg:`clone` will
964 check it out by default if it exists.
964 check it out by default if it exists.
965
965
966 .. container:: verbose
966 .. container:: verbose
967
967
968 Template:
968 Template:
969
969
970 The following keywords are supported in addition to the common template
970 The following keywords are supported in addition to the common template
971 keywords and functions such as ``{bookmark}``. See also
971 keywords and functions such as ``{bookmark}``. See also
972 :hg:`help templates`.
972 :hg:`help templates`.
973
973
974 :active: Boolean. True if the bookmark is active.
974 :active: Boolean. True if the bookmark is active.
975
975
976 Examples:
976 Examples:
977
977
978 - create an active bookmark for a new line of development::
978 - create an active bookmark for a new line of development::
979
979
980 hg book new-feature
980 hg book new-feature
981
981
982 - create an inactive bookmark as a place marker::
982 - create an inactive bookmark as a place marker::
983
983
984 hg book -i reviewed
984 hg book -i reviewed
985
985
986 - create an inactive bookmark on another changeset::
986 - create an inactive bookmark on another changeset::
987
987
988 hg book -r .^ tested
988 hg book -r .^ tested
989
989
990 - rename bookmark turkey to dinner::
990 - rename bookmark turkey to dinner::
991
991
992 hg book -m turkey dinner
992 hg book -m turkey dinner
993
993
994 - move the '@' bookmark from another branch::
994 - move the '@' bookmark from another branch::
995
995
996 hg book -f @
996 hg book -f @
997
997
998 - print only the active bookmark name::
998 - print only the active bookmark name::
999
999
1000 hg book -ql .
1000 hg book -ql .
1001 '''
1001 '''
1002 opts = pycompat.byteskwargs(opts)
1002 opts = pycompat.byteskwargs(opts)
1003 force = opts.get('force')
1003 force = opts.get('force')
1004 rev = opts.get('rev')
1004 rev = opts.get('rev')
1005 inactive = opts.get('inactive') # meaning add/rename to inactive bookmark
1005 inactive = opts.get('inactive') # meaning add/rename to inactive bookmark
1006
1006
1007 selactions = [k for k in ['delete', 'rename', 'list'] if opts.get(k)]
1007 selactions = [k for k in ['delete', 'rename', 'list'] if opts.get(k)]
1008 if len(selactions) > 1:
1008 if len(selactions) > 1:
1009 raise error.Abort(_('--%s and --%s are incompatible')
1009 raise error.Abort(_('--%s and --%s are incompatible')
1010 % tuple(selactions[:2]))
1010 % tuple(selactions[:2]))
1011 if selactions:
1011 if selactions:
1012 action = selactions[0]
1012 action = selactions[0]
1013 elif names or rev:
1013 elif names or rev:
1014 action = 'add'
1014 action = 'add'
1015 elif inactive:
1015 elif inactive:
1016 action = 'inactive' # meaning deactivate
1016 action = 'inactive' # meaning deactivate
1017 else:
1017 else:
1018 action = 'list'
1018 action = 'list'
1019
1019
1020 if rev and action in {'delete', 'rename', 'list'}:
1020 if rev and action in {'delete', 'rename', 'list'}:
1021 raise error.Abort(_("--rev is incompatible with --%s") % action)
1021 raise error.Abort(_("--rev is incompatible with --%s") % action)
1022 if inactive and action in {'delete', 'list'}:
1022 if inactive and action in {'delete', 'list'}:
1023 raise error.Abort(_("--inactive is incompatible with --%s") % action)
1023 raise error.Abort(_("--inactive is incompatible with --%s") % action)
1024 if not names and action in {'add', 'delete'}:
1024 if not names and action in {'add', 'delete'}:
1025 raise error.Abort(_("bookmark name required"))
1025 raise error.Abort(_("bookmark name required"))
1026
1026
1027 if action in {'add', 'delete', 'rename', 'inactive'}:
1027 if action in {'add', 'delete', 'rename', 'inactive'}:
1028 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
1028 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
1029 if action == 'delete':
1029 if action == 'delete':
1030 names = pycompat.maplist(repo._bookmarks.expandname, names)
1030 names = pycompat.maplist(repo._bookmarks.expandname, names)
1031 bookmarks.delete(repo, tr, names)
1031 bookmarks.delete(repo, tr, names)
1032 elif action == 'rename':
1032 elif action == 'rename':
1033 if not names:
1033 if not names:
1034 raise error.Abort(_("new bookmark name required"))
1034 raise error.Abort(_("new bookmark name required"))
1035 elif len(names) > 1:
1035 elif len(names) > 1:
1036 raise error.Abort(_("only one new bookmark name allowed"))
1036 raise error.Abort(_("only one new bookmark name allowed"))
1037 oldname = repo._bookmarks.expandname(opts['rename'])
1037 oldname = repo._bookmarks.expandname(opts['rename'])
1038 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1038 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1039 elif action == 'add':
1039 elif action == 'add':
1040 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1040 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1041 elif action == 'inactive':
1041 elif action == 'inactive':
1042 if len(repo._bookmarks) == 0:
1042 if len(repo._bookmarks) == 0:
1043 ui.status(_("no bookmarks set\n"))
1043 ui.status(_("no bookmarks set\n"))
1044 elif not repo._activebookmark:
1044 elif not repo._activebookmark:
1045 ui.status(_("no active bookmark\n"))
1045 ui.status(_("no active bookmark\n"))
1046 else:
1046 else:
1047 bookmarks.deactivate(repo)
1047 bookmarks.deactivate(repo)
1048 elif action == 'list':
1048 elif action == 'list':
1049 names = pycompat.maplist(repo._bookmarks.expandname, names)
1049 names = pycompat.maplist(repo._bookmarks.expandname, names)
1050 with ui.formatter('bookmarks', opts) as fm:
1050 with ui.formatter('bookmarks', opts) as fm:
1051 bookmarks.printbookmarks(ui, repo, fm, names)
1051 bookmarks.printbookmarks(ui, repo, fm, names)
1052 else:
1052 else:
1053 raise error.ProgrammingError('invalid action: %s' % action)
1053 raise error.ProgrammingError('invalid action: %s' % action)
1054
1054
1055 @command('branch',
1055 @command('branch',
1056 [('f', 'force', None,
1056 [('f', 'force', None,
1057 _('set branch name even if it shadows an existing branch')),
1057 _('set branch name even if it shadows an existing branch')),
1058 ('C', 'clean', None, _('reset branch name to parent branch name')),
1058 ('C', 'clean', None, _('reset branch name to parent branch name')),
1059 ('r', 'rev', [], _('change branches of the given revs (EXPERIMENTAL)')),
1059 ('r', 'rev', [], _('change branches of the given revs (EXPERIMENTAL)')),
1060 ],
1060 ],
1061 _('[-fC] [NAME]'),
1061 _('[-fC] [NAME]'),
1062 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
1062 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
1063 def branch(ui, repo, label=None, **opts):
1063 def branch(ui, repo, label=None, **opts):
1064 """set or show the current branch name
1064 """set or show the current branch name
1065
1065
1066 .. note::
1066 .. note::
1067
1067
1068 Branch names are permanent and global. Use :hg:`bookmark` to create a
1068 Branch names are permanent and global. Use :hg:`bookmark` to create a
1069 light-weight bookmark instead. See :hg:`help glossary` for more
1069 light-weight bookmark instead. See :hg:`help glossary` for more
1070 information about named branches and bookmarks.
1070 information about named branches and bookmarks.
1071
1071
1072 With no argument, show the current branch name. With one argument,
1072 With no argument, show the current branch name. With one argument,
1073 set the working directory branch name (the branch will not exist
1073 set the working directory branch name (the branch will not exist
1074 in the repository until the next commit). Standard practice
1074 in the repository until the next commit). Standard practice
1075 recommends that primary development take place on the 'default'
1075 recommends that primary development take place on the 'default'
1076 branch.
1076 branch.
1077
1077
1078 Unless -f/--force is specified, branch will not let you set a
1078 Unless -f/--force is specified, branch will not let you set a
1079 branch name that already exists.
1079 branch name that already exists.
1080
1080
1081 Use -C/--clean to reset the working directory branch to that of
1081 Use -C/--clean to reset the working directory branch to that of
1082 the parent of the working directory, negating a previous branch
1082 the parent of the working directory, negating a previous branch
1083 change.
1083 change.
1084
1084
1085 Use the command :hg:`update` to switch to an existing branch. Use
1085 Use the command :hg:`update` to switch to an existing branch. Use
1086 :hg:`commit --close-branch` to mark this branch head as closed.
1086 :hg:`commit --close-branch` to mark this branch head as closed.
1087 When all heads of a branch are closed, the branch will be
1087 When all heads of a branch are closed, the branch will be
1088 considered closed.
1088 considered closed.
1089
1089
1090 Returns 0 on success.
1090 Returns 0 on success.
1091 """
1091 """
1092 opts = pycompat.byteskwargs(opts)
1092 opts = pycompat.byteskwargs(opts)
1093 revs = opts.get('rev')
1093 revs = opts.get('rev')
1094 if label:
1094 if label:
1095 label = label.strip()
1095 label = label.strip()
1096
1096
1097 if not opts.get('clean') and not label:
1097 if not opts.get('clean') and not label:
1098 if revs:
1098 if revs:
1099 raise error.Abort(_("no branch name specified for the revisions"))
1099 raise error.Abort(_("no branch name specified for the revisions"))
1100 ui.write("%s\n" % repo.dirstate.branch())
1100 ui.write("%s\n" % repo.dirstate.branch())
1101 return
1101 return
1102
1102
1103 with repo.wlock():
1103 with repo.wlock():
1104 if opts.get('clean'):
1104 if opts.get('clean'):
1105 label = repo[None].p1().branch()
1105 label = repo[None].p1().branch()
1106 repo.dirstate.setbranch(label)
1106 repo.dirstate.setbranch(label)
1107 ui.status(_('reset working directory to branch %s\n') % label)
1107 ui.status(_('reset working directory to branch %s\n') % label)
1108 elif label:
1108 elif label:
1109
1109
1110 scmutil.checknewlabel(repo, label, 'branch')
1110 scmutil.checknewlabel(repo, label, 'branch')
1111 if revs:
1111 if revs:
1112 return cmdutil.changebranch(ui, repo, revs, label)
1112 return cmdutil.changebranch(ui, repo, revs, label)
1113
1113
1114 if not opts.get('force') and label in repo.branchmap():
1114 if not opts.get('force') and label in repo.branchmap():
1115 if label not in [p.branch() for p in repo[None].parents()]:
1115 if label not in [p.branch() for p in repo[None].parents()]:
1116 raise error.Abort(_('a branch of the same name already'
1116 raise error.Abort(_('a branch of the same name already'
1117 ' exists'),
1117 ' exists'),
1118 # i18n: "it" refers to an existing branch
1118 # i18n: "it" refers to an existing branch
1119 hint=_("use 'hg update' to switch to it"))
1119 hint=_("use 'hg update' to switch to it"))
1120
1120
1121 repo.dirstate.setbranch(label)
1121 repo.dirstate.setbranch(label)
1122 ui.status(_('marked working directory as branch %s\n') % label)
1122 ui.status(_('marked working directory as branch %s\n') % label)
1123
1123
1124 # find any open named branches aside from default
1124 # find any open named branches aside from default
1125 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1125 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1126 if n != "default" and not c]
1126 if n != "default" and not c]
1127 if not others:
1127 if not others:
1128 ui.status(_('(branches are permanent and global, '
1128 ui.status(_('(branches are permanent and global, '
1129 'did you want a bookmark?)\n'))
1129 'did you want a bookmark?)\n'))
1130
1130
1131 @command('branches',
1131 @command('branches',
1132 [('a', 'active', False,
1132 [('a', 'active', False,
1133 _('show only branches that have unmerged heads (DEPRECATED)')),
1133 _('show only branches that have unmerged heads (DEPRECATED)')),
1134 ('c', 'closed', False, _('show normal and closed branches')),
1134 ('c', 'closed', False, _('show normal and closed branches')),
1135 ] + formatteropts,
1135 ] + formatteropts,
1136 _('[-c]'),
1136 _('[-c]'),
1137 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1137 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1138 intents={INTENT_READONLY})
1138 intents={INTENT_READONLY})
1139 def branches(ui, repo, active=False, closed=False, **opts):
1139 def branches(ui, repo, active=False, closed=False, **opts):
1140 """list repository named branches
1140 """list repository named branches
1141
1141
1142 List the repository's named branches, indicating which ones are
1142 List the repository's named branches, indicating which ones are
1143 inactive. If -c/--closed is specified, also list branches which have
1143 inactive. If -c/--closed is specified, also list branches which have
1144 been marked closed (see :hg:`commit --close-branch`).
1144 been marked closed (see :hg:`commit --close-branch`).
1145
1145
1146 Use the command :hg:`update` to switch to an existing branch.
1146 Use the command :hg:`update` to switch to an existing branch.
1147
1147
1148 .. container:: verbose
1148 .. container:: verbose
1149
1149
1150 Template:
1150 Template:
1151
1151
1152 The following keywords are supported in addition to the common template
1152 The following keywords are supported in addition to the common template
1153 keywords and functions such as ``{branch}``. See also
1153 keywords and functions such as ``{branch}``. See also
1154 :hg:`help templates`.
1154 :hg:`help templates`.
1155
1155
1156 :active: Boolean. True if the branch is active.
1156 :active: Boolean. True if the branch is active.
1157 :closed: Boolean. True if the branch is closed.
1157 :closed: Boolean. True if the branch is closed.
1158 :current: Boolean. True if it is the current branch.
1158 :current: Boolean. True if it is the current branch.
1159
1159
1160 Returns 0.
1160 Returns 0.
1161 """
1161 """
1162
1162
1163 opts = pycompat.byteskwargs(opts)
1163 opts = pycompat.byteskwargs(opts)
1164 ui.pager('branches')
1164 ui.pager('branches')
1165 fm = ui.formatter('branches', opts)
1165 fm = ui.formatter('branches', opts)
1166 hexfunc = fm.hexfunc
1166 hexfunc = fm.hexfunc
1167
1167
1168 allheads = set(repo.heads())
1168 allheads = set(repo.heads())
1169 branches = []
1169 branches = []
1170 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1170 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1171 isactive = False
1171 isactive = False
1172 if not isclosed:
1172 if not isclosed:
1173 openheads = set(repo.branchmap().iteropen(heads))
1173 openheads = set(repo.branchmap().iteropen(heads))
1174 isactive = bool(openheads & allheads)
1174 isactive = bool(openheads & allheads)
1175 branches.append((tag, repo[tip], isactive, not isclosed))
1175 branches.append((tag, repo[tip], isactive, not isclosed))
1176 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1176 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1177 reverse=True)
1177 reverse=True)
1178
1178
1179 for tag, ctx, isactive, isopen in branches:
1179 for tag, ctx, isactive, isopen in branches:
1180 if active and not isactive:
1180 if active and not isactive:
1181 continue
1181 continue
1182 if isactive:
1182 if isactive:
1183 label = 'branches.active'
1183 label = 'branches.active'
1184 notice = ''
1184 notice = ''
1185 elif not isopen:
1185 elif not isopen:
1186 if not closed:
1186 if not closed:
1187 continue
1187 continue
1188 label = 'branches.closed'
1188 label = 'branches.closed'
1189 notice = _(' (closed)')
1189 notice = _(' (closed)')
1190 else:
1190 else:
1191 label = 'branches.inactive'
1191 label = 'branches.inactive'
1192 notice = _(' (inactive)')
1192 notice = _(' (inactive)')
1193 current = (tag == repo.dirstate.branch())
1193 current = (tag == repo.dirstate.branch())
1194 if current:
1194 if current:
1195 label = 'branches.current'
1195 label = 'branches.current'
1196
1196
1197 fm.startitem()
1197 fm.startitem()
1198 fm.write('branch', '%s', tag, label=label)
1198 fm.write('branch', '%s', tag, label=label)
1199 rev = ctx.rev()
1199 rev = ctx.rev()
1200 padsize = max(31 - len("%d" % rev) - encoding.colwidth(tag), 0)
1200 padsize = max(31 - len("%d" % rev) - encoding.colwidth(tag), 0)
1201 fmt = ' ' * padsize + ' %d:%s'
1201 fmt = ' ' * padsize + ' %d:%s'
1202 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1202 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1203 label='log.changeset changeset.%s' % ctx.phasestr())
1203 label='log.changeset changeset.%s' % ctx.phasestr())
1204 fm.context(ctx=ctx)
1204 fm.context(ctx=ctx)
1205 fm.data(active=isactive, closed=not isopen, current=current)
1205 fm.data(active=isactive, closed=not isopen, current=current)
1206 if not ui.quiet:
1206 if not ui.quiet:
1207 fm.plain(notice)
1207 fm.plain(notice)
1208 fm.plain('\n')
1208 fm.plain('\n')
1209 fm.end()
1209 fm.end()
1210
1210
1211 @command('bundle',
1211 @command('bundle',
1212 [('f', 'force', None, _('run even when the destination is unrelated')),
1212 [('f', 'force', None, _('run even when the destination is unrelated')),
1213 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1213 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1214 _('REV')),
1214 _('REV')),
1215 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1215 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1216 _('BRANCH')),
1216 _('BRANCH')),
1217 ('', 'base', [],
1217 ('', 'base', [],
1218 _('a base changeset assumed to be available at the destination'),
1218 _('a base changeset assumed to be available at the destination'),
1219 _('REV')),
1219 _('REV')),
1220 ('a', 'all', None, _('bundle all changesets in the repository')),
1220 ('a', 'all', None, _('bundle all changesets in the repository')),
1221 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1221 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1222 ] + remoteopts,
1222 ] + remoteopts,
1223 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1223 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1224 helpcategory=command.CATEGORY_IMPORT_EXPORT)
1224 helpcategory=command.CATEGORY_IMPORT_EXPORT)
1225 def bundle(ui, repo, fname, dest=None, **opts):
1225 def bundle(ui, repo, fname, dest=None, **opts):
1226 """create a bundle file
1226 """create a bundle file
1227
1227
1228 Generate a bundle file containing data to be transferred to another
1228 Generate a bundle file containing data to be transferred to another
1229 repository.
1229 repository.
1230
1230
1231 To create a bundle containing all changesets, use -a/--all
1231 To create a bundle containing all changesets, use -a/--all
1232 (or --base null). Otherwise, hg assumes the destination will have
1232 (or --base null). Otherwise, hg assumes the destination will have
1233 all the nodes you specify with --base parameters. Otherwise, hg
1233 all the nodes you specify with --base parameters. Otherwise, hg
1234 will assume the repository has all the nodes in destination, or
1234 will assume the repository has all the nodes in destination, or
1235 default-push/default if no destination is specified, where destination
1235 default-push/default if no destination is specified, where destination
1236 is the repository you provide through DEST option.
1236 is the repository you provide through DEST option.
1237
1237
1238 You can change bundle format with the -t/--type option. See
1238 You can change bundle format with the -t/--type option. See
1239 :hg:`help bundlespec` for documentation on this format. By default,
1239 :hg:`help bundlespec` for documentation on this format. By default,
1240 the most appropriate format is used and compression defaults to
1240 the most appropriate format is used and compression defaults to
1241 bzip2.
1241 bzip2.
1242
1242
1243 The bundle file can then be transferred using conventional means
1243 The bundle file can then be transferred using conventional means
1244 and applied to another repository with the unbundle or pull
1244 and applied to another repository with the unbundle or pull
1245 command. This is useful when direct push and pull are not
1245 command. This is useful when direct push and pull are not
1246 available or when exporting an entire repository is undesirable.
1246 available or when exporting an entire repository is undesirable.
1247
1247
1248 Applying bundles preserves all changeset contents including
1248 Applying bundles preserves all changeset contents including
1249 permissions, copy/rename information, and revision history.
1249 permissions, copy/rename information, and revision history.
1250
1250
1251 Returns 0 on success, 1 if no changes found.
1251 Returns 0 on success, 1 if no changes found.
1252 """
1252 """
1253 opts = pycompat.byteskwargs(opts)
1253 opts = pycompat.byteskwargs(opts)
1254 revs = None
1254 revs = None
1255 if 'rev' in opts:
1255 if 'rev' in opts:
1256 revstrings = opts['rev']
1256 revstrings = opts['rev']
1257 revs = scmutil.revrange(repo, revstrings)
1257 revs = scmutil.revrange(repo, revstrings)
1258 if revstrings and not revs:
1258 if revstrings and not revs:
1259 raise error.Abort(_('no commits to bundle'))
1259 raise error.Abort(_('no commits to bundle'))
1260
1260
1261 bundletype = opts.get('type', 'bzip2').lower()
1261 bundletype = opts.get('type', 'bzip2').lower()
1262 try:
1262 try:
1263 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1263 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1264 except error.UnsupportedBundleSpecification as e:
1264 except error.UnsupportedBundleSpecification as e:
1265 raise error.Abort(pycompat.bytestr(e),
1265 raise error.Abort(pycompat.bytestr(e),
1266 hint=_("see 'hg help bundlespec' for supported "
1266 hint=_("see 'hg help bundlespec' for supported "
1267 "values for --type"))
1267 "values for --type"))
1268 cgversion = bundlespec.contentopts["cg.version"]
1268 cgversion = bundlespec.contentopts["cg.version"]
1269
1269
1270 # Packed bundles are a pseudo bundle format for now.
1270 # Packed bundles are a pseudo bundle format for now.
1271 if cgversion == 's1':
1271 if cgversion == 's1':
1272 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1272 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1273 hint=_("use 'hg debugcreatestreamclonebundle'"))
1273 hint=_("use 'hg debugcreatestreamclonebundle'"))
1274
1274
1275 if opts.get('all'):
1275 if opts.get('all'):
1276 if dest:
1276 if dest:
1277 raise error.Abort(_("--all is incompatible with specifying "
1277 raise error.Abort(_("--all is incompatible with specifying "
1278 "a destination"))
1278 "a destination"))
1279 if opts.get('base'):
1279 if opts.get('base'):
1280 ui.warn(_("ignoring --base because --all was specified\n"))
1280 ui.warn(_("ignoring --base because --all was specified\n"))
1281 base = [nullrev]
1281 base = [nullrev]
1282 else:
1282 else:
1283 base = scmutil.revrange(repo, opts.get('base'))
1283 base = scmutil.revrange(repo, opts.get('base'))
1284 if cgversion not in changegroup.supportedoutgoingversions(repo):
1284 if cgversion not in changegroup.supportedoutgoingversions(repo):
1285 raise error.Abort(_("repository does not support bundle version %s") %
1285 raise error.Abort(_("repository does not support bundle version %s") %
1286 cgversion)
1286 cgversion)
1287
1287
1288 if base:
1288 if base:
1289 if dest:
1289 if dest:
1290 raise error.Abort(_("--base is incompatible with specifying "
1290 raise error.Abort(_("--base is incompatible with specifying "
1291 "a destination"))
1291 "a destination"))
1292 common = [repo[rev].node() for rev in base]
1292 common = [repo[rev].node() for rev in base]
1293 heads = [repo[r].node() for r in revs] if revs else None
1293 heads = [repo[r].node() for r in revs] if revs else None
1294 outgoing = discovery.outgoing(repo, common, heads)
1294 outgoing = discovery.outgoing(repo, common, heads)
1295 else:
1295 else:
1296 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1296 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1297 dest, branches = hg.parseurl(dest, opts.get('branch'))
1297 dest, branches = hg.parseurl(dest, opts.get('branch'))
1298 other = hg.peer(repo, opts, dest)
1298 other = hg.peer(repo, opts, dest)
1299 revs = [repo[r].hex() for r in revs]
1299 revs = [repo[r].hex() for r in revs]
1300 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1300 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1301 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1301 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1302 outgoing = discovery.findcommonoutgoing(repo, other,
1302 outgoing = discovery.findcommonoutgoing(repo, other,
1303 onlyheads=heads,
1303 onlyheads=heads,
1304 force=opts.get('force'),
1304 force=opts.get('force'),
1305 portable=True)
1305 portable=True)
1306
1306
1307 if not outgoing.missing:
1307 if not outgoing.missing:
1308 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1308 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1309 return 1
1309 return 1
1310
1310
1311 if cgversion == '01': #bundle1
1311 if cgversion == '01': #bundle1
1312 bversion = 'HG10' + bundlespec.wirecompression
1312 bversion = 'HG10' + bundlespec.wirecompression
1313 bcompression = None
1313 bcompression = None
1314 elif cgversion in ('02', '03'):
1314 elif cgversion in ('02', '03'):
1315 bversion = 'HG20'
1315 bversion = 'HG20'
1316 bcompression = bundlespec.wirecompression
1316 bcompression = bundlespec.wirecompression
1317 else:
1317 else:
1318 raise error.ProgrammingError(
1318 raise error.ProgrammingError(
1319 'bundle: unexpected changegroup version %s' % cgversion)
1319 'bundle: unexpected changegroup version %s' % cgversion)
1320
1320
1321 # TODO compression options should be derived from bundlespec parsing.
1321 # TODO compression options should be derived from bundlespec parsing.
1322 # This is a temporary hack to allow adjusting bundle compression
1322 # This is a temporary hack to allow adjusting bundle compression
1323 # level without a) formalizing the bundlespec changes to declare it
1323 # level without a) formalizing the bundlespec changes to declare it
1324 # b) introducing a command flag.
1324 # b) introducing a command flag.
1325 compopts = {}
1325 compopts = {}
1326 complevel = ui.configint('experimental',
1326 complevel = ui.configint('experimental',
1327 'bundlecomplevel.' + bundlespec.compression)
1327 'bundlecomplevel.' + bundlespec.compression)
1328 if complevel is None:
1328 if complevel is None:
1329 complevel = ui.configint('experimental', 'bundlecomplevel')
1329 complevel = ui.configint('experimental', 'bundlecomplevel')
1330 if complevel is not None:
1330 if complevel is not None:
1331 compopts['level'] = complevel
1331 compopts['level'] = complevel
1332
1332
1333 # Allow overriding the bundling of obsmarker in phases through
1333 # Allow overriding the bundling of obsmarker in phases through
1334 # configuration while we don't have a bundle version that include them
1334 # configuration while we don't have a bundle version that include them
1335 if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker'):
1335 if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker'):
1336 bundlespec.contentopts['obsolescence'] = True
1336 bundlespec.contentopts['obsolescence'] = True
1337 if repo.ui.configbool('experimental', 'bundle-phases'):
1337 if repo.ui.configbool('experimental', 'bundle-phases'):
1338 bundlespec.contentopts['phases'] = True
1338 bundlespec.contentopts['phases'] = True
1339
1339
1340 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1340 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1341 bundlespec.contentopts, compression=bcompression,
1341 bundlespec.contentopts, compression=bcompression,
1342 compopts=compopts)
1342 compopts=compopts)
1343
1343
1344 @command('cat',
1344 @command('cat',
1345 [('o', 'output', '',
1345 [('o', 'output', '',
1346 _('print output to file with formatted name'), _('FORMAT')),
1346 _('print output to file with formatted name'), _('FORMAT')),
1347 ('r', 'rev', '', _('print the given revision'), _('REV')),
1347 ('r', 'rev', '', _('print the given revision'), _('REV')),
1348 ('', 'decode', None, _('apply any matching decode filter')),
1348 ('', 'decode', None, _('apply any matching decode filter')),
1349 ] + walkopts + formatteropts,
1349 ] + walkopts + formatteropts,
1350 _('[OPTION]... FILE...'),
1350 _('[OPTION]... FILE...'),
1351 helpcategory=command.CATEGORY_FILE_CONTENTS,
1351 helpcategory=command.CATEGORY_FILE_CONTENTS,
1352 inferrepo=True,
1352 inferrepo=True,
1353 intents={INTENT_READONLY})
1353 intents={INTENT_READONLY})
1354 def cat(ui, repo, file1, *pats, **opts):
1354 def cat(ui, repo, file1, *pats, **opts):
1355 """output the current or given revision of files
1355 """output the current or given revision of files
1356
1356
1357 Print the specified files as they were at the given revision. If
1357 Print the specified files as they were at the given revision. If
1358 no revision is given, the parent of the working directory is used.
1358 no revision is given, the parent of the working directory is used.
1359
1359
1360 Output may be to a file, in which case the name of the file is
1360 Output may be to a file, in which case the name of the file is
1361 given using a template string. See :hg:`help templates`. In addition
1361 given using a template string. See :hg:`help templates`. In addition
1362 to the common template keywords, the following formatting rules are
1362 to the common template keywords, the following formatting rules are
1363 supported:
1363 supported:
1364
1364
1365 :``%%``: literal "%" character
1365 :``%%``: literal "%" character
1366 :``%s``: basename of file being printed
1366 :``%s``: basename of file being printed
1367 :``%d``: dirname of file being printed, or '.' if in repository root
1367 :``%d``: dirname of file being printed, or '.' if in repository root
1368 :``%p``: root-relative path name of file being printed
1368 :``%p``: root-relative path name of file being printed
1369 :``%H``: changeset hash (40 hexadecimal digits)
1369 :``%H``: changeset hash (40 hexadecimal digits)
1370 :``%R``: changeset revision number
1370 :``%R``: changeset revision number
1371 :``%h``: short-form changeset hash (12 hexadecimal digits)
1371 :``%h``: short-form changeset hash (12 hexadecimal digits)
1372 :``%r``: zero-padded changeset revision number
1372 :``%r``: zero-padded changeset revision number
1373 :``%b``: basename of the exporting repository
1373 :``%b``: basename of the exporting repository
1374 :``\\``: literal "\\" character
1374 :``\\``: literal "\\" character
1375
1375
1376 .. container:: verbose
1376 .. container:: verbose
1377
1377
1378 Template:
1378 Template:
1379
1379
1380 The following keywords are supported in addition to the common template
1380 The following keywords are supported in addition to the common template
1381 keywords and functions. See also :hg:`help templates`.
1381 keywords and functions. See also :hg:`help templates`.
1382
1382
1383 :data: String. File content.
1383 :data: String. File content.
1384 :path: String. Repository-absolute path of the file.
1384 :path: String. Repository-absolute path of the file.
1385
1385
1386 Returns 0 on success.
1386 Returns 0 on success.
1387 """
1387 """
1388 opts = pycompat.byteskwargs(opts)
1388 opts = pycompat.byteskwargs(opts)
1389 rev = opts.get('rev')
1389 rev = opts.get('rev')
1390 if rev:
1390 if rev:
1391 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
1391 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
1392 ctx = scmutil.revsingle(repo, rev)
1392 ctx = scmutil.revsingle(repo, rev)
1393 m = scmutil.match(ctx, (file1,) + pats, opts)
1393 m = scmutil.match(ctx, (file1,) + pats, opts)
1394 fntemplate = opts.pop('output', '')
1394 fntemplate = opts.pop('output', '')
1395 if cmdutil.isstdiofilename(fntemplate):
1395 if cmdutil.isstdiofilename(fntemplate):
1396 fntemplate = ''
1396 fntemplate = ''
1397
1397
1398 if fntemplate:
1398 if fntemplate:
1399 fm = formatter.nullformatter(ui, 'cat', opts)
1399 fm = formatter.nullformatter(ui, 'cat', opts)
1400 else:
1400 else:
1401 ui.pager('cat')
1401 ui.pager('cat')
1402 fm = ui.formatter('cat', opts)
1402 fm = ui.formatter('cat', opts)
1403 with fm:
1403 with fm:
1404 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '',
1404 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '',
1405 **pycompat.strkwargs(opts))
1405 **pycompat.strkwargs(opts))
1406
1406
1407 @command('clone',
1407 @command('clone',
1408 [('U', 'noupdate', None, _('the clone will include an empty working '
1408 [('U', 'noupdate', None, _('the clone will include an empty working '
1409 'directory (only a repository)')),
1409 'directory (only a repository)')),
1410 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1410 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1411 _('REV')),
1411 _('REV')),
1412 ('r', 'rev', [], _('do not clone everything, but include this changeset'
1412 ('r', 'rev', [], _('do not clone everything, but include this changeset'
1413 ' and its ancestors'), _('REV')),
1413 ' and its ancestors'), _('REV')),
1414 ('b', 'branch', [], _('do not clone everything, but include this branch\'s'
1414 ('b', 'branch', [], _('do not clone everything, but include this branch\'s'
1415 ' changesets and their ancestors'), _('BRANCH')),
1415 ' changesets and their ancestors'), _('BRANCH')),
1416 ('', 'pull', None, _('use pull protocol to copy metadata')),
1416 ('', 'pull', None, _('use pull protocol to copy metadata')),
1417 ('', 'uncompressed', None,
1417 ('', 'uncompressed', None,
1418 _('an alias to --stream (DEPRECATED)')),
1418 _('an alias to --stream (DEPRECATED)')),
1419 ('', 'stream', None,
1419 ('', 'stream', None,
1420 _('clone with minimal data processing')),
1420 _('clone with minimal data processing')),
1421 ] + remoteopts,
1421 ] + remoteopts,
1422 _('[OPTION]... SOURCE [DEST]'),
1422 _('[OPTION]... SOURCE [DEST]'),
1423 helpcategory=command.CATEGORY_REPO_CREATION,
1423 helpcategory=command.CATEGORY_REPO_CREATION,
1424 helpbasic=True, norepo=True)
1424 helpbasic=True, norepo=True)
1425 def clone(ui, source, dest=None, **opts):
1425 def clone(ui, source, dest=None, **opts):
1426 """make a copy of an existing repository
1426 """make a copy of an existing repository
1427
1427
1428 Create a copy of an existing repository in a new directory.
1428 Create a copy of an existing repository in a new directory.
1429
1429
1430 If no destination directory name is specified, it defaults to the
1430 If no destination directory name is specified, it defaults to the
1431 basename of the source.
1431 basename of the source.
1432
1432
1433 The location of the source is added to the new repository's
1433 The location of the source is added to the new repository's
1434 ``.hg/hgrc`` file, as the default to be used for future pulls.
1434 ``.hg/hgrc`` file, as the default to be used for future pulls.
1435
1435
1436 Only local paths and ``ssh://`` URLs are supported as
1436 Only local paths and ``ssh://`` URLs are supported as
1437 destinations. For ``ssh://`` destinations, no working directory or
1437 destinations. For ``ssh://`` destinations, no working directory or
1438 ``.hg/hgrc`` will be created on the remote side.
1438 ``.hg/hgrc`` will be created on the remote side.
1439
1439
1440 If the source repository has a bookmark called '@' set, that
1440 If the source repository has a bookmark called '@' set, that
1441 revision will be checked out in the new repository by default.
1441 revision will be checked out in the new repository by default.
1442
1442
1443 To check out a particular version, use -u/--update, or
1443 To check out a particular version, use -u/--update, or
1444 -U/--noupdate to create a clone with no working directory.
1444 -U/--noupdate to create a clone with no working directory.
1445
1445
1446 To pull only a subset of changesets, specify one or more revisions
1446 To pull only a subset of changesets, specify one or more revisions
1447 identifiers with -r/--rev or branches with -b/--branch. The
1447 identifiers with -r/--rev or branches with -b/--branch. The
1448 resulting clone will contain only the specified changesets and
1448 resulting clone will contain only the specified changesets and
1449 their ancestors. These options (or 'clone src#rev dest') imply
1449 their ancestors. These options (or 'clone src#rev dest') imply
1450 --pull, even for local source repositories.
1450 --pull, even for local source repositories.
1451
1451
1452 In normal clone mode, the remote normalizes repository data into a common
1452 In normal clone mode, the remote normalizes repository data into a common
1453 exchange format and the receiving end translates this data into its local
1453 exchange format and the receiving end translates this data into its local
1454 storage format. --stream activates a different clone mode that essentially
1454 storage format. --stream activates a different clone mode that essentially
1455 copies repository files from the remote with minimal data processing. This
1455 copies repository files from the remote with minimal data processing. This
1456 significantly reduces the CPU cost of a clone both remotely and locally.
1456 significantly reduces the CPU cost of a clone both remotely and locally.
1457 However, it often increases the transferred data size by 30-40%. This can
1457 However, it often increases the transferred data size by 30-40%. This can
1458 result in substantially faster clones where I/O throughput is plentiful,
1458 result in substantially faster clones where I/O throughput is plentiful,
1459 especially for larger repositories. A side-effect of --stream clones is
1459 especially for larger repositories. A side-effect of --stream clones is
1460 that storage settings and requirements on the remote are applied locally:
1460 that storage settings and requirements on the remote are applied locally:
1461 a modern client may inherit legacy or inefficient storage used by the
1461 a modern client may inherit legacy or inefficient storage used by the
1462 remote or a legacy Mercurial client may not be able to clone from a
1462 remote or a legacy Mercurial client may not be able to clone from a
1463 modern Mercurial remote.
1463 modern Mercurial remote.
1464
1464
1465 .. note::
1465 .. note::
1466
1466
1467 Specifying a tag will include the tagged changeset but not the
1467 Specifying a tag will include the tagged changeset but not the
1468 changeset containing the tag.
1468 changeset containing the tag.
1469
1469
1470 .. container:: verbose
1470 .. container:: verbose
1471
1471
1472 For efficiency, hardlinks are used for cloning whenever the
1472 For efficiency, hardlinks are used for cloning whenever the
1473 source and destination are on the same filesystem (note this
1473 source and destination are on the same filesystem (note this
1474 applies only to the repository data, not to the working
1474 applies only to the repository data, not to the working
1475 directory). Some filesystems, such as AFS, implement hardlinking
1475 directory). Some filesystems, such as AFS, implement hardlinking
1476 incorrectly, but do not report errors. In these cases, use the
1476 incorrectly, but do not report errors. In these cases, use the
1477 --pull option to avoid hardlinking.
1477 --pull option to avoid hardlinking.
1478
1478
1479 Mercurial will update the working directory to the first applicable
1479 Mercurial will update the working directory to the first applicable
1480 revision from this list:
1480 revision from this list:
1481
1481
1482 a) null if -U or the source repository has no changesets
1482 a) null if -U or the source repository has no changesets
1483 b) if -u . and the source repository is local, the first parent of
1483 b) if -u . and the source repository is local, the first parent of
1484 the source repository's working directory
1484 the source repository's working directory
1485 c) the changeset specified with -u (if a branch name, this means the
1485 c) the changeset specified with -u (if a branch name, this means the
1486 latest head of that branch)
1486 latest head of that branch)
1487 d) the changeset specified with -r
1487 d) the changeset specified with -r
1488 e) the tipmost head specified with -b
1488 e) the tipmost head specified with -b
1489 f) the tipmost head specified with the url#branch source syntax
1489 f) the tipmost head specified with the url#branch source syntax
1490 g) the revision marked with the '@' bookmark, if present
1490 g) the revision marked with the '@' bookmark, if present
1491 h) the tipmost head of the default branch
1491 h) the tipmost head of the default branch
1492 i) tip
1492 i) tip
1493
1493
1494 When cloning from servers that support it, Mercurial may fetch
1494 When cloning from servers that support it, Mercurial may fetch
1495 pre-generated data from a server-advertised URL or inline from the
1495 pre-generated data from a server-advertised URL or inline from the
1496 same stream. When this is done, hooks operating on incoming changesets
1496 same stream. When this is done, hooks operating on incoming changesets
1497 and changegroups may fire more than once, once for each pre-generated
1497 and changegroups may fire more than once, once for each pre-generated
1498 bundle and as well as for any additional remaining data. In addition,
1498 bundle and as well as for any additional remaining data. In addition,
1499 if an error occurs, the repository may be rolled back to a partial
1499 if an error occurs, the repository may be rolled back to a partial
1500 clone. This behavior may change in future releases.
1500 clone. This behavior may change in future releases.
1501 See :hg:`help -e clonebundles` for more.
1501 See :hg:`help -e clonebundles` for more.
1502
1502
1503 Examples:
1503 Examples:
1504
1504
1505 - clone a remote repository to a new directory named hg/::
1505 - clone a remote repository to a new directory named hg/::
1506
1506
1507 hg clone https://www.mercurial-scm.org/repo/hg/
1507 hg clone https://www.mercurial-scm.org/repo/hg/
1508
1508
1509 - create a lightweight local clone::
1509 - create a lightweight local clone::
1510
1510
1511 hg clone project/ project-feature/
1511 hg clone project/ project-feature/
1512
1512
1513 - clone from an absolute path on an ssh server (note double-slash)::
1513 - clone from an absolute path on an ssh server (note double-slash)::
1514
1514
1515 hg clone ssh://user@server//home/projects/alpha/
1515 hg clone ssh://user@server//home/projects/alpha/
1516
1516
1517 - do a streaming clone while checking out a specified version::
1517 - do a streaming clone while checking out a specified version::
1518
1518
1519 hg clone --stream http://server/repo -u 1.5
1519 hg clone --stream http://server/repo -u 1.5
1520
1520
1521 - create a repository without changesets after a particular revision::
1521 - create a repository without changesets after a particular revision::
1522
1522
1523 hg clone -r 04e544 experimental/ good/
1523 hg clone -r 04e544 experimental/ good/
1524
1524
1525 - clone (and track) a particular named branch::
1525 - clone (and track) a particular named branch::
1526
1526
1527 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1527 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1528
1528
1529 See :hg:`help urls` for details on specifying URLs.
1529 See :hg:`help urls` for details on specifying URLs.
1530
1530
1531 Returns 0 on success.
1531 Returns 0 on success.
1532 """
1532 """
1533 opts = pycompat.byteskwargs(opts)
1533 opts = pycompat.byteskwargs(opts)
1534 if opts.get('noupdate') and opts.get('updaterev'):
1534 if opts.get('noupdate') and opts.get('updaterev'):
1535 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1535 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1536
1536
1537 # --include/--exclude can come from narrow or sparse.
1537 # --include/--exclude can come from narrow or sparse.
1538 includepats, excludepats = None, None
1538 includepats, excludepats = None, None
1539
1539
1540 # hg.clone() differentiates between None and an empty set. So make sure
1540 # hg.clone() differentiates between None and an empty set. So make sure
1541 # patterns are sets if narrow is requested without patterns.
1541 # patterns are sets if narrow is requested without patterns.
1542 if opts.get('narrow'):
1542 if opts.get('narrow'):
1543 includepats = set()
1543 includepats = set()
1544 excludepats = set()
1544 excludepats = set()
1545
1545
1546 if opts.get('include'):
1546 if opts.get('include'):
1547 includepats = narrowspec.parsepatterns(opts.get('include'))
1547 includepats = narrowspec.parsepatterns(opts.get('include'))
1548 if opts.get('exclude'):
1548 if opts.get('exclude'):
1549 excludepats = narrowspec.parsepatterns(opts.get('exclude'))
1549 excludepats = narrowspec.parsepatterns(opts.get('exclude'))
1550
1550
1551 r = hg.clone(ui, opts, source, dest,
1551 r = hg.clone(ui, opts, source, dest,
1552 pull=opts.get('pull'),
1552 pull=opts.get('pull'),
1553 stream=opts.get('stream') or opts.get('uncompressed'),
1553 stream=opts.get('stream') or opts.get('uncompressed'),
1554 revs=opts.get('rev'),
1554 revs=opts.get('rev'),
1555 update=opts.get('updaterev') or not opts.get('noupdate'),
1555 update=opts.get('updaterev') or not opts.get('noupdate'),
1556 branch=opts.get('branch'),
1556 branch=opts.get('branch'),
1557 shareopts=opts.get('shareopts'),
1557 shareopts=opts.get('shareopts'),
1558 storeincludepats=includepats,
1558 storeincludepats=includepats,
1559 storeexcludepats=excludepats,
1559 storeexcludepats=excludepats,
1560 depth=opts.get('depth') or None)
1560 depth=opts.get('depth') or None)
1561
1561
1562 return r is None
1562 return r is None
1563
1563
1564 @command('commit|ci',
1564 @command('commit|ci',
1565 [('A', 'addremove', None,
1565 [('A', 'addremove', None,
1566 _('mark new/missing files as added/removed before committing')),
1566 _('mark new/missing files as added/removed before committing')),
1567 ('', 'close-branch', None,
1567 ('', 'close-branch', None,
1568 _('mark a branch head as closed')),
1568 _('mark a branch head as closed')),
1569 ('', 'amend', None, _('amend the parent of the working directory')),
1569 ('', 'amend', None, _('amend the parent of the working directory')),
1570 ('s', 'secret', None, _('use the secret phase for committing')),
1570 ('s', 'secret', None, _('use the secret phase for committing')),
1571 ('e', 'edit', None, _('invoke editor on commit messages')),
1571 ('e', 'edit', None, _('invoke editor on commit messages')),
1572 ('i', 'interactive', None, _('use interactive mode')),
1572 ('i', 'interactive', None, _('use interactive mode')),
1573 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1573 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1574 _('[OPTION]... [FILE]...'),
1574 _('[OPTION]... [FILE]...'),
1575 helpcategory=command.CATEGORY_COMMITTING, helpbasic=True,
1575 helpcategory=command.CATEGORY_COMMITTING, helpbasic=True,
1576 inferrepo=True)
1576 inferrepo=True)
1577 def commit(ui, repo, *pats, **opts):
1577 def commit(ui, repo, *pats, **opts):
1578 """commit the specified files or all outstanding changes
1578 """commit the specified files or all outstanding changes
1579
1579
1580 Commit changes to the given files into the repository. Unlike a
1580 Commit changes to the given files into the repository. Unlike a
1581 centralized SCM, this operation is a local operation. See
1581 centralized SCM, this operation is a local operation. See
1582 :hg:`push` for a way to actively distribute your changes.
1582 :hg:`push` for a way to actively distribute your changes.
1583
1583
1584 If a list of files is omitted, all changes reported by :hg:`status`
1584 If a list of files is omitted, all changes reported by :hg:`status`
1585 will be committed.
1585 will be committed.
1586
1586
1587 If you are committing the result of a merge, do not provide any
1587 If you are committing the result of a merge, do not provide any
1588 filenames or -I/-X filters.
1588 filenames or -I/-X filters.
1589
1589
1590 If no commit message is specified, Mercurial starts your
1590 If no commit message is specified, Mercurial starts your
1591 configured editor where you can enter a message. In case your
1591 configured editor where you can enter a message. In case your
1592 commit fails, you will find a backup of your message in
1592 commit fails, you will find a backup of your message in
1593 ``.hg/last-message.txt``.
1593 ``.hg/last-message.txt``.
1594
1594
1595 The --close-branch flag can be used to mark the current branch
1595 The --close-branch flag can be used to mark the current branch
1596 head closed. When all heads of a branch are closed, the branch
1596 head closed. When all heads of a branch are closed, the branch
1597 will be considered closed and no longer listed.
1597 will be considered closed and no longer listed.
1598
1598
1599 The --amend flag can be used to amend the parent of the
1599 The --amend flag can be used to amend the parent of the
1600 working directory with a new commit that contains the changes
1600 working directory with a new commit that contains the changes
1601 in the parent in addition to those currently reported by :hg:`status`,
1601 in the parent in addition to those currently reported by :hg:`status`,
1602 if there are any. The old commit is stored in a backup bundle in
1602 if there are any. The old commit is stored in a backup bundle in
1603 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1603 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1604 on how to restore it).
1604 on how to restore it).
1605
1605
1606 Message, user and date are taken from the amended commit unless
1606 Message, user and date are taken from the amended commit unless
1607 specified. When a message isn't specified on the command line,
1607 specified. When a message isn't specified on the command line,
1608 the editor will open with the message of the amended commit.
1608 the editor will open with the message of the amended commit.
1609
1609
1610 It is not possible to amend public changesets (see :hg:`help phases`)
1610 It is not possible to amend public changesets (see :hg:`help phases`)
1611 or changesets that have children.
1611 or changesets that have children.
1612
1612
1613 See :hg:`help dates` for a list of formats valid for -d/--date.
1613 See :hg:`help dates` for a list of formats valid for -d/--date.
1614
1614
1615 Returns 0 on success, 1 if nothing changed.
1615 Returns 0 on success, 1 if nothing changed.
1616
1616
1617 .. container:: verbose
1617 .. container:: verbose
1618
1618
1619 Examples:
1619 Examples:
1620
1620
1621 - commit all files ending in .py::
1621 - commit all files ending in .py::
1622
1622
1623 hg commit --include "set:**.py"
1623 hg commit --include "set:**.py"
1624
1624
1625 - commit all non-binary files::
1625 - commit all non-binary files::
1626
1626
1627 hg commit --exclude "set:binary()"
1627 hg commit --exclude "set:binary()"
1628
1628
1629 - amend the current commit and set the date to now::
1629 - amend the current commit and set the date to now::
1630
1630
1631 hg commit --amend --date now
1631 hg commit --amend --date now
1632 """
1632 """
1633 with repo.wlock(), repo.lock():
1633 with repo.wlock(), repo.lock():
1634 return _docommit(ui, repo, *pats, **opts)
1634 return _docommit(ui, repo, *pats, **opts)
1635
1635
1636 def _docommit(ui, repo, *pats, **opts):
1636 def _docommit(ui, repo, *pats, **opts):
1637 if opts.get(r'interactive'):
1637 if opts.get(r'interactive'):
1638 opts.pop(r'interactive')
1638 opts.pop(r'interactive')
1639 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1639 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1640 cmdutil.recordfilter, *pats,
1640 cmdutil.recordfilter, *pats,
1641 **opts)
1641 **opts)
1642 # ret can be 0 (no changes to record) or the value returned by
1642 # ret can be 0 (no changes to record) or the value returned by
1643 # commit(), 1 if nothing changed or None on success.
1643 # commit(), 1 if nothing changed or None on success.
1644 return 1 if ret == 0 else ret
1644 return 1 if ret == 0 else ret
1645
1645
1646 opts = pycompat.byteskwargs(opts)
1646 opts = pycompat.byteskwargs(opts)
1647 if opts.get('subrepos'):
1647 if opts.get('subrepos'):
1648 if opts.get('amend'):
1648 if opts.get('amend'):
1649 raise error.Abort(_('cannot amend with --subrepos'))
1649 raise error.Abort(_('cannot amend with --subrepos'))
1650 # Let --subrepos on the command line override config setting.
1650 # Let --subrepos on the command line override config setting.
1651 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1651 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1652
1652
1653 cmdutil.checkunfinished(repo, commit=True)
1653 cmdutil.checkunfinished(repo, commit=True)
1654
1654
1655 branch = repo[None].branch()
1655 branch = repo[None].branch()
1656 bheads = repo.branchheads(branch)
1656 bheads = repo.branchheads(branch)
1657
1657
1658 extra = {}
1658 extra = {}
1659 if opts.get('close_branch'):
1659 if opts.get('close_branch'):
1660 extra['close'] = '1'
1660 extra['close'] = '1'
1661
1661
1662 if not bheads:
1662 if not bheads:
1663 raise error.Abort(_('can only close branch heads'))
1663 raise error.Abort(_('can only close branch heads'))
1664 elif opts.get('amend'):
1664 elif opts.get('amend'):
1665 if repo[None].parents()[0].p1().branch() != branch and \
1665 if repo[None].parents()[0].p1().branch() != branch and \
1666 repo[None].parents()[0].p2().branch() != branch:
1666 repo[None].parents()[0].p2().branch() != branch:
1667 raise error.Abort(_('can only close branch heads'))
1667 raise error.Abort(_('can only close branch heads'))
1668
1668
1669 if opts.get('amend'):
1669 if opts.get('amend'):
1670 if ui.configbool('ui', 'commitsubrepos'):
1670 if ui.configbool('ui', 'commitsubrepos'):
1671 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1671 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1672
1672
1673 old = repo['.']
1673 old = repo['.']
1674 rewriteutil.precheck(repo, [old.rev()], 'amend')
1674 rewriteutil.precheck(repo, [old.rev()], 'amend')
1675
1675
1676 # Currently histedit gets confused if an amend happens while histedit
1676 # Currently histedit gets confused if an amend happens while histedit
1677 # is in progress. Since we have a checkunfinished command, we are
1677 # is in progress. Since we have a checkunfinished command, we are
1678 # temporarily honoring it.
1678 # temporarily honoring it.
1679 #
1679 #
1680 # Note: eventually this guard will be removed. Please do not expect
1680 # Note: eventually this guard will be removed. Please do not expect
1681 # this behavior to remain.
1681 # this behavior to remain.
1682 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1682 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1683 cmdutil.checkunfinished(repo)
1683 cmdutil.checkunfinished(repo)
1684
1684
1685 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
1685 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
1686 if node == old.node():
1686 if node == old.node():
1687 ui.status(_("nothing changed\n"))
1687 ui.status(_("nothing changed\n"))
1688 return 1
1688 return 1
1689 else:
1689 else:
1690 def commitfunc(ui, repo, message, match, opts):
1690 def commitfunc(ui, repo, message, match, opts):
1691 overrides = {}
1691 overrides = {}
1692 if opts.get('secret'):
1692 if opts.get('secret'):
1693 overrides[('phases', 'new-commit')] = 'secret'
1693 overrides[('phases', 'new-commit')] = 'secret'
1694
1694
1695 baseui = repo.baseui
1695 baseui = repo.baseui
1696 with baseui.configoverride(overrides, 'commit'):
1696 with baseui.configoverride(overrides, 'commit'):
1697 with ui.configoverride(overrides, 'commit'):
1697 with ui.configoverride(overrides, 'commit'):
1698 editform = cmdutil.mergeeditform(repo[None],
1698 editform = cmdutil.mergeeditform(repo[None],
1699 'commit.normal')
1699 'commit.normal')
1700 editor = cmdutil.getcommiteditor(
1700 editor = cmdutil.getcommiteditor(
1701 editform=editform, **pycompat.strkwargs(opts))
1701 editform=editform, **pycompat.strkwargs(opts))
1702 return repo.commit(message,
1702 return repo.commit(message,
1703 opts.get('user'),
1703 opts.get('user'),
1704 opts.get('date'),
1704 opts.get('date'),
1705 match,
1705 match,
1706 editor=editor,
1706 editor=editor,
1707 extra=extra)
1707 extra=extra)
1708
1708
1709 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1709 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1710
1710
1711 if not node:
1711 if not node:
1712 stat = cmdutil.postcommitstatus(repo, pats, opts)
1712 stat = cmdutil.postcommitstatus(repo, pats, opts)
1713 if stat[3]:
1713 if stat[3]:
1714 ui.status(_("nothing changed (%d missing files, see "
1714 ui.status(_("nothing changed (%d missing files, see "
1715 "'hg status')\n") % len(stat[3]))
1715 "'hg status')\n") % len(stat[3]))
1716 else:
1716 else:
1717 ui.status(_("nothing changed\n"))
1717 ui.status(_("nothing changed\n"))
1718 return 1
1718 return 1
1719
1719
1720 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1720 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1721
1721
1722 @command('config|showconfig|debugconfig',
1722 @command('config|showconfig|debugconfig',
1723 [('u', 'untrusted', None, _('show untrusted configuration options')),
1723 [('u', 'untrusted', None, _('show untrusted configuration options')),
1724 ('e', 'edit', None, _('edit user config')),
1724 ('e', 'edit', None, _('edit user config')),
1725 ('l', 'local', None, _('edit repository config')),
1725 ('l', 'local', None, _('edit repository config')),
1726 ('g', 'global', None, _('edit global config'))] + formatteropts,
1726 ('g', 'global', None, _('edit global config'))] + formatteropts,
1727 _('[-u] [NAME]...'),
1727 _('[-u] [NAME]...'),
1728 helpcategory=command.CATEGORY_HELP,
1728 helpcategory=command.CATEGORY_HELP,
1729 optionalrepo=True,
1729 optionalrepo=True,
1730 intents={INTENT_READONLY})
1730 intents={INTENT_READONLY})
1731 def config(ui, repo, *values, **opts):
1731 def config(ui, repo, *values, **opts):
1732 """show combined config settings from all hgrc files
1732 """show combined config settings from all hgrc files
1733
1733
1734 With no arguments, print names and values of all config items.
1734 With no arguments, print names and values of all config items.
1735
1735
1736 With one argument of the form section.name, print just the value
1736 With one argument of the form section.name, print just the value
1737 of that config item.
1737 of that config item.
1738
1738
1739 With multiple arguments, print names and values of all config
1739 With multiple arguments, print names and values of all config
1740 items with matching section names or section.names.
1740 items with matching section names or section.names.
1741
1741
1742 With --edit, start an editor on the user-level config file. With
1742 With --edit, start an editor on the user-level config file. With
1743 --global, edit the system-wide config file. With --local, edit the
1743 --global, edit the system-wide config file. With --local, edit the
1744 repository-level config file.
1744 repository-level config file.
1745
1745
1746 With --debug, the source (filename and line number) is printed
1746 With --debug, the source (filename and line number) is printed
1747 for each config item.
1747 for each config item.
1748
1748
1749 See :hg:`help config` for more information about config files.
1749 See :hg:`help config` for more information about config files.
1750
1750
1751 .. container:: verbose
1751 .. container:: verbose
1752
1752
1753 Template:
1753 Template:
1754
1754
1755 The following keywords are supported. See also :hg:`help templates`.
1755 The following keywords are supported. See also :hg:`help templates`.
1756
1756
1757 :name: String. Config name.
1757 :name: String. Config name.
1758 :source: String. Filename and line number where the item is defined.
1758 :source: String. Filename and line number where the item is defined.
1759 :value: String. Config value.
1759 :value: String. Config value.
1760
1760
1761 Returns 0 on success, 1 if NAME does not exist.
1761 Returns 0 on success, 1 if NAME does not exist.
1762
1762
1763 """
1763 """
1764
1764
1765 opts = pycompat.byteskwargs(opts)
1765 opts = pycompat.byteskwargs(opts)
1766 if opts.get('edit') or opts.get('local') or opts.get('global'):
1766 if opts.get('edit') or opts.get('local') or opts.get('global'):
1767 if opts.get('local') and opts.get('global'):
1767 if opts.get('local') and opts.get('global'):
1768 raise error.Abort(_("can't use --local and --global together"))
1768 raise error.Abort(_("can't use --local and --global together"))
1769
1769
1770 if opts.get('local'):
1770 if opts.get('local'):
1771 if not repo:
1771 if not repo:
1772 raise error.Abort(_("can't use --local outside a repository"))
1772 raise error.Abort(_("can't use --local outside a repository"))
1773 paths = [repo.vfs.join('hgrc')]
1773 paths = [repo.vfs.join('hgrc')]
1774 elif opts.get('global'):
1774 elif opts.get('global'):
1775 paths = rcutil.systemrcpath()
1775 paths = rcutil.systemrcpath()
1776 else:
1776 else:
1777 paths = rcutil.userrcpath()
1777 paths = rcutil.userrcpath()
1778
1778
1779 for f in paths:
1779 for f in paths:
1780 if os.path.exists(f):
1780 if os.path.exists(f):
1781 break
1781 break
1782 else:
1782 else:
1783 if opts.get('global'):
1783 if opts.get('global'):
1784 samplehgrc = uimod.samplehgrcs['global']
1784 samplehgrc = uimod.samplehgrcs['global']
1785 elif opts.get('local'):
1785 elif opts.get('local'):
1786 samplehgrc = uimod.samplehgrcs['local']
1786 samplehgrc = uimod.samplehgrcs['local']
1787 else:
1787 else:
1788 samplehgrc = uimod.samplehgrcs['user']
1788 samplehgrc = uimod.samplehgrcs['user']
1789
1789
1790 f = paths[0]
1790 f = paths[0]
1791 fp = open(f, "wb")
1791 fp = open(f, "wb")
1792 fp.write(util.tonativeeol(samplehgrc))
1792 fp.write(util.tonativeeol(samplehgrc))
1793 fp.close()
1793 fp.close()
1794
1794
1795 editor = ui.geteditor()
1795 editor = ui.geteditor()
1796 ui.system("%s \"%s\"" % (editor, f),
1796 ui.system("%s \"%s\"" % (editor, f),
1797 onerr=error.Abort, errprefix=_("edit failed"),
1797 onerr=error.Abort, errprefix=_("edit failed"),
1798 blockedtag='config_edit')
1798 blockedtag='config_edit')
1799 return
1799 return
1800 ui.pager('config')
1800 ui.pager('config')
1801 fm = ui.formatter('config', opts)
1801 fm = ui.formatter('config', opts)
1802 for t, f in rcutil.rccomponents():
1802 for t, f in rcutil.rccomponents():
1803 if t == 'path':
1803 if t == 'path':
1804 ui.debug('read config from: %s\n' % f)
1804 ui.debug('read config from: %s\n' % f)
1805 elif t == 'items':
1805 elif t == 'items':
1806 for section, name, value, source in f:
1806 for section, name, value, source in f:
1807 ui.debug('set config by: %s\n' % source)
1807 ui.debug('set config by: %s\n' % source)
1808 else:
1808 else:
1809 raise error.ProgrammingError('unknown rctype: %s' % t)
1809 raise error.ProgrammingError('unknown rctype: %s' % t)
1810 untrusted = bool(opts.get('untrusted'))
1810 untrusted = bool(opts.get('untrusted'))
1811
1811
1812 selsections = selentries = []
1812 selsections = selentries = []
1813 if values:
1813 if values:
1814 selsections = [v for v in values if '.' not in v]
1814 selsections = [v for v in values if '.' not in v]
1815 selentries = [v for v in values if '.' in v]
1815 selentries = [v for v in values if '.' in v]
1816 uniquesel = (len(selentries) == 1 and not selsections)
1816 uniquesel = (len(selentries) == 1 and not selsections)
1817 selsections = set(selsections)
1817 selsections = set(selsections)
1818 selentries = set(selentries)
1818 selentries = set(selentries)
1819
1819
1820 matched = False
1820 matched = False
1821 for section, name, value in ui.walkconfig(untrusted=untrusted):
1821 for section, name, value in ui.walkconfig(untrusted=untrusted):
1822 source = ui.configsource(section, name, untrusted)
1822 source = ui.configsource(section, name, untrusted)
1823 value = pycompat.bytestr(value)
1823 value = pycompat.bytestr(value)
1824 if fm.isplain():
1824 if fm.isplain():
1825 source = source or 'none'
1825 source = source or 'none'
1826 value = value.replace('\n', '\\n')
1826 value = value.replace('\n', '\\n')
1827 entryname = section + '.' + name
1827 entryname = section + '.' + name
1828 if values and not (section in selsections or entryname in selentries):
1828 if values and not (section in selsections or entryname in selentries):
1829 continue
1829 continue
1830 fm.startitem()
1830 fm.startitem()
1831 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1831 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1832 if uniquesel:
1832 if uniquesel:
1833 fm.data(name=entryname)
1833 fm.data(name=entryname)
1834 fm.write('value', '%s\n', value)
1834 fm.write('value', '%s\n', value)
1835 else:
1835 else:
1836 fm.write('name value', '%s=%s\n', entryname, value)
1836 fm.write('name value', '%s=%s\n', entryname, value)
1837 matched = True
1837 matched = True
1838 fm.end()
1838 fm.end()
1839 if matched:
1839 if matched:
1840 return 0
1840 return 0
1841 return 1
1841 return 1
1842
1842
1843 @command('copy|cp',
1843 @command('copy|cp',
1844 [('A', 'after', None, _('record a copy that has already occurred')),
1844 [('A', 'after', None, _('record a copy that has already occurred')),
1845 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1845 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1846 ] + walkopts + dryrunopts,
1846 ] + walkopts + dryrunopts,
1847 _('[OPTION]... [SOURCE]... DEST'),
1847 _('[OPTION]... [SOURCE]... DEST'),
1848 helpcategory=command.CATEGORY_FILE_CONTENTS)
1848 helpcategory=command.CATEGORY_FILE_CONTENTS)
1849 def copy(ui, repo, *pats, **opts):
1849 def copy(ui, repo, *pats, **opts):
1850 """mark files as copied for the next commit
1850 """mark files as copied for the next commit
1851
1851
1852 Mark dest as having copies of source files. If dest is a
1852 Mark dest as having copies of source files. If dest is a
1853 directory, copies are put in that directory. If dest is a file,
1853 directory, copies are put in that directory. If dest is a file,
1854 the source must be a single file.
1854 the source must be a single file.
1855
1855
1856 By default, this command copies the contents of files as they
1856 By default, this command copies the contents of files as they
1857 exist in the working directory. If invoked with -A/--after, the
1857 exist in the working directory. If invoked with -A/--after, the
1858 operation is recorded, but no copying is performed.
1858 operation is recorded, but no copying is performed.
1859
1859
1860 This command takes effect with the next commit. To undo a copy
1860 This command takes effect with the next commit. To undo a copy
1861 before that, see :hg:`revert`.
1861 before that, see :hg:`revert`.
1862
1862
1863 Returns 0 on success, 1 if errors are encountered.
1863 Returns 0 on success, 1 if errors are encountered.
1864 """
1864 """
1865 opts = pycompat.byteskwargs(opts)
1865 opts = pycompat.byteskwargs(opts)
1866 with repo.wlock(False):
1866 with repo.wlock(False):
1867 return cmdutil.copy(ui, repo, pats, opts)
1867 return cmdutil.copy(ui, repo, pats, opts)
1868
1868
1869 @command(
1869 @command(
1870 'debugcommands', [], _('[COMMAND]'),
1870 'debugcommands', [], _('[COMMAND]'),
1871 helpcategory=command.CATEGORY_HELP,
1871 helpcategory=command.CATEGORY_HELP,
1872 norepo=True)
1872 norepo=True)
1873 def debugcommands(ui, cmd='', *args):
1873 def debugcommands(ui, cmd='', *args):
1874 """list all available commands and options"""
1874 """list all available commands and options"""
1875 for cmd, vals in sorted(table.iteritems()):
1875 for cmd, vals in sorted(table.iteritems()):
1876 cmd = cmd.split('|')[0]
1876 cmd = cmd.split('|')[0]
1877 opts = ', '.join([i[1] for i in vals[1]])
1877 opts = ', '.join([i[1] for i in vals[1]])
1878 ui.write('%s: %s\n' % (cmd, opts))
1878 ui.write('%s: %s\n' % (cmd, opts))
1879
1879
1880 @command('debugcomplete',
1880 @command('debugcomplete',
1881 [('o', 'options', None, _('show the command options'))],
1881 [('o', 'options', None, _('show the command options'))],
1882 _('[-o] CMD'),
1882 _('[-o] CMD'),
1883 helpcategory=command.CATEGORY_HELP,
1883 helpcategory=command.CATEGORY_HELP,
1884 norepo=True)
1884 norepo=True)
1885 def debugcomplete(ui, cmd='', **opts):
1885 def debugcomplete(ui, cmd='', **opts):
1886 """returns the completion list associated with the given command"""
1886 """returns the completion list associated with the given command"""
1887
1887
1888 if opts.get(r'options'):
1888 if opts.get(r'options'):
1889 options = []
1889 options = []
1890 otables = [globalopts]
1890 otables = [globalopts]
1891 if cmd:
1891 if cmd:
1892 aliases, entry = cmdutil.findcmd(cmd, table, False)
1892 aliases, entry = cmdutil.findcmd(cmd, table, False)
1893 otables.append(entry[1])
1893 otables.append(entry[1])
1894 for t in otables:
1894 for t in otables:
1895 for o in t:
1895 for o in t:
1896 if "(DEPRECATED)" in o[3]:
1896 if "(DEPRECATED)" in o[3]:
1897 continue
1897 continue
1898 if o[0]:
1898 if o[0]:
1899 options.append('-%s' % o[0])
1899 options.append('-%s' % o[0])
1900 options.append('--%s' % o[1])
1900 options.append('--%s' % o[1])
1901 ui.write("%s\n" % "\n".join(options))
1901 ui.write("%s\n" % "\n".join(options))
1902 return
1902 return
1903
1903
1904 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1904 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1905 if ui.verbose:
1905 if ui.verbose:
1906 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1906 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1907 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1907 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1908
1908
1909 @command('diff',
1909 @command('diff',
1910 [('r', 'rev', [], _('revision'), _('REV')),
1910 [('r', 'rev', [], _('revision'), _('REV')),
1911 ('c', 'change', '', _('change made by revision'), _('REV'))
1911 ('c', 'change', '', _('change made by revision'), _('REV'))
1912 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1912 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1913 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1913 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1914 helpcategory=command.CATEGORY_FILE_CONTENTS,
1914 helpcategory=command.CATEGORY_FILE_CONTENTS,
1915 helpbasic=True, inferrepo=True, intents={INTENT_READONLY})
1915 helpbasic=True, inferrepo=True, intents={INTENT_READONLY})
1916 def diff(ui, repo, *pats, **opts):
1916 def diff(ui, repo, *pats, **opts):
1917 """diff repository (or selected files)
1917 """diff repository (or selected files)
1918
1918
1919 Show differences between revisions for the specified files.
1919 Show differences between revisions for the specified files.
1920
1920
1921 Differences between files are shown using the unified diff format.
1921 Differences between files are shown using the unified diff format.
1922
1922
1923 .. note::
1923 .. note::
1924
1924
1925 :hg:`diff` may generate unexpected results for merges, as it will
1925 :hg:`diff` may generate unexpected results for merges, as it will
1926 default to comparing against the working directory's first
1926 default to comparing against the working directory's first
1927 parent changeset if no revisions are specified.
1927 parent changeset if no revisions are specified.
1928
1928
1929 When two revision arguments are given, then changes are shown
1929 When two revision arguments are given, then changes are shown
1930 between those revisions. If only one revision is specified then
1930 between those revisions. If only one revision is specified then
1931 that revision is compared to the working directory, and, when no
1931 that revision is compared to the working directory, and, when no
1932 revisions are specified, the working directory files are compared
1932 revisions are specified, the working directory files are compared
1933 to its first parent.
1933 to its first parent.
1934
1934
1935 Alternatively you can specify -c/--change with a revision to see
1935 Alternatively you can specify -c/--change with a revision to see
1936 the changes in that changeset relative to its first parent.
1936 the changes in that changeset relative to its first parent.
1937
1937
1938 Without the -a/--text option, diff will avoid generating diffs of
1938 Without the -a/--text option, diff will avoid generating diffs of
1939 files it detects as binary. With -a, diff will generate a diff
1939 files it detects as binary. With -a, diff will generate a diff
1940 anyway, probably with undesirable results.
1940 anyway, probably with undesirable results.
1941
1941
1942 Use the -g/--git option to generate diffs in the git extended diff
1942 Use the -g/--git option to generate diffs in the git extended diff
1943 format. For more information, read :hg:`help diffs`.
1943 format. For more information, read :hg:`help diffs`.
1944
1944
1945 .. container:: verbose
1945 .. container:: verbose
1946
1946
1947 Examples:
1947 Examples:
1948
1948
1949 - compare a file in the current working directory to its parent::
1949 - compare a file in the current working directory to its parent::
1950
1950
1951 hg diff foo.c
1951 hg diff foo.c
1952
1952
1953 - compare two historical versions of a directory, with rename info::
1953 - compare two historical versions of a directory, with rename info::
1954
1954
1955 hg diff --git -r 1.0:1.2 lib/
1955 hg diff --git -r 1.0:1.2 lib/
1956
1956
1957 - get change stats relative to the last change on some date::
1957 - get change stats relative to the last change on some date::
1958
1958
1959 hg diff --stat -r "date('may 2')"
1959 hg diff --stat -r "date('may 2')"
1960
1960
1961 - diff all newly-added files that contain a keyword::
1961 - diff all newly-added files that contain a keyword::
1962
1962
1963 hg diff "set:added() and grep(GNU)"
1963 hg diff "set:added() and grep(GNU)"
1964
1964
1965 - compare a revision and its parents::
1965 - compare a revision and its parents::
1966
1966
1967 hg diff -c 9353 # compare against first parent
1967 hg diff -c 9353 # compare against first parent
1968 hg diff -r 9353^:9353 # same using revset syntax
1968 hg diff -r 9353^:9353 # same using revset syntax
1969 hg diff -r 9353^2:9353 # compare against the second parent
1969 hg diff -r 9353^2:9353 # compare against the second parent
1970
1970
1971 Returns 0 on success.
1971 Returns 0 on success.
1972 """
1972 """
1973
1973
1974 opts = pycompat.byteskwargs(opts)
1974 opts = pycompat.byteskwargs(opts)
1975 revs = opts.get('rev')
1975 revs = opts.get('rev')
1976 change = opts.get('change')
1976 change = opts.get('change')
1977 stat = opts.get('stat')
1977 stat = opts.get('stat')
1978 reverse = opts.get('reverse')
1978 reverse = opts.get('reverse')
1979
1979
1980 if revs and change:
1980 if revs and change:
1981 msg = _('cannot specify --rev and --change at the same time')
1981 msg = _('cannot specify --rev and --change at the same time')
1982 raise error.Abort(msg)
1982 raise error.Abort(msg)
1983 elif change:
1983 elif change:
1984 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
1984 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
1985 ctx2 = scmutil.revsingle(repo, change, None)
1985 ctx2 = scmutil.revsingle(repo, change, None)
1986 ctx1 = ctx2.p1()
1986 ctx1 = ctx2.p1()
1987 else:
1987 else:
1988 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
1988 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
1989 ctx1, ctx2 = scmutil.revpair(repo, revs)
1989 ctx1, ctx2 = scmutil.revpair(repo, revs)
1990 node1, node2 = ctx1.node(), ctx2.node()
1990 node1, node2 = ctx1.node(), ctx2.node()
1991
1991
1992 if reverse:
1992 if reverse:
1993 node1, node2 = node2, node1
1993 node1, node2 = node2, node1
1994
1994
1995 diffopts = patch.diffallopts(ui, opts)
1995 diffopts = patch.diffallopts(ui, opts)
1996 m = scmutil.match(ctx2, pats, opts)
1996 m = scmutil.match(ctx2, pats, opts)
1997 m = repo.narrowmatch(m)
1997 m = repo.narrowmatch(m)
1998 ui.pager('diff')
1998 ui.pager('diff')
1999 logcmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1999 logcmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2000 listsubrepos=opts.get('subrepos'),
2000 listsubrepos=opts.get('subrepos'),
2001 root=opts.get('root'))
2001 root=opts.get('root'))
2002
2002
2003 @command('export',
2003 @command('export',
2004 [('B', 'bookmark', '',
2004 [('B', 'bookmark', '',
2005 _('export changes only reachable by given bookmark'), _('BOOKMARK')),
2005 _('export changes only reachable by given bookmark'), _('BOOKMARK')),
2006 ('o', 'output', '',
2006 ('o', 'output', '',
2007 _('print output to file with formatted name'), _('FORMAT')),
2007 _('print output to file with formatted name'), _('FORMAT')),
2008 ('', 'switch-parent', None, _('diff against the second parent')),
2008 ('', 'switch-parent', None, _('diff against the second parent')),
2009 ('r', 'rev', [], _('revisions to export'), _('REV')),
2009 ('r', 'rev', [], _('revisions to export'), _('REV')),
2010 ] + diffopts + formatteropts,
2010 ] + diffopts + formatteropts,
2011 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2011 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2012 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2012 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2013 helpbasic=True, intents={INTENT_READONLY})
2013 helpbasic=True, intents={INTENT_READONLY})
2014 def export(ui, repo, *changesets, **opts):
2014 def export(ui, repo, *changesets, **opts):
2015 """dump the header and diffs for one or more changesets
2015 """dump the header and diffs for one or more changesets
2016
2016
2017 Print the changeset header and diffs for one or more revisions.
2017 Print the changeset header and diffs for one or more revisions.
2018 If no revision is given, the parent of the working directory is used.
2018 If no revision is given, the parent of the working directory is used.
2019
2019
2020 The information shown in the changeset header is: author, date,
2020 The information shown in the changeset header is: author, date,
2021 branch name (if non-default), changeset hash, parent(s) and commit
2021 branch name (if non-default), changeset hash, parent(s) and commit
2022 comment.
2022 comment.
2023
2023
2024 .. note::
2024 .. note::
2025
2025
2026 :hg:`export` may generate unexpected diff output for merge
2026 :hg:`export` may generate unexpected diff output for merge
2027 changesets, as it will compare the merge changeset against its
2027 changesets, as it will compare the merge changeset against its
2028 first parent only.
2028 first parent only.
2029
2029
2030 Output may be to a file, in which case the name of the file is
2030 Output may be to a file, in which case the name of the file is
2031 given using a template string. See :hg:`help templates`. In addition
2031 given using a template string. See :hg:`help templates`. In addition
2032 to the common template keywords, the following formatting rules are
2032 to the common template keywords, the following formatting rules are
2033 supported:
2033 supported:
2034
2034
2035 :``%%``: literal "%" character
2035 :``%%``: literal "%" character
2036 :``%H``: changeset hash (40 hexadecimal digits)
2036 :``%H``: changeset hash (40 hexadecimal digits)
2037 :``%N``: number of patches being generated
2037 :``%N``: number of patches being generated
2038 :``%R``: changeset revision number
2038 :``%R``: changeset revision number
2039 :``%b``: basename of the exporting repository
2039 :``%b``: basename of the exporting repository
2040 :``%h``: short-form changeset hash (12 hexadecimal digits)
2040 :``%h``: short-form changeset hash (12 hexadecimal digits)
2041 :``%m``: first line of the commit message (only alphanumeric characters)
2041 :``%m``: first line of the commit message (only alphanumeric characters)
2042 :``%n``: zero-padded sequence number, starting at 1
2042 :``%n``: zero-padded sequence number, starting at 1
2043 :``%r``: zero-padded changeset revision number
2043 :``%r``: zero-padded changeset revision number
2044 :``\\``: literal "\\" character
2044 :``\\``: literal "\\" character
2045
2045
2046 Without the -a/--text option, export will avoid generating diffs
2046 Without the -a/--text option, export will avoid generating diffs
2047 of files it detects as binary. With -a, export will generate a
2047 of files it detects as binary. With -a, export will generate a
2048 diff anyway, probably with undesirable results.
2048 diff anyway, probably with undesirable results.
2049
2049
2050 With -B/--bookmark changesets reachable by the given bookmark are
2050 With -B/--bookmark changesets reachable by the given bookmark are
2051 selected.
2051 selected.
2052
2052
2053 Use the -g/--git option to generate diffs in the git extended diff
2053 Use the -g/--git option to generate diffs in the git extended diff
2054 format. See :hg:`help diffs` for more information.
2054 format. See :hg:`help diffs` for more information.
2055
2055
2056 With the --switch-parent option, the diff will be against the
2056 With the --switch-parent option, the diff will be against the
2057 second parent. It can be useful to review a merge.
2057 second parent. It can be useful to review a merge.
2058
2058
2059 .. container:: verbose
2059 .. container:: verbose
2060
2060
2061 Template:
2061 Template:
2062
2062
2063 The following keywords are supported in addition to the common template
2063 The following keywords are supported in addition to the common template
2064 keywords and functions. See also :hg:`help templates`.
2064 keywords and functions. See also :hg:`help templates`.
2065
2065
2066 :diff: String. Diff content.
2066 :diff: String. Diff content.
2067 :parents: List of strings. Parent nodes of the changeset.
2067 :parents: List of strings. Parent nodes of the changeset.
2068
2068
2069 Examples:
2069 Examples:
2070
2070
2071 - use export and import to transplant a bugfix to the current
2071 - use export and import to transplant a bugfix to the current
2072 branch::
2072 branch::
2073
2073
2074 hg export -r 9353 | hg import -
2074 hg export -r 9353 | hg import -
2075
2075
2076 - export all the changesets between two revisions to a file with
2076 - export all the changesets between two revisions to a file with
2077 rename information::
2077 rename information::
2078
2078
2079 hg export --git -r 123:150 > changes.txt
2079 hg export --git -r 123:150 > changes.txt
2080
2080
2081 - split outgoing changes into a series of patches with
2081 - split outgoing changes into a series of patches with
2082 descriptive names::
2082 descriptive names::
2083
2083
2084 hg export -r "outgoing()" -o "%n-%m.patch"
2084 hg export -r "outgoing()" -o "%n-%m.patch"
2085
2085
2086 Returns 0 on success.
2086 Returns 0 on success.
2087 """
2087 """
2088 opts = pycompat.byteskwargs(opts)
2088 opts = pycompat.byteskwargs(opts)
2089 bookmark = opts.get('bookmark')
2089 bookmark = opts.get('bookmark')
2090 changesets += tuple(opts.get('rev', []))
2090 changesets += tuple(opts.get('rev', []))
2091
2091
2092 if bookmark and changesets:
2092 if bookmark and changesets:
2093 raise error.Abort(_("-r and -B are mutually exclusive"))
2093 raise error.Abort(_("-r and -B are mutually exclusive"))
2094
2094
2095 if bookmark:
2095 if bookmark:
2096 if bookmark not in repo._bookmarks:
2096 if bookmark not in repo._bookmarks:
2097 raise error.Abort(_("bookmark '%s' not found") % bookmark)
2097 raise error.Abort(_("bookmark '%s' not found") % bookmark)
2098
2098
2099 revs = scmutil.bookmarkrevs(repo, bookmark)
2099 revs = scmutil.bookmarkrevs(repo, bookmark)
2100 else:
2100 else:
2101 if not changesets:
2101 if not changesets:
2102 changesets = ['.']
2102 changesets = ['.']
2103
2103
2104 repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn')
2104 repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn')
2105 revs = scmutil.revrange(repo, changesets)
2105 revs = scmutil.revrange(repo, changesets)
2106
2106
2107 if not revs:
2107 if not revs:
2108 raise error.Abort(_("export requires at least one changeset"))
2108 raise error.Abort(_("export requires at least one changeset"))
2109 if len(revs) > 1:
2109 if len(revs) > 1:
2110 ui.note(_('exporting patches:\n'))
2110 ui.note(_('exporting patches:\n'))
2111 else:
2111 else:
2112 ui.note(_('exporting patch:\n'))
2112 ui.note(_('exporting patch:\n'))
2113
2113
2114 fntemplate = opts.get('output')
2114 fntemplate = opts.get('output')
2115 if cmdutil.isstdiofilename(fntemplate):
2115 if cmdutil.isstdiofilename(fntemplate):
2116 fntemplate = ''
2116 fntemplate = ''
2117
2117
2118 if fntemplate:
2118 if fntemplate:
2119 fm = formatter.nullformatter(ui, 'export', opts)
2119 fm = formatter.nullformatter(ui, 'export', opts)
2120 else:
2120 else:
2121 ui.pager('export')
2121 ui.pager('export')
2122 fm = ui.formatter('export', opts)
2122 fm = ui.formatter('export', opts)
2123 with fm:
2123 with fm:
2124 cmdutil.export(repo, revs, fm, fntemplate=fntemplate,
2124 cmdutil.export(repo, revs, fm, fntemplate=fntemplate,
2125 switch_parent=opts.get('switch_parent'),
2125 switch_parent=opts.get('switch_parent'),
2126 opts=patch.diffallopts(ui, opts))
2126 opts=patch.diffallopts(ui, opts))
2127
2127
2128 @command('files',
2128 @command('files',
2129 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
2129 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
2130 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2130 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2131 ] + walkopts + formatteropts + subrepoopts,
2131 ] + walkopts + formatteropts + subrepoopts,
2132 _('[OPTION]... [FILE]...'),
2132 _('[OPTION]... [FILE]...'),
2133 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2133 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2134 intents={INTENT_READONLY})
2134 intents={INTENT_READONLY})
2135 def files(ui, repo, *pats, **opts):
2135 def files(ui, repo, *pats, **opts):
2136 """list tracked files
2136 """list tracked files
2137
2137
2138 Print files under Mercurial control in the working directory or
2138 Print files under Mercurial control in the working directory or
2139 specified revision for given files (excluding removed files).
2139 specified revision for given files (excluding removed files).
2140 Files can be specified as filenames or filesets.
2140 Files can be specified as filenames or filesets.
2141
2141
2142 If no files are given to match, this command prints the names
2142 If no files are given to match, this command prints the names
2143 of all files under Mercurial control.
2143 of all files under Mercurial control.
2144
2144
2145 .. container:: verbose
2145 .. container:: verbose
2146
2146
2147 Template:
2147 Template:
2148
2148
2149 The following keywords are supported in addition to the common template
2149 The following keywords are supported in addition to the common template
2150 keywords and functions. See also :hg:`help templates`.
2150 keywords and functions. See also :hg:`help templates`.
2151
2151
2152 :flags: String. Character denoting file's symlink and executable bits.
2152 :flags: String. Character denoting file's symlink and executable bits.
2153 :path: String. Repository-absolute path of the file.
2153 :path: String. Repository-absolute path of the file.
2154 :size: Integer. Size of the file in bytes.
2154 :size: Integer. Size of the file in bytes.
2155
2155
2156 Examples:
2156 Examples:
2157
2157
2158 - list all files under the current directory::
2158 - list all files under the current directory::
2159
2159
2160 hg files .
2160 hg files .
2161
2161
2162 - shows sizes and flags for current revision::
2162 - shows sizes and flags for current revision::
2163
2163
2164 hg files -vr .
2164 hg files -vr .
2165
2165
2166 - list all files named README::
2166 - list all files named README::
2167
2167
2168 hg files -I "**/README"
2168 hg files -I "**/README"
2169
2169
2170 - list all binary files::
2170 - list all binary files::
2171
2171
2172 hg files "set:binary()"
2172 hg files "set:binary()"
2173
2173
2174 - find files containing a regular expression::
2174 - find files containing a regular expression::
2175
2175
2176 hg files "set:grep('bob')"
2176 hg files "set:grep('bob')"
2177
2177
2178 - search tracked file contents with xargs and grep::
2178 - search tracked file contents with xargs and grep::
2179
2179
2180 hg files -0 | xargs -0 grep foo
2180 hg files -0 | xargs -0 grep foo
2181
2181
2182 See :hg:`help patterns` and :hg:`help filesets` for more information
2182 See :hg:`help patterns` and :hg:`help filesets` for more information
2183 on specifying file patterns.
2183 on specifying file patterns.
2184
2184
2185 Returns 0 if a match is found, 1 otherwise.
2185 Returns 0 if a match is found, 1 otherwise.
2186
2186
2187 """
2187 """
2188
2188
2189 opts = pycompat.byteskwargs(opts)
2189 opts = pycompat.byteskwargs(opts)
2190 rev = opts.get('rev')
2190 rev = opts.get('rev')
2191 if rev:
2191 if rev:
2192 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2192 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2193 ctx = scmutil.revsingle(repo, rev, None)
2193 ctx = scmutil.revsingle(repo, rev, None)
2194
2194
2195 end = '\n'
2195 end = '\n'
2196 if opts.get('print0'):
2196 if opts.get('print0'):
2197 end = '\0'
2197 end = '\0'
2198 fmt = '%s' + end
2198 fmt = '%s' + end
2199
2199
2200 m = scmutil.match(ctx, pats, opts)
2200 m = scmutil.match(ctx, pats, opts)
2201 ui.pager('files')
2201 ui.pager('files')
2202 with ui.formatter('files', opts) as fm:
2202 with ui.formatter('files', opts) as fm:
2203 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2203 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2204
2204
2205 @command(
2205 @command(
2206 'forget',
2206 'forget',
2207 [('i', 'interactive', None, _('use interactive mode')),
2207 [('i', 'interactive', None, _('use interactive mode')),
2208 ] + walkopts + dryrunopts,
2208 ] + walkopts + dryrunopts,
2209 _('[OPTION]... FILE...'),
2209 _('[OPTION]... FILE...'),
2210 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2210 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2211 helpbasic=True, inferrepo=True)
2211 helpbasic=True, inferrepo=True)
2212 def forget(ui, repo, *pats, **opts):
2212 def forget(ui, repo, *pats, **opts):
2213 """forget the specified files on the next commit
2213 """forget the specified files on the next commit
2214
2214
2215 Mark the specified files so they will no longer be tracked
2215 Mark the specified files so they will no longer be tracked
2216 after the next commit.
2216 after the next commit.
2217
2217
2218 This only removes files from the current branch, not from the
2218 This only removes files from the current branch, not from the
2219 entire project history, and it does not delete them from the
2219 entire project history, and it does not delete them from the
2220 working directory.
2220 working directory.
2221
2221
2222 To delete the file from the working directory, see :hg:`remove`.
2222 To delete the file from the working directory, see :hg:`remove`.
2223
2223
2224 To undo a forget before the next commit, see :hg:`add`.
2224 To undo a forget before the next commit, see :hg:`add`.
2225
2225
2226 .. container:: verbose
2226 .. container:: verbose
2227
2227
2228 Examples:
2228 Examples:
2229
2229
2230 - forget newly-added binary files::
2230 - forget newly-added binary files::
2231
2231
2232 hg forget "set:added() and binary()"
2232 hg forget "set:added() and binary()"
2233
2233
2234 - forget files that would be excluded by .hgignore::
2234 - forget files that would be excluded by .hgignore::
2235
2235
2236 hg forget "set:hgignore()"
2236 hg forget "set:hgignore()"
2237
2237
2238 Returns 0 on success.
2238 Returns 0 on success.
2239 """
2239 """
2240
2240
2241 opts = pycompat.byteskwargs(opts)
2241 opts = pycompat.byteskwargs(opts)
2242 if not pats:
2242 if not pats:
2243 raise error.Abort(_('no files specified'))
2243 raise error.Abort(_('no files specified'))
2244
2244
2245 m = scmutil.match(repo[None], pats, opts)
2245 m = scmutil.match(repo[None], pats, opts)
2246 dryrun, interactive = opts.get('dry_run'), opts.get('interactive')
2246 dryrun, interactive = opts.get('dry_run'), opts.get('interactive')
2247 rejected = cmdutil.forget(ui, repo, m, prefix="",
2247 rejected = cmdutil.forget(ui, repo, m, prefix="",
2248 explicitonly=False, dryrun=dryrun,
2248 explicitonly=False, dryrun=dryrun,
2249 interactive=interactive)[0]
2249 interactive=interactive)[0]
2250 return rejected and 1 or 0
2250 return rejected and 1 or 0
2251
2251
2252 @command(
2252 @command(
2253 'graft',
2253 'graft',
2254 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2254 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2255 ('', 'base', '',
2255 ('', 'base', '',
2256 _('base revision when doing the graft merge (ADVANCED)'), _('REV')),
2256 _('base revision when doing the graft merge (ADVANCED)'), _('REV')),
2257 ('c', 'continue', False, _('resume interrupted graft')),
2257 ('c', 'continue', False, _('resume interrupted graft')),
2258 ('', 'stop', False, _('stop interrupted graft')),
2258 ('', 'stop', False, _('stop interrupted graft')),
2259 ('', 'abort', False, _('abort interrupted graft')),
2259 ('', 'abort', False, _('abort interrupted graft')),
2260 ('e', 'edit', False, _('invoke editor on commit messages')),
2260 ('e', 'edit', False, _('invoke editor on commit messages')),
2261 ('', 'log', None, _('append graft info to log message')),
2261 ('', 'log', None, _('append graft info to log message')),
2262 ('', 'no-commit', None,
2262 ('', 'no-commit', None,
2263 _("don't commit, just apply the changes in working directory")),
2263 _("don't commit, just apply the changes in working directory")),
2264 ('f', 'force', False, _('force graft')),
2264 ('f', 'force', False, _('force graft')),
2265 ('D', 'currentdate', False,
2265 ('D', 'currentdate', False,
2266 _('record the current date as commit date')),
2266 _('record the current date as commit date')),
2267 ('U', 'currentuser', False,
2267 ('U', 'currentuser', False,
2268 _('record the current user as committer'))]
2268 _('record the current user as committer'))]
2269 + commitopts2 + mergetoolopts + dryrunopts,
2269 + commitopts2 + mergetoolopts + dryrunopts,
2270 _('[OPTION]... [-r REV]... REV...'),
2270 _('[OPTION]... [-r REV]... REV...'),
2271 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
2271 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
2272 def graft(ui, repo, *revs, **opts):
2272 def graft(ui, repo, *revs, **opts):
2273 '''copy changes from other branches onto the current branch
2273 '''copy changes from other branches onto the current branch
2274
2274
2275 This command uses Mercurial's merge logic to copy individual
2275 This command uses Mercurial's merge logic to copy individual
2276 changes from other branches without merging branches in the
2276 changes from other branches without merging branches in the
2277 history graph. This is sometimes known as 'backporting' or
2277 history graph. This is sometimes known as 'backporting' or
2278 'cherry-picking'. By default, graft will copy user, date, and
2278 'cherry-picking'. By default, graft will copy user, date, and
2279 description from the source changesets.
2279 description from the source changesets.
2280
2280
2281 Changesets that are ancestors of the current revision, that have
2281 Changesets that are ancestors of the current revision, that have
2282 already been grafted, or that are merges will be skipped.
2282 already been grafted, or that are merges will be skipped.
2283
2283
2284 If --log is specified, log messages will have a comment appended
2284 If --log is specified, log messages will have a comment appended
2285 of the form::
2285 of the form::
2286
2286
2287 (grafted from CHANGESETHASH)
2287 (grafted from CHANGESETHASH)
2288
2288
2289 If --force is specified, revisions will be grafted even if they
2289 If --force is specified, revisions will be grafted even if they
2290 are already ancestors of, or have been grafted to, the destination.
2290 are already ancestors of, or have been grafted to, the destination.
2291 This is useful when the revisions have since been backed out.
2291 This is useful when the revisions have since been backed out.
2292
2292
2293 If a graft merge results in conflicts, the graft process is
2293 If a graft merge results in conflicts, the graft process is
2294 interrupted so that the current merge can be manually resolved.
2294 interrupted so that the current merge can be manually resolved.
2295 Once all conflicts are addressed, the graft process can be
2295 Once all conflicts are addressed, the graft process can be
2296 continued with the -c/--continue option.
2296 continued with the -c/--continue option.
2297
2297
2298 The -c/--continue option reapplies all the earlier options.
2298 The -c/--continue option reapplies all the earlier options.
2299
2299
2300 .. container:: verbose
2300 .. container:: verbose
2301
2301
2302 The --base option exposes more of how graft internally uses merge with a
2302 The --base option exposes more of how graft internally uses merge with a
2303 custom base revision. --base can be used to specify another ancestor than
2303 custom base revision. --base can be used to specify another ancestor than
2304 the first and only parent.
2304 the first and only parent.
2305
2305
2306 The command::
2306 The command::
2307
2307
2308 hg graft -r 345 --base 234
2308 hg graft -r 345 --base 234
2309
2309
2310 is thus pretty much the same as::
2310 is thus pretty much the same as::
2311
2311
2312 hg diff -r 234 -r 345 | hg import
2312 hg diff -r 234 -r 345 | hg import
2313
2313
2314 but using merge to resolve conflicts and track moved files.
2314 but using merge to resolve conflicts and track moved files.
2315
2315
2316 The result of a merge can thus be backported as a single commit by
2316 The result of a merge can thus be backported as a single commit by
2317 specifying one of the merge parents as base, and thus effectively
2317 specifying one of the merge parents as base, and thus effectively
2318 grafting the changes from the other side.
2318 grafting the changes from the other side.
2319
2319
2320 It is also possible to collapse multiple changesets and clean up history
2320 It is also possible to collapse multiple changesets and clean up history
2321 by specifying another ancestor as base, much like rebase --collapse
2321 by specifying another ancestor as base, much like rebase --collapse
2322 --keep.
2322 --keep.
2323
2323
2324 The commit message can be tweaked after the fact using commit --amend .
2324 The commit message can be tweaked after the fact using commit --amend .
2325
2325
2326 For using non-ancestors as the base to backout changes, see the backout
2326 For using non-ancestors as the base to backout changes, see the backout
2327 command and the hidden --parent option.
2327 command and the hidden --parent option.
2328
2328
2329 .. container:: verbose
2329 .. container:: verbose
2330
2330
2331 Examples:
2331 Examples:
2332
2332
2333 - copy a single change to the stable branch and edit its description::
2333 - copy a single change to the stable branch and edit its description::
2334
2334
2335 hg update stable
2335 hg update stable
2336 hg graft --edit 9393
2336 hg graft --edit 9393
2337
2337
2338 - graft a range of changesets with one exception, updating dates::
2338 - graft a range of changesets with one exception, updating dates::
2339
2339
2340 hg graft -D "2085::2093 and not 2091"
2340 hg graft -D "2085::2093 and not 2091"
2341
2341
2342 - continue a graft after resolving conflicts::
2342 - continue a graft after resolving conflicts::
2343
2343
2344 hg graft -c
2344 hg graft -c
2345
2345
2346 - show the source of a grafted changeset::
2346 - show the source of a grafted changeset::
2347
2347
2348 hg log --debug -r .
2348 hg log --debug -r .
2349
2349
2350 - show revisions sorted by date::
2350 - show revisions sorted by date::
2351
2351
2352 hg log -r "sort(all(), date)"
2352 hg log -r "sort(all(), date)"
2353
2353
2354 - backport the result of a merge as a single commit::
2354 - backport the result of a merge as a single commit::
2355
2355
2356 hg graft -r 123 --base 123^
2356 hg graft -r 123 --base 123^
2357
2357
2358 - land a feature branch as one changeset::
2358 - land a feature branch as one changeset::
2359
2359
2360 hg up -cr default
2360 hg up -cr default
2361 hg graft -r featureX --base "ancestor('featureX', 'default')"
2361 hg graft -r featureX --base "ancestor('featureX', 'default')"
2362
2362
2363 See :hg:`help revisions` for more about specifying revisions.
2363 See :hg:`help revisions` for more about specifying revisions.
2364
2364
2365 Returns 0 on successful completion.
2365 Returns 0 on successful completion.
2366 '''
2366 '''
2367 with repo.wlock():
2367 with repo.wlock():
2368 return _dograft(ui, repo, *revs, **opts)
2368 return _dograft(ui, repo, *revs, **opts)
2369
2369
2370 def _dograft(ui, repo, *revs, **opts):
2370 def _dograft(ui, repo, *revs, **opts):
2371 opts = pycompat.byteskwargs(opts)
2371 opts = pycompat.byteskwargs(opts)
2372 if revs and opts.get('rev'):
2372 if revs and opts.get('rev'):
2373 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2373 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2374 'revision ordering!\n'))
2374 'revision ordering!\n'))
2375
2375
2376 revs = list(revs)
2376 revs = list(revs)
2377 revs.extend(opts.get('rev'))
2377 revs.extend(opts.get('rev'))
2378 basectx = None
2378 basectx = None
2379 if opts.get('base'):
2379 if opts.get('base'):
2380 basectx = scmutil.revsingle(repo, opts['base'], None)
2380 basectx = scmutil.revsingle(repo, opts['base'], None)
2381 # a dict of data to be stored in state file
2381 # a dict of data to be stored in state file
2382 statedata = {}
2382 statedata = {}
2383 # list of new nodes created by ongoing graft
2383 # list of new nodes created by ongoing graft
2384 statedata['newnodes'] = []
2384 statedata['newnodes'] = []
2385
2385
2386 if not opts.get('user') and opts.get('currentuser'):
2386 if not opts.get('user') and opts.get('currentuser'):
2387 opts['user'] = ui.username()
2387 opts['user'] = ui.username()
2388 if not opts.get('date') and opts.get('currentdate'):
2388 if not opts.get('date') and opts.get('currentdate'):
2389 opts['date'] = "%d %d" % dateutil.makedate()
2389 opts['date'] = "%d %d" % dateutil.makedate()
2390
2390
2391 editor = cmdutil.getcommiteditor(editform='graft',
2391 editor = cmdutil.getcommiteditor(editform='graft',
2392 **pycompat.strkwargs(opts))
2392 **pycompat.strkwargs(opts))
2393
2393
2394 cont = False
2394 cont = False
2395 if opts.get('no_commit'):
2395 if opts.get('no_commit'):
2396 if opts.get('edit'):
2396 if opts.get('edit'):
2397 raise error.Abort(_("cannot specify --no-commit and "
2397 raise error.Abort(_("cannot specify --no-commit and "
2398 "--edit together"))
2398 "--edit together"))
2399 if opts.get('currentuser'):
2399 if opts.get('currentuser'):
2400 raise error.Abort(_("cannot specify --no-commit and "
2400 raise error.Abort(_("cannot specify --no-commit and "
2401 "--currentuser together"))
2401 "--currentuser together"))
2402 if opts.get('currentdate'):
2402 if opts.get('currentdate'):
2403 raise error.Abort(_("cannot specify --no-commit and "
2403 raise error.Abort(_("cannot specify --no-commit and "
2404 "--currentdate together"))
2404 "--currentdate together"))
2405 if opts.get('log'):
2405 if opts.get('log'):
2406 raise error.Abort(_("cannot specify --no-commit and "
2406 raise error.Abort(_("cannot specify --no-commit and "
2407 "--log together"))
2407 "--log together"))
2408
2408
2409 graftstate = statemod.cmdstate(repo, 'graftstate')
2409 graftstate = statemod.cmdstate(repo, 'graftstate')
2410
2410
2411 if opts.get('stop'):
2411 if opts.get('stop'):
2412 if opts.get('continue'):
2412 if opts.get('continue'):
2413 raise error.Abort(_("cannot use '--continue' and "
2413 raise error.Abort(_("cannot use '--continue' and "
2414 "'--stop' together"))
2414 "'--stop' together"))
2415 if opts.get('abort'):
2415 if opts.get('abort'):
2416 raise error.Abort(_("cannot use '--abort' and '--stop' together"))
2416 raise error.Abort(_("cannot use '--abort' and '--stop' together"))
2417
2417
2418 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2418 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2419 opts.get('date'), opts.get('currentdate'),
2419 opts.get('date'), opts.get('currentdate'),
2420 opts.get('currentuser'), opts.get('rev'))):
2420 opts.get('currentuser'), opts.get('rev'))):
2421 raise error.Abort(_("cannot specify any other flag with '--stop'"))
2421 raise error.Abort(_("cannot specify any other flag with '--stop'"))
2422 return _stopgraft(ui, repo, graftstate)
2422 return _stopgraft(ui, repo, graftstate)
2423 elif opts.get('abort'):
2423 elif opts.get('abort'):
2424 if opts.get('continue'):
2424 if opts.get('continue'):
2425 raise error.Abort(_("cannot use '--continue' and "
2425 raise error.Abort(_("cannot use '--continue' and "
2426 "'--abort' together"))
2426 "'--abort' together"))
2427 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2427 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2428 opts.get('date'), opts.get('currentdate'),
2428 opts.get('date'), opts.get('currentdate'),
2429 opts.get('currentuser'), opts.get('rev'))):
2429 opts.get('currentuser'), opts.get('rev'))):
2430 raise error.Abort(_("cannot specify any other flag with '--abort'"))
2430 raise error.Abort(_("cannot specify any other flag with '--abort'"))
2431
2431
2432 return _abortgraft(ui, repo, graftstate)
2432 return _abortgraft(ui, repo, graftstate)
2433 elif opts.get('continue'):
2433 elif opts.get('continue'):
2434 cont = True
2434 cont = True
2435 if revs:
2435 if revs:
2436 raise error.Abort(_("can't specify --continue and revisions"))
2436 raise error.Abort(_("can't specify --continue and revisions"))
2437 # read in unfinished revisions
2437 # read in unfinished revisions
2438 if graftstate.exists():
2438 if graftstate.exists():
2439 statedata = _readgraftstate(repo, graftstate)
2439 statedata = _readgraftstate(repo, graftstate)
2440 if statedata.get('date'):
2440 if statedata.get('date'):
2441 opts['date'] = statedata['date']
2441 opts['date'] = statedata['date']
2442 if statedata.get('user'):
2442 if statedata.get('user'):
2443 opts['user'] = statedata['user']
2443 opts['user'] = statedata['user']
2444 if statedata.get('log'):
2444 if statedata.get('log'):
2445 opts['log'] = True
2445 opts['log'] = True
2446 if statedata.get('no_commit'):
2446 if statedata.get('no_commit'):
2447 opts['no_commit'] = statedata.get('no_commit')
2447 opts['no_commit'] = statedata.get('no_commit')
2448 nodes = statedata['nodes']
2448 nodes = statedata['nodes']
2449 revs = [repo[node].rev() for node in nodes]
2449 revs = [repo[node].rev() for node in nodes]
2450 else:
2450 else:
2451 cmdutil.wrongtooltocontinue(repo, _('graft'))
2451 cmdutil.wrongtooltocontinue(repo, _('graft'))
2452 else:
2452 else:
2453 if not revs:
2453 if not revs:
2454 raise error.Abort(_('no revisions specified'))
2454 raise error.Abort(_('no revisions specified'))
2455 cmdutil.checkunfinished(repo)
2455 cmdutil.checkunfinished(repo)
2456 cmdutil.bailifchanged(repo)
2456 cmdutil.bailifchanged(repo)
2457 revs = scmutil.revrange(repo, revs)
2457 revs = scmutil.revrange(repo, revs)
2458
2458
2459 skipped = set()
2459 skipped = set()
2460 if basectx is None:
2460 if basectx is None:
2461 # check for merges
2461 # check for merges
2462 for rev in repo.revs('%ld and merge()', revs):
2462 for rev in repo.revs('%ld and merge()', revs):
2463 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2463 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2464 skipped.add(rev)
2464 skipped.add(rev)
2465 revs = [r for r in revs if r not in skipped]
2465 revs = [r for r in revs if r not in skipped]
2466 if not revs:
2466 if not revs:
2467 return -1
2467 return -1
2468 if basectx is not None and len(revs) != 1:
2468 if basectx is not None and len(revs) != 1:
2469 raise error.Abort(_('only one revision allowed with --base '))
2469 raise error.Abort(_('only one revision allowed with --base '))
2470
2470
2471 # Don't check in the --continue case, in effect retaining --force across
2471 # Don't check in the --continue case, in effect retaining --force across
2472 # --continues. That's because without --force, any revisions we decided to
2472 # --continues. That's because without --force, any revisions we decided to
2473 # skip would have been filtered out here, so they wouldn't have made their
2473 # skip would have been filtered out here, so they wouldn't have made their
2474 # way to the graftstate. With --force, any revisions we would have otherwise
2474 # way to the graftstate. With --force, any revisions we would have otherwise
2475 # skipped would not have been filtered out, and if they hadn't been applied
2475 # skipped would not have been filtered out, and if they hadn't been applied
2476 # already, they'd have been in the graftstate.
2476 # already, they'd have been in the graftstate.
2477 if not (cont or opts.get('force')) and basectx is None:
2477 if not (cont or opts.get('force')) and basectx is None:
2478 # check for ancestors of dest branch
2478 # check for ancestors of dest branch
2479 crev = repo['.'].rev()
2479 crev = repo['.'].rev()
2480 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2480 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2481 # XXX make this lazy in the future
2481 # XXX make this lazy in the future
2482 # don't mutate while iterating, create a copy
2482 # don't mutate while iterating, create a copy
2483 for rev in list(revs):
2483 for rev in list(revs):
2484 if rev in ancestors:
2484 if rev in ancestors:
2485 ui.warn(_('skipping ancestor revision %d:%s\n') %
2485 ui.warn(_('skipping ancestor revision %d:%s\n') %
2486 (rev, repo[rev]))
2486 (rev, repo[rev]))
2487 # XXX remove on list is slow
2487 # XXX remove on list is slow
2488 revs.remove(rev)
2488 revs.remove(rev)
2489 if not revs:
2489 if not revs:
2490 return -1
2490 return -1
2491
2491
2492 # analyze revs for earlier grafts
2492 # analyze revs for earlier grafts
2493 ids = {}
2493 ids = {}
2494 for ctx in repo.set("%ld", revs):
2494 for ctx in repo.set("%ld", revs):
2495 ids[ctx.hex()] = ctx.rev()
2495 ids[ctx.hex()] = ctx.rev()
2496 n = ctx.extra().get('source')
2496 n = ctx.extra().get('source')
2497 if n:
2497 if n:
2498 ids[n] = ctx.rev()
2498 ids[n] = ctx.rev()
2499
2499
2500 # check ancestors for earlier grafts
2500 # check ancestors for earlier grafts
2501 ui.debug('scanning for duplicate grafts\n')
2501 ui.debug('scanning for duplicate grafts\n')
2502
2502
2503 # The only changesets we can be sure doesn't contain grafts of any
2503 # The only changesets we can be sure doesn't contain grafts of any
2504 # revs, are the ones that are common ancestors of *all* revs:
2504 # revs, are the ones that are common ancestors of *all* revs:
2505 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2505 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2506 ctx = repo[rev]
2506 ctx = repo[rev]
2507 n = ctx.extra().get('source')
2507 n = ctx.extra().get('source')
2508 if n in ids:
2508 if n in ids:
2509 try:
2509 try:
2510 r = repo[n].rev()
2510 r = repo[n].rev()
2511 except error.RepoLookupError:
2511 except error.RepoLookupError:
2512 r = None
2512 r = None
2513 if r in revs:
2513 if r in revs:
2514 ui.warn(_('skipping revision %d:%s '
2514 ui.warn(_('skipping revision %d:%s '
2515 '(already grafted to %d:%s)\n')
2515 '(already grafted to %d:%s)\n')
2516 % (r, repo[r], rev, ctx))
2516 % (r, repo[r], rev, ctx))
2517 revs.remove(r)
2517 revs.remove(r)
2518 elif ids[n] in revs:
2518 elif ids[n] in revs:
2519 if r is None:
2519 if r is None:
2520 ui.warn(_('skipping already grafted revision %d:%s '
2520 ui.warn(_('skipping already grafted revision %d:%s '
2521 '(%d:%s also has unknown origin %s)\n')
2521 '(%d:%s also has unknown origin %s)\n')
2522 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2522 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2523 else:
2523 else:
2524 ui.warn(_('skipping already grafted revision %d:%s '
2524 ui.warn(_('skipping already grafted revision %d:%s '
2525 '(%d:%s also has origin %d:%s)\n')
2525 '(%d:%s also has origin %d:%s)\n')
2526 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2526 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2527 revs.remove(ids[n])
2527 revs.remove(ids[n])
2528 elif ctx.hex() in ids:
2528 elif ctx.hex() in ids:
2529 r = ids[ctx.hex()]
2529 r = ids[ctx.hex()]
2530 if r in revs:
2530 if r in revs:
2531 ui.warn(_('skipping already grafted revision %d:%s '
2531 ui.warn(_('skipping already grafted revision %d:%s '
2532 '(was grafted from %d:%s)\n') %
2532 '(was grafted from %d:%s)\n') %
2533 (r, repo[r], rev, ctx))
2533 (r, repo[r], rev, ctx))
2534 revs.remove(r)
2534 revs.remove(r)
2535 if not revs:
2535 if not revs:
2536 return -1
2536 return -1
2537
2537
2538 if opts.get('no_commit'):
2538 if opts.get('no_commit'):
2539 statedata['no_commit'] = True
2539 statedata['no_commit'] = True
2540 for pos, ctx in enumerate(repo.set("%ld", revs)):
2540 for pos, ctx in enumerate(repo.set("%ld", revs)):
2541 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2541 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2542 ctx.description().split('\n', 1)[0])
2542 ctx.description().split('\n', 1)[0])
2543 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2543 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2544 if names:
2544 if names:
2545 desc += ' (%s)' % ' '.join(names)
2545 desc += ' (%s)' % ' '.join(names)
2546 ui.status(_('grafting %s\n') % desc)
2546 ui.status(_('grafting %s\n') % desc)
2547 if opts.get('dry_run'):
2547 if opts.get('dry_run'):
2548 continue
2548 continue
2549
2549
2550 source = ctx.extra().get('source')
2550 source = ctx.extra().get('source')
2551 extra = {}
2551 extra = {}
2552 if source:
2552 if source:
2553 extra['source'] = source
2553 extra['source'] = source
2554 extra['intermediate-source'] = ctx.hex()
2554 extra['intermediate-source'] = ctx.hex()
2555 else:
2555 else:
2556 extra['source'] = ctx.hex()
2556 extra['source'] = ctx.hex()
2557 user = ctx.user()
2557 user = ctx.user()
2558 if opts.get('user'):
2558 if opts.get('user'):
2559 user = opts['user']
2559 user = opts['user']
2560 statedata['user'] = user
2560 statedata['user'] = user
2561 date = ctx.date()
2561 date = ctx.date()
2562 if opts.get('date'):
2562 if opts.get('date'):
2563 date = opts['date']
2563 date = opts['date']
2564 statedata['date'] = date
2564 statedata['date'] = date
2565 message = ctx.description()
2565 message = ctx.description()
2566 if opts.get('log'):
2566 if opts.get('log'):
2567 message += '\n(grafted from %s)' % ctx.hex()
2567 message += '\n(grafted from %s)' % ctx.hex()
2568 statedata['log'] = True
2568 statedata['log'] = True
2569
2569
2570 # we don't merge the first commit when continuing
2570 # we don't merge the first commit when continuing
2571 if not cont:
2571 if not cont:
2572 # perform the graft merge with p1(rev) as 'ancestor'
2572 # perform the graft merge with p1(rev) as 'ancestor'
2573 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
2573 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
2574 base = ctx.p1() if basectx is None else basectx
2574 base = ctx.p1() if basectx is None else basectx
2575 with ui.configoverride(overrides, 'graft'):
2575 with ui.configoverride(overrides, 'graft'):
2576 stats = mergemod.graft(repo, ctx, base, ['local', 'graft'])
2576 stats = mergemod.graft(repo, ctx, base, ['local', 'graft'])
2577 # report any conflicts
2577 # report any conflicts
2578 if stats.unresolvedcount > 0:
2578 if stats.unresolvedcount > 0:
2579 # write out state for --continue
2579 # write out state for --continue
2580 nodes = [repo[rev].hex() for rev in revs[pos:]]
2580 nodes = [repo[rev].hex() for rev in revs[pos:]]
2581 statedata['nodes'] = nodes
2581 statedata['nodes'] = nodes
2582 stateversion = 1
2582 stateversion = 1
2583 graftstate.save(stateversion, statedata)
2583 graftstate.save(stateversion, statedata)
2584 hint = _("use 'hg resolve' and 'hg graft --continue'")
2584 hint = _("use 'hg resolve' and 'hg graft --continue'")
2585 raise error.Abort(
2585 raise error.Abort(
2586 _("unresolved conflicts, can't continue"),
2586 _("unresolved conflicts, can't continue"),
2587 hint=hint)
2587 hint=hint)
2588 else:
2588 else:
2589 cont = False
2589 cont = False
2590
2590
2591 # commit if --no-commit is false
2591 # commit if --no-commit is false
2592 if not opts.get('no_commit'):
2592 if not opts.get('no_commit'):
2593 node = repo.commit(text=message, user=user, date=date, extra=extra,
2593 node = repo.commit(text=message, user=user, date=date, extra=extra,
2594 editor=editor)
2594 editor=editor)
2595 if node is None:
2595 if node is None:
2596 ui.warn(
2596 ui.warn(
2597 _('note: graft of %d:%s created no changes to commit\n') %
2597 _('note: graft of %d:%s created no changes to commit\n') %
2598 (ctx.rev(), ctx))
2598 (ctx.rev(), ctx))
2599 # checking that newnodes exist because old state files won't have it
2599 # checking that newnodes exist because old state files won't have it
2600 elif statedata.get('newnodes') is not None:
2600 elif statedata.get('newnodes') is not None:
2601 statedata['newnodes'].append(node)
2601 statedata['newnodes'].append(node)
2602
2602
2603 # remove state when we complete successfully
2603 # remove state when we complete successfully
2604 if not opts.get('dry_run'):
2604 if not opts.get('dry_run'):
2605 graftstate.delete()
2605 graftstate.delete()
2606
2606
2607 return 0
2607 return 0
2608
2608
2609 def _abortgraft(ui, repo, graftstate):
2609 def _abortgraft(ui, repo, graftstate):
2610 """abort the interrupted graft and rollbacks to the state before interrupted
2610 """abort the interrupted graft and rollbacks to the state before interrupted
2611 graft"""
2611 graft"""
2612 if not graftstate.exists():
2612 if not graftstate.exists():
2613 raise error.Abort(_("no interrupted graft to abort"))
2613 raise error.Abort(_("no interrupted graft to abort"))
2614 statedata = _readgraftstate(repo, graftstate)
2614 statedata = _readgraftstate(repo, graftstate)
2615 newnodes = statedata.get('newnodes')
2615 newnodes = statedata.get('newnodes')
2616 if newnodes is None:
2616 if newnodes is None:
2617 # and old graft state which does not have all the data required to abort
2617 # and old graft state which does not have all the data required to abort
2618 # the graft
2618 # the graft
2619 raise error.Abort(_("cannot abort using an old graftstate"))
2619 raise error.Abort(_("cannot abort using an old graftstate"))
2620
2620
2621 # changeset from which graft operation was started
2621 # changeset from which graft operation was started
2622 startctx = None
2622 startctx = None
2623 if len(newnodes) > 0:
2623 if len(newnodes) > 0:
2624 startctx = repo[newnodes[0]].p1()
2624 startctx = repo[newnodes[0]].p1()
2625 else:
2625 else:
2626 startctx = repo['.']
2626 startctx = repo['.']
2627 # whether to strip or not
2627 # whether to strip or not
2628 cleanup = False
2628 cleanup = False
2629 if newnodes:
2629 if newnodes:
2630 newnodes = [repo[r].rev() for r in newnodes]
2630 newnodes = [repo[r].rev() for r in newnodes]
2631 cleanup = True
2631 cleanup = True
2632 # checking that none of the newnodes turned public or is public
2632 # checking that none of the newnodes turned public or is public
2633 immutable = [c for c in newnodes if not repo[c].mutable()]
2633 immutable = [c for c in newnodes if not repo[c].mutable()]
2634 if immutable:
2634 if immutable:
2635 repo.ui.warn(_("cannot clean up public changesets %s\n")
2635 repo.ui.warn(_("cannot clean up public changesets %s\n")
2636 % ', '.join(bytes(repo[r]) for r in immutable),
2636 % ', '.join(bytes(repo[r]) for r in immutable),
2637 hint=_("see 'hg help phases' for details"))
2637 hint=_("see 'hg help phases' for details"))
2638 cleanup = False
2638 cleanup = False
2639
2639
2640 # checking that no new nodes are created on top of grafted revs
2640 # checking that no new nodes are created on top of grafted revs
2641 desc = set(repo.changelog.descendants(newnodes))
2641 desc = set(repo.changelog.descendants(newnodes))
2642 if desc - set(newnodes):
2642 if desc - set(newnodes):
2643 repo.ui.warn(_("new changesets detected on destination "
2643 repo.ui.warn(_("new changesets detected on destination "
2644 "branch, can't strip\n"))
2644 "branch, can't strip\n"))
2645 cleanup = False
2645 cleanup = False
2646
2646
2647 if cleanup:
2647 if cleanup:
2648 with repo.wlock(), repo.lock():
2648 with repo.wlock(), repo.lock():
2649 hg.updaterepo(repo, startctx.node(), overwrite=True)
2649 hg.updaterepo(repo, startctx.node(), overwrite=True)
2650 # stripping the new nodes created
2650 # stripping the new nodes created
2651 strippoints = [c.node() for c in repo.set("roots(%ld)",
2651 strippoints = [c.node() for c in repo.set("roots(%ld)",
2652 newnodes)]
2652 newnodes)]
2653 repair.strip(repo.ui, repo, strippoints, backup=False)
2653 repair.strip(repo.ui, repo, strippoints, backup=False)
2654
2654
2655 if not cleanup:
2655 if not cleanup:
2656 # we don't update to the startnode if we can't strip
2656 # we don't update to the startnode if we can't strip
2657 startctx = repo['.']
2657 startctx = repo['.']
2658 hg.updaterepo(repo, startctx.node(), overwrite=True)
2658 hg.updaterepo(repo, startctx.node(), overwrite=True)
2659
2659
2660 ui.status(_("graft aborted\n"))
2660 ui.status(_("graft aborted\n"))
2661 ui.status(_("working directory is now at %s\n") % startctx.hex()[:12])
2661 ui.status(_("working directory is now at %s\n") % startctx.hex()[:12])
2662 graftstate.delete()
2662 graftstate.delete()
2663 return 0
2663 return 0
2664
2664
2665 def _readgraftstate(repo, graftstate):
2665 def _readgraftstate(repo, graftstate):
2666 """read the graft state file and return a dict of the data stored in it"""
2666 """read the graft state file and return a dict of the data stored in it"""
2667 try:
2667 try:
2668 return graftstate.read()
2668 return graftstate.read()
2669 except error.CorruptedState:
2669 except error.CorruptedState:
2670 nodes = repo.vfs.read('graftstate').splitlines()
2670 nodes = repo.vfs.read('graftstate').splitlines()
2671 return {'nodes': nodes}
2671 return {'nodes': nodes}
2672
2672
2673 def _stopgraft(ui, repo, graftstate):
2673 def _stopgraft(ui, repo, graftstate):
2674 """stop the interrupted graft"""
2674 """stop the interrupted graft"""
2675 if not graftstate.exists():
2675 if not graftstate.exists():
2676 raise error.Abort(_("no interrupted graft found"))
2676 raise error.Abort(_("no interrupted graft found"))
2677 pctx = repo['.']
2677 pctx = repo['.']
2678 hg.updaterepo(repo, pctx.node(), overwrite=True)
2678 hg.updaterepo(repo, pctx.node(), overwrite=True)
2679 graftstate.delete()
2679 graftstate.delete()
2680 ui.status(_("stopped the interrupted graft\n"))
2680 ui.status(_("stopped the interrupted graft\n"))
2681 ui.status(_("working directory is now at %s\n") % pctx.hex()[:12])
2681 ui.status(_("working directory is now at %s\n") % pctx.hex()[:12])
2682 return 0
2682 return 0
2683
2683
2684 @command('grep',
2684 @command('grep',
2685 [('0', 'print0', None, _('end fields with NUL')),
2685 [('0', 'print0', None, _('end fields with NUL')),
2686 ('', 'all', None, _('print all revisions that match (DEPRECATED) ')),
2686 ('', 'all', None, _('print all revisions that match (DEPRECATED) ')),
2687 ('', 'diff', None, _('print all revisions when the term was introduced '
2687 ('', 'diff', None, _('print all revisions when the term was introduced '
2688 'or removed')),
2688 'or removed')),
2689 ('a', 'text', None, _('treat all files as text')),
2689 ('a', 'text', None, _('treat all files as text')),
2690 ('f', 'follow', None,
2690 ('f', 'follow', None,
2691 _('follow changeset history,'
2691 _('follow changeset history,'
2692 ' or file history across copies and renames')),
2692 ' or file history across copies and renames')),
2693 ('i', 'ignore-case', None, _('ignore case when matching')),
2693 ('i', 'ignore-case', None, _('ignore case when matching')),
2694 ('l', 'files-with-matches', None,
2694 ('l', 'files-with-matches', None,
2695 _('print only filenames and revisions that match')),
2695 _('print only filenames and revisions that match')),
2696 ('n', 'line-number', None, _('print matching line numbers')),
2696 ('n', 'line-number', None, _('print matching line numbers')),
2697 ('r', 'rev', [],
2697 ('r', 'rev', [],
2698 _('only search files changed within revision range'), _('REV')),
2698 _('only search files changed within revision range'), _('REV')),
2699 ('', 'all-files', None,
2699 ('', 'all-files', None,
2700 _('include all files in the changeset while grepping (EXPERIMENTAL)')),
2700 _('include all files in the changeset while grepping (EXPERIMENTAL)')),
2701 ('u', 'user', None, _('list the author (long with -v)')),
2701 ('u', 'user', None, _('list the author (long with -v)')),
2702 ('d', 'date', None, _('list the date (short with -q)')),
2702 ('d', 'date', None, _('list the date (short with -q)')),
2703 ] + formatteropts + walkopts,
2703 ] + formatteropts + walkopts,
2704 _('[OPTION]... PATTERN [FILE]...'),
2704 _('[OPTION]... PATTERN [FILE]...'),
2705 helpcategory=command.CATEGORY_FILE_CONTENTS,
2705 helpcategory=command.CATEGORY_FILE_CONTENTS,
2706 inferrepo=True,
2706 inferrepo=True,
2707 intents={INTENT_READONLY})
2707 intents={INTENT_READONLY})
2708 def grep(ui, repo, pattern, *pats, **opts):
2708 def grep(ui, repo, pattern, *pats, **opts):
2709 """search revision history for a pattern in specified files
2709 """search revision history for a pattern in specified files
2710
2710
2711 Search revision history for a regular expression in the specified
2711 Search revision history for a regular expression in the specified
2712 files or the entire project.
2712 files or the entire project.
2713
2713
2714 By default, grep prints the most recent revision number for each
2714 By default, grep prints the most recent revision number for each
2715 file in which it finds a match. To get it to print every revision
2715 file in which it finds a match. To get it to print every revision
2716 that contains a change in match status ("-" for a match that becomes
2716 that contains a change in match status ("-" for a match that becomes
2717 a non-match, or "+" for a non-match that becomes a match), use the
2717 a non-match, or "+" for a non-match that becomes a match), use the
2718 --diff flag.
2718 --diff flag.
2719
2719
2720 PATTERN can be any Python (roughly Perl-compatible) regular
2720 PATTERN can be any Python (roughly Perl-compatible) regular
2721 expression.
2721 expression.
2722
2722
2723 If no FILEs are specified (and -f/--follow isn't set), all files in
2723 If no FILEs are specified (and -f/--follow isn't set), all files in
2724 the repository are searched, including those that don't exist in the
2724 the repository are searched, including those that don't exist in the
2725 current branch or have been deleted in a prior changeset.
2725 current branch or have been deleted in a prior changeset.
2726
2726
2727 .. container:: verbose
2727 .. container:: verbose
2728
2728
2729 Template:
2729 Template:
2730
2730
2731 The following keywords are supported in addition to the common template
2731 The following keywords are supported in addition to the common template
2732 keywords and functions. See also :hg:`help templates`.
2732 keywords and functions. See also :hg:`help templates`.
2733
2733
2734 :change: String. Character denoting insertion ``+`` or removal ``-``.
2734 :change: String. Character denoting insertion ``+`` or removal ``-``.
2735 Available if ``--diff`` is specified.
2735 Available if ``--diff`` is specified.
2736 :lineno: Integer. Line number of the match.
2736 :lineno: Integer. Line number of the match.
2737 :path: String. Repository-absolute path of the file.
2737 :path: String. Repository-absolute path of the file.
2738 :texts: List of text chunks.
2738 :texts: List of text chunks.
2739
2739
2740 And each entry of ``{texts}`` provides the following sub-keywords.
2740 And each entry of ``{texts}`` provides the following sub-keywords.
2741
2741
2742 :matched: Boolean. True if the chunk matches the specified pattern.
2742 :matched: Boolean. True if the chunk matches the specified pattern.
2743 :text: String. Chunk content.
2743 :text: String. Chunk content.
2744
2744
2745 See :hg:`help templates.operators` for the list expansion syntax.
2745 See :hg:`help templates.operators` for the list expansion syntax.
2746
2746
2747 Returns 0 if a match is found, 1 otherwise.
2747 Returns 0 if a match is found, 1 otherwise.
2748 """
2748 """
2749 opts = pycompat.byteskwargs(opts)
2749 opts = pycompat.byteskwargs(opts)
2750 diff = opts.get('all') or opts.get('diff')
2750 diff = opts.get('all') or opts.get('diff')
2751 all_files = opts.get('all_files')
2751 all_files = opts.get('all_files')
2752 if diff and opts.get('all_files'):
2752 if diff and opts.get('all_files'):
2753 raise error.Abort(_('--diff and --all-files are mutually exclusive'))
2753 raise error.Abort(_('--diff and --all-files are mutually exclusive'))
2754 # TODO: remove "not opts.get('rev')" if --all-files -rMULTIREV gets working
2754 # TODO: remove "not opts.get('rev')" if --all-files -rMULTIREV gets working
2755 if opts.get('all_files') is None and not opts.get('rev') and not diff:
2755 if opts.get('all_files') is None and not opts.get('rev') and not diff:
2756 # experimental config: commands.grep.all-files
2756 # experimental config: commands.grep.all-files
2757 opts['all_files'] = ui.configbool('commands', 'grep.all-files')
2757 opts['all_files'] = ui.configbool('commands', 'grep.all-files')
2758 plaingrep = opts.get('all_files') and not opts.get('rev')
2758 plaingrep = opts.get('all_files') and not opts.get('rev')
2759 if plaingrep:
2759 if plaingrep:
2760 opts['rev'] = ['wdir()']
2760 opts['rev'] = ['wdir()']
2761
2761
2762 reflags = re.M
2762 reflags = re.M
2763 if opts.get('ignore_case'):
2763 if opts.get('ignore_case'):
2764 reflags |= re.I
2764 reflags |= re.I
2765 try:
2765 try:
2766 regexp = util.re.compile(pattern, reflags)
2766 regexp = util.re.compile(pattern, reflags)
2767 except re.error as inst:
2767 except re.error as inst:
2768 ui.warn(_("grep: invalid match pattern: %s\n") % pycompat.bytestr(inst))
2768 ui.warn(_("grep: invalid match pattern: %s\n") % pycompat.bytestr(inst))
2769 return 1
2769 return 1
2770 sep, eol = ':', '\n'
2770 sep, eol = ':', '\n'
2771 if opts.get('print0'):
2771 if opts.get('print0'):
2772 sep = eol = '\0'
2772 sep = eol = '\0'
2773
2773
2774 getfile = util.lrucachefunc(repo.file)
2774 getfile = util.lrucachefunc(repo.file)
2775
2775
2776 def matchlines(body):
2776 def matchlines(body):
2777 begin = 0
2777 begin = 0
2778 linenum = 0
2778 linenum = 0
2779 while begin < len(body):
2779 while begin < len(body):
2780 match = regexp.search(body, begin)
2780 match = regexp.search(body, begin)
2781 if not match:
2781 if not match:
2782 break
2782 break
2783 mstart, mend = match.span()
2783 mstart, mend = match.span()
2784 linenum += body.count('\n', begin, mstart) + 1
2784 linenum += body.count('\n', begin, mstart) + 1
2785 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2785 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2786 begin = body.find('\n', mend) + 1 or len(body) + 1
2786 begin = body.find('\n', mend) + 1 or len(body) + 1
2787 lend = begin - 1
2787 lend = begin - 1
2788 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2788 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2789
2789
2790 class linestate(object):
2790 class linestate(object):
2791 def __init__(self, line, linenum, colstart, colend):
2791 def __init__(self, line, linenum, colstart, colend):
2792 self.line = line
2792 self.line = line
2793 self.linenum = linenum
2793 self.linenum = linenum
2794 self.colstart = colstart
2794 self.colstart = colstart
2795 self.colend = colend
2795 self.colend = colend
2796
2796
2797 def __hash__(self):
2797 def __hash__(self):
2798 return hash((self.linenum, self.line))
2798 return hash((self.linenum, self.line))
2799
2799
2800 def __eq__(self, other):
2800 def __eq__(self, other):
2801 return self.line == other.line
2801 return self.line == other.line
2802
2802
2803 def findpos(self):
2803 def findpos(self):
2804 """Iterate all (start, end) indices of matches"""
2804 """Iterate all (start, end) indices of matches"""
2805 yield self.colstart, self.colend
2805 yield self.colstart, self.colend
2806 p = self.colend
2806 p = self.colend
2807 while p < len(self.line):
2807 while p < len(self.line):
2808 m = regexp.search(self.line, p)
2808 m = regexp.search(self.line, p)
2809 if not m:
2809 if not m:
2810 break
2810 break
2811 yield m.span()
2811 yield m.span()
2812 p = m.end()
2812 p = m.end()
2813
2813
2814 matches = {}
2814 matches = {}
2815 copies = {}
2815 copies = {}
2816 def grepbody(fn, rev, body):
2816 def grepbody(fn, rev, body):
2817 matches[rev].setdefault(fn, [])
2817 matches[rev].setdefault(fn, [])
2818 m = matches[rev][fn]
2818 m = matches[rev][fn]
2819 for lnum, cstart, cend, line in matchlines(body):
2819 for lnum, cstart, cend, line in matchlines(body):
2820 s = linestate(line, lnum, cstart, cend)
2820 s = linestate(line, lnum, cstart, cend)
2821 m.append(s)
2821 m.append(s)
2822
2822
2823 def difflinestates(a, b):
2823 def difflinestates(a, b):
2824 sm = difflib.SequenceMatcher(None, a, b)
2824 sm = difflib.SequenceMatcher(None, a, b)
2825 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2825 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2826 if tag == r'insert':
2826 if tag == r'insert':
2827 for i in pycompat.xrange(blo, bhi):
2827 for i in pycompat.xrange(blo, bhi):
2828 yield ('+', b[i])
2828 yield ('+', b[i])
2829 elif tag == r'delete':
2829 elif tag == r'delete':
2830 for i in pycompat.xrange(alo, ahi):
2830 for i in pycompat.xrange(alo, ahi):
2831 yield ('-', a[i])
2831 yield ('-', a[i])
2832 elif tag == r'replace':
2832 elif tag == r'replace':
2833 for i in pycompat.xrange(alo, ahi):
2833 for i in pycompat.xrange(alo, ahi):
2834 yield ('-', a[i])
2834 yield ('-', a[i])
2835 for i in pycompat.xrange(blo, bhi):
2835 for i in pycompat.xrange(blo, bhi):
2836 yield ('+', b[i])
2836 yield ('+', b[i])
2837
2837
2838 def display(fm, fn, ctx, pstates, states):
2838 def display(fm, fn, ctx, pstates, states):
2839 rev = scmutil.intrev(ctx)
2839 rev = scmutil.intrev(ctx)
2840 if fm.isplain():
2840 if fm.isplain():
2841 formatuser = ui.shortuser
2841 formatuser = ui.shortuser
2842 else:
2842 else:
2843 formatuser = pycompat.bytestr
2843 formatuser = pycompat.bytestr
2844 if ui.quiet:
2844 if ui.quiet:
2845 datefmt = '%Y-%m-%d'
2845 datefmt = '%Y-%m-%d'
2846 else:
2846 else:
2847 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2847 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2848 found = False
2848 found = False
2849 @util.cachefunc
2849 @util.cachefunc
2850 def binary():
2850 def binary():
2851 flog = getfile(fn)
2851 flog = getfile(fn)
2852 try:
2852 try:
2853 return stringutil.binary(flog.read(ctx.filenode(fn)))
2853 return stringutil.binary(flog.read(ctx.filenode(fn)))
2854 except error.WdirUnsupported:
2854 except error.WdirUnsupported:
2855 return ctx[fn].isbinary()
2855 return ctx[fn].isbinary()
2856
2856
2857 fieldnamemap = {'filename': 'path', 'linenumber': 'lineno'}
2857 fieldnamemap = {'filename': 'path', 'linenumber': 'lineno'}
2858 if diff:
2858 if diff:
2859 iter = difflinestates(pstates, states)
2859 iter = difflinestates(pstates, states)
2860 else:
2860 else:
2861 iter = [('', l) for l in states]
2861 iter = [('', l) for l in states]
2862 for change, l in iter:
2862 for change, l in iter:
2863 fm.startitem()
2863 fm.startitem()
2864 fm.context(ctx=ctx)
2864 fm.context(ctx=ctx)
2865 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)))
2865 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)))
2866
2866
2867 cols = [
2867 cols = [
2868 ('filename', '%s', fn, True),
2868 ('filename', '%s', fn, True),
2869 ('rev', '%d', rev, not plaingrep),
2869 ('rev', '%d', rev, not plaingrep),
2870 ('linenumber', '%d', l.linenum, opts.get('line_number')),
2870 ('linenumber', '%d', l.linenum, opts.get('line_number')),
2871 ]
2871 ]
2872 if diff:
2872 if diff:
2873 cols.append(('change', '%s', change, True))
2873 cols.append(('change', '%s', change, True))
2874 cols.extend([
2874 cols.extend([
2875 ('user', '%s', formatuser(ctx.user()), opts.get('user')),
2875 ('user', '%s', formatuser(ctx.user()), opts.get('user')),
2876 ('date', '%s', fm.formatdate(ctx.date(), datefmt),
2876 ('date', '%s', fm.formatdate(ctx.date(), datefmt),
2877 opts.get('date')),
2877 opts.get('date')),
2878 ])
2878 ])
2879 lastcol = next(
2879 lastcol = next(
2880 name for name, fmt, data, cond in reversed(cols) if cond)
2880 name for name, fmt, data, cond in reversed(cols) if cond)
2881 for name, fmt, data, cond in cols:
2881 for name, fmt, data, cond in cols:
2882 field = fieldnamemap.get(name, name)
2882 field = fieldnamemap.get(name, name)
2883 fm.condwrite(cond, field, fmt, data, label='grep.%s' % name)
2883 fm.condwrite(cond, field, fmt, data, label='grep.%s' % name)
2884 if cond and name != lastcol:
2884 if cond and name != lastcol:
2885 fm.plain(sep, label='grep.sep')
2885 fm.plain(sep, label='grep.sep')
2886 if not opts.get('files_with_matches'):
2886 if not opts.get('files_with_matches'):
2887 fm.plain(sep, label='grep.sep')
2887 fm.plain(sep, label='grep.sep')
2888 if not opts.get('text') and binary():
2888 if not opts.get('text') and binary():
2889 fm.plain(_(" Binary file matches"))
2889 fm.plain(_(" Binary file matches"))
2890 else:
2890 else:
2891 displaymatches(fm.nested('texts', tmpl='{text}'), l)
2891 displaymatches(fm.nested('texts', tmpl='{text}'), l)
2892 fm.plain(eol)
2892 fm.plain(eol)
2893 found = True
2893 found = True
2894 if opts.get('files_with_matches'):
2894 if opts.get('files_with_matches'):
2895 break
2895 break
2896 return found
2896 return found
2897
2897
2898 def displaymatches(fm, l):
2898 def displaymatches(fm, l):
2899 p = 0
2899 p = 0
2900 for s, e in l.findpos():
2900 for s, e in l.findpos():
2901 if p < s:
2901 if p < s:
2902 fm.startitem()
2902 fm.startitem()
2903 fm.write('text', '%s', l.line[p:s])
2903 fm.write('text', '%s', l.line[p:s])
2904 fm.data(matched=False)
2904 fm.data(matched=False)
2905 fm.startitem()
2905 fm.startitem()
2906 fm.write('text', '%s', l.line[s:e], label='grep.match')
2906 fm.write('text', '%s', l.line[s:e], label='grep.match')
2907 fm.data(matched=True)
2907 fm.data(matched=True)
2908 p = e
2908 p = e
2909 if p < len(l.line):
2909 if p < len(l.line):
2910 fm.startitem()
2910 fm.startitem()
2911 fm.write('text', '%s', l.line[p:])
2911 fm.write('text', '%s', l.line[p:])
2912 fm.data(matched=False)
2912 fm.data(matched=False)
2913 fm.end()
2913 fm.end()
2914
2914
2915 skip = {}
2915 skip = {}
2916 revfiles = {}
2916 revfiles = {}
2917 match = scmutil.match(repo[None], pats, opts)
2917 match = scmutil.match(repo[None], pats, opts)
2918 found = False
2918 found = False
2919 follow = opts.get('follow')
2919 follow = opts.get('follow')
2920
2920
2921 def prep(ctx, fns):
2921 def prep(ctx, fns):
2922 rev = ctx.rev()
2922 rev = ctx.rev()
2923 pctx = ctx.p1()
2923 pctx = ctx.p1()
2924 parent = pctx.rev()
2924 parent = pctx.rev()
2925 matches.setdefault(rev, {})
2925 matches.setdefault(rev, {})
2926 matches.setdefault(parent, {})
2926 matches.setdefault(parent, {})
2927 files = revfiles.setdefault(rev, [])
2927 files = revfiles.setdefault(rev, [])
2928 for fn in fns:
2928 for fn in fns:
2929 flog = getfile(fn)
2929 flog = getfile(fn)
2930 try:
2930 try:
2931 fnode = ctx.filenode(fn)
2931 fnode = ctx.filenode(fn)
2932 except error.LookupError:
2932 except error.LookupError:
2933 continue
2933 continue
2934 try:
2934 try:
2935 copied = flog.renamed(fnode)
2935 copied = flog.renamed(fnode)
2936 except error.WdirUnsupported:
2936 except error.WdirUnsupported:
2937 copied = ctx[fn].renamed()
2937 copied = ctx[fn].renamed()
2938 copy = follow and copied and copied[0]
2938 copy = follow and copied and copied[0]
2939 if copy:
2939 if copy:
2940 copies.setdefault(rev, {})[fn] = copy
2940 copies.setdefault(rev, {})[fn] = copy
2941 if fn in skip:
2941 if fn in skip:
2942 if copy:
2942 if copy:
2943 skip[copy] = True
2943 skip[copy] = True
2944 continue
2944 continue
2945 files.append(fn)
2945 files.append(fn)
2946
2946
2947 if fn not in matches[rev]:
2947 if fn not in matches[rev]:
2948 try:
2948 try:
2949 content = flog.read(fnode)
2949 content = flog.read(fnode)
2950 except error.WdirUnsupported:
2950 except error.WdirUnsupported:
2951 content = ctx[fn].data()
2951 content = ctx[fn].data()
2952 grepbody(fn, rev, content)
2952 grepbody(fn, rev, content)
2953
2953
2954 pfn = copy or fn
2954 pfn = copy or fn
2955 if pfn not in matches[parent]:
2955 if pfn not in matches[parent]:
2956 try:
2956 try:
2957 fnode = pctx.filenode(pfn)
2957 fnode = pctx.filenode(pfn)
2958 grepbody(pfn, parent, flog.read(fnode))
2958 grepbody(pfn, parent, flog.read(fnode))
2959 except error.LookupError:
2959 except error.LookupError:
2960 pass
2960 pass
2961
2961
2962 ui.pager('grep')
2962 ui.pager('grep')
2963 fm = ui.formatter('grep', opts)
2963 fm = ui.formatter('grep', opts)
2964 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2964 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2965 rev = ctx.rev()
2965 rev = ctx.rev()
2966 parent = ctx.p1().rev()
2966 parent = ctx.p1().rev()
2967 for fn in sorted(revfiles.get(rev, [])):
2967 for fn in sorted(revfiles.get(rev, [])):
2968 states = matches[rev][fn]
2968 states = matches[rev][fn]
2969 copy = copies.get(rev, {}).get(fn)
2969 copy = copies.get(rev, {}).get(fn)
2970 if fn in skip:
2970 if fn in skip:
2971 if copy:
2971 if copy:
2972 skip[copy] = True
2972 skip[copy] = True
2973 continue
2973 continue
2974 pstates = matches.get(parent, {}).get(copy or fn, [])
2974 pstates = matches.get(parent, {}).get(copy or fn, [])
2975 if pstates or states:
2975 if pstates or states:
2976 r = display(fm, fn, ctx, pstates, states)
2976 r = display(fm, fn, ctx, pstates, states)
2977 found = found or r
2977 found = found or r
2978 if r and not diff and not all_files:
2978 if r and not diff and not all_files:
2979 skip[fn] = True
2979 skip[fn] = True
2980 if copy:
2980 if copy:
2981 skip[copy] = True
2981 skip[copy] = True
2982 del revfiles[rev]
2982 del revfiles[rev]
2983 # We will keep the matches dict for the duration of the window
2983 # We will keep the matches dict for the duration of the window
2984 # clear the matches dict once the window is over
2984 # clear the matches dict once the window is over
2985 if not revfiles:
2985 if not revfiles:
2986 matches.clear()
2986 matches.clear()
2987 fm.end()
2987 fm.end()
2988
2988
2989 return not found
2989 return not found
2990
2990
2991 @command('heads',
2991 @command('heads',
2992 [('r', 'rev', '',
2992 [('r', 'rev', '',
2993 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2993 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2994 ('t', 'topo', False, _('show topological heads only')),
2994 ('t', 'topo', False, _('show topological heads only')),
2995 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2995 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2996 ('c', 'closed', False, _('show normal and closed branch heads')),
2996 ('c', 'closed', False, _('show normal and closed branch heads')),
2997 ] + templateopts,
2997 ] + templateopts,
2998 _('[-ct] [-r STARTREV] [REV]...'),
2998 _('[-ct] [-r STARTREV] [REV]...'),
2999 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
2999 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3000 intents={INTENT_READONLY})
3000 intents={INTENT_READONLY})
3001 def heads(ui, repo, *branchrevs, **opts):
3001 def heads(ui, repo, *branchrevs, **opts):
3002 """show branch heads
3002 """show branch heads
3003
3003
3004 With no arguments, show all open branch heads in the repository.
3004 With no arguments, show all open branch heads in the repository.
3005 Branch heads are changesets that have no descendants on the
3005 Branch heads are changesets that have no descendants on the
3006 same branch. They are where development generally takes place and
3006 same branch. They are where development generally takes place and
3007 are the usual targets for update and merge operations.
3007 are the usual targets for update and merge operations.
3008
3008
3009 If one or more REVs are given, only open branch heads on the
3009 If one or more REVs are given, only open branch heads on the
3010 branches associated with the specified changesets are shown. This
3010 branches associated with the specified changesets are shown. This
3011 means that you can use :hg:`heads .` to see the heads on the
3011 means that you can use :hg:`heads .` to see the heads on the
3012 currently checked-out branch.
3012 currently checked-out branch.
3013
3013
3014 If -c/--closed is specified, also show branch heads marked closed
3014 If -c/--closed is specified, also show branch heads marked closed
3015 (see :hg:`commit --close-branch`).
3015 (see :hg:`commit --close-branch`).
3016
3016
3017 If STARTREV is specified, only those heads that are descendants of
3017 If STARTREV is specified, only those heads that are descendants of
3018 STARTREV will be displayed.
3018 STARTREV will be displayed.
3019
3019
3020 If -t/--topo is specified, named branch mechanics will be ignored and only
3020 If -t/--topo is specified, named branch mechanics will be ignored and only
3021 topological heads (changesets with no children) will be shown.
3021 topological heads (changesets with no children) will be shown.
3022
3022
3023 Returns 0 if matching heads are found, 1 if not.
3023 Returns 0 if matching heads are found, 1 if not.
3024 """
3024 """
3025
3025
3026 opts = pycompat.byteskwargs(opts)
3026 opts = pycompat.byteskwargs(opts)
3027 start = None
3027 start = None
3028 rev = opts.get('rev')
3028 rev = opts.get('rev')
3029 if rev:
3029 if rev:
3030 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3030 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3031 start = scmutil.revsingle(repo, rev, None).node()
3031 start = scmutil.revsingle(repo, rev, None).node()
3032
3032
3033 if opts.get('topo'):
3033 if opts.get('topo'):
3034 heads = [repo[h] for h in repo.heads(start)]
3034 heads = [repo[h] for h in repo.heads(start)]
3035 else:
3035 else:
3036 heads = []
3036 heads = []
3037 for branch in repo.branchmap():
3037 for branch in repo.branchmap():
3038 heads += repo.branchheads(branch, start, opts.get('closed'))
3038 heads += repo.branchheads(branch, start, opts.get('closed'))
3039 heads = [repo[h] for h in heads]
3039 heads = [repo[h] for h in heads]
3040
3040
3041 if branchrevs:
3041 if branchrevs:
3042 branches = set(repo[r].branch()
3042 branches = set(repo[r].branch()
3043 for r in scmutil.revrange(repo, branchrevs))
3043 for r in scmutil.revrange(repo, branchrevs))
3044 heads = [h for h in heads if h.branch() in branches]
3044 heads = [h for h in heads if h.branch() in branches]
3045
3045
3046 if opts.get('active') and branchrevs:
3046 if opts.get('active') and branchrevs:
3047 dagheads = repo.heads(start)
3047 dagheads = repo.heads(start)
3048 heads = [h for h in heads if h.node() in dagheads]
3048 heads = [h for h in heads if h.node() in dagheads]
3049
3049
3050 if branchrevs:
3050 if branchrevs:
3051 haveheads = set(h.branch() for h in heads)
3051 haveheads = set(h.branch() for h in heads)
3052 if branches - haveheads:
3052 if branches - haveheads:
3053 headless = ', '.join(b for b in branches - haveheads)
3053 headless = ', '.join(b for b in branches - haveheads)
3054 msg = _('no open branch heads found on branches %s')
3054 msg = _('no open branch heads found on branches %s')
3055 if opts.get('rev'):
3055 if opts.get('rev'):
3056 msg += _(' (started at %s)') % opts['rev']
3056 msg += _(' (started at %s)') % opts['rev']
3057 ui.warn((msg + '\n') % headless)
3057 ui.warn((msg + '\n') % headless)
3058
3058
3059 if not heads:
3059 if not heads:
3060 return 1
3060 return 1
3061
3061
3062 ui.pager('heads')
3062 ui.pager('heads')
3063 heads = sorted(heads, key=lambda x: -x.rev())
3063 heads = sorted(heads, key=lambda x: -x.rev())
3064 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3064 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3065 for ctx in heads:
3065 for ctx in heads:
3066 displayer.show(ctx)
3066 displayer.show(ctx)
3067 displayer.close()
3067 displayer.close()
3068
3068
3069 @command('help',
3069 @command('help',
3070 [('e', 'extension', None, _('show only help for extensions')),
3070 [('e', 'extension', None, _('show only help for extensions')),
3071 ('c', 'command', None, _('show only help for commands')),
3071 ('c', 'command', None, _('show only help for commands')),
3072 ('k', 'keyword', None, _('show topics matching keyword')),
3072 ('k', 'keyword', None, _('show topics matching keyword')),
3073 ('s', 'system', [],
3073 ('s', 'system', [],
3074 _('show help for specific platform(s)'), _('PLATFORM')),
3074 _('show help for specific platform(s)'), _('PLATFORM')),
3075 ],
3075 ],
3076 _('[-eck] [-s PLATFORM] [TOPIC]'),
3076 _('[-eck] [-s PLATFORM] [TOPIC]'),
3077 helpcategory=command.CATEGORY_HELP,
3077 helpcategory=command.CATEGORY_HELP,
3078 norepo=True,
3078 norepo=True,
3079 intents={INTENT_READONLY})
3079 intents={INTENT_READONLY})
3080 def help_(ui, name=None, **opts):
3080 def help_(ui, name=None, **opts):
3081 """show help for a given topic or a help overview
3081 """show help for a given topic or a help overview
3082
3082
3083 With no arguments, print a list of commands with short help messages.
3083 With no arguments, print a list of commands with short help messages.
3084
3084
3085 Given a topic, extension, or command name, print help for that
3085 Given a topic, extension, or command name, print help for that
3086 topic.
3086 topic.
3087
3087
3088 Returns 0 if successful.
3088 Returns 0 if successful.
3089 """
3089 """
3090
3090
3091 keep = opts.get(r'system') or []
3091 keep = opts.get(r'system') or []
3092 if len(keep) == 0:
3092 if len(keep) == 0:
3093 if pycompat.sysplatform.startswith('win'):
3093 if pycompat.sysplatform.startswith('win'):
3094 keep.append('windows')
3094 keep.append('windows')
3095 elif pycompat.sysplatform == 'OpenVMS':
3095 elif pycompat.sysplatform == 'OpenVMS':
3096 keep.append('vms')
3096 keep.append('vms')
3097 elif pycompat.sysplatform == 'plan9':
3097 elif pycompat.sysplatform == 'plan9':
3098 keep.append('plan9')
3098 keep.append('plan9')
3099 else:
3099 else:
3100 keep.append('unix')
3100 keep.append('unix')
3101 keep.append(pycompat.sysplatform.lower())
3101 keep.append(pycompat.sysplatform.lower())
3102 if ui.verbose:
3102 if ui.verbose:
3103 keep.append('verbose')
3103 keep.append('verbose')
3104
3104
3105 commands = sys.modules[__name__]
3105 commands = sys.modules[__name__]
3106 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3106 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3107 ui.pager('help')
3107 ui.pager('help')
3108 ui.write(formatted)
3108 ui.write(formatted)
3109
3109
3110
3110
3111 @command('identify|id',
3111 @command('identify|id',
3112 [('r', 'rev', '',
3112 [('r', 'rev', '',
3113 _('identify the specified revision'), _('REV')),
3113 _('identify the specified revision'), _('REV')),
3114 ('n', 'num', None, _('show local revision number')),
3114 ('n', 'num', None, _('show local revision number')),
3115 ('i', 'id', None, _('show global revision id')),
3115 ('i', 'id', None, _('show global revision id')),
3116 ('b', 'branch', None, _('show branch')),
3116 ('b', 'branch', None, _('show branch')),
3117 ('t', 'tags', None, _('show tags')),
3117 ('t', 'tags', None, _('show tags')),
3118 ('B', 'bookmarks', None, _('show bookmarks')),
3118 ('B', 'bookmarks', None, _('show bookmarks')),
3119 ] + remoteopts + formatteropts,
3119 ] + remoteopts + formatteropts,
3120 _('[-nibtB] [-r REV] [SOURCE]'),
3120 _('[-nibtB] [-r REV] [SOURCE]'),
3121 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3121 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3122 optionalrepo=True,
3122 optionalrepo=True,
3123 intents={INTENT_READONLY})
3123 intents={INTENT_READONLY})
3124 def identify(ui, repo, source=None, rev=None,
3124 def identify(ui, repo, source=None, rev=None,
3125 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3125 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3126 """identify the working directory or specified revision
3126 """identify the working directory or specified revision
3127
3127
3128 Print a summary identifying the repository state at REV using one or
3128 Print a summary identifying the repository state at REV using one or
3129 two parent hash identifiers, followed by a "+" if the working
3129 two parent hash identifiers, followed by a "+" if the working
3130 directory has uncommitted changes, the branch name (if not default),
3130 directory has uncommitted changes, the branch name (if not default),
3131 a list of tags, and a list of bookmarks.
3131 a list of tags, and a list of bookmarks.
3132
3132
3133 When REV is not given, print a summary of the current state of the
3133 When REV is not given, print a summary of the current state of the
3134 repository including the working directory. Specify -r. to get information
3134 repository including the working directory. Specify -r. to get information
3135 of the working directory parent without scanning uncommitted changes.
3135 of the working directory parent without scanning uncommitted changes.
3136
3136
3137 Specifying a path to a repository root or Mercurial bundle will
3137 Specifying a path to a repository root or Mercurial bundle will
3138 cause lookup to operate on that repository/bundle.
3138 cause lookup to operate on that repository/bundle.
3139
3139
3140 .. container:: verbose
3140 .. container:: verbose
3141
3141
3142 Template:
3142 Template:
3143
3143
3144 The following keywords are supported in addition to the common template
3144 The following keywords are supported in addition to the common template
3145 keywords and functions. See also :hg:`help templates`.
3145 keywords and functions. See also :hg:`help templates`.
3146
3146
3147 :dirty: String. Character ``+`` denoting if the working directory has
3147 :dirty: String. Character ``+`` denoting if the working directory has
3148 uncommitted changes.
3148 uncommitted changes.
3149 :id: String. One or two nodes, optionally followed by ``+``.
3149 :id: String. One or two nodes, optionally followed by ``+``.
3150 :parents: List of strings. Parent nodes of the changeset.
3150 :parents: List of strings. Parent nodes of the changeset.
3151
3151
3152 Examples:
3152 Examples:
3153
3153
3154 - generate a build identifier for the working directory::
3154 - generate a build identifier for the working directory::
3155
3155
3156 hg id --id > build-id.dat
3156 hg id --id > build-id.dat
3157
3157
3158 - find the revision corresponding to a tag::
3158 - find the revision corresponding to a tag::
3159
3159
3160 hg id -n -r 1.3
3160 hg id -n -r 1.3
3161
3161
3162 - check the most recent revision of a remote repository::
3162 - check the most recent revision of a remote repository::
3163
3163
3164 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3164 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3165
3165
3166 See :hg:`log` for generating more information about specific revisions,
3166 See :hg:`log` for generating more information about specific revisions,
3167 including full hash identifiers.
3167 including full hash identifiers.
3168
3168
3169 Returns 0 if successful.
3169 Returns 0 if successful.
3170 """
3170 """
3171
3171
3172 opts = pycompat.byteskwargs(opts)
3172 opts = pycompat.byteskwargs(opts)
3173 if not repo and not source:
3173 if not repo and not source:
3174 raise error.Abort(_("there is no Mercurial repository here "
3174 raise error.Abort(_("there is no Mercurial repository here "
3175 "(.hg not found)"))
3175 "(.hg not found)"))
3176
3176
3177 default = not (num or id or branch or tags or bookmarks)
3177 default = not (num or id or branch or tags or bookmarks)
3178 output = []
3178 output = []
3179 revs = []
3179 revs = []
3180
3180
3181 if source:
3181 if source:
3182 source, branches = hg.parseurl(ui.expandpath(source))
3182 source, branches = hg.parseurl(ui.expandpath(source))
3183 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3183 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3184 repo = peer.local()
3184 repo = peer.local()
3185 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3185 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3186
3186
3187 fm = ui.formatter('identify', opts)
3187 fm = ui.formatter('identify', opts)
3188 fm.startitem()
3188 fm.startitem()
3189
3189
3190 if not repo:
3190 if not repo:
3191 if num or branch or tags:
3191 if num or branch or tags:
3192 raise error.Abort(
3192 raise error.Abort(
3193 _("can't query remote revision number, branch, or tags"))
3193 _("can't query remote revision number, branch, or tags"))
3194 if not rev and revs:
3194 if not rev and revs:
3195 rev = revs[0]
3195 rev = revs[0]
3196 if not rev:
3196 if not rev:
3197 rev = "tip"
3197 rev = "tip"
3198
3198
3199 remoterev = peer.lookup(rev)
3199 remoterev = peer.lookup(rev)
3200 hexrev = fm.hexfunc(remoterev)
3200 hexrev = fm.hexfunc(remoterev)
3201 if default or id:
3201 if default or id:
3202 output = [hexrev]
3202 output = [hexrev]
3203 fm.data(id=hexrev)
3203 fm.data(id=hexrev)
3204
3204
3205 @util.cachefunc
3205 @util.cachefunc
3206 def getbms():
3206 def getbms():
3207 bms = []
3207 bms = []
3208
3208
3209 if 'bookmarks' in peer.listkeys('namespaces'):
3209 if 'bookmarks' in peer.listkeys('namespaces'):
3210 hexremoterev = hex(remoterev)
3210 hexremoterev = hex(remoterev)
3211 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3211 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3212 if bmr == hexremoterev]
3212 if bmr == hexremoterev]
3213
3213
3214 return sorted(bms)
3214 return sorted(bms)
3215
3215
3216 if fm.isplain():
3216 if fm.isplain():
3217 if bookmarks:
3217 if bookmarks:
3218 output.extend(getbms())
3218 output.extend(getbms())
3219 elif default and not ui.quiet:
3219 elif default and not ui.quiet:
3220 # multiple bookmarks for a single parent separated by '/'
3220 # multiple bookmarks for a single parent separated by '/'
3221 bm = '/'.join(getbms())
3221 bm = '/'.join(getbms())
3222 if bm:
3222 if bm:
3223 output.append(bm)
3223 output.append(bm)
3224 else:
3224 else:
3225 fm.data(node=hex(remoterev))
3225 fm.data(node=hex(remoterev))
3226 if bookmarks or 'bookmarks' in fm.datahint():
3226 if bookmarks or 'bookmarks' in fm.datahint():
3227 fm.data(bookmarks=fm.formatlist(getbms(), name='bookmark'))
3227 fm.data(bookmarks=fm.formatlist(getbms(), name='bookmark'))
3228 else:
3228 else:
3229 if rev:
3229 if rev:
3230 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3230 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3231 ctx = scmutil.revsingle(repo, rev, None)
3231 ctx = scmutil.revsingle(repo, rev, None)
3232
3232
3233 if ctx.rev() is None:
3233 if ctx.rev() is None:
3234 ctx = repo[None]
3234 ctx = repo[None]
3235 parents = ctx.parents()
3235 parents = ctx.parents()
3236 taglist = []
3236 taglist = []
3237 for p in parents:
3237 for p in parents:
3238 taglist.extend(p.tags())
3238 taglist.extend(p.tags())
3239
3239
3240 dirty = ""
3240 dirty = ""
3241 if ctx.dirty(missing=True, merge=False, branch=False):
3241 if ctx.dirty(missing=True, merge=False, branch=False):
3242 dirty = '+'
3242 dirty = '+'
3243 fm.data(dirty=dirty)
3243 fm.data(dirty=dirty)
3244
3244
3245 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3245 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3246 if default or id:
3246 if default or id:
3247 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
3247 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
3248 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
3248 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
3249
3249
3250 if num:
3250 if num:
3251 numoutput = ["%d" % p.rev() for p in parents]
3251 numoutput = ["%d" % p.rev() for p in parents]
3252 output.append("%s%s" % ('+'.join(numoutput), dirty))
3252 output.append("%s%s" % ('+'.join(numoutput), dirty))
3253
3253
3254 fm.data(parents=fm.formatlist([fm.hexfunc(p.node())
3254 fm.data(parents=fm.formatlist([fm.hexfunc(p.node())
3255 for p in parents], name='node'))
3255 for p in parents], name='node'))
3256 else:
3256 else:
3257 hexoutput = fm.hexfunc(ctx.node())
3257 hexoutput = fm.hexfunc(ctx.node())
3258 if default or id:
3258 if default or id:
3259 output = [hexoutput]
3259 output = [hexoutput]
3260 fm.data(id=hexoutput)
3260 fm.data(id=hexoutput)
3261
3261
3262 if num:
3262 if num:
3263 output.append(pycompat.bytestr(ctx.rev()))
3263 output.append(pycompat.bytestr(ctx.rev()))
3264 taglist = ctx.tags()
3264 taglist = ctx.tags()
3265
3265
3266 if default and not ui.quiet:
3266 if default and not ui.quiet:
3267 b = ctx.branch()
3267 b = ctx.branch()
3268 if b != 'default':
3268 if b != 'default':
3269 output.append("(%s)" % b)
3269 output.append("(%s)" % b)
3270
3270
3271 # multiple tags for a single parent separated by '/'
3271 # multiple tags for a single parent separated by '/'
3272 t = '/'.join(taglist)
3272 t = '/'.join(taglist)
3273 if t:
3273 if t:
3274 output.append(t)
3274 output.append(t)
3275
3275
3276 # multiple bookmarks for a single parent separated by '/'
3276 # multiple bookmarks for a single parent separated by '/'
3277 bm = '/'.join(ctx.bookmarks())
3277 bm = '/'.join(ctx.bookmarks())
3278 if bm:
3278 if bm:
3279 output.append(bm)
3279 output.append(bm)
3280 else:
3280 else:
3281 if branch:
3281 if branch:
3282 output.append(ctx.branch())
3282 output.append(ctx.branch())
3283
3283
3284 if tags:
3284 if tags:
3285 output.extend(taglist)
3285 output.extend(taglist)
3286
3286
3287 if bookmarks:
3287 if bookmarks:
3288 output.extend(ctx.bookmarks())
3288 output.extend(ctx.bookmarks())
3289
3289
3290 fm.data(node=ctx.hex())
3290 fm.data(node=ctx.hex())
3291 fm.data(branch=ctx.branch())
3291 fm.data(branch=ctx.branch())
3292 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
3292 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
3293 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
3293 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
3294 fm.context(ctx=ctx)
3294 fm.context(ctx=ctx)
3295
3295
3296 fm.plain("%s\n" % ' '.join(output))
3296 fm.plain("%s\n" % ' '.join(output))
3297 fm.end()
3297 fm.end()
3298
3298
3299 @command('import|patch',
3299 @command('import|patch',
3300 [('p', 'strip', 1,
3300 [('p', 'strip', 1,
3301 _('directory strip option for patch. This has the same '
3301 _('directory strip option for patch. This has the same '
3302 'meaning as the corresponding patch option'), _('NUM')),
3302 'meaning as the corresponding patch option'), _('NUM')),
3303 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3303 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3304 ('e', 'edit', False, _('invoke editor on commit messages')),
3304 ('e', 'edit', False, _('invoke editor on commit messages')),
3305 ('f', 'force', None,
3305 ('f', 'force', None,
3306 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
3306 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
3307 ('', 'no-commit', None,
3307 ('', 'no-commit', None,
3308 _("don't commit, just update the working directory")),
3308 _("don't commit, just update the working directory")),
3309 ('', 'bypass', None,
3309 ('', 'bypass', None,
3310 _("apply patch without touching the working directory")),
3310 _("apply patch without touching the working directory")),
3311 ('', 'partial', None,
3311 ('', 'partial', None,
3312 _('commit even if some hunks fail')),
3312 _('commit even if some hunks fail')),
3313 ('', 'exact', None,
3313 ('', 'exact', None,
3314 _('abort if patch would apply lossily')),
3314 _('abort if patch would apply lossily')),
3315 ('', 'prefix', '',
3315 ('', 'prefix', '',
3316 _('apply patch to subdirectory'), _('DIR')),
3316 _('apply patch to subdirectory'), _('DIR')),
3317 ('', 'import-branch', None,
3317 ('', 'import-branch', None,
3318 _('use any branch information in patch (implied by --exact)'))] +
3318 _('use any branch information in patch (implied by --exact)'))] +
3319 commitopts + commitopts2 + similarityopts,
3319 commitopts + commitopts2 + similarityopts,
3320 _('[OPTION]... PATCH...'),
3320 _('[OPTION]... PATCH...'),
3321 helpcategory=command.CATEGORY_IMPORT_EXPORT)
3321 helpcategory=command.CATEGORY_IMPORT_EXPORT)
3322 def import_(ui, repo, patch1=None, *patches, **opts):
3322 def import_(ui, repo, patch1=None, *patches, **opts):
3323 """import an ordered set of patches
3323 """import an ordered set of patches
3324
3324
3325 Import a list of patches and commit them individually (unless
3325 Import a list of patches and commit them individually (unless
3326 --no-commit is specified).
3326 --no-commit is specified).
3327
3327
3328 To read a patch from standard input (stdin), use "-" as the patch
3328 To read a patch from standard input (stdin), use "-" as the patch
3329 name. If a URL is specified, the patch will be downloaded from
3329 name. If a URL is specified, the patch will be downloaded from
3330 there.
3330 there.
3331
3331
3332 Import first applies changes to the working directory (unless
3332 Import first applies changes to the working directory (unless
3333 --bypass is specified), import will abort if there are outstanding
3333 --bypass is specified), import will abort if there are outstanding
3334 changes.
3334 changes.
3335
3335
3336 Use --bypass to apply and commit patches directly to the
3336 Use --bypass to apply and commit patches directly to the
3337 repository, without affecting the working directory. Without
3337 repository, without affecting the working directory. Without
3338 --exact, patches will be applied on top of the working directory
3338 --exact, patches will be applied on top of the working directory
3339 parent revision.
3339 parent revision.
3340
3340
3341 You can import a patch straight from a mail message. Even patches
3341 You can import a patch straight from a mail message. Even patches
3342 as attachments work (to use the body part, it must have type
3342 as attachments work (to use the body part, it must have type
3343 text/plain or text/x-patch). From and Subject headers of email
3343 text/plain or text/x-patch). From and Subject headers of email
3344 message are used as default committer and commit message. All
3344 message are used as default committer and commit message. All
3345 text/plain body parts before first diff are added to the commit
3345 text/plain body parts before first diff are added to the commit
3346 message.
3346 message.
3347
3347
3348 If the imported patch was generated by :hg:`export`, user and
3348 If the imported patch was generated by :hg:`export`, user and
3349 description from patch override values from message headers and
3349 description from patch override values from message headers and
3350 body. Values given on command line with -m/--message and -u/--user
3350 body. Values given on command line with -m/--message and -u/--user
3351 override these.
3351 override these.
3352
3352
3353 If --exact is specified, import will set the working directory to
3353 If --exact is specified, import will set the working directory to
3354 the parent of each patch before applying it, and will abort if the
3354 the parent of each patch before applying it, and will abort if the
3355 resulting changeset has a different ID than the one recorded in
3355 resulting changeset has a different ID than the one recorded in
3356 the patch. This will guard against various ways that portable
3356 the patch. This will guard against various ways that portable
3357 patch formats and mail systems might fail to transfer Mercurial
3357 patch formats and mail systems might fail to transfer Mercurial
3358 data or metadata. See :hg:`bundle` for lossless transmission.
3358 data or metadata. See :hg:`bundle` for lossless transmission.
3359
3359
3360 Use --partial to ensure a changeset will be created from the patch
3360 Use --partial to ensure a changeset will be created from the patch
3361 even if some hunks fail to apply. Hunks that fail to apply will be
3361 even if some hunks fail to apply. Hunks that fail to apply will be
3362 written to a <target-file>.rej file. Conflicts can then be resolved
3362 written to a <target-file>.rej file. Conflicts can then be resolved
3363 by hand before :hg:`commit --amend` is run to update the created
3363 by hand before :hg:`commit --amend` is run to update the created
3364 changeset. This flag exists to let people import patches that
3364 changeset. This flag exists to let people import patches that
3365 partially apply without losing the associated metadata (author,
3365 partially apply without losing the associated metadata (author,
3366 date, description, ...).
3366 date, description, ...).
3367
3367
3368 .. note::
3368 .. note::
3369
3369
3370 When no hunks apply cleanly, :hg:`import --partial` will create
3370 When no hunks apply cleanly, :hg:`import --partial` will create
3371 an empty changeset, importing only the patch metadata.
3371 an empty changeset, importing only the patch metadata.
3372
3372
3373 With -s/--similarity, hg will attempt to discover renames and
3373 With -s/--similarity, hg will attempt to discover renames and
3374 copies in the patch in the same way as :hg:`addremove`.
3374 copies in the patch in the same way as :hg:`addremove`.
3375
3375
3376 It is possible to use external patch programs to perform the patch
3376 It is possible to use external patch programs to perform the patch
3377 by setting the ``ui.patch`` configuration option. For the default
3377 by setting the ``ui.patch`` configuration option. For the default
3378 internal tool, the fuzz can also be configured via ``patch.fuzz``.
3378 internal tool, the fuzz can also be configured via ``patch.fuzz``.
3379 See :hg:`help config` for more information about configuration
3379 See :hg:`help config` for more information about configuration
3380 files and how to use these options.
3380 files and how to use these options.
3381
3381
3382 See :hg:`help dates` for a list of formats valid for -d/--date.
3382 See :hg:`help dates` for a list of formats valid for -d/--date.
3383
3383
3384 .. container:: verbose
3384 .. container:: verbose
3385
3385
3386 Examples:
3386 Examples:
3387
3387
3388 - import a traditional patch from a website and detect renames::
3388 - import a traditional patch from a website and detect renames::
3389
3389
3390 hg import -s 80 http://example.com/bugfix.patch
3390 hg import -s 80 http://example.com/bugfix.patch
3391
3391
3392 - import a changeset from an hgweb server::
3392 - import a changeset from an hgweb server::
3393
3393
3394 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3394 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3395
3395
3396 - import all the patches in an Unix-style mbox::
3396 - import all the patches in an Unix-style mbox::
3397
3397
3398 hg import incoming-patches.mbox
3398 hg import incoming-patches.mbox
3399
3399
3400 - import patches from stdin::
3400 - import patches from stdin::
3401
3401
3402 hg import -
3402 hg import -
3403
3403
3404 - attempt to exactly restore an exported changeset (not always
3404 - attempt to exactly restore an exported changeset (not always
3405 possible)::
3405 possible)::
3406
3406
3407 hg import --exact proposed-fix.patch
3407 hg import --exact proposed-fix.patch
3408
3408
3409 - use an external tool to apply a patch which is too fuzzy for
3409 - use an external tool to apply a patch which is too fuzzy for
3410 the default internal tool.
3410 the default internal tool.
3411
3411
3412 hg import --config ui.patch="patch --merge" fuzzy.patch
3412 hg import --config ui.patch="patch --merge" fuzzy.patch
3413
3413
3414 - change the default fuzzing from 2 to a less strict 7
3414 - change the default fuzzing from 2 to a less strict 7
3415
3415
3416 hg import --config ui.fuzz=7 fuzz.patch
3416 hg import --config ui.fuzz=7 fuzz.patch
3417
3417
3418 Returns 0 on success, 1 on partial success (see --partial).
3418 Returns 0 on success, 1 on partial success (see --partial).
3419 """
3419 """
3420
3420
3421 opts = pycompat.byteskwargs(opts)
3421 opts = pycompat.byteskwargs(opts)
3422 if not patch1:
3422 if not patch1:
3423 raise error.Abort(_('need at least one patch to import'))
3423 raise error.Abort(_('need at least one patch to import'))
3424
3424
3425 patches = (patch1,) + patches
3425 patches = (patch1,) + patches
3426
3426
3427 date = opts.get('date')
3427 date = opts.get('date')
3428 if date:
3428 if date:
3429 opts['date'] = dateutil.parsedate(date)
3429 opts['date'] = dateutil.parsedate(date)
3430
3430
3431 exact = opts.get('exact')
3431 exact = opts.get('exact')
3432 update = not opts.get('bypass')
3432 update = not opts.get('bypass')
3433 if not update and opts.get('no_commit'):
3433 if not update and opts.get('no_commit'):
3434 raise error.Abort(_('cannot use --no-commit with --bypass'))
3434 raise error.Abort(_('cannot use --no-commit with --bypass'))
3435 try:
3435 try:
3436 sim = float(opts.get('similarity') or 0)
3436 sim = float(opts.get('similarity') or 0)
3437 except ValueError:
3437 except ValueError:
3438 raise error.Abort(_('similarity must be a number'))
3438 raise error.Abort(_('similarity must be a number'))
3439 if sim < 0 or sim > 100:
3439 if sim < 0 or sim > 100:
3440 raise error.Abort(_('similarity must be between 0 and 100'))
3440 raise error.Abort(_('similarity must be between 0 and 100'))
3441 if sim and not update:
3441 if sim and not update:
3442 raise error.Abort(_('cannot use --similarity with --bypass'))
3442 raise error.Abort(_('cannot use --similarity with --bypass'))
3443 if exact:
3443 if exact:
3444 if opts.get('edit'):
3444 if opts.get('edit'):
3445 raise error.Abort(_('cannot use --exact with --edit'))
3445 raise error.Abort(_('cannot use --exact with --edit'))
3446 if opts.get('prefix'):
3446 if opts.get('prefix'):
3447 raise error.Abort(_('cannot use --exact with --prefix'))
3447 raise error.Abort(_('cannot use --exact with --prefix'))
3448
3448
3449 base = opts["base"]
3449 base = opts["base"]
3450 msgs = []
3450 msgs = []
3451 ret = 0
3451 ret = 0
3452
3452
3453 with repo.wlock():
3453 with repo.wlock():
3454 if update:
3454 if update:
3455 cmdutil.checkunfinished(repo)
3455 cmdutil.checkunfinished(repo)
3456 if (exact or not opts.get('force')):
3456 if (exact or not opts.get('force')):
3457 cmdutil.bailifchanged(repo)
3457 cmdutil.bailifchanged(repo)
3458
3458
3459 if not opts.get('no_commit'):
3459 if not opts.get('no_commit'):
3460 lock = repo.lock
3460 lock = repo.lock
3461 tr = lambda: repo.transaction('import')
3461 tr = lambda: repo.transaction('import')
3462 dsguard = util.nullcontextmanager
3462 dsguard = util.nullcontextmanager
3463 else:
3463 else:
3464 lock = util.nullcontextmanager
3464 lock = util.nullcontextmanager
3465 tr = util.nullcontextmanager
3465 tr = util.nullcontextmanager
3466 dsguard = lambda: dirstateguard.dirstateguard(repo, 'import')
3466 dsguard = lambda: dirstateguard.dirstateguard(repo, 'import')
3467 with lock(), tr(), dsguard():
3467 with lock(), tr(), dsguard():
3468 parents = repo[None].parents()
3468 parents = repo[None].parents()
3469 for patchurl in patches:
3469 for patchurl in patches:
3470 if patchurl == '-':
3470 if patchurl == '-':
3471 ui.status(_('applying patch from stdin\n'))
3471 ui.status(_('applying patch from stdin\n'))
3472 patchfile = ui.fin
3472 patchfile = ui.fin
3473 patchurl = 'stdin' # for error message
3473 patchurl = 'stdin' # for error message
3474 else:
3474 else:
3475 patchurl = os.path.join(base, patchurl)
3475 patchurl = os.path.join(base, patchurl)
3476 ui.status(_('applying %s\n') % patchurl)
3476 ui.status(_('applying %s\n') % patchurl)
3477 patchfile = hg.openpath(ui, patchurl)
3477 patchfile = hg.openpath(ui, patchurl)
3478
3478
3479 haspatch = False
3479 haspatch = False
3480 for hunk in patch.split(patchfile):
3480 for hunk in patch.split(patchfile):
3481 with patch.extract(ui, hunk) as patchdata:
3481 with patch.extract(ui, hunk) as patchdata:
3482 msg, node, rej = cmdutil.tryimportone(ui, repo,
3482 msg, node, rej = cmdutil.tryimportone(ui, repo,
3483 patchdata,
3483 patchdata,
3484 parents, opts,
3484 parents, opts,
3485 msgs, hg.clean)
3485 msgs, hg.clean)
3486 if msg:
3486 if msg:
3487 haspatch = True
3487 haspatch = True
3488 ui.note(msg + '\n')
3488 ui.note(msg + '\n')
3489 if update or exact:
3489 if update or exact:
3490 parents = repo[None].parents()
3490 parents = repo[None].parents()
3491 else:
3491 else:
3492 parents = [repo[node]]
3492 parents = [repo[node]]
3493 if rej:
3493 if rej:
3494 ui.write_err(_("patch applied partially\n"))
3494 ui.write_err(_("patch applied partially\n"))
3495 ui.write_err(_("(fix the .rej files and run "
3495 ui.write_err(_("(fix the .rej files and run "
3496 "`hg commit --amend`)\n"))
3496 "`hg commit --amend`)\n"))
3497 ret = 1
3497 ret = 1
3498 break
3498 break
3499
3499
3500 if not haspatch:
3500 if not haspatch:
3501 raise error.Abort(_('%s: no diffs found') % patchurl)
3501 raise error.Abort(_('%s: no diffs found') % patchurl)
3502
3502
3503 if msgs:
3503 if msgs:
3504 repo.savecommitmessage('\n* * *\n'.join(msgs))
3504 repo.savecommitmessage('\n* * *\n'.join(msgs))
3505 return ret
3505 return ret
3506
3506
3507 @command('incoming|in',
3507 @command('incoming|in',
3508 [('f', 'force', None,
3508 [('f', 'force', None,
3509 _('run even if remote repository is unrelated')),
3509 _('run even if remote repository is unrelated')),
3510 ('n', 'newest-first', None, _('show newest record first')),
3510 ('n', 'newest-first', None, _('show newest record first')),
3511 ('', 'bundle', '',
3511 ('', 'bundle', '',
3512 _('file to store the bundles into'), _('FILE')),
3512 _('file to store the bundles into'), _('FILE')),
3513 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3513 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3514 ('B', 'bookmarks', False, _("compare bookmarks")),
3514 ('B', 'bookmarks', False, _("compare bookmarks")),
3515 ('b', 'branch', [],
3515 ('b', 'branch', [],
3516 _('a specific branch you would like to pull'), _('BRANCH')),
3516 _('a specific branch you would like to pull'), _('BRANCH')),
3517 ] + logopts + remoteopts + subrepoopts,
3517 ] + logopts + remoteopts + subrepoopts,
3518 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
3518 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
3519 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
3519 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
3520 def incoming(ui, repo, source="default", **opts):
3520 def incoming(ui, repo, source="default", **opts):
3521 """show new changesets found in source
3521 """show new changesets found in source
3522
3522
3523 Show new changesets found in the specified path/URL or the default
3523 Show new changesets found in the specified path/URL or the default
3524 pull location. These are the changesets that would have been pulled
3524 pull location. These are the changesets that would have been pulled
3525 by :hg:`pull` at the time you issued this command.
3525 by :hg:`pull` at the time you issued this command.
3526
3526
3527 See pull for valid source format details.
3527 See pull for valid source format details.
3528
3528
3529 .. container:: verbose
3529 .. container:: verbose
3530
3530
3531 With -B/--bookmarks, the result of bookmark comparison between
3531 With -B/--bookmarks, the result of bookmark comparison between
3532 local and remote repositories is displayed. With -v/--verbose,
3532 local and remote repositories is displayed. With -v/--verbose,
3533 status is also displayed for each bookmark like below::
3533 status is also displayed for each bookmark like below::
3534
3534
3535 BM1 01234567890a added
3535 BM1 01234567890a added
3536 BM2 1234567890ab advanced
3536 BM2 1234567890ab advanced
3537 BM3 234567890abc diverged
3537 BM3 234567890abc diverged
3538 BM4 34567890abcd changed
3538 BM4 34567890abcd changed
3539
3539
3540 The action taken locally when pulling depends on the
3540 The action taken locally when pulling depends on the
3541 status of each bookmark:
3541 status of each bookmark:
3542
3542
3543 :``added``: pull will create it
3543 :``added``: pull will create it
3544 :``advanced``: pull will update it
3544 :``advanced``: pull will update it
3545 :``diverged``: pull will create a divergent bookmark
3545 :``diverged``: pull will create a divergent bookmark
3546 :``changed``: result depends on remote changesets
3546 :``changed``: result depends on remote changesets
3547
3547
3548 From the point of view of pulling behavior, bookmark
3548 From the point of view of pulling behavior, bookmark
3549 existing only in the remote repository are treated as ``added``,
3549 existing only in the remote repository are treated as ``added``,
3550 even if it is in fact locally deleted.
3550 even if it is in fact locally deleted.
3551
3551
3552 .. container:: verbose
3552 .. container:: verbose
3553
3553
3554 For remote repository, using --bundle avoids downloading the
3554 For remote repository, using --bundle avoids downloading the
3555 changesets twice if the incoming is followed by a pull.
3555 changesets twice if the incoming is followed by a pull.
3556
3556
3557 Examples:
3557 Examples:
3558
3558
3559 - show incoming changes with patches and full description::
3559 - show incoming changes with patches and full description::
3560
3560
3561 hg incoming -vp
3561 hg incoming -vp
3562
3562
3563 - show incoming changes excluding merges, store a bundle::
3563 - show incoming changes excluding merges, store a bundle::
3564
3564
3565 hg in -vpM --bundle incoming.hg
3565 hg in -vpM --bundle incoming.hg
3566 hg pull incoming.hg
3566 hg pull incoming.hg
3567
3567
3568 - briefly list changes inside a bundle::
3568 - briefly list changes inside a bundle::
3569
3569
3570 hg in changes.hg -T "{desc|firstline}\\n"
3570 hg in changes.hg -T "{desc|firstline}\\n"
3571
3571
3572 Returns 0 if there are incoming changes, 1 otherwise.
3572 Returns 0 if there are incoming changes, 1 otherwise.
3573 """
3573 """
3574 opts = pycompat.byteskwargs(opts)
3574 opts = pycompat.byteskwargs(opts)
3575 if opts.get('graph'):
3575 if opts.get('graph'):
3576 logcmdutil.checkunsupportedgraphflags([], opts)
3576 logcmdutil.checkunsupportedgraphflags([], opts)
3577 def display(other, chlist, displayer):
3577 def display(other, chlist, displayer):
3578 revdag = logcmdutil.graphrevs(other, chlist, opts)
3578 revdag = logcmdutil.graphrevs(other, chlist, opts)
3579 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3579 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3580 graphmod.asciiedges)
3580 graphmod.asciiedges)
3581
3581
3582 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3582 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3583 return 0
3583 return 0
3584
3584
3585 if opts.get('bundle') and opts.get('subrepos'):
3585 if opts.get('bundle') and opts.get('subrepos'):
3586 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3586 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3587
3587
3588 if opts.get('bookmarks'):
3588 if opts.get('bookmarks'):
3589 source, branches = hg.parseurl(ui.expandpath(source),
3589 source, branches = hg.parseurl(ui.expandpath(source),
3590 opts.get('branch'))
3590 opts.get('branch'))
3591 other = hg.peer(repo, opts, source)
3591 other = hg.peer(repo, opts, source)
3592 if 'bookmarks' not in other.listkeys('namespaces'):
3592 if 'bookmarks' not in other.listkeys('namespaces'):
3593 ui.warn(_("remote doesn't support bookmarks\n"))
3593 ui.warn(_("remote doesn't support bookmarks\n"))
3594 return 0
3594 return 0
3595 ui.pager('incoming')
3595 ui.pager('incoming')
3596 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3596 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3597 return bookmarks.incoming(ui, repo, other)
3597 return bookmarks.incoming(ui, repo, other)
3598
3598
3599 repo._subtoppath = ui.expandpath(source)
3599 repo._subtoppath = ui.expandpath(source)
3600 try:
3600 try:
3601 return hg.incoming(ui, repo, source, opts)
3601 return hg.incoming(ui, repo, source, opts)
3602 finally:
3602 finally:
3603 del repo._subtoppath
3603 del repo._subtoppath
3604
3604
3605
3605
3606 @command('init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3606 @command('init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3607 helpcategory=command.CATEGORY_REPO_CREATION,
3607 helpcategory=command.CATEGORY_REPO_CREATION,
3608 helpbasic=True, norepo=True)
3608 helpbasic=True, norepo=True)
3609 def init(ui, dest=".", **opts):
3609 def init(ui, dest=".", **opts):
3610 """create a new repository in the given directory
3610 """create a new repository in the given directory
3611
3611
3612 Initialize a new repository in the given directory. If the given
3612 Initialize a new repository in the given directory. If the given
3613 directory does not exist, it will be created.
3613 directory does not exist, it will be created.
3614
3614
3615 If no directory is given, the current directory is used.
3615 If no directory is given, the current directory is used.
3616
3616
3617 It is possible to specify an ``ssh://`` URL as the destination.
3617 It is possible to specify an ``ssh://`` URL as the destination.
3618 See :hg:`help urls` for more information.
3618 See :hg:`help urls` for more information.
3619
3619
3620 Returns 0 on success.
3620 Returns 0 on success.
3621 """
3621 """
3622 opts = pycompat.byteskwargs(opts)
3622 opts = pycompat.byteskwargs(opts)
3623 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3623 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3624
3624
3625 @command('locate',
3625 @command('locate',
3626 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3626 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3627 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3627 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3628 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3628 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3629 ] + walkopts,
3629 ] + walkopts,
3630 _('[OPTION]... [PATTERN]...'),
3630 _('[OPTION]... [PATTERN]...'),
3631 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
3631 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
3632 def locate(ui, repo, *pats, **opts):
3632 def locate(ui, repo, *pats, **opts):
3633 """locate files matching specific patterns (DEPRECATED)
3633 """locate files matching specific patterns (DEPRECATED)
3634
3634
3635 Print files under Mercurial control in the working directory whose
3635 Print files under Mercurial control in the working directory whose
3636 names match the given patterns.
3636 names match the given patterns.
3637
3637
3638 By default, this command searches all directories in the working
3638 By default, this command searches all directories in the working
3639 directory. To search just the current directory and its
3639 directory. To search just the current directory and its
3640 subdirectories, use "--include .".
3640 subdirectories, use "--include .".
3641
3641
3642 If no patterns are given to match, this command prints the names
3642 If no patterns are given to match, this command prints the names
3643 of all files under Mercurial control in the working directory.
3643 of all files under Mercurial control in the working directory.
3644
3644
3645 If you want to feed the output of this command into the "xargs"
3645 If you want to feed the output of this command into the "xargs"
3646 command, use the -0 option to both this command and "xargs". This
3646 command, use the -0 option to both this command and "xargs". This
3647 will avoid the problem of "xargs" treating single filenames that
3647 will avoid the problem of "xargs" treating single filenames that
3648 contain whitespace as multiple filenames.
3648 contain whitespace as multiple filenames.
3649
3649
3650 See :hg:`help files` for a more versatile command.
3650 See :hg:`help files` for a more versatile command.
3651
3651
3652 Returns 0 if a match is found, 1 otherwise.
3652 Returns 0 if a match is found, 1 otherwise.
3653 """
3653 """
3654 opts = pycompat.byteskwargs(opts)
3654 opts = pycompat.byteskwargs(opts)
3655 if opts.get('print0'):
3655 if opts.get('print0'):
3656 end = '\0'
3656 end = '\0'
3657 else:
3657 else:
3658 end = '\n'
3658 end = '\n'
3659 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3659 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3660
3660
3661 ret = 1
3661 ret = 1
3662 m = scmutil.match(ctx, pats, opts, default='relglob',
3662 m = scmutil.match(ctx, pats, opts, default='relglob',
3663 badfn=lambda x, y: False)
3663 badfn=lambda x, y: False)
3664
3664
3665 ui.pager('locate')
3665 ui.pager('locate')
3666 if ctx.rev() is None:
3666 if ctx.rev() is None:
3667 # When run on the working copy, "locate" includes removed files, so
3667 # When run on the working copy, "locate" includes removed files, so
3668 # we get the list of files from the dirstate.
3668 # we get the list of files from the dirstate.
3669 filesgen = sorted(repo.dirstate.matches(m))
3669 filesgen = sorted(repo.dirstate.matches(m))
3670 else:
3670 else:
3671 filesgen = ctx.matches(m)
3671 filesgen = ctx.matches(m)
3672 for abs in filesgen:
3672 for abs in filesgen:
3673 if opts.get('fullpath'):
3673 if opts.get('fullpath'):
3674 ui.write(repo.wjoin(abs), end)
3674 ui.write(repo.wjoin(abs), end)
3675 else:
3675 else:
3676 ui.write(((pats and m.rel(abs)) or abs), end)
3676 ui.write(((pats and m.rel(abs)) or abs), end)
3677 ret = 0
3677 ret = 0
3678
3678
3679 return ret
3679 return ret
3680
3680
3681 @command('log|history',
3681 @command('log|history',
3682 [('f', 'follow', None,
3682 [('f', 'follow', None,
3683 _('follow changeset history, or file history across copies and renames')),
3683 _('follow changeset history, or file history across copies and renames')),
3684 ('', 'follow-first', None,
3684 ('', 'follow-first', None,
3685 _('only follow the first parent of merge changesets (DEPRECATED)')),
3685 _('only follow the first parent of merge changesets (DEPRECATED)')),
3686 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3686 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3687 ('C', 'copies', None, _('show copied files')),
3687 ('C', 'copies', None, _('show copied files')),
3688 ('k', 'keyword', [],
3688 ('k', 'keyword', [],
3689 _('do case-insensitive search for a given text'), _('TEXT')),
3689 _('do case-insensitive search for a given text'), _('TEXT')),
3690 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3690 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3691 ('L', 'line-range', [],
3691 ('L', 'line-range', [],
3692 _('follow line range of specified file (EXPERIMENTAL)'),
3692 _('follow line range of specified file (EXPERIMENTAL)'),
3693 _('FILE,RANGE')),
3693 _('FILE,RANGE')),
3694 ('', 'removed', None, _('include revisions where files were removed')),
3694 ('', 'removed', None, _('include revisions where files were removed')),
3695 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3695 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3696 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3696 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3697 ('', 'only-branch', [],
3697 ('', 'only-branch', [],
3698 _('show only changesets within the given named branch (DEPRECATED)'),
3698 _('show only changesets within the given named branch (DEPRECATED)'),
3699 _('BRANCH')),
3699 _('BRANCH')),
3700 ('b', 'branch', [],
3700 ('b', 'branch', [],
3701 _('show changesets within the given named branch'), _('BRANCH')),
3701 _('show changesets within the given named branch'), _('BRANCH')),
3702 ('P', 'prune', [],
3702 ('P', 'prune', [],
3703 _('do not display revision or any of its ancestors'), _('REV')),
3703 _('do not display revision or any of its ancestors'), _('REV')),
3704 ] + logopts + walkopts,
3704 ] + logopts + walkopts,
3705 _('[OPTION]... [FILE]'),
3705 _('[OPTION]... [FILE]'),
3706 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3706 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3707 helpbasic=True, inferrepo=True,
3707 helpbasic=True, inferrepo=True,
3708 intents={INTENT_READONLY})
3708 intents={INTENT_READONLY})
3709 def log(ui, repo, *pats, **opts):
3709 def log(ui, repo, *pats, **opts):
3710 """show revision history of entire repository or files
3710 """show revision history of entire repository or files
3711
3711
3712 Print the revision history of the specified files or the entire
3712 Print the revision history of the specified files or the entire
3713 project.
3713 project.
3714
3714
3715 If no revision range is specified, the default is ``tip:0`` unless
3715 If no revision range is specified, the default is ``tip:0`` unless
3716 --follow is set, in which case the working directory parent is
3716 --follow is set, in which case the working directory parent is
3717 used as the starting revision.
3717 used as the starting revision.
3718
3718
3719 File history is shown without following rename or copy history of
3719 File history is shown without following rename or copy history of
3720 files. Use -f/--follow with a filename to follow history across
3720 files. Use -f/--follow with a filename to follow history across
3721 renames and copies. --follow without a filename will only show
3721 renames and copies. --follow without a filename will only show
3722 ancestors of the starting revision.
3722 ancestors of the starting revision.
3723
3723
3724 By default this command prints revision number and changeset id,
3724 By default this command prints revision number and changeset id,
3725 tags, non-trivial parents, user, date and time, and a summary for
3725 tags, non-trivial parents, user, date and time, and a summary for
3726 each commit. When the -v/--verbose switch is used, the list of
3726 each commit. When the -v/--verbose switch is used, the list of
3727 changed files and full commit message are shown.
3727 changed files and full commit message are shown.
3728
3728
3729 With --graph the revisions are shown as an ASCII art DAG with the most
3729 With --graph the revisions are shown as an ASCII art DAG with the most
3730 recent changeset at the top.
3730 recent changeset at the top.
3731 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
3731 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
3732 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
3732 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
3733 changeset from the lines below is a parent of the 'o' merge on the same
3733 changeset from the lines below is a parent of the 'o' merge on the same
3734 line.
3734 line.
3735 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3735 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3736 of a '|' indicates one or more revisions in a path are omitted.
3736 of a '|' indicates one or more revisions in a path are omitted.
3737
3737
3738 .. container:: verbose
3738 .. container:: verbose
3739
3739
3740 Use -L/--line-range FILE,M:N options to follow the history of lines
3740 Use -L/--line-range FILE,M:N options to follow the history of lines
3741 from M to N in FILE. With -p/--patch only diff hunks affecting
3741 from M to N in FILE. With -p/--patch only diff hunks affecting
3742 specified line range will be shown. This option requires --follow;
3742 specified line range will be shown. This option requires --follow;
3743 it can be specified multiple times. Currently, this option is not
3743 it can be specified multiple times. Currently, this option is not
3744 compatible with --graph. This option is experimental.
3744 compatible with --graph. This option is experimental.
3745
3745
3746 .. note::
3746 .. note::
3747
3747
3748 :hg:`log --patch` may generate unexpected diff output for merge
3748 :hg:`log --patch` may generate unexpected diff output for merge
3749 changesets, as it will only compare the merge changeset against
3749 changesets, as it will only compare the merge changeset against
3750 its first parent. Also, only files different from BOTH parents
3750 its first parent. Also, only files different from BOTH parents
3751 will appear in files:.
3751 will appear in files:.
3752
3752
3753 .. note::
3753 .. note::
3754
3754
3755 For performance reasons, :hg:`log FILE` may omit duplicate changes
3755 For performance reasons, :hg:`log FILE` may omit duplicate changes
3756 made on branches and will not show removals or mode changes. To
3756 made on branches and will not show removals or mode changes. To
3757 see all such changes, use the --removed switch.
3757 see all such changes, use the --removed switch.
3758
3758
3759 .. container:: verbose
3759 .. container:: verbose
3760
3760
3761 .. note::
3761 .. note::
3762
3762
3763 The history resulting from -L/--line-range options depends on diff
3763 The history resulting from -L/--line-range options depends on diff
3764 options; for instance if white-spaces are ignored, respective changes
3764 options; for instance if white-spaces are ignored, respective changes
3765 with only white-spaces in specified line range will not be listed.
3765 with only white-spaces in specified line range will not be listed.
3766
3766
3767 .. container:: verbose
3767 .. container:: verbose
3768
3768
3769 Some examples:
3769 Some examples:
3770
3770
3771 - changesets with full descriptions and file lists::
3771 - changesets with full descriptions and file lists::
3772
3772
3773 hg log -v
3773 hg log -v
3774
3774
3775 - changesets ancestral to the working directory::
3775 - changesets ancestral to the working directory::
3776
3776
3777 hg log -f
3777 hg log -f
3778
3778
3779 - last 10 commits on the current branch::
3779 - last 10 commits on the current branch::
3780
3780
3781 hg log -l 10 -b .
3781 hg log -l 10 -b .
3782
3782
3783 - changesets showing all modifications of a file, including removals::
3783 - changesets showing all modifications of a file, including removals::
3784
3784
3785 hg log --removed file.c
3785 hg log --removed file.c
3786
3786
3787 - all changesets that touch a directory, with diffs, excluding merges::
3787 - all changesets that touch a directory, with diffs, excluding merges::
3788
3788
3789 hg log -Mp lib/
3789 hg log -Mp lib/
3790
3790
3791 - all revision numbers that match a keyword::
3791 - all revision numbers that match a keyword::
3792
3792
3793 hg log -k bug --template "{rev}\\n"
3793 hg log -k bug --template "{rev}\\n"
3794
3794
3795 - the full hash identifier of the working directory parent::
3795 - the full hash identifier of the working directory parent::
3796
3796
3797 hg log -r . --template "{node}\\n"
3797 hg log -r . --template "{node}\\n"
3798
3798
3799 - list available log templates::
3799 - list available log templates::
3800
3800
3801 hg log -T list
3801 hg log -T list
3802
3802
3803 - check if a given changeset is included in a tagged release::
3803 - check if a given changeset is included in a tagged release::
3804
3804
3805 hg log -r "a21ccf and ancestor(1.9)"
3805 hg log -r "a21ccf and ancestor(1.9)"
3806
3806
3807 - find all changesets by some user in a date range::
3807 - find all changesets by some user in a date range::
3808
3808
3809 hg log -k alice -d "may 2008 to jul 2008"
3809 hg log -k alice -d "may 2008 to jul 2008"
3810
3810
3811 - summary of all changesets after the last tag::
3811 - summary of all changesets after the last tag::
3812
3812
3813 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3813 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3814
3814
3815 - changesets touching lines 13 to 23 for file.c::
3815 - changesets touching lines 13 to 23 for file.c::
3816
3816
3817 hg log -L file.c,13:23
3817 hg log -L file.c,13:23
3818
3818
3819 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
3819 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
3820 main.c with patch::
3820 main.c with patch::
3821
3821
3822 hg log -L file.c,13:23 -L main.c,2:6 -p
3822 hg log -L file.c,13:23 -L main.c,2:6 -p
3823
3823
3824 See :hg:`help dates` for a list of formats valid for -d/--date.
3824 See :hg:`help dates` for a list of formats valid for -d/--date.
3825
3825
3826 See :hg:`help revisions` for more about specifying and ordering
3826 See :hg:`help revisions` for more about specifying and ordering
3827 revisions.
3827 revisions.
3828
3828
3829 See :hg:`help templates` for more about pre-packaged styles and
3829 See :hg:`help templates` for more about pre-packaged styles and
3830 specifying custom templates. The default template used by the log
3830 specifying custom templates. The default template used by the log
3831 command can be customized via the ``ui.logtemplate`` configuration
3831 command can be customized via the ``ui.logtemplate`` configuration
3832 setting.
3832 setting.
3833
3833
3834 Returns 0 on success.
3834 Returns 0 on success.
3835
3835
3836 """
3836 """
3837 opts = pycompat.byteskwargs(opts)
3837 opts = pycompat.byteskwargs(opts)
3838 linerange = opts.get('line_range')
3838 linerange = opts.get('line_range')
3839
3839
3840 if linerange and not opts.get('follow'):
3840 if linerange and not opts.get('follow'):
3841 raise error.Abort(_('--line-range requires --follow'))
3841 raise error.Abort(_('--line-range requires --follow'))
3842
3842
3843 if linerange and pats:
3843 if linerange and pats:
3844 # TODO: take pats as patterns with no line-range filter
3844 # TODO: take pats as patterns with no line-range filter
3845 raise error.Abort(
3845 raise error.Abort(
3846 _('FILE arguments are not compatible with --line-range option')
3846 _('FILE arguments are not compatible with --line-range option')
3847 )
3847 )
3848
3848
3849 repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
3849 repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
3850 revs, differ = logcmdutil.getrevs(repo, pats, opts)
3850 revs, differ = logcmdutil.getrevs(repo, pats, opts)
3851 if linerange:
3851 if linerange:
3852 # TODO: should follow file history from logcmdutil._initialrevs(),
3852 # TODO: should follow file history from logcmdutil._initialrevs(),
3853 # then filter the result by logcmdutil._makerevset() and --limit
3853 # then filter the result by logcmdutil._makerevset() and --limit
3854 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
3854 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
3855
3855
3856 getrenamed = None
3856 getrenamed = None
3857 if opts.get('copies'):
3857 if opts.get('copies'):
3858 endrev = None
3858 endrev = None
3859 if revs:
3859 if revs:
3860 endrev = revs.max() + 1
3860 endrev = revs.max() + 1
3861 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3861 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3862
3862
3863 ui.pager('log')
3863 ui.pager('log')
3864 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, differ,
3864 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, differ,
3865 buffered=True)
3865 buffered=True)
3866 if opts.get('graph'):
3866 if opts.get('graph'):
3867 displayfn = logcmdutil.displaygraphrevs
3867 displayfn = logcmdutil.displaygraphrevs
3868 else:
3868 else:
3869 displayfn = logcmdutil.displayrevs
3869 displayfn = logcmdutil.displayrevs
3870 displayfn(ui, repo, revs, displayer, getrenamed)
3870 displayfn(ui, repo, revs, displayer, getrenamed)
3871
3871
3872 @command('manifest',
3872 @command('manifest',
3873 [('r', 'rev', '', _('revision to display'), _('REV')),
3873 [('r', 'rev', '', _('revision to display'), _('REV')),
3874 ('', 'all', False, _("list files from all revisions"))]
3874 ('', 'all', False, _("list files from all revisions"))]
3875 + formatteropts,
3875 + formatteropts,
3876 _('[-r REV]'),
3876 _('[-r REV]'),
3877 helpcategory=command.CATEGORY_MAINTENANCE,
3877 helpcategory=command.CATEGORY_MAINTENANCE,
3878 intents={INTENT_READONLY})
3878 intents={INTENT_READONLY})
3879 def manifest(ui, repo, node=None, rev=None, **opts):
3879 def manifest(ui, repo, node=None, rev=None, **opts):
3880 """output the current or given revision of the project manifest
3880 """output the current or given revision of the project manifest
3881
3881
3882 Print a list of version controlled files for the given revision.
3882 Print a list of version controlled files for the given revision.
3883 If no revision is given, the first parent of the working directory
3883 If no revision is given, the first parent of the working directory
3884 is used, or the null revision if no revision is checked out.
3884 is used, or the null revision if no revision is checked out.
3885
3885
3886 With -v, print file permissions, symlink and executable bits.
3886 With -v, print file permissions, symlink and executable bits.
3887 With --debug, print file revision hashes.
3887 With --debug, print file revision hashes.
3888
3888
3889 If option --all is specified, the list of all files from all revisions
3889 If option --all is specified, the list of all files from all revisions
3890 is printed. This includes deleted and renamed files.
3890 is printed. This includes deleted and renamed files.
3891
3891
3892 Returns 0 on success.
3892 Returns 0 on success.
3893 """
3893 """
3894 opts = pycompat.byteskwargs(opts)
3894 opts = pycompat.byteskwargs(opts)
3895 fm = ui.formatter('manifest', opts)
3895 fm = ui.formatter('manifest', opts)
3896
3896
3897 if opts.get('all'):
3897 if opts.get('all'):
3898 if rev or node:
3898 if rev or node:
3899 raise error.Abort(_("can't specify a revision with --all"))
3899 raise error.Abort(_("can't specify a revision with --all"))
3900
3900
3901 res = set()
3901 res = set()
3902 for rev in repo:
3902 for rev in repo:
3903 ctx = repo[rev]
3903 ctx = repo[rev]
3904 res |= set(ctx.files())
3904 res |= set(ctx.files())
3905
3905
3906 ui.pager('manifest')
3906 ui.pager('manifest')
3907 for f in sorted(res):
3907 for f in sorted(res):
3908 fm.startitem()
3908 fm.startitem()
3909 fm.write("path", '%s\n', f)
3909 fm.write("path", '%s\n', f)
3910 fm.end()
3910 fm.end()
3911 return
3911 return
3912
3912
3913 if rev and node:
3913 if rev and node:
3914 raise error.Abort(_("please specify just one revision"))
3914 raise error.Abort(_("please specify just one revision"))
3915
3915
3916 if not node:
3916 if not node:
3917 node = rev
3917 node = rev
3918
3918
3919 char = {'l': '@', 'x': '*', '': '', 't': 'd'}
3919 char = {'l': '@', 'x': '*', '': '', 't': 'd'}
3920 mode = {'l': '644', 'x': '755', '': '644', 't': '755'}
3920 mode = {'l': '644', 'x': '755', '': '644', 't': '755'}
3921 if node:
3921 if node:
3922 repo = scmutil.unhidehashlikerevs(repo, [node], 'nowarn')
3922 repo = scmutil.unhidehashlikerevs(repo, [node], 'nowarn')
3923 ctx = scmutil.revsingle(repo, node)
3923 ctx = scmutil.revsingle(repo, node)
3924 mf = ctx.manifest()
3924 mf = ctx.manifest()
3925 ui.pager('manifest')
3925 ui.pager('manifest')
3926 for f in ctx:
3926 for f in ctx:
3927 fm.startitem()
3927 fm.startitem()
3928 fm.context(ctx=ctx)
3928 fm.context(ctx=ctx)
3929 fl = ctx[f].flags()
3929 fl = ctx[f].flags()
3930 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3930 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3931 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3931 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3932 fm.write('path', '%s\n', f)
3932 fm.write('path', '%s\n', f)
3933 fm.end()
3933 fm.end()
3934
3934
3935 @command('merge',
3935 @command('merge',
3936 [('f', 'force', None,
3936 [('f', 'force', None,
3937 _('force a merge including outstanding changes (DEPRECATED)')),
3937 _('force a merge including outstanding changes (DEPRECATED)')),
3938 ('r', 'rev', '', _('revision to merge'), _('REV')),
3938 ('r', 'rev', '', _('revision to merge'), _('REV')),
3939 ('P', 'preview', None,
3939 ('P', 'preview', None,
3940 _('review revisions to merge (no merge is performed)')),
3940 _('review revisions to merge (no merge is performed)')),
3941 ('', 'abort', None, _('abort the ongoing merge')),
3941 ('', 'abort', None, _('abort the ongoing merge')),
3942 ] + mergetoolopts,
3942 ] + mergetoolopts,
3943 _('[-P] [[-r] REV]'),
3943 _('[-P] [[-r] REV]'),
3944 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT, helpbasic=True)
3944 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT, helpbasic=True)
3945 def merge(ui, repo, node=None, **opts):
3945 def merge(ui, repo, node=None, **opts):
3946 """merge another revision into working directory
3946 """merge another revision into working directory
3947
3947
3948 The current working directory is updated with all changes made in
3948 The current working directory is updated with all changes made in
3949 the requested revision since the last common predecessor revision.
3949 the requested revision since the last common predecessor revision.
3950
3950
3951 Files that changed between either parent are marked as changed for
3951 Files that changed between either parent are marked as changed for
3952 the next commit and a commit must be performed before any further
3952 the next commit and a commit must be performed before any further
3953 updates to the repository are allowed. The next commit will have
3953 updates to the repository are allowed. The next commit will have
3954 two parents.
3954 two parents.
3955
3955
3956 ``--tool`` can be used to specify the merge tool used for file
3956 ``--tool`` can be used to specify the merge tool used for file
3957 merges. It overrides the HGMERGE environment variable and your
3957 merges. It overrides the HGMERGE environment variable and your
3958 configuration files. See :hg:`help merge-tools` for options.
3958 configuration files. See :hg:`help merge-tools` for options.
3959
3959
3960 If no revision is specified, the working directory's parent is a
3960 If no revision is specified, the working directory's parent is a
3961 head revision, and the current branch contains exactly one other
3961 head revision, and the current branch contains exactly one other
3962 head, the other head is merged with by default. Otherwise, an
3962 head, the other head is merged with by default. Otherwise, an
3963 explicit revision with which to merge with must be provided.
3963 explicit revision with which to merge with must be provided.
3964
3964
3965 See :hg:`help resolve` for information on handling file conflicts.
3965 See :hg:`help resolve` for information on handling file conflicts.
3966
3966
3967 To undo an uncommitted merge, use :hg:`merge --abort` which
3967 To undo an uncommitted merge, use :hg:`merge --abort` which
3968 will check out a clean copy of the original merge parent, losing
3968 will check out a clean copy of the original merge parent, losing
3969 all changes.
3969 all changes.
3970
3970
3971 Returns 0 on success, 1 if there are unresolved files.
3971 Returns 0 on success, 1 if there are unresolved files.
3972 """
3972 """
3973
3973
3974 opts = pycompat.byteskwargs(opts)
3974 opts = pycompat.byteskwargs(opts)
3975 abort = opts.get('abort')
3975 abort = opts.get('abort')
3976 if abort and repo.dirstate.p2() == nullid:
3976 if abort and repo.dirstate.p2() == nullid:
3977 cmdutil.wrongtooltocontinue(repo, _('merge'))
3977 cmdutil.wrongtooltocontinue(repo, _('merge'))
3978 if abort:
3978 if abort:
3979 if node:
3979 if node:
3980 raise error.Abort(_("cannot specify a node with --abort"))
3980 raise error.Abort(_("cannot specify a node with --abort"))
3981 if opts.get('rev'):
3981 if opts.get('rev'):
3982 raise error.Abort(_("cannot specify both --rev and --abort"))
3982 raise error.Abort(_("cannot specify both --rev and --abort"))
3983 if opts.get('preview'):
3983 if opts.get('preview'):
3984 raise error.Abort(_("cannot specify --preview with --abort"))
3984 raise error.Abort(_("cannot specify --preview with --abort"))
3985 if opts.get('rev') and node:
3985 if opts.get('rev') and node:
3986 raise error.Abort(_("please specify just one revision"))
3986 raise error.Abort(_("please specify just one revision"))
3987 if not node:
3987 if not node:
3988 node = opts.get('rev')
3988 node = opts.get('rev')
3989
3989
3990 if node:
3990 if node:
3991 node = scmutil.revsingle(repo, node).node()
3991 node = scmutil.revsingle(repo, node).node()
3992
3992
3993 if not node and not abort:
3993 if not node and not abort:
3994 node = repo[destutil.destmerge(repo)].node()
3994 node = repo[destutil.destmerge(repo)].node()
3995
3995
3996 if opts.get('preview'):
3996 if opts.get('preview'):
3997 # find nodes that are ancestors of p2 but not of p1
3997 # find nodes that are ancestors of p2 but not of p1
3998 p1 = repo.lookup('.')
3998 p1 = repo.lookup('.')
3999 p2 = node
3999 p2 = node
4000 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4000 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4001
4001
4002 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4002 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4003 for node in nodes:
4003 for node in nodes:
4004 displayer.show(repo[node])
4004 displayer.show(repo[node])
4005 displayer.close()
4005 displayer.close()
4006 return 0
4006 return 0
4007
4007
4008 # ui.forcemerge is an internal variable, do not document
4008 # ui.forcemerge is an internal variable, do not document
4009 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4009 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4010 with ui.configoverride(overrides, 'merge'):
4010 with ui.configoverride(overrides, 'merge'):
4011 force = opts.get('force')
4011 force = opts.get('force')
4012 labels = ['working copy', 'merge rev']
4012 labels = ['working copy', 'merge rev']
4013 return hg.merge(repo, node, force=force, mergeforce=force,
4013 return hg.merge(repo, node, force=force, mergeforce=force,
4014 labels=labels, abort=abort)
4014 labels=labels, abort=abort)
4015
4015
4016 @command('outgoing|out',
4016 @command('outgoing|out',
4017 [('f', 'force', None, _('run even when the destination is unrelated')),
4017 [('f', 'force', None, _('run even when the destination is unrelated')),
4018 ('r', 'rev', [],
4018 ('r', 'rev', [],
4019 _('a changeset intended to be included in the destination'), _('REV')),
4019 _('a changeset intended to be included in the destination'), _('REV')),
4020 ('n', 'newest-first', None, _('show newest record first')),
4020 ('n', 'newest-first', None, _('show newest record first')),
4021 ('B', 'bookmarks', False, _('compare bookmarks')),
4021 ('B', 'bookmarks', False, _('compare bookmarks')),
4022 ('b', 'branch', [], _('a specific branch you would like to push'),
4022 ('b', 'branch', [], _('a specific branch you would like to push'),
4023 _('BRANCH')),
4023 _('BRANCH')),
4024 ] + logopts + remoteopts + subrepoopts,
4024 ] + logopts + remoteopts + subrepoopts,
4025 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4025 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4026 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
4026 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
4027 def outgoing(ui, repo, dest=None, **opts):
4027 def outgoing(ui, repo, dest=None, **opts):
4028 """show changesets not found in the destination
4028 """show changesets not found in the destination
4029
4029
4030 Show changesets not found in the specified destination repository
4030 Show changesets not found in the specified destination repository
4031 or the default push location. These are the changesets that would
4031 or the default push location. These are the changesets that would
4032 be pushed if a push was requested.
4032 be pushed if a push was requested.
4033
4033
4034 See pull for details of valid destination formats.
4034 See pull for details of valid destination formats.
4035
4035
4036 .. container:: verbose
4036 .. container:: verbose
4037
4037
4038 With -B/--bookmarks, the result of bookmark comparison between
4038 With -B/--bookmarks, the result of bookmark comparison between
4039 local and remote repositories is displayed. With -v/--verbose,
4039 local and remote repositories is displayed. With -v/--verbose,
4040 status is also displayed for each bookmark like below::
4040 status is also displayed for each bookmark like below::
4041
4041
4042 BM1 01234567890a added
4042 BM1 01234567890a added
4043 BM2 deleted
4043 BM2 deleted
4044 BM3 234567890abc advanced
4044 BM3 234567890abc advanced
4045 BM4 34567890abcd diverged
4045 BM4 34567890abcd diverged
4046 BM5 4567890abcde changed
4046 BM5 4567890abcde changed
4047
4047
4048 The action taken when pushing depends on the
4048 The action taken when pushing depends on the
4049 status of each bookmark:
4049 status of each bookmark:
4050
4050
4051 :``added``: push with ``-B`` will create it
4051 :``added``: push with ``-B`` will create it
4052 :``deleted``: push with ``-B`` will delete it
4052 :``deleted``: push with ``-B`` will delete it
4053 :``advanced``: push will update it
4053 :``advanced``: push will update it
4054 :``diverged``: push with ``-B`` will update it
4054 :``diverged``: push with ``-B`` will update it
4055 :``changed``: push with ``-B`` will update it
4055 :``changed``: push with ``-B`` will update it
4056
4056
4057 From the point of view of pushing behavior, bookmarks
4057 From the point of view of pushing behavior, bookmarks
4058 existing only in the remote repository are treated as
4058 existing only in the remote repository are treated as
4059 ``deleted``, even if it is in fact added remotely.
4059 ``deleted``, even if it is in fact added remotely.
4060
4060
4061 Returns 0 if there are outgoing changes, 1 otherwise.
4061 Returns 0 if there are outgoing changes, 1 otherwise.
4062 """
4062 """
4063 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4063 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4064 # style URLs, so don't overwrite dest.
4064 # style URLs, so don't overwrite dest.
4065 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4065 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4066 if not path:
4066 if not path:
4067 raise error.Abort(_('default repository not configured!'),
4067 raise error.Abort(_('default repository not configured!'),
4068 hint=_("see 'hg help config.paths'"))
4068 hint=_("see 'hg help config.paths'"))
4069
4069
4070 opts = pycompat.byteskwargs(opts)
4070 opts = pycompat.byteskwargs(opts)
4071 if opts.get('graph'):
4071 if opts.get('graph'):
4072 logcmdutil.checkunsupportedgraphflags([], opts)
4072 logcmdutil.checkunsupportedgraphflags([], opts)
4073 o, other = hg._outgoing(ui, repo, dest, opts)
4073 o, other = hg._outgoing(ui, repo, dest, opts)
4074 if not o:
4074 if not o:
4075 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4075 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4076 return
4076 return
4077
4077
4078 revdag = logcmdutil.graphrevs(repo, o, opts)
4078 revdag = logcmdutil.graphrevs(repo, o, opts)
4079 ui.pager('outgoing')
4079 ui.pager('outgoing')
4080 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
4080 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
4081 logcmdutil.displaygraph(ui, repo, revdag, displayer,
4081 logcmdutil.displaygraph(ui, repo, revdag, displayer,
4082 graphmod.asciiedges)
4082 graphmod.asciiedges)
4083 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4083 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4084 return 0
4084 return 0
4085
4085
4086 if opts.get('bookmarks'):
4086 if opts.get('bookmarks'):
4087 dest = path.pushloc or path.loc
4087 dest = path.pushloc or path.loc
4088 other = hg.peer(repo, opts, dest)
4088 other = hg.peer(repo, opts, dest)
4089 if 'bookmarks' not in other.listkeys('namespaces'):
4089 if 'bookmarks' not in other.listkeys('namespaces'):
4090 ui.warn(_("remote doesn't support bookmarks\n"))
4090 ui.warn(_("remote doesn't support bookmarks\n"))
4091 return 0
4091 return 0
4092 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4092 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4093 ui.pager('outgoing')
4093 ui.pager('outgoing')
4094 return bookmarks.outgoing(ui, repo, other)
4094 return bookmarks.outgoing(ui, repo, other)
4095
4095
4096 repo._subtoppath = path.pushloc or path.loc
4096 repo._subtoppath = path.pushloc or path.loc
4097 try:
4097 try:
4098 return hg.outgoing(ui, repo, dest, opts)
4098 return hg.outgoing(ui, repo, dest, opts)
4099 finally:
4099 finally:
4100 del repo._subtoppath
4100 del repo._subtoppath
4101
4101
4102 @command('parents',
4102 @command('parents',
4103 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4103 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4104 ] + templateopts,
4104 ] + templateopts,
4105 _('[-r REV] [FILE]'),
4105 _('[-r REV] [FILE]'),
4106 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4106 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4107 inferrepo=True)
4107 inferrepo=True)
4108 def parents(ui, repo, file_=None, **opts):
4108 def parents(ui, repo, file_=None, **opts):
4109 """show the parents of the working directory or revision (DEPRECATED)
4109 """show the parents of the working directory or revision (DEPRECATED)
4110
4110
4111 Print the working directory's parent revisions. If a revision is
4111 Print the working directory's parent revisions. If a revision is
4112 given via -r/--rev, the parent of that revision will be printed.
4112 given via -r/--rev, the parent of that revision will be printed.
4113 If a file argument is given, the revision in which the file was
4113 If a file argument is given, the revision in which the file was
4114 last changed (before the working directory revision or the
4114 last changed (before the working directory revision or the
4115 argument to --rev if given) is printed.
4115 argument to --rev if given) is printed.
4116
4116
4117 This command is equivalent to::
4117 This command is equivalent to::
4118
4118
4119 hg log -r "p1()+p2()" or
4119 hg log -r "p1()+p2()" or
4120 hg log -r "p1(REV)+p2(REV)" or
4120 hg log -r "p1(REV)+p2(REV)" or
4121 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
4121 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
4122 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
4122 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
4123
4123
4124 See :hg:`summary` and :hg:`help revsets` for related information.
4124 See :hg:`summary` and :hg:`help revsets` for related information.
4125
4125
4126 Returns 0 on success.
4126 Returns 0 on success.
4127 """
4127 """
4128
4128
4129 opts = pycompat.byteskwargs(opts)
4129 opts = pycompat.byteskwargs(opts)
4130 rev = opts.get('rev')
4130 rev = opts.get('rev')
4131 if rev:
4131 if rev:
4132 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
4132 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
4133 ctx = scmutil.revsingle(repo, rev, None)
4133 ctx = scmutil.revsingle(repo, rev, None)
4134
4134
4135 if file_:
4135 if file_:
4136 m = scmutil.match(ctx, (file_,), opts)
4136 m = scmutil.match(ctx, (file_,), opts)
4137 if m.anypats() or len(m.files()) != 1:
4137 if m.anypats() or len(m.files()) != 1:
4138 raise error.Abort(_('can only specify an explicit filename'))
4138 raise error.Abort(_('can only specify an explicit filename'))
4139 file_ = m.files()[0]
4139 file_ = m.files()[0]
4140 filenodes = []
4140 filenodes = []
4141 for cp in ctx.parents():
4141 for cp in ctx.parents():
4142 if not cp:
4142 if not cp:
4143 continue
4143 continue
4144 try:
4144 try:
4145 filenodes.append(cp.filenode(file_))
4145 filenodes.append(cp.filenode(file_))
4146 except error.LookupError:
4146 except error.LookupError:
4147 pass
4147 pass
4148 if not filenodes:
4148 if not filenodes:
4149 raise error.Abort(_("'%s' not found in manifest!") % file_)
4149 raise error.Abort(_("'%s' not found in manifest!") % file_)
4150 p = []
4150 p = []
4151 for fn in filenodes:
4151 for fn in filenodes:
4152 fctx = repo.filectx(file_, fileid=fn)
4152 fctx = repo.filectx(file_, fileid=fn)
4153 p.append(fctx.node())
4153 p.append(fctx.node())
4154 else:
4154 else:
4155 p = [cp.node() for cp in ctx.parents()]
4155 p = [cp.node() for cp in ctx.parents()]
4156
4156
4157 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4157 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4158 for n in p:
4158 for n in p:
4159 if n != nullid:
4159 if n != nullid:
4160 displayer.show(repo[n])
4160 displayer.show(repo[n])
4161 displayer.close()
4161 displayer.close()
4162
4162
4163 @command('paths', formatteropts, _('[NAME]'),
4163 @command('paths', formatteropts, _('[NAME]'),
4164 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4164 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4165 optionalrepo=True, intents={INTENT_READONLY})
4165 optionalrepo=True, intents={INTENT_READONLY})
4166 def paths(ui, repo, search=None, **opts):
4166 def paths(ui, repo, search=None, **opts):
4167 """show aliases for remote repositories
4167 """show aliases for remote repositories
4168
4168
4169 Show definition of symbolic path name NAME. If no name is given,
4169 Show definition of symbolic path name NAME. If no name is given,
4170 show definition of all available names.
4170 show definition of all available names.
4171
4171
4172 Option -q/--quiet suppresses all output when searching for NAME
4172 Option -q/--quiet suppresses all output when searching for NAME
4173 and shows only the path names when listing all definitions.
4173 and shows only the path names when listing all definitions.
4174
4174
4175 Path names are defined in the [paths] section of your
4175 Path names are defined in the [paths] section of your
4176 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4176 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4177 repository, ``.hg/hgrc`` is used, too.
4177 repository, ``.hg/hgrc`` is used, too.
4178
4178
4179 The path names ``default`` and ``default-push`` have a special
4179 The path names ``default`` and ``default-push`` have a special
4180 meaning. When performing a push or pull operation, they are used
4180 meaning. When performing a push or pull operation, they are used
4181 as fallbacks if no location is specified on the command-line.
4181 as fallbacks if no location is specified on the command-line.
4182 When ``default-push`` is set, it will be used for push and
4182 When ``default-push`` is set, it will be used for push and
4183 ``default`` will be used for pull; otherwise ``default`` is used
4183 ``default`` will be used for pull; otherwise ``default`` is used
4184 as the fallback for both. When cloning a repository, the clone
4184 as the fallback for both. When cloning a repository, the clone
4185 source is written as ``default`` in ``.hg/hgrc``.
4185 source is written as ``default`` in ``.hg/hgrc``.
4186
4186
4187 .. note::
4187 .. note::
4188
4188
4189 ``default`` and ``default-push`` apply to all inbound (e.g.
4189 ``default`` and ``default-push`` apply to all inbound (e.g.
4190 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
4190 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
4191 and :hg:`bundle`) operations.
4191 and :hg:`bundle`) operations.
4192
4192
4193 See :hg:`help urls` for more information.
4193 See :hg:`help urls` for more information.
4194
4194
4195 .. container:: verbose
4195 .. container:: verbose
4196
4196
4197 Template:
4197 Template:
4198
4198
4199 The following keywords are supported. See also :hg:`help templates`.
4199 The following keywords are supported. See also :hg:`help templates`.
4200
4200
4201 :name: String. Symbolic name of the path alias.
4201 :name: String. Symbolic name of the path alias.
4202 :pushurl: String. URL for push operations.
4202 :pushurl: String. URL for push operations.
4203 :url: String. URL or directory path for the other operations.
4203 :url: String. URL or directory path for the other operations.
4204
4204
4205 Returns 0 on success.
4205 Returns 0 on success.
4206 """
4206 """
4207
4207
4208 opts = pycompat.byteskwargs(opts)
4208 opts = pycompat.byteskwargs(opts)
4209 ui.pager('paths')
4209 ui.pager('paths')
4210 if search:
4210 if search:
4211 pathitems = [(name, path) for name, path in ui.paths.iteritems()
4211 pathitems = [(name, path) for name, path in ui.paths.iteritems()
4212 if name == search]
4212 if name == search]
4213 else:
4213 else:
4214 pathitems = sorted(ui.paths.iteritems())
4214 pathitems = sorted(ui.paths.iteritems())
4215
4215
4216 fm = ui.formatter('paths', opts)
4216 fm = ui.formatter('paths', opts)
4217 if fm.isplain():
4217 if fm.isplain():
4218 hidepassword = util.hidepassword
4218 hidepassword = util.hidepassword
4219 else:
4219 else:
4220 hidepassword = bytes
4220 hidepassword = bytes
4221 if ui.quiet:
4221 if ui.quiet:
4222 namefmt = '%s\n'
4222 namefmt = '%s\n'
4223 else:
4223 else:
4224 namefmt = '%s = '
4224 namefmt = '%s = '
4225 showsubopts = not search and not ui.quiet
4225 showsubopts = not search and not ui.quiet
4226
4226
4227 for name, path in pathitems:
4227 for name, path in pathitems:
4228 fm.startitem()
4228 fm.startitem()
4229 fm.condwrite(not search, 'name', namefmt, name)
4229 fm.condwrite(not search, 'name', namefmt, name)
4230 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
4230 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
4231 for subopt, value in sorted(path.suboptions.items()):
4231 for subopt, value in sorted(path.suboptions.items()):
4232 assert subopt not in ('name', 'url')
4232 assert subopt not in ('name', 'url')
4233 if showsubopts:
4233 if showsubopts:
4234 fm.plain('%s:%s = ' % (name, subopt))
4234 fm.plain('%s:%s = ' % (name, subopt))
4235 fm.condwrite(showsubopts, subopt, '%s\n', value)
4235 fm.condwrite(showsubopts, subopt, '%s\n', value)
4236
4236
4237 fm.end()
4237 fm.end()
4238
4238
4239 if search and not pathitems:
4239 if search and not pathitems:
4240 if not ui.quiet:
4240 if not ui.quiet:
4241 ui.warn(_("not found!\n"))
4241 ui.warn(_("not found!\n"))
4242 return 1
4242 return 1
4243 else:
4243 else:
4244 return 0
4244 return 0
4245
4245
4246 @command('phase',
4246 @command('phase',
4247 [('p', 'public', False, _('set changeset phase to public')),
4247 [('p', 'public', False, _('set changeset phase to public')),
4248 ('d', 'draft', False, _('set changeset phase to draft')),
4248 ('d', 'draft', False, _('set changeset phase to draft')),
4249 ('s', 'secret', False, _('set changeset phase to secret')),
4249 ('s', 'secret', False, _('set changeset phase to secret')),
4250 ('f', 'force', False, _('allow to move boundary backward')),
4250 ('f', 'force', False, _('allow to move boundary backward')),
4251 ('r', 'rev', [], _('target revision'), _('REV')),
4251 ('r', 'rev', [], _('target revision'), _('REV')),
4252 ],
4252 ],
4253 _('[-p|-d|-s] [-f] [-r] [REV...]'),
4253 _('[-p|-d|-s] [-f] [-r] [REV...]'),
4254 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
4254 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
4255 def phase(ui, repo, *revs, **opts):
4255 def phase(ui, repo, *revs, **opts):
4256 """set or show the current phase name
4256 """set or show the current phase name
4257
4257
4258 With no argument, show the phase name of the current revision(s).
4258 With no argument, show the phase name of the current revision(s).
4259
4259
4260 With one of -p/--public, -d/--draft or -s/--secret, change the
4260 With one of -p/--public, -d/--draft or -s/--secret, change the
4261 phase value of the specified revisions.
4261 phase value of the specified revisions.
4262
4262
4263 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
4263 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
4264 lower phase to a higher phase. Phases are ordered as follows::
4264 lower phase to a higher phase. Phases are ordered as follows::
4265
4265
4266 public < draft < secret
4266 public < draft < secret
4267
4267
4268 Returns 0 on success, 1 if some phases could not be changed.
4268 Returns 0 on success, 1 if some phases could not be changed.
4269
4269
4270 (For more information about the phases concept, see :hg:`help phases`.)
4270 (For more information about the phases concept, see :hg:`help phases`.)
4271 """
4271 """
4272 opts = pycompat.byteskwargs(opts)
4272 opts = pycompat.byteskwargs(opts)
4273 # search for a unique phase argument
4273 # search for a unique phase argument
4274 targetphase = None
4274 targetphase = None
4275 for idx, name in enumerate(phases.cmdphasenames):
4275 for idx, name in enumerate(phases.cmdphasenames):
4276 if opts[name]:
4276 if opts[name]:
4277 if targetphase is not None:
4277 if targetphase is not None:
4278 raise error.Abort(_('only one phase can be specified'))
4278 raise error.Abort(_('only one phase can be specified'))
4279 targetphase = idx
4279 targetphase = idx
4280
4280
4281 # look for specified revision
4281 # look for specified revision
4282 revs = list(revs)
4282 revs = list(revs)
4283 revs.extend(opts['rev'])
4283 revs.extend(opts['rev'])
4284 if not revs:
4284 if not revs:
4285 # display both parents as the second parent phase can influence
4285 # display both parents as the second parent phase can influence
4286 # the phase of a merge commit
4286 # the phase of a merge commit
4287 revs = [c.rev() for c in repo[None].parents()]
4287 revs = [c.rev() for c in repo[None].parents()]
4288
4288
4289 revs = scmutil.revrange(repo, revs)
4289 revs = scmutil.revrange(repo, revs)
4290
4290
4291 ret = 0
4291 ret = 0
4292 if targetphase is None:
4292 if targetphase is None:
4293 # display
4293 # display
4294 for r in revs:
4294 for r in revs:
4295 ctx = repo[r]
4295 ctx = repo[r]
4296 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4296 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4297 else:
4297 else:
4298 with repo.lock(), repo.transaction("phase") as tr:
4298 with repo.lock(), repo.transaction("phase") as tr:
4299 # set phase
4299 # set phase
4300 if not revs:
4300 if not revs:
4301 raise error.Abort(_('empty revision set'))
4301 raise error.Abort(_('empty revision set'))
4302 nodes = [repo[r].node() for r in revs]
4302 nodes = [repo[r].node() for r in revs]
4303 # moving revision from public to draft may hide them
4303 # moving revision from public to draft may hide them
4304 # We have to check result on an unfiltered repository
4304 # We have to check result on an unfiltered repository
4305 unfi = repo.unfiltered()
4305 unfi = repo.unfiltered()
4306 getphase = unfi._phasecache.phase
4306 getphase = unfi._phasecache.phase
4307 olddata = [getphase(unfi, r) for r in unfi]
4307 olddata = [getphase(unfi, r) for r in unfi]
4308 phases.advanceboundary(repo, tr, targetphase, nodes)
4308 phases.advanceboundary(repo, tr, targetphase, nodes)
4309 if opts['force']:
4309 if opts['force']:
4310 phases.retractboundary(repo, tr, targetphase, nodes)
4310 phases.retractboundary(repo, tr, targetphase, nodes)
4311 getphase = unfi._phasecache.phase
4311 getphase = unfi._phasecache.phase
4312 newdata = [getphase(unfi, r) for r in unfi]
4312 newdata = [getphase(unfi, r) for r in unfi]
4313 changes = sum(newdata[r] != olddata[r] for r in unfi)
4313 changes = sum(newdata[r] != olddata[r] for r in unfi)
4314 cl = unfi.changelog
4314 cl = unfi.changelog
4315 rejected = [n for n in nodes
4315 rejected = [n for n in nodes
4316 if newdata[cl.rev(n)] < targetphase]
4316 if newdata[cl.rev(n)] < targetphase]
4317 if rejected:
4317 if rejected:
4318 ui.warn(_('cannot move %i changesets to a higher '
4318 ui.warn(_('cannot move %i changesets to a higher '
4319 'phase, use --force\n') % len(rejected))
4319 'phase, use --force\n') % len(rejected))
4320 ret = 1
4320 ret = 1
4321 if changes:
4321 if changes:
4322 msg = _('phase changed for %i changesets\n') % changes
4322 msg = _('phase changed for %i changesets\n') % changes
4323 if ret:
4323 if ret:
4324 ui.status(msg)
4324 ui.status(msg)
4325 else:
4325 else:
4326 ui.note(msg)
4326 ui.note(msg)
4327 else:
4327 else:
4328 ui.warn(_('no phases changed\n'))
4328 ui.warn(_('no phases changed\n'))
4329 return ret
4329 return ret
4330
4330
4331 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
4331 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
4332 """Run after a changegroup has been added via pull/unbundle
4332 """Run after a changegroup has been added via pull/unbundle
4333
4333
4334 This takes arguments below:
4334 This takes arguments below:
4335
4335
4336 :modheads: change of heads by pull/unbundle
4336 :modheads: change of heads by pull/unbundle
4337 :optupdate: updating working directory is needed or not
4337 :optupdate: updating working directory is needed or not
4338 :checkout: update destination revision (or None to default destination)
4338 :checkout: update destination revision (or None to default destination)
4339 :brev: a name, which might be a bookmark to be activated after updating
4339 :brev: a name, which might be a bookmark to be activated after updating
4340 """
4340 """
4341 if modheads == 0:
4341 if modheads == 0:
4342 return
4342 return
4343 if optupdate:
4343 if optupdate:
4344 try:
4344 try:
4345 return hg.updatetotally(ui, repo, checkout, brev)
4345 return hg.updatetotally(ui, repo, checkout, brev)
4346 except error.UpdateAbort as inst:
4346 except error.UpdateAbort as inst:
4347 msg = _("not updating: %s") % stringutil.forcebytestr(inst)
4347 msg = _("not updating: %s") % stringutil.forcebytestr(inst)
4348 hint = inst.hint
4348 hint = inst.hint
4349 raise error.UpdateAbort(msg, hint=hint)
4349 raise error.UpdateAbort(msg, hint=hint)
4350 if modheads > 1:
4350 if modheads > 1:
4351 currentbranchheads = len(repo.branchheads())
4351 currentbranchheads = len(repo.branchheads())
4352 if currentbranchheads == modheads:
4352 if currentbranchheads == modheads:
4353 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4353 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4354 elif currentbranchheads > 1:
4354 elif currentbranchheads > 1:
4355 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4355 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4356 "merge)\n"))
4356 "merge)\n"))
4357 else:
4357 else:
4358 ui.status(_("(run 'hg heads' to see heads)\n"))
4358 ui.status(_("(run 'hg heads' to see heads)\n"))
4359 elif not ui.configbool('commands', 'update.requiredest'):
4359 elif not ui.configbool('commands', 'update.requiredest'):
4360 ui.status(_("(run 'hg update' to get a working copy)\n"))
4360 ui.status(_("(run 'hg update' to get a working copy)\n"))
4361
4361
4362 @command('pull',
4362 @command('pull',
4363 [('u', 'update', None,
4363 [('u', 'update', None,
4364 _('update to new branch head if new descendants were pulled')),
4364 _('update to new branch head if new descendants were pulled')),
4365 ('f', 'force', None, _('run even when remote repository is unrelated')),
4365 ('f', 'force', None, _('run even when remote repository is unrelated')),
4366 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4366 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4367 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4367 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4368 ('b', 'branch', [], _('a specific branch you would like to pull'),
4368 ('b', 'branch', [], _('a specific branch you would like to pull'),
4369 _('BRANCH')),
4369 _('BRANCH')),
4370 ] + remoteopts,
4370 ] + remoteopts,
4371 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
4371 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
4372 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4372 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4373 helpbasic=True)
4373 helpbasic=True)
4374 def pull(ui, repo, source="default", **opts):
4374 def pull(ui, repo, source="default", **opts):
4375 """pull changes from the specified source
4375 """pull changes from the specified source
4376
4376
4377 Pull changes from a remote repository to a local one.
4377 Pull changes from a remote repository to a local one.
4378
4378
4379 This finds all changes from the repository at the specified path
4379 This finds all changes from the repository at the specified path
4380 or URL and adds them to a local repository (the current one unless
4380 or URL and adds them to a local repository (the current one unless
4381 -R is specified). By default, this does not update the copy of the
4381 -R is specified). By default, this does not update the copy of the
4382 project in the working directory.
4382 project in the working directory.
4383
4383
4384 When cloning from servers that support it, Mercurial may fetch
4384 When cloning from servers that support it, Mercurial may fetch
4385 pre-generated data. When this is done, hooks operating on incoming
4385 pre-generated data. When this is done, hooks operating on incoming
4386 changesets and changegroups may fire more than once, once for each
4386 changesets and changegroups may fire more than once, once for each
4387 pre-generated bundle and as well as for any additional remaining
4387 pre-generated bundle and as well as for any additional remaining
4388 data. See :hg:`help -e clonebundles` for more.
4388 data. See :hg:`help -e clonebundles` for more.
4389
4389
4390 Use :hg:`incoming` if you want to see what would have been added
4390 Use :hg:`incoming` if you want to see what would have been added
4391 by a pull at the time you issued this command. If you then decide
4391 by a pull at the time you issued this command. If you then decide
4392 to add those changes to the repository, you should use :hg:`pull
4392 to add those changes to the repository, you should use :hg:`pull
4393 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4393 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4394
4394
4395 If SOURCE is omitted, the 'default' path will be used.
4395 If SOURCE is omitted, the 'default' path will be used.
4396 See :hg:`help urls` for more information.
4396 See :hg:`help urls` for more information.
4397
4397
4398 Specifying bookmark as ``.`` is equivalent to specifying the active
4398 Specifying bookmark as ``.`` is equivalent to specifying the active
4399 bookmark's name.
4399 bookmark's name.
4400
4400
4401 Returns 0 on success, 1 if an update had unresolved files.
4401 Returns 0 on success, 1 if an update had unresolved files.
4402 """
4402 """
4403
4403
4404 opts = pycompat.byteskwargs(opts)
4404 opts = pycompat.byteskwargs(opts)
4405 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
4405 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
4406 msg = _('update destination required by configuration')
4406 msg = _('update destination required by configuration')
4407 hint = _('use hg pull followed by hg update DEST')
4407 hint = _('use hg pull followed by hg update DEST')
4408 raise error.Abort(msg, hint=hint)
4408 raise error.Abort(msg, hint=hint)
4409
4409
4410 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4410 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4411 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4411 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4412 other = hg.peer(repo, opts, source)
4412 other = hg.peer(repo, opts, source)
4413 try:
4413 try:
4414 revs, checkout = hg.addbranchrevs(repo, other, branches,
4414 revs, checkout = hg.addbranchrevs(repo, other, branches,
4415 opts.get('rev'))
4415 opts.get('rev'))
4416
4416
4417
4418 pullopargs = {}
4417 pullopargs = {}
4419 if opts.get('bookmark'):
4418
4420 if not revs:
4419 nodes = None
4421 revs = []
4420 if opts['bookmark'] or revs:
4422 # The list of bookmark used here is not the one used to actually
4421 # The list of bookmark used here is the same used to actually update
4423 # update the bookmark name. This can result in the revision pulled
4422 # the bookmark names, to avoid the race from issue 4689 and we do
4424 # not ending up with the name of the bookmark because of a race
4423 # all lookup and bookmark queries in one go so they see the same
4425 # condition on the server. (See issue 4689 for details)
4424 # version of the server state (issue 4700).
4426 remotebookmarks = other.listkeys('bookmarks')
4425 nodes = []
4426 fnodes = []
4427 revs = revs or []
4428 if revs and not other.capable('lookup'):
4429 err = _("other repository doesn't support revision lookup, "
4430 "so a rev cannot be specified.")
4431 raise error.Abort(err)
4432 with other.commandexecutor() as e:
4433 fremotebookmarks = e.callcommand('listkeys', {
4434 'namespace': 'bookmarks'
4435 })
4436 for r in revs:
4437 fnodes.append(e.callcommand('lookup', {'key': r}))
4438 remotebookmarks = fremotebookmarks.result()
4427 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
4439 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
4428 pullopargs['remotebookmarks'] = remotebookmarks
4440 pullopargs['remotebookmarks'] = remotebookmarks
4429 for b in opts['bookmark']:
4441 for b in opts['bookmark']:
4430 b = repo._bookmarks.expandname(b)
4442 b = repo._bookmarks.expandname(b)
4431 if b not in remotebookmarks:
4443 if b not in remotebookmarks:
4432 raise error.Abort(_('remote bookmark %s not found!') % b)
4444 raise error.Abort(_('remote bookmark %s not found!') % b)
4433 revs.append(hex(remotebookmarks[b]))
4445 nodes.append(remotebookmarks[b])
4434
4446 for i, rev in enumerate(revs):
4435 if revs:
4447 node = fnodes[i].result()
4436 try:
4448 nodes.append(node)
4437 # When 'rev' is a bookmark name, we cannot guarantee that it
4449 if rev == checkout:
4438 # will be updated with that name because of a race condition
4439 # server side. (See issue 4689 for details)
4440 oldrevs = revs
4441 revs = [] # actually, nodes
4442 for r in oldrevs:
4443 with other.commandexecutor() as e:
4444 node = e.callcommand('lookup', {'key': r}).result()
4445
4446 revs.append(node)
4447 if r == checkout:
4448 checkout = node
4450 checkout = node
4449 except error.CapabilityError:
4450 err = _("other repository doesn't support revision lookup, "
4451 "so a rev cannot be specified.")
4452 raise error.Abort(err)
4453
4451
4454 wlock = util.nullcontextmanager()
4452 wlock = util.nullcontextmanager()
4455 if opts.get('update'):
4453 if opts.get('update'):
4456 wlock = repo.wlock()
4454 wlock = repo.wlock()
4457 with wlock:
4455 with wlock:
4458 pullopargs.update(opts.get('opargs', {}))
4456 pullopargs.update(opts.get('opargs', {}))
4459 modheads = exchange.pull(repo, other, heads=revs,
4457 modheads = exchange.pull(repo, other, heads=nodes,
4460 force=opts.get('force'),
4458 force=opts.get('force'),
4461 bookmarks=opts.get('bookmark', ()),
4459 bookmarks=opts.get('bookmark', ()),
4462 opargs=pullopargs).cgresult
4460 opargs=pullopargs).cgresult
4463
4461
4464 # brev is a name, which might be a bookmark to be activated at
4462 # brev is a name, which might be a bookmark to be activated at
4465 # the end of the update. In other words, it is an explicit
4463 # the end of the update. In other words, it is an explicit
4466 # destination of the update
4464 # destination of the update
4467 brev = None
4465 brev = None
4468
4466
4469 if checkout:
4467 if checkout:
4470 checkout = repo.changelog.rev(checkout)
4468 checkout = repo.changelog.rev(checkout)
4471
4469
4472 # order below depends on implementation of
4470 # order below depends on implementation of
4473 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4471 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4474 # because 'checkout' is determined without it.
4472 # because 'checkout' is determined without it.
4475 if opts.get('rev'):
4473 if opts.get('rev'):
4476 brev = opts['rev'][0]
4474 brev = opts['rev'][0]
4477 elif opts.get('branch'):
4475 elif opts.get('branch'):
4478 brev = opts['branch'][0]
4476 brev = opts['branch'][0]
4479 else:
4477 else:
4480 brev = branches[0]
4478 brev = branches[0]
4481 repo._subtoppath = source
4479 repo._subtoppath = source
4482 try:
4480 try:
4483 ret = postincoming(ui, repo, modheads, opts.get('update'),
4481 ret = postincoming(ui, repo, modheads, opts.get('update'),
4484 checkout, brev)
4482 checkout, brev)
4485
4483
4486 finally:
4484 finally:
4487 del repo._subtoppath
4485 del repo._subtoppath
4488
4486
4489 finally:
4487 finally:
4490 other.close()
4488 other.close()
4491 return ret
4489 return ret
4492
4490
4493 @command('push',
4491 @command('push',
4494 [('f', 'force', None, _('force push')),
4492 [('f', 'force', None, _('force push')),
4495 ('r', 'rev', [],
4493 ('r', 'rev', [],
4496 _('a changeset intended to be included in the destination'),
4494 _('a changeset intended to be included in the destination'),
4497 _('REV')),
4495 _('REV')),
4498 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4496 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4499 ('b', 'branch', [],
4497 ('b', 'branch', [],
4500 _('a specific branch you would like to push'), _('BRANCH')),
4498 _('a specific branch you would like to push'), _('BRANCH')),
4501 ('', 'new-branch', False, _('allow pushing a new branch')),
4499 ('', 'new-branch', False, _('allow pushing a new branch')),
4502 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4500 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4503 ('', 'publish', False, _('push the changeset as public (EXPERIMENTAL)')),
4501 ('', 'publish', False, _('push the changeset as public (EXPERIMENTAL)')),
4504 ] + remoteopts,
4502 ] + remoteopts,
4505 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
4503 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
4506 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4504 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4507 helpbasic=True)
4505 helpbasic=True)
4508 def push(ui, repo, dest=None, **opts):
4506 def push(ui, repo, dest=None, **opts):
4509 """push changes to the specified destination
4507 """push changes to the specified destination
4510
4508
4511 Push changesets from the local repository to the specified
4509 Push changesets from the local repository to the specified
4512 destination.
4510 destination.
4513
4511
4514 This operation is symmetrical to pull: it is identical to a pull
4512 This operation is symmetrical to pull: it is identical to a pull
4515 in the destination repository from the current one.
4513 in the destination repository from the current one.
4516
4514
4517 By default, push will not allow creation of new heads at the
4515 By default, push will not allow creation of new heads at the
4518 destination, since multiple heads would make it unclear which head
4516 destination, since multiple heads would make it unclear which head
4519 to use. In this situation, it is recommended to pull and merge
4517 to use. In this situation, it is recommended to pull and merge
4520 before pushing.
4518 before pushing.
4521
4519
4522 Use --new-branch if you want to allow push to create a new named
4520 Use --new-branch if you want to allow push to create a new named
4523 branch that is not present at the destination. This allows you to
4521 branch that is not present at the destination. This allows you to
4524 only create a new branch without forcing other changes.
4522 only create a new branch without forcing other changes.
4525
4523
4526 .. note::
4524 .. note::
4527
4525
4528 Extra care should be taken with the -f/--force option,
4526 Extra care should be taken with the -f/--force option,
4529 which will push all new heads on all branches, an action which will
4527 which will push all new heads on all branches, an action which will
4530 almost always cause confusion for collaborators.
4528 almost always cause confusion for collaborators.
4531
4529
4532 If -r/--rev is used, the specified revision and all its ancestors
4530 If -r/--rev is used, the specified revision and all its ancestors
4533 will be pushed to the remote repository.
4531 will be pushed to the remote repository.
4534
4532
4535 If -B/--bookmark is used, the specified bookmarked revision, its
4533 If -B/--bookmark is used, the specified bookmarked revision, its
4536 ancestors, and the bookmark will be pushed to the remote
4534 ancestors, and the bookmark will be pushed to the remote
4537 repository. Specifying ``.`` is equivalent to specifying the active
4535 repository. Specifying ``.`` is equivalent to specifying the active
4538 bookmark's name.
4536 bookmark's name.
4539
4537
4540 Please see :hg:`help urls` for important details about ``ssh://``
4538 Please see :hg:`help urls` for important details about ``ssh://``
4541 URLs. If DESTINATION is omitted, a default path will be used.
4539 URLs. If DESTINATION is omitted, a default path will be used.
4542
4540
4543 .. container:: verbose
4541 .. container:: verbose
4544
4542
4545 The --pushvars option sends strings to the server that become
4543 The --pushvars option sends strings to the server that become
4546 environment variables prepended with ``HG_USERVAR_``. For example,
4544 environment variables prepended with ``HG_USERVAR_``. For example,
4547 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4545 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4548 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4546 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4549
4547
4550 pushvars can provide for user-overridable hooks as well as set debug
4548 pushvars can provide for user-overridable hooks as well as set debug
4551 levels. One example is having a hook that blocks commits containing
4549 levels. One example is having a hook that blocks commits containing
4552 conflict markers, but enables the user to override the hook if the file
4550 conflict markers, but enables the user to override the hook if the file
4553 is using conflict markers for testing purposes or the file format has
4551 is using conflict markers for testing purposes or the file format has
4554 strings that look like conflict markers.
4552 strings that look like conflict markers.
4555
4553
4556 By default, servers will ignore `--pushvars`. To enable it add the
4554 By default, servers will ignore `--pushvars`. To enable it add the
4557 following to your configuration file::
4555 following to your configuration file::
4558
4556
4559 [push]
4557 [push]
4560 pushvars.server = true
4558 pushvars.server = true
4561
4559
4562 Returns 0 if push was successful, 1 if nothing to push.
4560 Returns 0 if push was successful, 1 if nothing to push.
4563 """
4561 """
4564
4562
4565 opts = pycompat.byteskwargs(opts)
4563 opts = pycompat.byteskwargs(opts)
4566 if opts.get('bookmark'):
4564 if opts.get('bookmark'):
4567 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4565 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4568 for b in opts['bookmark']:
4566 for b in opts['bookmark']:
4569 # translate -B options to -r so changesets get pushed
4567 # translate -B options to -r so changesets get pushed
4570 b = repo._bookmarks.expandname(b)
4568 b = repo._bookmarks.expandname(b)
4571 if b in repo._bookmarks:
4569 if b in repo._bookmarks:
4572 opts.setdefault('rev', []).append(b)
4570 opts.setdefault('rev', []).append(b)
4573 else:
4571 else:
4574 # if we try to push a deleted bookmark, translate it to null
4572 # if we try to push a deleted bookmark, translate it to null
4575 # this lets simultaneous -r, -b options continue working
4573 # this lets simultaneous -r, -b options continue working
4576 opts.setdefault('rev', []).append("null")
4574 opts.setdefault('rev', []).append("null")
4577
4575
4578 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4576 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4579 if not path:
4577 if not path:
4580 raise error.Abort(_('default repository not configured!'),
4578 raise error.Abort(_('default repository not configured!'),
4581 hint=_("see 'hg help config.paths'"))
4579 hint=_("see 'hg help config.paths'"))
4582 dest = path.pushloc or path.loc
4580 dest = path.pushloc or path.loc
4583 branches = (path.branch, opts.get('branch') or [])
4581 branches = (path.branch, opts.get('branch') or [])
4584 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4582 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4585 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4583 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4586 other = hg.peer(repo, opts, dest)
4584 other = hg.peer(repo, opts, dest)
4587
4585
4588 if revs:
4586 if revs:
4589 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
4587 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
4590 if not revs:
4588 if not revs:
4591 raise error.Abort(_("specified revisions evaluate to an empty set"),
4589 raise error.Abort(_("specified revisions evaluate to an empty set"),
4592 hint=_("use different revision arguments"))
4590 hint=_("use different revision arguments"))
4593 elif path.pushrev:
4591 elif path.pushrev:
4594 # It doesn't make any sense to specify ancestor revisions. So limit
4592 # It doesn't make any sense to specify ancestor revisions. So limit
4595 # to DAG heads to make discovery simpler.
4593 # to DAG heads to make discovery simpler.
4596 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4594 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4597 revs = scmutil.revrange(repo, [expr])
4595 revs = scmutil.revrange(repo, [expr])
4598 revs = [repo[rev].node() for rev in revs]
4596 revs = [repo[rev].node() for rev in revs]
4599 if not revs:
4597 if not revs:
4600 raise error.Abort(_('default push revset for path evaluates to an '
4598 raise error.Abort(_('default push revset for path evaluates to an '
4601 'empty set'))
4599 'empty set'))
4602
4600
4603 repo._subtoppath = dest
4601 repo._subtoppath = dest
4604 try:
4602 try:
4605 # push subrepos depth-first for coherent ordering
4603 # push subrepos depth-first for coherent ordering
4606 c = repo['.']
4604 c = repo['.']
4607 subs = c.substate # only repos that are committed
4605 subs = c.substate # only repos that are committed
4608 for s in sorted(subs):
4606 for s in sorted(subs):
4609 result = c.sub(s).push(opts)
4607 result = c.sub(s).push(opts)
4610 if result == 0:
4608 if result == 0:
4611 return not result
4609 return not result
4612 finally:
4610 finally:
4613 del repo._subtoppath
4611 del repo._subtoppath
4614
4612
4615 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4613 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4616 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4614 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4617
4615
4618 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4616 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4619 newbranch=opts.get('new_branch'),
4617 newbranch=opts.get('new_branch'),
4620 bookmarks=opts.get('bookmark', ()),
4618 bookmarks=opts.get('bookmark', ()),
4621 publish=opts.get('publish'),
4619 publish=opts.get('publish'),
4622 opargs=opargs)
4620 opargs=opargs)
4623
4621
4624 result = not pushop.cgresult
4622 result = not pushop.cgresult
4625
4623
4626 if pushop.bkresult is not None:
4624 if pushop.bkresult is not None:
4627 if pushop.bkresult == 2:
4625 if pushop.bkresult == 2:
4628 result = 2
4626 result = 2
4629 elif not result and pushop.bkresult:
4627 elif not result and pushop.bkresult:
4630 result = 2
4628 result = 2
4631
4629
4632 return result
4630 return result
4633
4631
4634 @command('recover', [], helpcategory=command.CATEGORY_MAINTENANCE)
4632 @command('recover', [], helpcategory=command.CATEGORY_MAINTENANCE)
4635 def recover(ui, repo):
4633 def recover(ui, repo):
4636 """roll back an interrupted transaction
4634 """roll back an interrupted transaction
4637
4635
4638 Recover from an interrupted commit or pull.
4636 Recover from an interrupted commit or pull.
4639
4637
4640 This command tries to fix the repository status after an
4638 This command tries to fix the repository status after an
4641 interrupted operation. It should only be necessary when Mercurial
4639 interrupted operation. It should only be necessary when Mercurial
4642 suggests it.
4640 suggests it.
4643
4641
4644 Returns 0 if successful, 1 if nothing to recover or verify fails.
4642 Returns 0 if successful, 1 if nothing to recover or verify fails.
4645 """
4643 """
4646 if repo.recover():
4644 if repo.recover():
4647 return hg.verify(repo)
4645 return hg.verify(repo)
4648 return 1
4646 return 1
4649
4647
4650 @command('remove|rm',
4648 @command('remove|rm',
4651 [('A', 'after', None, _('record delete for missing files')),
4649 [('A', 'after', None, _('record delete for missing files')),
4652 ('f', 'force', None,
4650 ('f', 'force', None,
4653 _('forget added files, delete modified files')),
4651 _('forget added files, delete modified files')),
4654 ] + subrepoopts + walkopts + dryrunopts,
4652 ] + subrepoopts + walkopts + dryrunopts,
4655 _('[OPTION]... FILE...'),
4653 _('[OPTION]... FILE...'),
4656 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4654 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4657 helpbasic=True, inferrepo=True)
4655 helpbasic=True, inferrepo=True)
4658 def remove(ui, repo, *pats, **opts):
4656 def remove(ui, repo, *pats, **opts):
4659 """remove the specified files on the next commit
4657 """remove the specified files on the next commit
4660
4658
4661 Schedule the indicated files for removal from the current branch.
4659 Schedule the indicated files for removal from the current branch.
4662
4660
4663 This command schedules the files to be removed at the next commit.
4661 This command schedules the files to be removed at the next commit.
4664 To undo a remove before that, see :hg:`revert`. To undo added
4662 To undo a remove before that, see :hg:`revert`. To undo added
4665 files, see :hg:`forget`.
4663 files, see :hg:`forget`.
4666
4664
4667 .. container:: verbose
4665 .. container:: verbose
4668
4666
4669 -A/--after can be used to remove only files that have already
4667 -A/--after can be used to remove only files that have already
4670 been deleted, -f/--force can be used to force deletion, and -Af
4668 been deleted, -f/--force can be used to force deletion, and -Af
4671 can be used to remove files from the next revision without
4669 can be used to remove files from the next revision without
4672 deleting them from the working directory.
4670 deleting them from the working directory.
4673
4671
4674 The following table details the behavior of remove for different
4672 The following table details the behavior of remove for different
4675 file states (columns) and option combinations (rows). The file
4673 file states (columns) and option combinations (rows). The file
4676 states are Added [A], Clean [C], Modified [M] and Missing [!]
4674 states are Added [A], Clean [C], Modified [M] and Missing [!]
4677 (as reported by :hg:`status`). The actions are Warn, Remove
4675 (as reported by :hg:`status`). The actions are Warn, Remove
4678 (from branch) and Delete (from disk):
4676 (from branch) and Delete (from disk):
4679
4677
4680 ========= == == == ==
4678 ========= == == == ==
4681 opt/state A C M !
4679 opt/state A C M !
4682 ========= == == == ==
4680 ========= == == == ==
4683 none W RD W R
4681 none W RD W R
4684 -f R RD RD R
4682 -f R RD RD R
4685 -A W W W R
4683 -A W W W R
4686 -Af R R R R
4684 -Af R R R R
4687 ========= == == == ==
4685 ========= == == == ==
4688
4686
4689 .. note::
4687 .. note::
4690
4688
4691 :hg:`remove` never deletes files in Added [A] state from the
4689 :hg:`remove` never deletes files in Added [A] state from the
4692 working directory, not even if ``--force`` is specified.
4690 working directory, not even if ``--force`` is specified.
4693
4691
4694 Returns 0 on success, 1 if any warnings encountered.
4692 Returns 0 on success, 1 if any warnings encountered.
4695 """
4693 """
4696
4694
4697 opts = pycompat.byteskwargs(opts)
4695 opts = pycompat.byteskwargs(opts)
4698 after, force = opts.get('after'), opts.get('force')
4696 after, force = opts.get('after'), opts.get('force')
4699 dryrun = opts.get('dry_run')
4697 dryrun = opts.get('dry_run')
4700 if not pats and not after:
4698 if not pats and not after:
4701 raise error.Abort(_('no files specified'))
4699 raise error.Abort(_('no files specified'))
4702
4700
4703 m = scmutil.match(repo[None], pats, opts)
4701 m = scmutil.match(repo[None], pats, opts)
4704 subrepos = opts.get('subrepos')
4702 subrepos = opts.get('subrepos')
4705 return cmdutil.remove(ui, repo, m, "", after, force, subrepos,
4703 return cmdutil.remove(ui, repo, m, "", after, force, subrepos,
4706 dryrun=dryrun)
4704 dryrun=dryrun)
4707
4705
4708 @command('rename|move|mv',
4706 @command('rename|move|mv',
4709 [('A', 'after', None, _('record a rename that has already occurred')),
4707 [('A', 'after', None, _('record a rename that has already occurred')),
4710 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4708 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4711 ] + walkopts + dryrunopts,
4709 ] + walkopts + dryrunopts,
4712 _('[OPTION]... SOURCE... DEST'),
4710 _('[OPTION]... SOURCE... DEST'),
4713 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
4711 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
4714 def rename(ui, repo, *pats, **opts):
4712 def rename(ui, repo, *pats, **opts):
4715 """rename files; equivalent of copy + remove
4713 """rename files; equivalent of copy + remove
4716
4714
4717 Mark dest as copies of sources; mark sources for deletion. If dest
4715 Mark dest as copies of sources; mark sources for deletion. If dest
4718 is a directory, copies are put in that directory. If dest is a
4716 is a directory, copies are put in that directory. If dest is a
4719 file, there can only be one source.
4717 file, there can only be one source.
4720
4718
4721 By default, this command copies the contents of files as they
4719 By default, this command copies the contents of files as they
4722 exist in the working directory. If invoked with -A/--after, the
4720 exist in the working directory. If invoked with -A/--after, the
4723 operation is recorded, but no copying is performed.
4721 operation is recorded, but no copying is performed.
4724
4722
4725 This command takes effect at the next commit. To undo a rename
4723 This command takes effect at the next commit. To undo a rename
4726 before that, see :hg:`revert`.
4724 before that, see :hg:`revert`.
4727
4725
4728 Returns 0 on success, 1 if errors are encountered.
4726 Returns 0 on success, 1 if errors are encountered.
4729 """
4727 """
4730 opts = pycompat.byteskwargs(opts)
4728 opts = pycompat.byteskwargs(opts)
4731 with repo.wlock(False):
4729 with repo.wlock(False):
4732 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4730 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4733
4731
4734 @command('resolve',
4732 @command('resolve',
4735 [('a', 'all', None, _('select all unresolved files')),
4733 [('a', 'all', None, _('select all unresolved files')),
4736 ('l', 'list', None, _('list state of files needing merge')),
4734 ('l', 'list', None, _('list state of files needing merge')),
4737 ('m', 'mark', None, _('mark files as resolved')),
4735 ('m', 'mark', None, _('mark files as resolved')),
4738 ('u', 'unmark', None, _('mark files as unresolved')),
4736 ('u', 'unmark', None, _('mark files as unresolved')),
4739 ('n', 'no-status', None, _('hide status prefix')),
4737 ('n', 'no-status', None, _('hide status prefix')),
4740 ('', 're-merge', None, _('re-merge files'))]
4738 ('', 're-merge', None, _('re-merge files'))]
4741 + mergetoolopts + walkopts + formatteropts,
4739 + mergetoolopts + walkopts + formatteropts,
4742 _('[OPTION]... [FILE]...'),
4740 _('[OPTION]... [FILE]...'),
4743 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4741 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4744 inferrepo=True)
4742 inferrepo=True)
4745 def resolve(ui, repo, *pats, **opts):
4743 def resolve(ui, repo, *pats, **opts):
4746 """redo merges or set/view the merge status of files
4744 """redo merges or set/view the merge status of files
4747
4745
4748 Merges with unresolved conflicts are often the result of
4746 Merges with unresolved conflicts are often the result of
4749 non-interactive merging using the ``internal:merge`` configuration
4747 non-interactive merging using the ``internal:merge`` configuration
4750 setting, or a command-line merge tool like ``diff3``. The resolve
4748 setting, or a command-line merge tool like ``diff3``. The resolve
4751 command is used to manage the files involved in a merge, after
4749 command is used to manage the files involved in a merge, after
4752 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4750 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4753 working directory must have two parents). See :hg:`help
4751 working directory must have two parents). See :hg:`help
4754 merge-tools` for information on configuring merge tools.
4752 merge-tools` for information on configuring merge tools.
4755
4753
4756 The resolve command can be used in the following ways:
4754 The resolve command can be used in the following ways:
4757
4755
4758 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
4756 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
4759 the specified files, discarding any previous merge attempts. Re-merging
4757 the specified files, discarding any previous merge attempts. Re-merging
4760 is not performed for files already marked as resolved. Use ``--all/-a``
4758 is not performed for files already marked as resolved. Use ``--all/-a``
4761 to select all unresolved files. ``--tool`` can be used to specify
4759 to select all unresolved files. ``--tool`` can be used to specify
4762 the merge tool used for the given files. It overrides the HGMERGE
4760 the merge tool used for the given files. It overrides the HGMERGE
4763 environment variable and your configuration files. Previous file
4761 environment variable and your configuration files. Previous file
4764 contents are saved with a ``.orig`` suffix.
4762 contents are saved with a ``.orig`` suffix.
4765
4763
4766 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4764 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4767 (e.g. after having manually fixed-up the files). The default is
4765 (e.g. after having manually fixed-up the files). The default is
4768 to mark all unresolved files.
4766 to mark all unresolved files.
4769
4767
4770 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4768 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4771 default is to mark all resolved files.
4769 default is to mark all resolved files.
4772
4770
4773 - :hg:`resolve -l`: list files which had or still have conflicts.
4771 - :hg:`resolve -l`: list files which had or still have conflicts.
4774 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4772 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4775 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4773 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4776 the list. See :hg:`help filesets` for details.
4774 the list. See :hg:`help filesets` for details.
4777
4775
4778 .. note::
4776 .. note::
4779
4777
4780 Mercurial will not let you commit files with unresolved merge
4778 Mercurial will not let you commit files with unresolved merge
4781 conflicts. You must use :hg:`resolve -m ...` before you can
4779 conflicts. You must use :hg:`resolve -m ...` before you can
4782 commit after a conflicting merge.
4780 commit after a conflicting merge.
4783
4781
4784 .. container:: verbose
4782 .. container:: verbose
4785
4783
4786 Template:
4784 Template:
4787
4785
4788 The following keywords are supported in addition to the common template
4786 The following keywords are supported in addition to the common template
4789 keywords and functions. See also :hg:`help templates`.
4787 keywords and functions. See also :hg:`help templates`.
4790
4788
4791 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
4789 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
4792 :path: String. Repository-absolute path of the file.
4790 :path: String. Repository-absolute path of the file.
4793
4791
4794 Returns 0 on success, 1 if any files fail a resolve attempt.
4792 Returns 0 on success, 1 if any files fail a resolve attempt.
4795 """
4793 """
4796
4794
4797 opts = pycompat.byteskwargs(opts)
4795 opts = pycompat.byteskwargs(opts)
4798 confirm = ui.configbool('commands', 'resolve.confirm')
4796 confirm = ui.configbool('commands', 'resolve.confirm')
4799 flaglist = 'all mark unmark list no_status re_merge'.split()
4797 flaglist = 'all mark unmark list no_status re_merge'.split()
4800 all, mark, unmark, show, nostatus, remerge = \
4798 all, mark, unmark, show, nostatus, remerge = \
4801 [opts.get(o) for o in flaglist]
4799 [opts.get(o) for o in flaglist]
4802
4800
4803 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
4801 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
4804 if actioncount > 1:
4802 if actioncount > 1:
4805 raise error.Abort(_("too many actions specified"))
4803 raise error.Abort(_("too many actions specified"))
4806 elif (actioncount == 0
4804 elif (actioncount == 0
4807 and ui.configbool('commands', 'resolve.explicit-re-merge')):
4805 and ui.configbool('commands', 'resolve.explicit-re-merge')):
4808 hint = _('use --mark, --unmark, --list or --re-merge')
4806 hint = _('use --mark, --unmark, --list or --re-merge')
4809 raise error.Abort(_('no action specified'), hint=hint)
4807 raise error.Abort(_('no action specified'), hint=hint)
4810 if pats and all:
4808 if pats and all:
4811 raise error.Abort(_("can't specify --all and patterns"))
4809 raise error.Abort(_("can't specify --all and patterns"))
4812 if not (all or pats or show or mark or unmark):
4810 if not (all or pats or show or mark or unmark):
4813 raise error.Abort(_('no files or directories specified'),
4811 raise error.Abort(_('no files or directories specified'),
4814 hint=('use --all to re-merge all unresolved files'))
4812 hint=('use --all to re-merge all unresolved files'))
4815
4813
4816 if confirm:
4814 if confirm:
4817 if all:
4815 if all:
4818 if ui.promptchoice(_(b're-merge all unresolved files (yn)?'
4816 if ui.promptchoice(_(b're-merge all unresolved files (yn)?'
4819 b'$$ &Yes $$ &No')):
4817 b'$$ &Yes $$ &No')):
4820 raise error.Abort(_('user quit'))
4818 raise error.Abort(_('user quit'))
4821 if mark and not pats:
4819 if mark and not pats:
4822 if ui.promptchoice(_(b'mark all unresolved files as resolved (yn)?'
4820 if ui.promptchoice(_(b'mark all unresolved files as resolved (yn)?'
4823 b'$$ &Yes $$ &No')):
4821 b'$$ &Yes $$ &No')):
4824 raise error.Abort(_('user quit'))
4822 raise error.Abort(_('user quit'))
4825 if unmark and not pats:
4823 if unmark and not pats:
4826 if ui.promptchoice(_(b'mark all resolved files as unresolved (yn)?'
4824 if ui.promptchoice(_(b'mark all resolved files as unresolved (yn)?'
4827 b'$$ &Yes $$ &No')):
4825 b'$$ &Yes $$ &No')):
4828 raise error.Abort(_('user quit'))
4826 raise error.Abort(_('user quit'))
4829
4827
4830 if show:
4828 if show:
4831 ui.pager('resolve')
4829 ui.pager('resolve')
4832 fm = ui.formatter('resolve', opts)
4830 fm = ui.formatter('resolve', opts)
4833 ms = mergemod.mergestate.read(repo)
4831 ms = mergemod.mergestate.read(repo)
4834 wctx = repo[None]
4832 wctx = repo[None]
4835 m = scmutil.match(wctx, pats, opts)
4833 m = scmutil.match(wctx, pats, opts)
4836
4834
4837 # Labels and keys based on merge state. Unresolved path conflicts show
4835 # Labels and keys based on merge state. Unresolved path conflicts show
4838 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4836 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4839 # resolved conflicts.
4837 # resolved conflicts.
4840 mergestateinfo = {
4838 mergestateinfo = {
4841 mergemod.MERGE_RECORD_UNRESOLVED: ('resolve.unresolved', 'U'),
4839 mergemod.MERGE_RECORD_UNRESOLVED: ('resolve.unresolved', 'U'),
4842 mergemod.MERGE_RECORD_RESOLVED: ('resolve.resolved', 'R'),
4840 mergemod.MERGE_RECORD_RESOLVED: ('resolve.resolved', 'R'),
4843 mergemod.MERGE_RECORD_UNRESOLVED_PATH: ('resolve.unresolved', 'P'),
4841 mergemod.MERGE_RECORD_UNRESOLVED_PATH: ('resolve.unresolved', 'P'),
4844 mergemod.MERGE_RECORD_RESOLVED_PATH: ('resolve.resolved', 'R'),
4842 mergemod.MERGE_RECORD_RESOLVED_PATH: ('resolve.resolved', 'R'),
4845 mergemod.MERGE_RECORD_DRIVER_RESOLVED: ('resolve.driverresolved',
4843 mergemod.MERGE_RECORD_DRIVER_RESOLVED: ('resolve.driverresolved',
4846 'D'),
4844 'D'),
4847 }
4845 }
4848
4846
4849 for f in ms:
4847 for f in ms:
4850 if not m(f):
4848 if not m(f):
4851 continue
4849 continue
4852
4850
4853 label, key = mergestateinfo[ms[f]]
4851 label, key = mergestateinfo[ms[f]]
4854 fm.startitem()
4852 fm.startitem()
4855 fm.context(ctx=wctx)
4853 fm.context(ctx=wctx)
4856 fm.condwrite(not nostatus, 'mergestatus', '%s ', key, label=label)
4854 fm.condwrite(not nostatus, 'mergestatus', '%s ', key, label=label)
4857 fm.write('path', '%s\n', f, label=label)
4855 fm.write('path', '%s\n', f, label=label)
4858 fm.end()
4856 fm.end()
4859 return 0
4857 return 0
4860
4858
4861 with repo.wlock():
4859 with repo.wlock():
4862 ms = mergemod.mergestate.read(repo)
4860 ms = mergemod.mergestate.read(repo)
4863
4861
4864 if not (ms.active() or repo.dirstate.p2() != nullid):
4862 if not (ms.active() or repo.dirstate.p2() != nullid):
4865 raise error.Abort(
4863 raise error.Abort(
4866 _('resolve command not applicable when not merging'))
4864 _('resolve command not applicable when not merging'))
4867
4865
4868 wctx = repo[None]
4866 wctx = repo[None]
4869
4867
4870 if (ms.mergedriver
4868 if (ms.mergedriver
4871 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED):
4869 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED):
4872 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4870 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4873 ms.commit()
4871 ms.commit()
4874 # allow mark and unmark to go through
4872 # allow mark and unmark to go through
4875 if not mark and not unmark and not proceed:
4873 if not mark and not unmark and not proceed:
4876 return 1
4874 return 1
4877
4875
4878 m = scmutil.match(wctx, pats, opts)
4876 m = scmutil.match(wctx, pats, opts)
4879 ret = 0
4877 ret = 0
4880 didwork = False
4878 didwork = False
4881 runconclude = False
4879 runconclude = False
4882
4880
4883 tocomplete = []
4881 tocomplete = []
4884 hasconflictmarkers = []
4882 hasconflictmarkers = []
4885 if mark:
4883 if mark:
4886 markcheck = ui.config('commands', 'resolve.mark-check')
4884 markcheck = ui.config('commands', 'resolve.mark-check')
4887 if markcheck not in ['warn', 'abort']:
4885 if markcheck not in ['warn', 'abort']:
4888 # Treat all invalid / unrecognized values as 'none'.
4886 # Treat all invalid / unrecognized values as 'none'.
4889 markcheck = False
4887 markcheck = False
4890 for f in ms:
4888 for f in ms:
4891 if not m(f):
4889 if not m(f):
4892 continue
4890 continue
4893
4891
4894 didwork = True
4892 didwork = True
4895
4893
4896 # don't let driver-resolved files be marked, and run the conclude
4894 # don't let driver-resolved files be marked, and run the conclude
4897 # step if asked to resolve
4895 # step if asked to resolve
4898 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
4896 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
4899 exact = m.exact(f)
4897 exact = m.exact(f)
4900 if mark:
4898 if mark:
4901 if exact:
4899 if exact:
4902 ui.warn(_('not marking %s as it is driver-resolved\n')
4900 ui.warn(_('not marking %s as it is driver-resolved\n')
4903 % f)
4901 % f)
4904 elif unmark:
4902 elif unmark:
4905 if exact:
4903 if exact:
4906 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4904 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4907 % f)
4905 % f)
4908 else:
4906 else:
4909 runconclude = True
4907 runconclude = True
4910 continue
4908 continue
4911
4909
4912 # path conflicts must be resolved manually
4910 # path conflicts must be resolved manually
4913 if ms[f] in (mergemod.MERGE_RECORD_UNRESOLVED_PATH,
4911 if ms[f] in (mergemod.MERGE_RECORD_UNRESOLVED_PATH,
4914 mergemod.MERGE_RECORD_RESOLVED_PATH):
4912 mergemod.MERGE_RECORD_RESOLVED_PATH):
4915 if mark:
4913 if mark:
4916 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
4914 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
4917 elif unmark:
4915 elif unmark:
4918 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
4916 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
4919 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
4917 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
4920 ui.warn(_('%s: path conflict must be resolved manually\n')
4918 ui.warn(_('%s: path conflict must be resolved manually\n')
4921 % f)
4919 % f)
4922 continue
4920 continue
4923
4921
4924 if mark:
4922 if mark:
4925 if markcheck:
4923 if markcheck:
4926 with repo.wvfs(f) as fobj:
4924 with repo.wvfs(f) as fobj:
4927 fdata = fobj.read()
4925 fdata = fobj.read()
4928 if filemerge.hasconflictmarkers(fdata) and \
4926 if filemerge.hasconflictmarkers(fdata) and \
4929 ms[f] != mergemod.MERGE_RECORD_RESOLVED:
4927 ms[f] != mergemod.MERGE_RECORD_RESOLVED:
4930 hasconflictmarkers.append(f)
4928 hasconflictmarkers.append(f)
4931 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
4929 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
4932 elif unmark:
4930 elif unmark:
4933 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
4931 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
4934 else:
4932 else:
4935 # backup pre-resolve (merge uses .orig for its own purposes)
4933 # backup pre-resolve (merge uses .orig for its own purposes)
4936 a = repo.wjoin(f)
4934 a = repo.wjoin(f)
4937 try:
4935 try:
4938 util.copyfile(a, a + ".resolve")
4936 util.copyfile(a, a + ".resolve")
4939 except (IOError, OSError) as inst:
4937 except (IOError, OSError) as inst:
4940 if inst.errno != errno.ENOENT:
4938 if inst.errno != errno.ENOENT:
4941 raise
4939 raise
4942
4940
4943 try:
4941 try:
4944 # preresolve file
4942 # preresolve file
4945 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4943 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4946 with ui.configoverride(overrides, 'resolve'):
4944 with ui.configoverride(overrides, 'resolve'):
4947 complete, r = ms.preresolve(f, wctx)
4945 complete, r = ms.preresolve(f, wctx)
4948 if not complete:
4946 if not complete:
4949 tocomplete.append(f)
4947 tocomplete.append(f)
4950 elif r:
4948 elif r:
4951 ret = 1
4949 ret = 1
4952 finally:
4950 finally:
4953 ms.commit()
4951 ms.commit()
4954
4952
4955 # replace filemerge's .orig file with our resolve file, but only
4953 # replace filemerge's .orig file with our resolve file, but only
4956 # for merges that are complete
4954 # for merges that are complete
4957 if complete:
4955 if complete:
4958 try:
4956 try:
4959 util.rename(a + ".resolve",
4957 util.rename(a + ".resolve",
4960 scmutil.origpath(ui, repo, a))
4958 scmutil.origpath(ui, repo, a))
4961 except OSError as inst:
4959 except OSError as inst:
4962 if inst.errno != errno.ENOENT:
4960 if inst.errno != errno.ENOENT:
4963 raise
4961 raise
4964
4962
4965 if hasconflictmarkers:
4963 if hasconflictmarkers:
4966 ui.warn(_('warning: the following files still have conflict '
4964 ui.warn(_('warning: the following files still have conflict '
4967 'markers:\n ') + '\n '.join(hasconflictmarkers) + '\n')
4965 'markers:\n ') + '\n '.join(hasconflictmarkers) + '\n')
4968 if markcheck == 'abort' and not all and not pats:
4966 if markcheck == 'abort' and not all and not pats:
4969 raise error.Abort(_('conflict markers detected'),
4967 raise error.Abort(_('conflict markers detected'),
4970 hint=_('use --all to mark anyway'))
4968 hint=_('use --all to mark anyway'))
4971
4969
4972 for f in tocomplete:
4970 for f in tocomplete:
4973 try:
4971 try:
4974 # resolve file
4972 # resolve file
4975 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4973 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4976 with ui.configoverride(overrides, 'resolve'):
4974 with ui.configoverride(overrides, 'resolve'):
4977 r = ms.resolve(f, wctx)
4975 r = ms.resolve(f, wctx)
4978 if r:
4976 if r:
4979 ret = 1
4977 ret = 1
4980 finally:
4978 finally:
4981 ms.commit()
4979 ms.commit()
4982
4980
4983 # replace filemerge's .orig file with our resolve file
4981 # replace filemerge's .orig file with our resolve file
4984 a = repo.wjoin(f)
4982 a = repo.wjoin(f)
4985 try:
4983 try:
4986 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4984 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4987 except OSError as inst:
4985 except OSError as inst:
4988 if inst.errno != errno.ENOENT:
4986 if inst.errno != errno.ENOENT:
4989 raise
4987 raise
4990
4988
4991 ms.commit()
4989 ms.commit()
4992 ms.recordactions()
4990 ms.recordactions()
4993
4991
4994 if not didwork and pats:
4992 if not didwork and pats:
4995 hint = None
4993 hint = None
4996 if not any([p for p in pats if p.find(':') >= 0]):
4994 if not any([p for p in pats if p.find(':') >= 0]):
4997 pats = ['path:%s' % p for p in pats]
4995 pats = ['path:%s' % p for p in pats]
4998 m = scmutil.match(wctx, pats, opts)
4996 m = scmutil.match(wctx, pats, opts)
4999 for f in ms:
4997 for f in ms:
5000 if not m(f):
4998 if not m(f):
5001 continue
4999 continue
5002 def flag(o):
5000 def flag(o):
5003 if o == 're_merge':
5001 if o == 're_merge':
5004 return '--re-merge '
5002 return '--re-merge '
5005 return '-%s ' % o[0:1]
5003 return '-%s ' % o[0:1]
5006 flags = ''.join([flag(o) for o in flaglist if opts.get(o)])
5004 flags = ''.join([flag(o) for o in flaglist if opts.get(o)])
5007 hint = _("(try: hg resolve %s%s)\n") % (
5005 hint = _("(try: hg resolve %s%s)\n") % (
5008 flags,
5006 flags,
5009 ' '.join(pats))
5007 ' '.join(pats))
5010 break
5008 break
5011 ui.warn(_("arguments do not match paths that need resolving\n"))
5009 ui.warn(_("arguments do not match paths that need resolving\n"))
5012 if hint:
5010 if hint:
5013 ui.warn(hint)
5011 ui.warn(hint)
5014 elif ms.mergedriver and ms.mdstate() != 's':
5012 elif ms.mergedriver and ms.mdstate() != 's':
5015 # run conclude step when either a driver-resolved file is requested
5013 # run conclude step when either a driver-resolved file is requested
5016 # or there are no driver-resolved files
5014 # or there are no driver-resolved files
5017 # we can't use 'ret' to determine whether any files are unresolved
5015 # we can't use 'ret' to determine whether any files are unresolved
5018 # because we might not have tried to resolve some
5016 # because we might not have tried to resolve some
5019 if ((runconclude or not list(ms.driverresolved()))
5017 if ((runconclude or not list(ms.driverresolved()))
5020 and not list(ms.unresolved())):
5018 and not list(ms.unresolved())):
5021 proceed = mergemod.driverconclude(repo, ms, wctx)
5019 proceed = mergemod.driverconclude(repo, ms, wctx)
5022 ms.commit()
5020 ms.commit()
5023 if not proceed:
5021 if not proceed:
5024 return 1
5022 return 1
5025
5023
5026 # Nudge users into finishing an unfinished operation
5024 # Nudge users into finishing an unfinished operation
5027 unresolvedf = list(ms.unresolved())
5025 unresolvedf = list(ms.unresolved())
5028 driverresolvedf = list(ms.driverresolved())
5026 driverresolvedf = list(ms.driverresolved())
5029 if not unresolvedf and not driverresolvedf:
5027 if not unresolvedf and not driverresolvedf:
5030 ui.status(_('(no more unresolved files)\n'))
5028 ui.status(_('(no more unresolved files)\n'))
5031 cmdutil.checkafterresolved(repo)
5029 cmdutil.checkafterresolved(repo)
5032 elif not unresolvedf:
5030 elif not unresolvedf:
5033 ui.status(_('(no more unresolved files -- '
5031 ui.status(_('(no more unresolved files -- '
5034 'run "hg resolve --all" to conclude)\n'))
5032 'run "hg resolve --all" to conclude)\n'))
5035
5033
5036 return ret
5034 return ret
5037
5035
5038 @command('revert',
5036 @command('revert',
5039 [('a', 'all', None, _('revert all changes when no arguments given')),
5037 [('a', 'all', None, _('revert all changes when no arguments given')),
5040 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5038 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5041 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5039 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5042 ('C', 'no-backup', None, _('do not save backup copies of files')),
5040 ('C', 'no-backup', None, _('do not save backup copies of files')),
5043 ('i', 'interactive', None, _('interactively select the changes')),
5041 ('i', 'interactive', None, _('interactively select the changes')),
5044 ] + walkopts + dryrunopts,
5042 ] + walkopts + dryrunopts,
5045 _('[OPTION]... [-r REV] [NAME]...'),
5043 _('[OPTION]... [-r REV] [NAME]...'),
5046 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5044 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5047 def revert(ui, repo, *pats, **opts):
5045 def revert(ui, repo, *pats, **opts):
5048 """restore files to their checkout state
5046 """restore files to their checkout state
5049
5047
5050 .. note::
5048 .. note::
5051
5049
5052 To check out earlier revisions, you should use :hg:`update REV`.
5050 To check out earlier revisions, you should use :hg:`update REV`.
5053 To cancel an uncommitted merge (and lose your changes),
5051 To cancel an uncommitted merge (and lose your changes),
5054 use :hg:`merge --abort`.
5052 use :hg:`merge --abort`.
5055
5053
5056 With no revision specified, revert the specified files or directories
5054 With no revision specified, revert the specified files or directories
5057 to the contents they had in the parent of the working directory.
5055 to the contents they had in the parent of the working directory.
5058 This restores the contents of files to an unmodified
5056 This restores the contents of files to an unmodified
5059 state and unschedules adds, removes, copies, and renames. If the
5057 state and unschedules adds, removes, copies, and renames. If the
5060 working directory has two parents, you must explicitly specify a
5058 working directory has two parents, you must explicitly specify a
5061 revision.
5059 revision.
5062
5060
5063 Using the -r/--rev or -d/--date options, revert the given files or
5061 Using the -r/--rev or -d/--date options, revert the given files or
5064 directories to their states as of a specific revision. Because
5062 directories to their states as of a specific revision. Because
5065 revert does not change the working directory parents, this will
5063 revert does not change the working directory parents, this will
5066 cause these files to appear modified. This can be helpful to "back
5064 cause these files to appear modified. This can be helpful to "back
5067 out" some or all of an earlier change. See :hg:`backout` for a
5065 out" some or all of an earlier change. See :hg:`backout` for a
5068 related method.
5066 related method.
5069
5067
5070 Modified files are saved with a .orig suffix before reverting.
5068 Modified files are saved with a .orig suffix before reverting.
5071 To disable these backups, use --no-backup. It is possible to store
5069 To disable these backups, use --no-backup. It is possible to store
5072 the backup files in a custom directory relative to the root of the
5070 the backup files in a custom directory relative to the root of the
5073 repository by setting the ``ui.origbackuppath`` configuration
5071 repository by setting the ``ui.origbackuppath`` configuration
5074 option.
5072 option.
5075
5073
5076 See :hg:`help dates` for a list of formats valid for -d/--date.
5074 See :hg:`help dates` for a list of formats valid for -d/--date.
5077
5075
5078 See :hg:`help backout` for a way to reverse the effect of an
5076 See :hg:`help backout` for a way to reverse the effect of an
5079 earlier changeset.
5077 earlier changeset.
5080
5078
5081 Returns 0 on success.
5079 Returns 0 on success.
5082 """
5080 """
5083
5081
5084 opts = pycompat.byteskwargs(opts)
5082 opts = pycompat.byteskwargs(opts)
5085 if opts.get("date"):
5083 if opts.get("date"):
5086 if opts.get("rev"):
5084 if opts.get("rev"):
5087 raise error.Abort(_("you can't specify a revision and a date"))
5085 raise error.Abort(_("you can't specify a revision and a date"))
5088 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5086 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5089
5087
5090 parent, p2 = repo.dirstate.parents()
5088 parent, p2 = repo.dirstate.parents()
5091 if not opts.get('rev') and p2 != nullid:
5089 if not opts.get('rev') and p2 != nullid:
5092 # revert after merge is a trap for new users (issue2915)
5090 # revert after merge is a trap for new users (issue2915)
5093 raise error.Abort(_('uncommitted merge with no revision specified'),
5091 raise error.Abort(_('uncommitted merge with no revision specified'),
5094 hint=_("use 'hg update' or see 'hg help revert'"))
5092 hint=_("use 'hg update' or see 'hg help revert'"))
5095
5093
5096 rev = opts.get('rev')
5094 rev = opts.get('rev')
5097 if rev:
5095 if rev:
5098 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
5096 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
5099 ctx = scmutil.revsingle(repo, rev)
5097 ctx = scmutil.revsingle(repo, rev)
5100
5098
5101 if (not (pats or opts.get('include') or opts.get('exclude') or
5099 if (not (pats or opts.get('include') or opts.get('exclude') or
5102 opts.get('all') or opts.get('interactive'))):
5100 opts.get('all') or opts.get('interactive'))):
5103 msg = _("no files or directories specified")
5101 msg = _("no files or directories specified")
5104 if p2 != nullid:
5102 if p2 != nullid:
5105 hint = _("uncommitted merge, use --all to discard all changes,"
5103 hint = _("uncommitted merge, use --all to discard all changes,"
5106 " or 'hg update -C .' to abort the merge")
5104 " or 'hg update -C .' to abort the merge")
5107 raise error.Abort(msg, hint=hint)
5105 raise error.Abort(msg, hint=hint)
5108 dirty = any(repo.status())
5106 dirty = any(repo.status())
5109 node = ctx.node()
5107 node = ctx.node()
5110 if node != parent:
5108 if node != parent:
5111 if dirty:
5109 if dirty:
5112 hint = _("uncommitted changes, use --all to discard all"
5110 hint = _("uncommitted changes, use --all to discard all"
5113 " changes, or 'hg update %d' to update") % ctx.rev()
5111 " changes, or 'hg update %d' to update") % ctx.rev()
5114 else:
5112 else:
5115 hint = _("use --all to revert all files,"
5113 hint = _("use --all to revert all files,"
5116 " or 'hg update %d' to update") % ctx.rev()
5114 " or 'hg update %d' to update") % ctx.rev()
5117 elif dirty:
5115 elif dirty:
5118 hint = _("uncommitted changes, use --all to discard all changes")
5116 hint = _("uncommitted changes, use --all to discard all changes")
5119 else:
5117 else:
5120 hint = _("use --all to revert all files")
5118 hint = _("use --all to revert all files")
5121 raise error.Abort(msg, hint=hint)
5119 raise error.Abort(msg, hint=hint)
5122
5120
5123 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats,
5121 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats,
5124 **pycompat.strkwargs(opts))
5122 **pycompat.strkwargs(opts))
5125
5123
5126 @command(
5124 @command(
5127 'rollback',
5125 'rollback',
5128 dryrunopts + [('f', 'force', False, _('ignore safety measures'))],
5126 dryrunopts + [('f', 'force', False, _('ignore safety measures'))],
5129 helpcategory=command.CATEGORY_MAINTENANCE)
5127 helpcategory=command.CATEGORY_MAINTENANCE)
5130 def rollback(ui, repo, **opts):
5128 def rollback(ui, repo, **opts):
5131 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5129 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5132
5130
5133 Please use :hg:`commit --amend` instead of rollback to correct
5131 Please use :hg:`commit --amend` instead of rollback to correct
5134 mistakes in the last commit.
5132 mistakes in the last commit.
5135
5133
5136 This command should be used with care. There is only one level of
5134 This command should be used with care. There is only one level of
5137 rollback, and there is no way to undo a rollback. It will also
5135 rollback, and there is no way to undo a rollback. It will also
5138 restore the dirstate at the time of the last transaction, losing
5136 restore the dirstate at the time of the last transaction, losing
5139 any dirstate changes since that time. This command does not alter
5137 any dirstate changes since that time. This command does not alter
5140 the working directory.
5138 the working directory.
5141
5139
5142 Transactions are used to encapsulate the effects of all commands
5140 Transactions are used to encapsulate the effects of all commands
5143 that create new changesets or propagate existing changesets into a
5141 that create new changesets or propagate existing changesets into a
5144 repository.
5142 repository.
5145
5143
5146 .. container:: verbose
5144 .. container:: verbose
5147
5145
5148 For example, the following commands are transactional, and their
5146 For example, the following commands are transactional, and their
5149 effects can be rolled back:
5147 effects can be rolled back:
5150
5148
5151 - commit
5149 - commit
5152 - import
5150 - import
5153 - pull
5151 - pull
5154 - push (with this repository as the destination)
5152 - push (with this repository as the destination)
5155 - unbundle
5153 - unbundle
5156
5154
5157 To avoid permanent data loss, rollback will refuse to rollback a
5155 To avoid permanent data loss, rollback will refuse to rollback a
5158 commit transaction if it isn't checked out. Use --force to
5156 commit transaction if it isn't checked out. Use --force to
5159 override this protection.
5157 override this protection.
5160
5158
5161 The rollback command can be entirely disabled by setting the
5159 The rollback command can be entirely disabled by setting the
5162 ``ui.rollback`` configuration setting to false. If you're here
5160 ``ui.rollback`` configuration setting to false. If you're here
5163 because you want to use rollback and it's disabled, you can
5161 because you want to use rollback and it's disabled, you can
5164 re-enable the command by setting ``ui.rollback`` to true.
5162 re-enable the command by setting ``ui.rollback`` to true.
5165
5163
5166 This command is not intended for use on public repositories. Once
5164 This command is not intended for use on public repositories. Once
5167 changes are visible for pull by other users, rolling a transaction
5165 changes are visible for pull by other users, rolling a transaction
5168 back locally is ineffective (someone else may already have pulled
5166 back locally is ineffective (someone else may already have pulled
5169 the changes). Furthermore, a race is possible with readers of the
5167 the changes). Furthermore, a race is possible with readers of the
5170 repository; for example an in-progress pull from the repository
5168 repository; for example an in-progress pull from the repository
5171 may fail if a rollback is performed.
5169 may fail if a rollback is performed.
5172
5170
5173 Returns 0 on success, 1 if no rollback data is available.
5171 Returns 0 on success, 1 if no rollback data is available.
5174 """
5172 """
5175 if not ui.configbool('ui', 'rollback'):
5173 if not ui.configbool('ui', 'rollback'):
5176 raise error.Abort(_('rollback is disabled because it is unsafe'),
5174 raise error.Abort(_('rollback is disabled because it is unsafe'),
5177 hint=('see `hg help -v rollback` for information'))
5175 hint=('see `hg help -v rollback` for information'))
5178 return repo.rollback(dryrun=opts.get(r'dry_run'),
5176 return repo.rollback(dryrun=opts.get(r'dry_run'),
5179 force=opts.get(r'force'))
5177 force=opts.get(r'force'))
5180
5178
5181 @command(
5179 @command(
5182 'root', [], intents={INTENT_READONLY},
5180 'root', [], intents={INTENT_READONLY},
5183 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5181 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5184 def root(ui, repo):
5182 def root(ui, repo):
5185 """print the root (top) of the current working directory
5183 """print the root (top) of the current working directory
5186
5184
5187 Print the root directory of the current repository.
5185 Print the root directory of the current repository.
5188
5186
5189 Returns 0 on success.
5187 Returns 0 on success.
5190 """
5188 """
5191 ui.write(repo.root + "\n")
5189 ui.write(repo.root + "\n")
5192
5190
5193 @command('serve',
5191 @command('serve',
5194 [('A', 'accesslog', '', _('name of access log file to write to'),
5192 [('A', 'accesslog', '', _('name of access log file to write to'),
5195 _('FILE')),
5193 _('FILE')),
5196 ('d', 'daemon', None, _('run server in background')),
5194 ('d', 'daemon', None, _('run server in background')),
5197 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
5195 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
5198 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5196 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5199 # use string type, then we can check if something was passed
5197 # use string type, then we can check if something was passed
5200 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5198 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5201 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5199 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5202 _('ADDR')),
5200 _('ADDR')),
5203 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5201 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5204 _('PREFIX')),
5202 _('PREFIX')),
5205 ('n', 'name', '',
5203 ('n', 'name', '',
5206 _('name to show in web pages (default: working directory)'), _('NAME')),
5204 _('name to show in web pages (default: working directory)'), _('NAME')),
5207 ('', 'web-conf', '',
5205 ('', 'web-conf', '',
5208 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
5206 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
5209 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5207 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5210 _('FILE')),
5208 _('FILE')),
5211 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5209 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5212 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
5210 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
5213 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
5211 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
5214 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5212 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5215 ('', 'style', '', _('template style to use'), _('STYLE')),
5213 ('', 'style', '', _('template style to use'), _('STYLE')),
5216 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5214 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5217 ('', 'certificate', '', _('SSL certificate file'), _('FILE')),
5215 ('', 'certificate', '', _('SSL certificate file'), _('FILE')),
5218 ('', 'print-url', None, _('start and print only the URL'))]
5216 ('', 'print-url', None, _('start and print only the URL'))]
5219 + subrepoopts,
5217 + subrepoopts,
5220 _('[OPTION]...'),
5218 _('[OPTION]...'),
5221 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5219 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5222 helpbasic=True, optionalrepo=True)
5220 helpbasic=True, optionalrepo=True)
5223 def serve(ui, repo, **opts):
5221 def serve(ui, repo, **opts):
5224 """start stand-alone webserver
5222 """start stand-alone webserver
5225
5223
5226 Start a local HTTP repository browser and pull server. You can use
5224 Start a local HTTP repository browser and pull server. You can use
5227 this for ad-hoc sharing and browsing of repositories. It is
5225 this for ad-hoc sharing and browsing of repositories. It is
5228 recommended to use a real web server to serve a repository for
5226 recommended to use a real web server to serve a repository for
5229 longer periods of time.
5227 longer periods of time.
5230
5228
5231 Please note that the server does not implement access control.
5229 Please note that the server does not implement access control.
5232 This means that, by default, anybody can read from the server and
5230 This means that, by default, anybody can read from the server and
5233 nobody can write to it by default. Set the ``web.allow-push``
5231 nobody can write to it by default. Set the ``web.allow-push``
5234 option to ``*`` to allow everybody to push to the server. You
5232 option to ``*`` to allow everybody to push to the server. You
5235 should use a real web server if you need to authenticate users.
5233 should use a real web server if you need to authenticate users.
5236
5234
5237 By default, the server logs accesses to stdout and errors to
5235 By default, the server logs accesses to stdout and errors to
5238 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5236 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5239 files.
5237 files.
5240
5238
5241 To have the server choose a free port number to listen on, specify
5239 To have the server choose a free port number to listen on, specify
5242 a port number of 0; in this case, the server will print the port
5240 a port number of 0; in this case, the server will print the port
5243 number it uses.
5241 number it uses.
5244
5242
5245 Returns 0 on success.
5243 Returns 0 on success.
5246 """
5244 """
5247
5245
5248 opts = pycompat.byteskwargs(opts)
5246 opts = pycompat.byteskwargs(opts)
5249 if opts["stdio"] and opts["cmdserver"]:
5247 if opts["stdio"] and opts["cmdserver"]:
5250 raise error.Abort(_("cannot use --stdio with --cmdserver"))
5248 raise error.Abort(_("cannot use --stdio with --cmdserver"))
5251 if opts["print_url"] and ui.verbose:
5249 if opts["print_url"] and ui.verbose:
5252 raise error.Abort(_("cannot use --print-url with --verbose"))
5250 raise error.Abort(_("cannot use --print-url with --verbose"))
5253
5251
5254 if opts["stdio"]:
5252 if opts["stdio"]:
5255 if repo is None:
5253 if repo is None:
5256 raise error.RepoError(_("there is no Mercurial repository here"
5254 raise error.RepoError(_("there is no Mercurial repository here"
5257 " (.hg not found)"))
5255 " (.hg not found)"))
5258 s = wireprotoserver.sshserver(ui, repo)
5256 s = wireprotoserver.sshserver(ui, repo)
5259 s.serve_forever()
5257 s.serve_forever()
5260
5258
5261 service = server.createservice(ui, repo, opts)
5259 service = server.createservice(ui, repo, opts)
5262 return server.runservice(opts, initfn=service.init, runfn=service.run)
5260 return server.runservice(opts, initfn=service.init, runfn=service.run)
5263
5261
5264 _NOTTERSE = 'nothing'
5262 _NOTTERSE = 'nothing'
5265
5263
5266 @command('status|st',
5264 @command('status|st',
5267 [('A', 'all', None, _('show status of all files')),
5265 [('A', 'all', None, _('show status of all files')),
5268 ('m', 'modified', None, _('show only modified files')),
5266 ('m', 'modified', None, _('show only modified files')),
5269 ('a', 'added', None, _('show only added files')),
5267 ('a', 'added', None, _('show only added files')),
5270 ('r', 'removed', None, _('show only removed files')),
5268 ('r', 'removed', None, _('show only removed files')),
5271 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5269 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5272 ('c', 'clean', None, _('show only files without changes')),
5270 ('c', 'clean', None, _('show only files without changes')),
5273 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5271 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5274 ('i', 'ignored', None, _('show only ignored files')),
5272 ('i', 'ignored', None, _('show only ignored files')),
5275 ('n', 'no-status', None, _('hide status prefix')),
5273 ('n', 'no-status', None, _('hide status prefix')),
5276 ('t', 'terse', _NOTTERSE, _('show the terse output (EXPERIMENTAL)')),
5274 ('t', 'terse', _NOTTERSE, _('show the terse output (EXPERIMENTAL)')),
5277 ('C', 'copies', None, _('show source of copied files')),
5275 ('C', 'copies', None, _('show source of copied files')),
5278 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5276 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5279 ('', 'rev', [], _('show difference from revision'), _('REV')),
5277 ('', 'rev', [], _('show difference from revision'), _('REV')),
5280 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5278 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5281 ] + walkopts + subrepoopts + formatteropts,
5279 ] + walkopts + subrepoopts + formatteropts,
5282 _('[OPTION]... [FILE]...'),
5280 _('[OPTION]... [FILE]...'),
5283 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5281 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5284 helpbasic=True, inferrepo=True,
5282 helpbasic=True, inferrepo=True,
5285 intents={INTENT_READONLY})
5283 intents={INTENT_READONLY})
5286 def status(ui, repo, *pats, **opts):
5284 def status(ui, repo, *pats, **opts):
5287 """show changed files in the working directory
5285 """show changed files in the working directory
5288
5286
5289 Show status of files in the repository. If names are given, only
5287 Show status of files in the repository. If names are given, only
5290 files that match are shown. Files that are clean or ignored or
5288 files that match are shown. Files that are clean or ignored or
5291 the source of a copy/move operation, are not listed unless
5289 the source of a copy/move operation, are not listed unless
5292 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5290 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5293 Unless options described with "show only ..." are given, the
5291 Unless options described with "show only ..." are given, the
5294 options -mardu are used.
5292 options -mardu are used.
5295
5293
5296 Option -q/--quiet hides untracked (unknown and ignored) files
5294 Option -q/--quiet hides untracked (unknown and ignored) files
5297 unless explicitly requested with -u/--unknown or -i/--ignored.
5295 unless explicitly requested with -u/--unknown or -i/--ignored.
5298
5296
5299 .. note::
5297 .. note::
5300
5298
5301 :hg:`status` may appear to disagree with diff if permissions have
5299 :hg:`status` may appear to disagree with diff if permissions have
5302 changed or a merge has occurred. The standard diff format does
5300 changed or a merge has occurred. The standard diff format does
5303 not report permission changes and diff only reports changes
5301 not report permission changes and diff only reports changes
5304 relative to one merge parent.
5302 relative to one merge parent.
5305
5303
5306 If one revision is given, it is used as the base revision.
5304 If one revision is given, it is used as the base revision.
5307 If two revisions are given, the differences between them are
5305 If two revisions are given, the differences between them are
5308 shown. The --change option can also be used as a shortcut to list
5306 shown. The --change option can also be used as a shortcut to list
5309 the changed files of a revision from its first parent.
5307 the changed files of a revision from its first parent.
5310
5308
5311 The codes used to show the status of files are::
5309 The codes used to show the status of files are::
5312
5310
5313 M = modified
5311 M = modified
5314 A = added
5312 A = added
5315 R = removed
5313 R = removed
5316 C = clean
5314 C = clean
5317 ! = missing (deleted by non-hg command, but still tracked)
5315 ! = missing (deleted by non-hg command, but still tracked)
5318 ? = not tracked
5316 ? = not tracked
5319 I = ignored
5317 I = ignored
5320 = origin of the previous file (with --copies)
5318 = origin of the previous file (with --copies)
5321
5319
5322 .. container:: verbose
5320 .. container:: verbose
5323
5321
5324 The -t/--terse option abbreviates the output by showing only the directory
5322 The -t/--terse option abbreviates the output by showing only the directory
5325 name if all the files in it share the same status. The option takes an
5323 name if all the files in it share the same status. The option takes an
5326 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
5324 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
5327 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
5325 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
5328 for 'ignored' and 'c' for clean.
5326 for 'ignored' and 'c' for clean.
5329
5327
5330 It abbreviates only those statuses which are passed. Note that clean and
5328 It abbreviates only those statuses which are passed. Note that clean and
5331 ignored files are not displayed with '--terse ic' unless the -c/--clean
5329 ignored files are not displayed with '--terse ic' unless the -c/--clean
5332 and -i/--ignored options are also used.
5330 and -i/--ignored options are also used.
5333
5331
5334 The -v/--verbose option shows information when the repository is in an
5332 The -v/--verbose option shows information when the repository is in an
5335 unfinished merge, shelve, rebase state etc. You can have this behavior
5333 unfinished merge, shelve, rebase state etc. You can have this behavior
5336 turned on by default by enabling the ``commands.status.verbose`` option.
5334 turned on by default by enabling the ``commands.status.verbose`` option.
5337
5335
5338 You can skip displaying some of these states by setting
5336 You can skip displaying some of these states by setting
5339 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
5337 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
5340 'histedit', 'merge', 'rebase', or 'unshelve'.
5338 'histedit', 'merge', 'rebase', or 'unshelve'.
5341
5339
5342 Template:
5340 Template:
5343
5341
5344 The following keywords are supported in addition to the common template
5342 The following keywords are supported in addition to the common template
5345 keywords and functions. See also :hg:`help templates`.
5343 keywords and functions. See also :hg:`help templates`.
5346
5344
5347 :path: String. Repository-absolute path of the file.
5345 :path: String. Repository-absolute path of the file.
5348 :source: String. Repository-absolute path of the file originated from.
5346 :source: String. Repository-absolute path of the file originated from.
5349 Available if ``--copies`` is specified.
5347 Available if ``--copies`` is specified.
5350 :status: String. Character denoting file's status.
5348 :status: String. Character denoting file's status.
5351
5349
5352 Examples:
5350 Examples:
5353
5351
5354 - show changes in the working directory relative to a
5352 - show changes in the working directory relative to a
5355 changeset::
5353 changeset::
5356
5354
5357 hg status --rev 9353
5355 hg status --rev 9353
5358
5356
5359 - show changes in the working directory relative to the
5357 - show changes in the working directory relative to the
5360 current directory (see :hg:`help patterns` for more information)::
5358 current directory (see :hg:`help patterns` for more information)::
5361
5359
5362 hg status re:
5360 hg status re:
5363
5361
5364 - show all changes including copies in an existing changeset::
5362 - show all changes including copies in an existing changeset::
5365
5363
5366 hg status --copies --change 9353
5364 hg status --copies --change 9353
5367
5365
5368 - get a NUL separated list of added files, suitable for xargs::
5366 - get a NUL separated list of added files, suitable for xargs::
5369
5367
5370 hg status -an0
5368 hg status -an0
5371
5369
5372 - show more information about the repository status, abbreviating
5370 - show more information about the repository status, abbreviating
5373 added, removed, modified, deleted, and untracked paths::
5371 added, removed, modified, deleted, and untracked paths::
5374
5372
5375 hg status -v -t mardu
5373 hg status -v -t mardu
5376
5374
5377 Returns 0 on success.
5375 Returns 0 on success.
5378
5376
5379 """
5377 """
5380
5378
5381 opts = pycompat.byteskwargs(opts)
5379 opts = pycompat.byteskwargs(opts)
5382 revs = opts.get('rev')
5380 revs = opts.get('rev')
5383 change = opts.get('change')
5381 change = opts.get('change')
5384 terse = opts.get('terse')
5382 terse = opts.get('terse')
5385 if terse is _NOTTERSE:
5383 if terse is _NOTTERSE:
5386 if revs:
5384 if revs:
5387 terse = ''
5385 terse = ''
5388 else:
5386 else:
5389 terse = ui.config('commands', 'status.terse')
5387 terse = ui.config('commands', 'status.terse')
5390
5388
5391 if revs and change:
5389 if revs and change:
5392 msg = _('cannot specify --rev and --change at the same time')
5390 msg = _('cannot specify --rev and --change at the same time')
5393 raise error.Abort(msg)
5391 raise error.Abort(msg)
5394 elif revs and terse:
5392 elif revs and terse:
5395 msg = _('cannot use --terse with --rev')
5393 msg = _('cannot use --terse with --rev')
5396 raise error.Abort(msg)
5394 raise error.Abort(msg)
5397 elif change:
5395 elif change:
5398 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
5396 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
5399 ctx2 = scmutil.revsingle(repo, change, None)
5397 ctx2 = scmutil.revsingle(repo, change, None)
5400 ctx1 = ctx2.p1()
5398 ctx1 = ctx2.p1()
5401 else:
5399 else:
5402 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
5400 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
5403 ctx1, ctx2 = scmutil.revpair(repo, revs)
5401 ctx1, ctx2 = scmutil.revpair(repo, revs)
5404
5402
5405 if pats or ui.configbool('commands', 'status.relative'):
5403 if pats or ui.configbool('commands', 'status.relative'):
5406 cwd = repo.getcwd()
5404 cwd = repo.getcwd()
5407 else:
5405 else:
5408 cwd = ''
5406 cwd = ''
5409
5407
5410 if opts.get('print0'):
5408 if opts.get('print0'):
5411 end = '\0'
5409 end = '\0'
5412 else:
5410 else:
5413 end = '\n'
5411 end = '\n'
5414 copy = {}
5412 copy = {}
5415 states = 'modified added removed deleted unknown ignored clean'.split()
5413 states = 'modified added removed deleted unknown ignored clean'.split()
5416 show = [k for k in states if opts.get(k)]
5414 show = [k for k in states if opts.get(k)]
5417 if opts.get('all'):
5415 if opts.get('all'):
5418 show += ui.quiet and (states[:4] + ['clean']) or states
5416 show += ui.quiet and (states[:4] + ['clean']) or states
5419
5417
5420 if not show:
5418 if not show:
5421 if ui.quiet:
5419 if ui.quiet:
5422 show = states[:4]
5420 show = states[:4]
5423 else:
5421 else:
5424 show = states[:5]
5422 show = states[:5]
5425
5423
5426 m = scmutil.match(ctx2, pats, opts)
5424 m = scmutil.match(ctx2, pats, opts)
5427 if terse:
5425 if terse:
5428 # we need to compute clean and unknown to terse
5426 # we need to compute clean and unknown to terse
5429 stat = repo.status(ctx1.node(), ctx2.node(), m,
5427 stat = repo.status(ctx1.node(), ctx2.node(), m,
5430 'ignored' in show or 'i' in terse,
5428 'ignored' in show or 'i' in terse,
5431 clean=True, unknown=True,
5429 clean=True, unknown=True,
5432 listsubrepos=opts.get('subrepos'))
5430 listsubrepos=opts.get('subrepos'))
5433
5431
5434 stat = cmdutil.tersedir(stat, terse)
5432 stat = cmdutil.tersedir(stat, terse)
5435 else:
5433 else:
5436 stat = repo.status(ctx1.node(), ctx2.node(), m,
5434 stat = repo.status(ctx1.node(), ctx2.node(), m,
5437 'ignored' in show, 'clean' in show,
5435 'ignored' in show, 'clean' in show,
5438 'unknown' in show, opts.get('subrepos'))
5436 'unknown' in show, opts.get('subrepos'))
5439
5437
5440 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
5438 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
5441
5439
5442 if (opts.get('all') or opts.get('copies')
5440 if (opts.get('all') or opts.get('copies')
5443 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5441 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5444 copy = copies.pathcopies(ctx1, ctx2, m)
5442 copy = copies.pathcopies(ctx1, ctx2, m)
5445
5443
5446 ui.pager('status')
5444 ui.pager('status')
5447 fm = ui.formatter('status', opts)
5445 fm = ui.formatter('status', opts)
5448 fmt = '%s' + end
5446 fmt = '%s' + end
5449 showchar = not opts.get('no_status')
5447 showchar = not opts.get('no_status')
5450
5448
5451 for state, char, files in changestates:
5449 for state, char, files in changestates:
5452 if state in show:
5450 if state in show:
5453 label = 'status.' + state
5451 label = 'status.' + state
5454 for f in files:
5452 for f in files:
5455 fm.startitem()
5453 fm.startitem()
5456 fm.context(ctx=ctx2)
5454 fm.context(ctx=ctx2)
5457 fm.data(path=f)
5455 fm.data(path=f)
5458 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5456 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5459 fm.plain(fmt % repo.pathto(f, cwd), label=label)
5457 fm.plain(fmt % repo.pathto(f, cwd), label=label)
5460 if f in copy:
5458 if f in copy:
5461 fm.data(source=copy[f])
5459 fm.data(source=copy[f])
5462 fm.plain((' %s' + end) % repo.pathto(copy[f], cwd),
5460 fm.plain((' %s' + end) % repo.pathto(copy[f], cwd),
5463 label='status.copied')
5461 label='status.copied')
5464
5462
5465 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
5463 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
5466 and not ui.plain()):
5464 and not ui.plain()):
5467 cmdutil.morestatus(repo, fm)
5465 cmdutil.morestatus(repo, fm)
5468 fm.end()
5466 fm.end()
5469
5467
5470 @command('summary|sum',
5468 @command('summary|sum',
5471 [('', 'remote', None, _('check for push and pull'))],
5469 [('', 'remote', None, _('check for push and pull'))],
5472 '[--remote]',
5470 '[--remote]',
5473 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5471 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5474 helpbasic=True,
5472 helpbasic=True,
5475 intents={INTENT_READONLY})
5473 intents={INTENT_READONLY})
5476 def summary(ui, repo, **opts):
5474 def summary(ui, repo, **opts):
5477 """summarize working directory state
5475 """summarize working directory state
5478
5476
5479 This generates a brief summary of the working directory state,
5477 This generates a brief summary of the working directory state,
5480 including parents, branch, commit status, phase and available updates.
5478 including parents, branch, commit status, phase and available updates.
5481
5479
5482 With the --remote option, this will check the default paths for
5480 With the --remote option, this will check the default paths for
5483 incoming and outgoing changes. This can be time-consuming.
5481 incoming and outgoing changes. This can be time-consuming.
5484
5482
5485 Returns 0 on success.
5483 Returns 0 on success.
5486 """
5484 """
5487
5485
5488 opts = pycompat.byteskwargs(opts)
5486 opts = pycompat.byteskwargs(opts)
5489 ui.pager('summary')
5487 ui.pager('summary')
5490 ctx = repo[None]
5488 ctx = repo[None]
5491 parents = ctx.parents()
5489 parents = ctx.parents()
5492 pnode = parents[0].node()
5490 pnode = parents[0].node()
5493 marks = []
5491 marks = []
5494
5492
5495 ms = None
5493 ms = None
5496 try:
5494 try:
5497 ms = mergemod.mergestate.read(repo)
5495 ms = mergemod.mergestate.read(repo)
5498 except error.UnsupportedMergeRecords as e:
5496 except error.UnsupportedMergeRecords as e:
5499 s = ' '.join(e.recordtypes)
5497 s = ' '.join(e.recordtypes)
5500 ui.warn(
5498 ui.warn(
5501 _('warning: merge state has unsupported record types: %s\n') % s)
5499 _('warning: merge state has unsupported record types: %s\n') % s)
5502 unresolved = []
5500 unresolved = []
5503 else:
5501 else:
5504 unresolved = list(ms.unresolved())
5502 unresolved = list(ms.unresolved())
5505
5503
5506 for p in parents:
5504 for p in parents:
5507 # label with log.changeset (instead of log.parent) since this
5505 # label with log.changeset (instead of log.parent) since this
5508 # shows a working directory parent *changeset*:
5506 # shows a working directory parent *changeset*:
5509 # i18n: column positioning for "hg summary"
5507 # i18n: column positioning for "hg summary"
5510 ui.write(_('parent: %d:%s ') % (p.rev(), p),
5508 ui.write(_('parent: %d:%s ') % (p.rev(), p),
5511 label=logcmdutil.changesetlabels(p))
5509 label=logcmdutil.changesetlabels(p))
5512 ui.write(' '.join(p.tags()), label='log.tag')
5510 ui.write(' '.join(p.tags()), label='log.tag')
5513 if p.bookmarks():
5511 if p.bookmarks():
5514 marks.extend(p.bookmarks())
5512 marks.extend(p.bookmarks())
5515 if p.rev() == -1:
5513 if p.rev() == -1:
5516 if not len(repo):
5514 if not len(repo):
5517 ui.write(_(' (empty repository)'))
5515 ui.write(_(' (empty repository)'))
5518 else:
5516 else:
5519 ui.write(_(' (no revision checked out)'))
5517 ui.write(_(' (no revision checked out)'))
5520 if p.obsolete():
5518 if p.obsolete():
5521 ui.write(_(' (obsolete)'))
5519 ui.write(_(' (obsolete)'))
5522 if p.isunstable():
5520 if p.isunstable():
5523 instabilities = (ui.label(instability, 'trouble.%s' % instability)
5521 instabilities = (ui.label(instability, 'trouble.%s' % instability)
5524 for instability in p.instabilities())
5522 for instability in p.instabilities())
5525 ui.write(' ('
5523 ui.write(' ('
5526 + ', '.join(instabilities)
5524 + ', '.join(instabilities)
5527 + ')')
5525 + ')')
5528 ui.write('\n')
5526 ui.write('\n')
5529 if p.description():
5527 if p.description():
5530 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5528 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5531 label='log.summary')
5529 label='log.summary')
5532
5530
5533 branch = ctx.branch()
5531 branch = ctx.branch()
5534 bheads = repo.branchheads(branch)
5532 bheads = repo.branchheads(branch)
5535 # i18n: column positioning for "hg summary"
5533 # i18n: column positioning for "hg summary"
5536 m = _('branch: %s\n') % branch
5534 m = _('branch: %s\n') % branch
5537 if branch != 'default':
5535 if branch != 'default':
5538 ui.write(m, label='log.branch')
5536 ui.write(m, label='log.branch')
5539 else:
5537 else:
5540 ui.status(m, label='log.branch')
5538 ui.status(m, label='log.branch')
5541
5539
5542 if marks:
5540 if marks:
5543 active = repo._activebookmark
5541 active = repo._activebookmark
5544 # i18n: column positioning for "hg summary"
5542 # i18n: column positioning for "hg summary"
5545 ui.write(_('bookmarks:'), label='log.bookmark')
5543 ui.write(_('bookmarks:'), label='log.bookmark')
5546 if active is not None:
5544 if active is not None:
5547 if active in marks:
5545 if active in marks:
5548 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
5546 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
5549 marks.remove(active)
5547 marks.remove(active)
5550 else:
5548 else:
5551 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
5549 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
5552 for m in marks:
5550 for m in marks:
5553 ui.write(' ' + m, label='log.bookmark')
5551 ui.write(' ' + m, label='log.bookmark')
5554 ui.write('\n', label='log.bookmark')
5552 ui.write('\n', label='log.bookmark')
5555
5553
5556 status = repo.status(unknown=True)
5554 status = repo.status(unknown=True)
5557
5555
5558 c = repo.dirstate.copies()
5556 c = repo.dirstate.copies()
5559 copied, renamed = [], []
5557 copied, renamed = [], []
5560 for d, s in c.iteritems():
5558 for d, s in c.iteritems():
5561 if s in status.removed:
5559 if s in status.removed:
5562 status.removed.remove(s)
5560 status.removed.remove(s)
5563 renamed.append(d)
5561 renamed.append(d)
5564 else:
5562 else:
5565 copied.append(d)
5563 copied.append(d)
5566 if d in status.added:
5564 if d in status.added:
5567 status.added.remove(d)
5565 status.added.remove(d)
5568
5566
5569 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5567 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5570
5568
5571 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5569 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5572 (ui.label(_('%d added'), 'status.added'), status.added),
5570 (ui.label(_('%d added'), 'status.added'), status.added),
5573 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5571 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5574 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5572 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5575 (ui.label(_('%d copied'), 'status.copied'), copied),
5573 (ui.label(_('%d copied'), 'status.copied'), copied),
5576 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5574 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5577 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5575 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5578 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5576 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5579 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5577 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5580 t = []
5578 t = []
5581 for l, s in labels:
5579 for l, s in labels:
5582 if s:
5580 if s:
5583 t.append(l % len(s))
5581 t.append(l % len(s))
5584
5582
5585 t = ', '.join(t)
5583 t = ', '.join(t)
5586 cleanworkdir = False
5584 cleanworkdir = False
5587
5585
5588 if repo.vfs.exists('graftstate'):
5586 if repo.vfs.exists('graftstate'):
5589 t += _(' (graft in progress)')
5587 t += _(' (graft in progress)')
5590 if repo.vfs.exists('updatestate'):
5588 if repo.vfs.exists('updatestate'):
5591 t += _(' (interrupted update)')
5589 t += _(' (interrupted update)')
5592 elif len(parents) > 1:
5590 elif len(parents) > 1:
5593 t += _(' (merge)')
5591 t += _(' (merge)')
5594 elif branch != parents[0].branch():
5592 elif branch != parents[0].branch():
5595 t += _(' (new branch)')
5593 t += _(' (new branch)')
5596 elif (parents[0].closesbranch() and
5594 elif (parents[0].closesbranch() and
5597 pnode in repo.branchheads(branch, closed=True)):
5595 pnode in repo.branchheads(branch, closed=True)):
5598 t += _(' (head closed)')
5596 t += _(' (head closed)')
5599 elif not (status.modified or status.added or status.removed or renamed or
5597 elif not (status.modified or status.added or status.removed or renamed or
5600 copied or subs):
5598 copied or subs):
5601 t += _(' (clean)')
5599 t += _(' (clean)')
5602 cleanworkdir = True
5600 cleanworkdir = True
5603 elif pnode not in bheads:
5601 elif pnode not in bheads:
5604 t += _(' (new branch head)')
5602 t += _(' (new branch head)')
5605
5603
5606 if parents:
5604 if parents:
5607 pendingphase = max(p.phase() for p in parents)
5605 pendingphase = max(p.phase() for p in parents)
5608 else:
5606 else:
5609 pendingphase = phases.public
5607 pendingphase = phases.public
5610
5608
5611 if pendingphase > phases.newcommitphase(ui):
5609 if pendingphase > phases.newcommitphase(ui):
5612 t += ' (%s)' % phases.phasenames[pendingphase]
5610 t += ' (%s)' % phases.phasenames[pendingphase]
5613
5611
5614 if cleanworkdir:
5612 if cleanworkdir:
5615 # i18n: column positioning for "hg summary"
5613 # i18n: column positioning for "hg summary"
5616 ui.status(_('commit: %s\n') % t.strip())
5614 ui.status(_('commit: %s\n') % t.strip())
5617 else:
5615 else:
5618 # i18n: column positioning for "hg summary"
5616 # i18n: column positioning for "hg summary"
5619 ui.write(_('commit: %s\n') % t.strip())
5617 ui.write(_('commit: %s\n') % t.strip())
5620
5618
5621 # all ancestors of branch heads - all ancestors of parent = new csets
5619 # all ancestors of branch heads - all ancestors of parent = new csets
5622 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5620 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5623 bheads))
5621 bheads))
5624
5622
5625 if new == 0:
5623 if new == 0:
5626 # i18n: column positioning for "hg summary"
5624 # i18n: column positioning for "hg summary"
5627 ui.status(_('update: (current)\n'))
5625 ui.status(_('update: (current)\n'))
5628 elif pnode not in bheads:
5626 elif pnode not in bheads:
5629 # i18n: column positioning for "hg summary"
5627 # i18n: column positioning for "hg summary"
5630 ui.write(_('update: %d new changesets (update)\n') % new)
5628 ui.write(_('update: %d new changesets (update)\n') % new)
5631 else:
5629 else:
5632 # i18n: column positioning for "hg summary"
5630 # i18n: column positioning for "hg summary"
5633 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5631 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5634 (new, len(bheads)))
5632 (new, len(bheads)))
5635
5633
5636 t = []
5634 t = []
5637 draft = len(repo.revs('draft()'))
5635 draft = len(repo.revs('draft()'))
5638 if draft:
5636 if draft:
5639 t.append(_('%d draft') % draft)
5637 t.append(_('%d draft') % draft)
5640 secret = len(repo.revs('secret()'))
5638 secret = len(repo.revs('secret()'))
5641 if secret:
5639 if secret:
5642 t.append(_('%d secret') % secret)
5640 t.append(_('%d secret') % secret)
5643
5641
5644 if draft or secret:
5642 if draft or secret:
5645 ui.status(_('phases: %s\n') % ', '.join(t))
5643 ui.status(_('phases: %s\n') % ', '.join(t))
5646
5644
5647 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5645 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5648 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5646 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5649 numtrouble = len(repo.revs(trouble + "()"))
5647 numtrouble = len(repo.revs(trouble + "()"))
5650 # We write all the possibilities to ease translation
5648 # We write all the possibilities to ease translation
5651 troublemsg = {
5649 troublemsg = {
5652 "orphan": _("orphan: %d changesets"),
5650 "orphan": _("orphan: %d changesets"),
5653 "contentdivergent": _("content-divergent: %d changesets"),
5651 "contentdivergent": _("content-divergent: %d changesets"),
5654 "phasedivergent": _("phase-divergent: %d changesets"),
5652 "phasedivergent": _("phase-divergent: %d changesets"),
5655 }
5653 }
5656 if numtrouble > 0:
5654 if numtrouble > 0:
5657 ui.status(troublemsg[trouble] % numtrouble + "\n")
5655 ui.status(troublemsg[trouble] % numtrouble + "\n")
5658
5656
5659 cmdutil.summaryhooks(ui, repo)
5657 cmdutil.summaryhooks(ui, repo)
5660
5658
5661 if opts.get('remote'):
5659 if opts.get('remote'):
5662 needsincoming, needsoutgoing = True, True
5660 needsincoming, needsoutgoing = True, True
5663 else:
5661 else:
5664 needsincoming, needsoutgoing = False, False
5662 needsincoming, needsoutgoing = False, False
5665 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5663 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5666 if i:
5664 if i:
5667 needsincoming = True
5665 needsincoming = True
5668 if o:
5666 if o:
5669 needsoutgoing = True
5667 needsoutgoing = True
5670 if not needsincoming and not needsoutgoing:
5668 if not needsincoming and not needsoutgoing:
5671 return
5669 return
5672
5670
5673 def getincoming():
5671 def getincoming():
5674 source, branches = hg.parseurl(ui.expandpath('default'))
5672 source, branches = hg.parseurl(ui.expandpath('default'))
5675 sbranch = branches[0]
5673 sbranch = branches[0]
5676 try:
5674 try:
5677 other = hg.peer(repo, {}, source)
5675 other = hg.peer(repo, {}, source)
5678 except error.RepoError:
5676 except error.RepoError:
5679 if opts.get('remote'):
5677 if opts.get('remote'):
5680 raise
5678 raise
5681 return source, sbranch, None, None, None
5679 return source, sbranch, None, None, None
5682 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5680 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5683 if revs:
5681 if revs:
5684 revs = [other.lookup(rev) for rev in revs]
5682 revs = [other.lookup(rev) for rev in revs]
5685 ui.debug('comparing with %s\n' % util.hidepassword(source))
5683 ui.debug('comparing with %s\n' % util.hidepassword(source))
5686 repo.ui.pushbuffer()
5684 repo.ui.pushbuffer()
5687 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5685 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5688 repo.ui.popbuffer()
5686 repo.ui.popbuffer()
5689 return source, sbranch, other, commoninc, commoninc[1]
5687 return source, sbranch, other, commoninc, commoninc[1]
5690
5688
5691 if needsincoming:
5689 if needsincoming:
5692 source, sbranch, sother, commoninc, incoming = getincoming()
5690 source, sbranch, sother, commoninc, incoming = getincoming()
5693 else:
5691 else:
5694 source = sbranch = sother = commoninc = incoming = None
5692 source = sbranch = sother = commoninc = incoming = None
5695
5693
5696 def getoutgoing():
5694 def getoutgoing():
5697 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5695 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5698 dbranch = branches[0]
5696 dbranch = branches[0]
5699 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5697 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5700 if source != dest:
5698 if source != dest:
5701 try:
5699 try:
5702 dother = hg.peer(repo, {}, dest)
5700 dother = hg.peer(repo, {}, dest)
5703 except error.RepoError:
5701 except error.RepoError:
5704 if opts.get('remote'):
5702 if opts.get('remote'):
5705 raise
5703 raise
5706 return dest, dbranch, None, None
5704 return dest, dbranch, None, None
5707 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5705 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5708 elif sother is None:
5706 elif sother is None:
5709 # there is no explicit destination peer, but source one is invalid
5707 # there is no explicit destination peer, but source one is invalid
5710 return dest, dbranch, None, None
5708 return dest, dbranch, None, None
5711 else:
5709 else:
5712 dother = sother
5710 dother = sother
5713 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5711 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5714 common = None
5712 common = None
5715 else:
5713 else:
5716 common = commoninc
5714 common = commoninc
5717 if revs:
5715 if revs:
5718 revs = [repo.lookup(rev) for rev in revs]
5716 revs = [repo.lookup(rev) for rev in revs]
5719 repo.ui.pushbuffer()
5717 repo.ui.pushbuffer()
5720 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5718 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5721 commoninc=common)
5719 commoninc=common)
5722 repo.ui.popbuffer()
5720 repo.ui.popbuffer()
5723 return dest, dbranch, dother, outgoing
5721 return dest, dbranch, dother, outgoing
5724
5722
5725 if needsoutgoing:
5723 if needsoutgoing:
5726 dest, dbranch, dother, outgoing = getoutgoing()
5724 dest, dbranch, dother, outgoing = getoutgoing()
5727 else:
5725 else:
5728 dest = dbranch = dother = outgoing = None
5726 dest = dbranch = dother = outgoing = None
5729
5727
5730 if opts.get('remote'):
5728 if opts.get('remote'):
5731 t = []
5729 t = []
5732 if incoming:
5730 if incoming:
5733 t.append(_('1 or more incoming'))
5731 t.append(_('1 or more incoming'))
5734 o = outgoing.missing
5732 o = outgoing.missing
5735 if o:
5733 if o:
5736 t.append(_('%d outgoing') % len(o))
5734 t.append(_('%d outgoing') % len(o))
5737 other = dother or sother
5735 other = dother or sother
5738 if 'bookmarks' in other.listkeys('namespaces'):
5736 if 'bookmarks' in other.listkeys('namespaces'):
5739 counts = bookmarks.summary(repo, other)
5737 counts = bookmarks.summary(repo, other)
5740 if counts[0] > 0:
5738 if counts[0] > 0:
5741 t.append(_('%d incoming bookmarks') % counts[0])
5739 t.append(_('%d incoming bookmarks') % counts[0])
5742 if counts[1] > 0:
5740 if counts[1] > 0:
5743 t.append(_('%d outgoing bookmarks') % counts[1])
5741 t.append(_('%d outgoing bookmarks') % counts[1])
5744
5742
5745 if t:
5743 if t:
5746 # i18n: column positioning for "hg summary"
5744 # i18n: column positioning for "hg summary"
5747 ui.write(_('remote: %s\n') % (', '.join(t)))
5745 ui.write(_('remote: %s\n') % (', '.join(t)))
5748 else:
5746 else:
5749 # i18n: column positioning for "hg summary"
5747 # i18n: column positioning for "hg summary"
5750 ui.status(_('remote: (synced)\n'))
5748 ui.status(_('remote: (synced)\n'))
5751
5749
5752 cmdutil.summaryremotehooks(ui, repo, opts,
5750 cmdutil.summaryremotehooks(ui, repo, opts,
5753 ((source, sbranch, sother, commoninc),
5751 ((source, sbranch, sother, commoninc),
5754 (dest, dbranch, dother, outgoing)))
5752 (dest, dbranch, dother, outgoing)))
5755
5753
5756 @command('tag',
5754 @command('tag',
5757 [('f', 'force', None, _('force tag')),
5755 [('f', 'force', None, _('force tag')),
5758 ('l', 'local', None, _('make the tag local')),
5756 ('l', 'local', None, _('make the tag local')),
5759 ('r', 'rev', '', _('revision to tag'), _('REV')),
5757 ('r', 'rev', '', _('revision to tag'), _('REV')),
5760 ('', 'remove', None, _('remove a tag')),
5758 ('', 'remove', None, _('remove a tag')),
5761 # -l/--local is already there, commitopts cannot be used
5759 # -l/--local is already there, commitopts cannot be used
5762 ('e', 'edit', None, _('invoke editor on commit messages')),
5760 ('e', 'edit', None, _('invoke editor on commit messages')),
5763 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5761 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5764 ] + commitopts2,
5762 ] + commitopts2,
5765 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
5763 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
5766 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
5764 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
5767 def tag(ui, repo, name1, *names, **opts):
5765 def tag(ui, repo, name1, *names, **opts):
5768 """add one or more tags for the current or given revision
5766 """add one or more tags for the current or given revision
5769
5767
5770 Name a particular revision using <name>.
5768 Name a particular revision using <name>.
5771
5769
5772 Tags are used to name particular revisions of the repository and are
5770 Tags are used to name particular revisions of the repository and are
5773 very useful to compare different revisions, to go back to significant
5771 very useful to compare different revisions, to go back to significant
5774 earlier versions or to mark branch points as releases, etc. Changing
5772 earlier versions or to mark branch points as releases, etc. Changing
5775 an existing tag is normally disallowed; use -f/--force to override.
5773 an existing tag is normally disallowed; use -f/--force to override.
5776
5774
5777 If no revision is given, the parent of the working directory is
5775 If no revision is given, the parent of the working directory is
5778 used.
5776 used.
5779
5777
5780 To facilitate version control, distribution, and merging of tags,
5778 To facilitate version control, distribution, and merging of tags,
5781 they are stored as a file named ".hgtags" which is managed similarly
5779 they are stored as a file named ".hgtags" which is managed similarly
5782 to other project files and can be hand-edited if necessary. This
5780 to other project files and can be hand-edited if necessary. This
5783 also means that tagging creates a new commit. The file
5781 also means that tagging creates a new commit. The file
5784 ".hg/localtags" is used for local tags (not shared among
5782 ".hg/localtags" is used for local tags (not shared among
5785 repositories).
5783 repositories).
5786
5784
5787 Tag commits are usually made at the head of a branch. If the parent
5785 Tag commits are usually made at the head of a branch. If the parent
5788 of the working directory is not a branch head, :hg:`tag` aborts; use
5786 of the working directory is not a branch head, :hg:`tag` aborts; use
5789 -f/--force to force the tag commit to be based on a non-head
5787 -f/--force to force the tag commit to be based on a non-head
5790 changeset.
5788 changeset.
5791
5789
5792 See :hg:`help dates` for a list of formats valid for -d/--date.
5790 See :hg:`help dates` for a list of formats valid for -d/--date.
5793
5791
5794 Since tag names have priority over branch names during revision
5792 Since tag names have priority over branch names during revision
5795 lookup, using an existing branch name as a tag name is discouraged.
5793 lookup, using an existing branch name as a tag name is discouraged.
5796
5794
5797 Returns 0 on success.
5795 Returns 0 on success.
5798 """
5796 """
5799 opts = pycompat.byteskwargs(opts)
5797 opts = pycompat.byteskwargs(opts)
5800 with repo.wlock(), repo.lock():
5798 with repo.wlock(), repo.lock():
5801 rev_ = "."
5799 rev_ = "."
5802 names = [t.strip() for t in (name1,) + names]
5800 names = [t.strip() for t in (name1,) + names]
5803 if len(names) != len(set(names)):
5801 if len(names) != len(set(names)):
5804 raise error.Abort(_('tag names must be unique'))
5802 raise error.Abort(_('tag names must be unique'))
5805 for n in names:
5803 for n in names:
5806 scmutil.checknewlabel(repo, n, 'tag')
5804 scmutil.checknewlabel(repo, n, 'tag')
5807 if not n:
5805 if not n:
5808 raise error.Abort(_('tag names cannot consist entirely of '
5806 raise error.Abort(_('tag names cannot consist entirely of '
5809 'whitespace'))
5807 'whitespace'))
5810 if opts.get('rev') and opts.get('remove'):
5808 if opts.get('rev') and opts.get('remove'):
5811 raise error.Abort(_("--rev and --remove are incompatible"))
5809 raise error.Abort(_("--rev and --remove are incompatible"))
5812 if opts.get('rev'):
5810 if opts.get('rev'):
5813 rev_ = opts['rev']
5811 rev_ = opts['rev']
5814 message = opts.get('message')
5812 message = opts.get('message')
5815 if opts.get('remove'):
5813 if opts.get('remove'):
5816 if opts.get('local'):
5814 if opts.get('local'):
5817 expectedtype = 'local'
5815 expectedtype = 'local'
5818 else:
5816 else:
5819 expectedtype = 'global'
5817 expectedtype = 'global'
5820
5818
5821 for n in names:
5819 for n in names:
5822 if not repo.tagtype(n):
5820 if not repo.tagtype(n):
5823 raise error.Abort(_("tag '%s' does not exist") % n)
5821 raise error.Abort(_("tag '%s' does not exist") % n)
5824 if repo.tagtype(n) != expectedtype:
5822 if repo.tagtype(n) != expectedtype:
5825 if expectedtype == 'global':
5823 if expectedtype == 'global':
5826 raise error.Abort(_("tag '%s' is not a global tag") % n)
5824 raise error.Abort(_("tag '%s' is not a global tag") % n)
5827 else:
5825 else:
5828 raise error.Abort(_("tag '%s' is not a local tag") % n)
5826 raise error.Abort(_("tag '%s' is not a local tag") % n)
5829 rev_ = 'null'
5827 rev_ = 'null'
5830 if not message:
5828 if not message:
5831 # we don't translate commit messages
5829 # we don't translate commit messages
5832 message = 'Removed tag %s' % ', '.join(names)
5830 message = 'Removed tag %s' % ', '.join(names)
5833 elif not opts.get('force'):
5831 elif not opts.get('force'):
5834 for n in names:
5832 for n in names:
5835 if n in repo.tags():
5833 if n in repo.tags():
5836 raise error.Abort(_("tag '%s' already exists "
5834 raise error.Abort(_("tag '%s' already exists "
5837 "(use -f to force)") % n)
5835 "(use -f to force)") % n)
5838 if not opts.get('local'):
5836 if not opts.get('local'):
5839 p1, p2 = repo.dirstate.parents()
5837 p1, p2 = repo.dirstate.parents()
5840 if p2 != nullid:
5838 if p2 != nullid:
5841 raise error.Abort(_('uncommitted merge'))
5839 raise error.Abort(_('uncommitted merge'))
5842 bheads = repo.branchheads()
5840 bheads = repo.branchheads()
5843 if not opts.get('force') and bheads and p1 not in bheads:
5841 if not opts.get('force') and bheads and p1 not in bheads:
5844 raise error.Abort(_('working directory is not at a branch head '
5842 raise error.Abort(_('working directory is not at a branch head '
5845 '(use -f to force)'))
5843 '(use -f to force)'))
5846 node = scmutil.revsingle(repo, rev_).node()
5844 node = scmutil.revsingle(repo, rev_).node()
5847
5845
5848 if not message:
5846 if not message:
5849 # we don't translate commit messages
5847 # we don't translate commit messages
5850 message = ('Added tag %s for changeset %s' %
5848 message = ('Added tag %s for changeset %s' %
5851 (', '.join(names), short(node)))
5849 (', '.join(names), short(node)))
5852
5850
5853 date = opts.get('date')
5851 date = opts.get('date')
5854 if date:
5852 if date:
5855 date = dateutil.parsedate(date)
5853 date = dateutil.parsedate(date)
5856
5854
5857 if opts.get('remove'):
5855 if opts.get('remove'):
5858 editform = 'tag.remove'
5856 editform = 'tag.remove'
5859 else:
5857 else:
5860 editform = 'tag.add'
5858 editform = 'tag.add'
5861 editor = cmdutil.getcommiteditor(editform=editform,
5859 editor = cmdutil.getcommiteditor(editform=editform,
5862 **pycompat.strkwargs(opts))
5860 **pycompat.strkwargs(opts))
5863
5861
5864 # don't allow tagging the null rev
5862 # don't allow tagging the null rev
5865 if (not opts.get('remove') and
5863 if (not opts.get('remove') and
5866 scmutil.revsingle(repo, rev_).rev() == nullrev):
5864 scmutil.revsingle(repo, rev_).rev() == nullrev):
5867 raise error.Abort(_("cannot tag null revision"))
5865 raise error.Abort(_("cannot tag null revision"))
5868
5866
5869 tagsmod.tag(repo, names, node, message, opts.get('local'),
5867 tagsmod.tag(repo, names, node, message, opts.get('local'),
5870 opts.get('user'), date, editor=editor)
5868 opts.get('user'), date, editor=editor)
5871
5869
5872 @command(
5870 @command(
5873 'tags', formatteropts, '',
5871 'tags', formatteropts, '',
5874 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5872 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5875 intents={INTENT_READONLY})
5873 intents={INTENT_READONLY})
5876 def tags(ui, repo, **opts):
5874 def tags(ui, repo, **opts):
5877 """list repository tags
5875 """list repository tags
5878
5876
5879 This lists both regular and local tags. When the -v/--verbose
5877 This lists both regular and local tags. When the -v/--verbose
5880 switch is used, a third column "local" is printed for local tags.
5878 switch is used, a third column "local" is printed for local tags.
5881 When the -q/--quiet switch is used, only the tag name is printed.
5879 When the -q/--quiet switch is used, only the tag name is printed.
5882
5880
5883 .. container:: verbose
5881 .. container:: verbose
5884
5882
5885 Template:
5883 Template:
5886
5884
5887 The following keywords are supported in addition to the common template
5885 The following keywords are supported in addition to the common template
5888 keywords and functions such as ``{tag}``. See also
5886 keywords and functions such as ``{tag}``. See also
5889 :hg:`help templates`.
5887 :hg:`help templates`.
5890
5888
5891 :type: String. ``local`` for local tags.
5889 :type: String. ``local`` for local tags.
5892
5890
5893 Returns 0 on success.
5891 Returns 0 on success.
5894 """
5892 """
5895
5893
5896 opts = pycompat.byteskwargs(opts)
5894 opts = pycompat.byteskwargs(opts)
5897 ui.pager('tags')
5895 ui.pager('tags')
5898 fm = ui.formatter('tags', opts)
5896 fm = ui.formatter('tags', opts)
5899 hexfunc = fm.hexfunc
5897 hexfunc = fm.hexfunc
5900 tagtype = ""
5898 tagtype = ""
5901
5899
5902 for t, n in reversed(repo.tagslist()):
5900 for t, n in reversed(repo.tagslist()):
5903 hn = hexfunc(n)
5901 hn = hexfunc(n)
5904 label = 'tags.normal'
5902 label = 'tags.normal'
5905 tagtype = ''
5903 tagtype = ''
5906 if repo.tagtype(t) == 'local':
5904 if repo.tagtype(t) == 'local':
5907 label = 'tags.local'
5905 label = 'tags.local'
5908 tagtype = 'local'
5906 tagtype = 'local'
5909
5907
5910 fm.startitem()
5908 fm.startitem()
5911 fm.context(repo=repo)
5909 fm.context(repo=repo)
5912 fm.write('tag', '%s', t, label=label)
5910 fm.write('tag', '%s', t, label=label)
5913 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5911 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5914 fm.condwrite(not ui.quiet, 'rev node', fmt,
5912 fm.condwrite(not ui.quiet, 'rev node', fmt,
5915 repo.changelog.rev(n), hn, label=label)
5913 repo.changelog.rev(n), hn, label=label)
5916 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5914 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5917 tagtype, label=label)
5915 tagtype, label=label)
5918 fm.plain('\n')
5916 fm.plain('\n')
5919 fm.end()
5917 fm.end()
5920
5918
5921 @command('tip',
5919 @command('tip',
5922 [('p', 'patch', None, _('show patch')),
5920 [('p', 'patch', None, _('show patch')),
5923 ('g', 'git', None, _('use git extended diff format')),
5921 ('g', 'git', None, _('use git extended diff format')),
5924 ] + templateopts,
5922 ] + templateopts,
5925 _('[-p] [-g]'),
5923 _('[-p] [-g]'),
5926 helpcategory=command.CATEGORY_CHANGE_NAVIGATION)
5924 helpcategory=command.CATEGORY_CHANGE_NAVIGATION)
5927 def tip(ui, repo, **opts):
5925 def tip(ui, repo, **opts):
5928 """show the tip revision (DEPRECATED)
5926 """show the tip revision (DEPRECATED)
5929
5927
5930 The tip revision (usually just called the tip) is the changeset
5928 The tip revision (usually just called the tip) is the changeset
5931 most recently added to the repository (and therefore the most
5929 most recently added to the repository (and therefore the most
5932 recently changed head).
5930 recently changed head).
5933
5931
5934 If you have just made a commit, that commit will be the tip. If
5932 If you have just made a commit, that commit will be the tip. If
5935 you have just pulled changes from another repository, the tip of
5933 you have just pulled changes from another repository, the tip of
5936 that repository becomes the current tip. The "tip" tag is special
5934 that repository becomes the current tip. The "tip" tag is special
5937 and cannot be renamed or assigned to a different changeset.
5935 and cannot be renamed or assigned to a different changeset.
5938
5936
5939 This command is deprecated, please use :hg:`heads` instead.
5937 This command is deprecated, please use :hg:`heads` instead.
5940
5938
5941 Returns 0 on success.
5939 Returns 0 on success.
5942 """
5940 """
5943 opts = pycompat.byteskwargs(opts)
5941 opts = pycompat.byteskwargs(opts)
5944 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5942 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5945 displayer.show(repo['tip'])
5943 displayer.show(repo['tip'])
5946 displayer.close()
5944 displayer.close()
5947
5945
5948 @command('unbundle',
5946 @command('unbundle',
5949 [('u', 'update', None,
5947 [('u', 'update', None,
5950 _('update to new branch head if changesets were unbundled'))],
5948 _('update to new branch head if changesets were unbundled'))],
5951 _('[-u] FILE...'),
5949 _('[-u] FILE...'),
5952 helpcategory=command.CATEGORY_IMPORT_EXPORT)
5950 helpcategory=command.CATEGORY_IMPORT_EXPORT)
5953 def unbundle(ui, repo, fname1, *fnames, **opts):
5951 def unbundle(ui, repo, fname1, *fnames, **opts):
5954 """apply one or more bundle files
5952 """apply one or more bundle files
5955
5953
5956 Apply one or more bundle files generated by :hg:`bundle`.
5954 Apply one or more bundle files generated by :hg:`bundle`.
5957
5955
5958 Returns 0 on success, 1 if an update has unresolved files.
5956 Returns 0 on success, 1 if an update has unresolved files.
5959 """
5957 """
5960 fnames = (fname1,) + fnames
5958 fnames = (fname1,) + fnames
5961
5959
5962 with repo.lock():
5960 with repo.lock():
5963 for fname in fnames:
5961 for fname in fnames:
5964 f = hg.openpath(ui, fname)
5962 f = hg.openpath(ui, fname)
5965 gen = exchange.readbundle(ui, f, fname)
5963 gen = exchange.readbundle(ui, f, fname)
5966 if isinstance(gen, streamclone.streamcloneapplier):
5964 if isinstance(gen, streamclone.streamcloneapplier):
5967 raise error.Abort(
5965 raise error.Abort(
5968 _('packed bundles cannot be applied with '
5966 _('packed bundles cannot be applied with '
5969 '"hg unbundle"'),
5967 '"hg unbundle"'),
5970 hint=_('use "hg debugapplystreamclonebundle"'))
5968 hint=_('use "hg debugapplystreamclonebundle"'))
5971 url = 'bundle:' + fname
5969 url = 'bundle:' + fname
5972 try:
5970 try:
5973 txnname = 'unbundle'
5971 txnname = 'unbundle'
5974 if not isinstance(gen, bundle2.unbundle20):
5972 if not isinstance(gen, bundle2.unbundle20):
5975 txnname = 'unbundle\n%s' % util.hidepassword(url)
5973 txnname = 'unbundle\n%s' % util.hidepassword(url)
5976 with repo.transaction(txnname) as tr:
5974 with repo.transaction(txnname) as tr:
5977 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5975 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5978 url=url)
5976 url=url)
5979 except error.BundleUnknownFeatureError as exc:
5977 except error.BundleUnknownFeatureError as exc:
5980 raise error.Abort(
5978 raise error.Abort(
5981 _('%s: unknown bundle feature, %s') % (fname, exc),
5979 _('%s: unknown bundle feature, %s') % (fname, exc),
5982 hint=_("see https://mercurial-scm.org/"
5980 hint=_("see https://mercurial-scm.org/"
5983 "wiki/BundleFeature for more "
5981 "wiki/BundleFeature for more "
5984 "information"))
5982 "information"))
5985 modheads = bundle2.combinechangegroupresults(op)
5983 modheads = bundle2.combinechangegroupresults(op)
5986
5984
5987 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5985 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5988
5986
5989 @command('update|up|checkout|co',
5987 @command('update|up|checkout|co',
5990 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5988 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5991 ('c', 'check', None, _('require clean working directory')),
5989 ('c', 'check', None, _('require clean working directory')),
5992 ('m', 'merge', None, _('merge uncommitted changes')),
5990 ('m', 'merge', None, _('merge uncommitted changes')),
5993 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5991 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5994 ('r', 'rev', '', _('revision'), _('REV'))
5992 ('r', 'rev', '', _('revision'), _('REV'))
5995 ] + mergetoolopts,
5993 ] + mergetoolopts,
5996 _('[-C|-c|-m] [-d DATE] [[-r] REV]'),
5994 _('[-C|-c|-m] [-d DATE] [[-r] REV]'),
5997 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5995 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5998 helpbasic=True)
5996 helpbasic=True)
5999 def update(ui, repo, node=None, **opts):
5997 def update(ui, repo, node=None, **opts):
6000 """update working directory (or switch revisions)
5998 """update working directory (or switch revisions)
6001
5999
6002 Update the repository's working directory to the specified
6000 Update the repository's working directory to the specified
6003 changeset. If no changeset is specified, update to the tip of the
6001 changeset. If no changeset is specified, update to the tip of the
6004 current named branch and move the active bookmark (see :hg:`help
6002 current named branch and move the active bookmark (see :hg:`help
6005 bookmarks`).
6003 bookmarks`).
6006
6004
6007 Update sets the working directory's parent revision to the specified
6005 Update sets the working directory's parent revision to the specified
6008 changeset (see :hg:`help parents`).
6006 changeset (see :hg:`help parents`).
6009
6007
6010 If the changeset is not a descendant or ancestor of the working
6008 If the changeset is not a descendant or ancestor of the working
6011 directory's parent and there are uncommitted changes, the update is
6009 directory's parent and there are uncommitted changes, the update is
6012 aborted. With the -c/--check option, the working directory is checked
6010 aborted. With the -c/--check option, the working directory is checked
6013 for uncommitted changes; if none are found, the working directory is
6011 for uncommitted changes; if none are found, the working directory is
6014 updated to the specified changeset.
6012 updated to the specified changeset.
6015
6013
6016 .. container:: verbose
6014 .. container:: verbose
6017
6015
6018 The -C/--clean, -c/--check, and -m/--merge options control what
6016 The -C/--clean, -c/--check, and -m/--merge options control what
6019 happens if the working directory contains uncommitted changes.
6017 happens if the working directory contains uncommitted changes.
6020 At most of one of them can be specified.
6018 At most of one of them can be specified.
6021
6019
6022 1. If no option is specified, and if
6020 1. If no option is specified, and if
6023 the requested changeset is an ancestor or descendant of
6021 the requested changeset is an ancestor or descendant of
6024 the working directory's parent, the uncommitted changes
6022 the working directory's parent, the uncommitted changes
6025 are merged into the requested changeset and the merged
6023 are merged into the requested changeset and the merged
6026 result is left uncommitted. If the requested changeset is
6024 result is left uncommitted. If the requested changeset is
6027 not an ancestor or descendant (that is, it is on another
6025 not an ancestor or descendant (that is, it is on another
6028 branch), the update is aborted and the uncommitted changes
6026 branch), the update is aborted and the uncommitted changes
6029 are preserved.
6027 are preserved.
6030
6028
6031 2. With the -m/--merge option, the update is allowed even if the
6029 2. With the -m/--merge option, the update is allowed even if the
6032 requested changeset is not an ancestor or descendant of
6030 requested changeset is not an ancestor or descendant of
6033 the working directory's parent.
6031 the working directory's parent.
6034
6032
6035 3. With the -c/--check option, the update is aborted and the
6033 3. With the -c/--check option, the update is aborted and the
6036 uncommitted changes are preserved.
6034 uncommitted changes are preserved.
6037
6035
6038 4. With the -C/--clean option, uncommitted changes are discarded and
6036 4. With the -C/--clean option, uncommitted changes are discarded and
6039 the working directory is updated to the requested changeset.
6037 the working directory is updated to the requested changeset.
6040
6038
6041 To cancel an uncommitted merge (and lose your changes), use
6039 To cancel an uncommitted merge (and lose your changes), use
6042 :hg:`merge --abort`.
6040 :hg:`merge --abort`.
6043
6041
6044 Use null as the changeset to remove the working directory (like
6042 Use null as the changeset to remove the working directory (like
6045 :hg:`clone -U`).
6043 :hg:`clone -U`).
6046
6044
6047 If you want to revert just one file to an older revision, use
6045 If you want to revert just one file to an older revision, use
6048 :hg:`revert [-r REV] NAME`.
6046 :hg:`revert [-r REV] NAME`.
6049
6047
6050 See :hg:`help dates` for a list of formats valid for -d/--date.
6048 See :hg:`help dates` for a list of formats valid for -d/--date.
6051
6049
6052 Returns 0 on success, 1 if there are unresolved files.
6050 Returns 0 on success, 1 if there are unresolved files.
6053 """
6051 """
6054 rev = opts.get(r'rev')
6052 rev = opts.get(r'rev')
6055 date = opts.get(r'date')
6053 date = opts.get(r'date')
6056 clean = opts.get(r'clean')
6054 clean = opts.get(r'clean')
6057 check = opts.get(r'check')
6055 check = opts.get(r'check')
6058 merge = opts.get(r'merge')
6056 merge = opts.get(r'merge')
6059 if rev and node:
6057 if rev and node:
6060 raise error.Abort(_("please specify just one revision"))
6058 raise error.Abort(_("please specify just one revision"))
6061
6059
6062 if ui.configbool('commands', 'update.requiredest'):
6060 if ui.configbool('commands', 'update.requiredest'):
6063 if not node and not rev and not date:
6061 if not node and not rev and not date:
6064 raise error.Abort(_('you must specify a destination'),
6062 raise error.Abort(_('you must specify a destination'),
6065 hint=_('for example: hg update ".::"'))
6063 hint=_('for example: hg update ".::"'))
6066
6064
6067 if rev is None or rev == '':
6065 if rev is None or rev == '':
6068 rev = node
6066 rev = node
6069
6067
6070 if date and rev is not None:
6068 if date and rev is not None:
6071 raise error.Abort(_("you can't specify a revision and a date"))
6069 raise error.Abort(_("you can't specify a revision and a date"))
6072
6070
6073 if len([x for x in (clean, check, merge) if x]) > 1:
6071 if len([x for x in (clean, check, merge) if x]) > 1:
6074 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
6072 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
6075 "or -m/--merge"))
6073 "or -m/--merge"))
6076
6074
6077 updatecheck = None
6075 updatecheck = None
6078 if check:
6076 if check:
6079 updatecheck = 'abort'
6077 updatecheck = 'abort'
6080 elif merge:
6078 elif merge:
6081 updatecheck = 'none'
6079 updatecheck = 'none'
6082
6080
6083 with repo.wlock():
6081 with repo.wlock():
6084 cmdutil.clearunfinished(repo)
6082 cmdutil.clearunfinished(repo)
6085
6083
6086 if date:
6084 if date:
6087 rev = cmdutil.finddate(ui, repo, date)
6085 rev = cmdutil.finddate(ui, repo, date)
6088
6086
6089 # if we defined a bookmark, we have to remember the original name
6087 # if we defined a bookmark, we have to remember the original name
6090 brev = rev
6088 brev = rev
6091 if rev:
6089 if rev:
6092 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
6090 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
6093 ctx = scmutil.revsingle(repo, rev, rev)
6091 ctx = scmutil.revsingle(repo, rev, rev)
6094 rev = ctx.rev()
6092 rev = ctx.rev()
6095 hidden = ctx.hidden()
6093 hidden = ctx.hidden()
6096 overrides = {('ui', 'forcemerge'): opts.get(r'tool', '')}
6094 overrides = {('ui', 'forcemerge'): opts.get(r'tool', '')}
6097 with ui.configoverride(overrides, 'update'):
6095 with ui.configoverride(overrides, 'update'):
6098 ret = hg.updatetotally(ui, repo, rev, brev, clean=clean,
6096 ret = hg.updatetotally(ui, repo, rev, brev, clean=clean,
6099 updatecheck=updatecheck)
6097 updatecheck=updatecheck)
6100 if hidden:
6098 if hidden:
6101 ctxstr = ctx.hex()[:12]
6099 ctxstr = ctx.hex()[:12]
6102 ui.warn(_("updated to hidden changeset %s\n") % ctxstr)
6100 ui.warn(_("updated to hidden changeset %s\n") % ctxstr)
6103
6101
6104 if ctx.obsolete():
6102 if ctx.obsolete():
6105 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
6103 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
6106 ui.warn("(%s)\n" % obsfatemsg)
6104 ui.warn("(%s)\n" % obsfatemsg)
6107 return ret
6105 return ret
6108
6106
6109 @command('verify', [], helpcategory=command.CATEGORY_MAINTENANCE)
6107 @command('verify', [], helpcategory=command.CATEGORY_MAINTENANCE)
6110 def verify(ui, repo):
6108 def verify(ui, repo):
6111 """verify the integrity of the repository
6109 """verify the integrity of the repository
6112
6110
6113 Verify the integrity of the current repository.
6111 Verify the integrity of the current repository.
6114
6112
6115 This will perform an extensive check of the repository's
6113 This will perform an extensive check of the repository's
6116 integrity, validating the hashes and checksums of each entry in
6114 integrity, validating the hashes and checksums of each entry in
6117 the changelog, manifest, and tracked files, as well as the
6115 the changelog, manifest, and tracked files, as well as the
6118 integrity of their crosslinks and indices.
6116 integrity of their crosslinks and indices.
6119
6117
6120 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
6118 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
6121 for more information about recovery from corruption of the
6119 for more information about recovery from corruption of the
6122 repository.
6120 repository.
6123
6121
6124 Returns 0 on success, 1 if errors are encountered.
6122 Returns 0 on success, 1 if errors are encountered.
6125 """
6123 """
6126 return hg.verify(repo)
6124 return hg.verify(repo)
6127
6125
6128 @command(
6126 @command(
6129 'version', [] + formatteropts, helpcategory=command.CATEGORY_HELP,
6127 'version', [] + formatteropts, helpcategory=command.CATEGORY_HELP,
6130 norepo=True, intents={INTENT_READONLY})
6128 norepo=True, intents={INTENT_READONLY})
6131 def version_(ui, **opts):
6129 def version_(ui, **opts):
6132 """output version and copyright information
6130 """output version and copyright information
6133
6131
6134 .. container:: verbose
6132 .. container:: verbose
6135
6133
6136 Template:
6134 Template:
6137
6135
6138 The following keywords are supported. See also :hg:`help templates`.
6136 The following keywords are supported. See also :hg:`help templates`.
6139
6137
6140 :extensions: List of extensions.
6138 :extensions: List of extensions.
6141 :ver: String. Version number.
6139 :ver: String. Version number.
6142
6140
6143 And each entry of ``{extensions}`` provides the following sub-keywords
6141 And each entry of ``{extensions}`` provides the following sub-keywords
6144 in addition to ``{ver}``.
6142 in addition to ``{ver}``.
6145
6143
6146 :bundled: Boolean. True if included in the release.
6144 :bundled: Boolean. True if included in the release.
6147 :name: String. Extension name.
6145 :name: String. Extension name.
6148 """
6146 """
6149 opts = pycompat.byteskwargs(opts)
6147 opts = pycompat.byteskwargs(opts)
6150 if ui.verbose:
6148 if ui.verbose:
6151 ui.pager('version')
6149 ui.pager('version')
6152 fm = ui.formatter("version", opts)
6150 fm = ui.formatter("version", opts)
6153 fm.startitem()
6151 fm.startitem()
6154 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
6152 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
6155 util.version())
6153 util.version())
6156 license = _(
6154 license = _(
6157 "(see https://mercurial-scm.org for more information)\n"
6155 "(see https://mercurial-scm.org for more information)\n"
6158 "\nCopyright (C) 2005-2018 Matt Mackall and others\n"
6156 "\nCopyright (C) 2005-2018 Matt Mackall and others\n"
6159 "This is free software; see the source for copying conditions. "
6157 "This is free software; see the source for copying conditions. "
6160 "There is NO\nwarranty; "
6158 "There is NO\nwarranty; "
6161 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6159 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6162 )
6160 )
6163 if not ui.quiet:
6161 if not ui.quiet:
6164 fm.plain(license)
6162 fm.plain(license)
6165
6163
6166 if ui.verbose:
6164 if ui.verbose:
6167 fm.plain(_("\nEnabled extensions:\n\n"))
6165 fm.plain(_("\nEnabled extensions:\n\n"))
6168 # format names and versions into columns
6166 # format names and versions into columns
6169 names = []
6167 names = []
6170 vers = []
6168 vers = []
6171 isinternals = []
6169 isinternals = []
6172 for name, module in extensions.extensions():
6170 for name, module in extensions.extensions():
6173 names.append(name)
6171 names.append(name)
6174 vers.append(extensions.moduleversion(module) or None)
6172 vers.append(extensions.moduleversion(module) or None)
6175 isinternals.append(extensions.ismoduleinternal(module))
6173 isinternals.append(extensions.ismoduleinternal(module))
6176 fn = fm.nested("extensions", tmpl='{name}\n')
6174 fn = fm.nested("extensions", tmpl='{name}\n')
6177 if names:
6175 if names:
6178 namefmt = " %%-%ds " % max(len(n) for n in names)
6176 namefmt = " %%-%ds " % max(len(n) for n in names)
6179 places = [_("external"), _("internal")]
6177 places = [_("external"), _("internal")]
6180 for n, v, p in zip(names, vers, isinternals):
6178 for n, v, p in zip(names, vers, isinternals):
6181 fn.startitem()
6179 fn.startitem()
6182 fn.condwrite(ui.verbose, "name", namefmt, n)
6180 fn.condwrite(ui.verbose, "name", namefmt, n)
6183 if ui.verbose:
6181 if ui.verbose:
6184 fn.plain("%s " % places[p])
6182 fn.plain("%s " % places[p])
6185 fn.data(bundled=p)
6183 fn.data(bundled=p)
6186 fn.condwrite(ui.verbose and v, "ver", "%s", v)
6184 fn.condwrite(ui.verbose and v, "ver", "%s", v)
6187 if ui.verbose:
6185 if ui.verbose:
6188 fn.plain("\n")
6186 fn.plain("\n")
6189 fn.end()
6187 fn.end()
6190 fm.end()
6188 fm.end()
6191
6189
6192 def loadcmdtable(ui, name, cmdtable):
6190 def loadcmdtable(ui, name, cmdtable):
6193 """Load command functions from specified cmdtable
6191 """Load command functions from specified cmdtable
6194 """
6192 """
6195 cmdtable = cmdtable.copy()
6193 cmdtable = cmdtable.copy()
6196 for cmd in list(cmdtable):
6194 for cmd in list(cmdtable):
6197 if not cmd.startswith('^'):
6195 if not cmd.startswith('^'):
6198 continue
6196 continue
6199 ui.deprecwarn("old-style command registration '%s' in extension '%s'"
6197 ui.deprecwarn("old-style command registration '%s' in extension '%s'"
6200 % (cmd, name), '4.8')
6198 % (cmd, name), '4.8')
6201 entry = cmdtable.pop(cmd)
6199 entry = cmdtable.pop(cmd)
6202 entry[0].helpbasic = True
6200 entry[0].helpbasic = True
6203 cmdtable[cmd[1:]] = entry
6201 cmdtable[cmd[1:]] = entry
6204
6202
6205 overrides = [cmd for cmd in cmdtable if cmd in table]
6203 overrides = [cmd for cmd in cmdtable if cmd in table]
6206 if overrides:
6204 if overrides:
6207 ui.warn(_("extension '%s' overrides commands: %s\n")
6205 ui.warn(_("extension '%s' overrides commands: %s\n")
6208 % (name, " ".join(overrides)))
6206 % (name, " ".join(overrides)))
6209 table.update(cmdtable)
6207 table.update(cmdtable)
@@ -1,1323 +1,1324 b''
1 #testcases b2-pushkey b2-binary
1 #testcases b2-pushkey b2-binary
2
2
3 #if b2-pushkey
3 #if b2-pushkey
4 $ cat << EOF >> $HGRCPATH
4 $ cat << EOF >> $HGRCPATH
5 > [devel]
5 > [devel]
6 > legacy.exchange=bookmarks
6 > legacy.exchange=bookmarks
7 > EOF
7 > EOF
8 #endif
8 #endif
9
9
10 #require serve
10 #require serve
11
11
12 $ cat << EOF >> $HGRCPATH
12 $ cat << EOF >> $HGRCPATH
13 > [ui]
13 > [ui]
14 > logtemplate={rev}:{node|short} {desc|firstline}
14 > logtemplate={rev}:{node|short} {desc|firstline}
15 > [phases]
15 > [phases]
16 > publish=False
16 > publish=False
17 > [experimental]
17 > [experimental]
18 > evolution.createmarkers=True
18 > evolution.createmarkers=True
19 > evolution.exchange=True
19 > evolution.exchange=True
20 > EOF
20 > EOF
21
21
22 $ cat > $TESTTMP/hook.sh <<'EOF'
22 $ cat > $TESTTMP/hook.sh <<'EOF'
23 > echo "test-hook-bookmark: $HG_BOOKMARK: $HG_OLDNODE -> $HG_NODE"
23 > echo "test-hook-bookmark: $HG_BOOKMARK: $HG_OLDNODE -> $HG_NODE"
24 > EOF
24 > EOF
25 $ TESTHOOK="hooks.txnclose-bookmark.test=sh $TESTTMP/hook.sh"
25 $ TESTHOOK="hooks.txnclose-bookmark.test=sh $TESTTMP/hook.sh"
26
26
27 initialize
27 initialize
28
28
29 $ hg init a
29 $ hg init a
30 $ cd a
30 $ cd a
31 $ echo 'test' > test
31 $ echo 'test' > test
32 $ hg commit -Am'test'
32 $ hg commit -Am'test'
33 adding test
33 adding test
34
34
35 set bookmarks
35 set bookmarks
36
36
37 $ hg bookmark X
37 $ hg bookmark X
38 $ hg bookmark Y
38 $ hg bookmark Y
39 $ hg bookmark Z
39 $ hg bookmark Z
40
40
41 import bookmark by name
41 import bookmark by name
42
42
43 $ hg init ../b
43 $ hg init ../b
44 $ cd ../b
44 $ cd ../b
45 $ hg book Y
45 $ hg book Y
46 $ hg book
46 $ hg book
47 * Y -1:000000000000
47 * Y -1:000000000000
48 $ hg pull ../a --config "$TESTHOOK"
48 $ hg pull ../a --config "$TESTHOOK"
49 pulling from ../a
49 pulling from ../a
50 requesting all changes
50 requesting all changes
51 adding changesets
51 adding changesets
52 adding manifests
52 adding manifests
53 adding file changes
53 adding file changes
54 added 1 changesets with 1 changes to 1 files
54 added 1 changesets with 1 changes to 1 files
55 adding remote bookmark X
55 adding remote bookmark X
56 updating bookmark Y
56 updating bookmark Y
57 adding remote bookmark Z
57 adding remote bookmark Z
58 new changesets 4e3505fd9583 (1 drafts)
58 new changesets 4e3505fd9583 (1 drafts)
59 test-hook-bookmark: X: -> 4e3505fd95835d721066b76e75dbb8cc554d7f77
59 test-hook-bookmark: X: -> 4e3505fd95835d721066b76e75dbb8cc554d7f77
60 test-hook-bookmark: Y: 0000000000000000000000000000000000000000 -> 4e3505fd95835d721066b76e75dbb8cc554d7f77
60 test-hook-bookmark: Y: 0000000000000000000000000000000000000000 -> 4e3505fd95835d721066b76e75dbb8cc554d7f77
61 test-hook-bookmark: Z: -> 4e3505fd95835d721066b76e75dbb8cc554d7f77
61 test-hook-bookmark: Z: -> 4e3505fd95835d721066b76e75dbb8cc554d7f77
62 (run 'hg update' to get a working copy)
62 (run 'hg update' to get a working copy)
63 $ hg bookmarks
63 $ hg bookmarks
64 X 0:4e3505fd9583
64 X 0:4e3505fd9583
65 * Y 0:4e3505fd9583
65 * Y 0:4e3505fd9583
66 Z 0:4e3505fd9583
66 Z 0:4e3505fd9583
67 $ hg debugpushkey ../a namespaces
67 $ hg debugpushkey ../a namespaces
68 bookmarks
68 bookmarks
69 namespaces
69 namespaces
70 obsolete
70 obsolete
71 phases
71 phases
72 $ hg debugpushkey ../a bookmarks
72 $ hg debugpushkey ../a bookmarks
73 X 4e3505fd95835d721066b76e75dbb8cc554d7f77
73 X 4e3505fd95835d721066b76e75dbb8cc554d7f77
74 Y 4e3505fd95835d721066b76e75dbb8cc554d7f77
74 Y 4e3505fd95835d721066b76e75dbb8cc554d7f77
75 Z 4e3505fd95835d721066b76e75dbb8cc554d7f77
75 Z 4e3505fd95835d721066b76e75dbb8cc554d7f77
76
76
77 delete the bookmark to re-pull it
77 delete the bookmark to re-pull it
78
78
79 $ hg book -d X
79 $ hg book -d X
80 $ hg pull -B X ../a
80 $ hg pull -B X ../a
81 pulling from ../a
81 pulling from ../a
82 no changes found
82 no changes found
83 adding remote bookmark X
83 adding remote bookmark X
84
84
85 finally no-op pull
85 finally no-op pull
86
86
87 $ hg pull -B X ../a
87 $ hg pull -B X ../a
88 pulling from ../a
88 pulling from ../a
89 no changes found
89 no changes found
90 $ hg bookmark
90 $ hg bookmark
91 X 0:4e3505fd9583
91 X 0:4e3505fd9583
92 * Y 0:4e3505fd9583
92 * Y 0:4e3505fd9583
93 Z 0:4e3505fd9583
93 Z 0:4e3505fd9583
94
94
95 export bookmark by name
95 export bookmark by name
96
96
97 $ hg bookmark W
97 $ hg bookmark W
98 $ hg bookmark foo
98 $ hg bookmark foo
99 $ hg bookmark foobar
99 $ hg bookmark foobar
100 $ hg push -B W ../a
100 $ hg push -B W ../a
101 pushing to ../a
101 pushing to ../a
102 searching for changes
102 searching for changes
103 no changes found
103 no changes found
104 exporting bookmark W
104 exporting bookmark W
105 [1]
105 [1]
106 $ hg -R ../a bookmarks
106 $ hg -R ../a bookmarks
107 W -1:000000000000
107 W -1:000000000000
108 X 0:4e3505fd9583
108 X 0:4e3505fd9583
109 Y 0:4e3505fd9583
109 Y 0:4e3505fd9583
110 * Z 0:4e3505fd9583
110 * Z 0:4e3505fd9583
111
111
112 delete a remote bookmark
112 delete a remote bookmark
113
113
114 $ hg book -d W
114 $ hg book -d W
115
115
116 #if b2-pushkey
116 #if b2-pushkey
117
117
118 $ hg push -B W ../a --config "$TESTHOOK" --debug --config devel.bundle2.debug=yes
118 $ hg push -B W ../a --config "$TESTHOOK" --debug --config devel.bundle2.debug=yes
119 pushing to ../a
119 pushing to ../a
120 query 1; heads
120 query 1; heads
121 searching for changes
121 searching for changes
122 all remote heads known locally
122 all remote heads known locally
123 listing keys for "phases"
123 listing keys for "phases"
124 checking for updated bookmarks
124 checking for updated bookmarks
125 listing keys for "bookmarks"
125 listing keys for "bookmarks"
126 no changes found
126 no changes found
127 bundle2-output-bundle: "HG20", 4 parts total
127 bundle2-output-bundle: "HG20", 4 parts total
128 bundle2-output: start emission of HG20 stream
128 bundle2-output: start emission of HG20 stream
129 bundle2-output: bundle parameter:
129 bundle2-output: bundle parameter:
130 bundle2-output: start of parts
130 bundle2-output: start of parts
131 bundle2-output: bundle part: "replycaps"
131 bundle2-output: bundle part: "replycaps"
132 bundle2-output-part: "replycaps" 222 bytes payload
132 bundle2-output-part: "replycaps" 222 bytes payload
133 bundle2-output: part 0: "REPLYCAPS"
133 bundle2-output: part 0: "REPLYCAPS"
134 bundle2-output: header chunk size: 16
134 bundle2-output: header chunk size: 16
135 bundle2-output: payload chunk size: 222
135 bundle2-output: payload chunk size: 222
136 bundle2-output: closing payload chunk
136 bundle2-output: closing payload chunk
137 bundle2-output: bundle part: "check:bookmarks"
137 bundle2-output: bundle part: "check:bookmarks"
138 bundle2-output-part: "check:bookmarks" 23 bytes payload
138 bundle2-output-part: "check:bookmarks" 23 bytes payload
139 bundle2-output: part 1: "CHECK:BOOKMARKS"
139 bundle2-output: part 1: "CHECK:BOOKMARKS"
140 bundle2-output: header chunk size: 22
140 bundle2-output: header chunk size: 22
141 bundle2-output: payload chunk size: 23
141 bundle2-output: payload chunk size: 23
142 bundle2-output: closing payload chunk
142 bundle2-output: closing payload chunk
143 bundle2-output: bundle part: "check:phases"
143 bundle2-output: bundle part: "check:phases"
144 bundle2-output-part: "check:phases" 24 bytes payload
144 bundle2-output-part: "check:phases" 24 bytes payload
145 bundle2-output: part 2: "CHECK:PHASES"
145 bundle2-output: part 2: "CHECK:PHASES"
146 bundle2-output: header chunk size: 19
146 bundle2-output: header chunk size: 19
147 bundle2-output: payload chunk size: 24
147 bundle2-output: payload chunk size: 24
148 bundle2-output: closing payload chunk
148 bundle2-output: closing payload chunk
149 bundle2-output: bundle part: "pushkey"
149 bundle2-output: bundle part: "pushkey"
150 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
150 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
151 bundle2-output: part 3: "PUSHKEY"
151 bundle2-output: part 3: "PUSHKEY"
152 bundle2-output: header chunk size: 90
152 bundle2-output: header chunk size: 90
153 bundle2-output: closing payload chunk
153 bundle2-output: closing payload chunk
154 bundle2-output: end of bundle
154 bundle2-output: end of bundle
155 bundle2-input: start processing of HG20 stream
155 bundle2-input: start processing of HG20 stream
156 bundle2-input: reading bundle2 stream parameters
156 bundle2-input: reading bundle2 stream parameters
157 bundle2-input-bundle: with-transaction
157 bundle2-input-bundle: with-transaction
158 bundle2-input: start extraction of bundle2 parts
158 bundle2-input: start extraction of bundle2 parts
159 bundle2-input: part header size: 16
159 bundle2-input: part header size: 16
160 bundle2-input: part type: "REPLYCAPS"
160 bundle2-input: part type: "REPLYCAPS"
161 bundle2-input: part id: "0"
161 bundle2-input: part id: "0"
162 bundle2-input: part parameters: 0
162 bundle2-input: part parameters: 0
163 bundle2-input: found a handler for part replycaps
163 bundle2-input: found a handler for part replycaps
164 bundle2-input-part: "replycaps" supported
164 bundle2-input-part: "replycaps" supported
165 bundle2-input: payload chunk size: 222
165 bundle2-input: payload chunk size: 222
166 bundle2-input: payload chunk size: 0
166 bundle2-input: payload chunk size: 0
167 bundle2-input-part: total payload size 222
167 bundle2-input-part: total payload size 222
168 bundle2-input: part header size: 22
168 bundle2-input: part header size: 22
169 bundle2-input: part type: "CHECK:BOOKMARKS"
169 bundle2-input: part type: "CHECK:BOOKMARKS"
170 bundle2-input: part id: "1"
170 bundle2-input: part id: "1"
171 bundle2-input: part parameters: 0
171 bundle2-input: part parameters: 0
172 bundle2-input: found a handler for part check:bookmarks
172 bundle2-input: found a handler for part check:bookmarks
173 bundle2-input-part: "check:bookmarks" supported
173 bundle2-input-part: "check:bookmarks" supported
174 bundle2-input: payload chunk size: 23
174 bundle2-input: payload chunk size: 23
175 bundle2-input: payload chunk size: 0
175 bundle2-input: payload chunk size: 0
176 bundle2-input-part: total payload size 23
176 bundle2-input-part: total payload size 23
177 bundle2-input: part header size: 19
177 bundle2-input: part header size: 19
178 bundle2-input: part type: "CHECK:PHASES"
178 bundle2-input: part type: "CHECK:PHASES"
179 bundle2-input: part id: "2"
179 bundle2-input: part id: "2"
180 bundle2-input: part parameters: 0
180 bundle2-input: part parameters: 0
181 bundle2-input: found a handler for part check:phases
181 bundle2-input: found a handler for part check:phases
182 bundle2-input-part: "check:phases" supported
182 bundle2-input-part: "check:phases" supported
183 bundle2-input: payload chunk size: 24
183 bundle2-input: payload chunk size: 24
184 bundle2-input: payload chunk size: 0
184 bundle2-input: payload chunk size: 0
185 bundle2-input-part: total payload size 24
185 bundle2-input-part: total payload size 24
186 bundle2-input: part header size: 90
186 bundle2-input: part header size: 90
187 bundle2-input: part type: "PUSHKEY"
187 bundle2-input: part type: "PUSHKEY"
188 bundle2-input: part id: "3"
188 bundle2-input: part id: "3"
189 bundle2-input: part parameters: 4
189 bundle2-input: part parameters: 4
190 bundle2-input: found a handler for part pushkey
190 bundle2-input: found a handler for part pushkey
191 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
191 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
192 pushing key for "bookmarks:W"
192 pushing key for "bookmarks:W"
193 bundle2-input: payload chunk size: 0
193 bundle2-input: payload chunk size: 0
194 bundle2-input: part header size: 0
194 bundle2-input: part header size: 0
195 bundle2-input: end of bundle2 stream
195 bundle2-input: end of bundle2 stream
196 bundle2-input-bundle: 3 parts total
196 bundle2-input-bundle: 3 parts total
197 running hook txnclose-bookmark.test: sh $TESTTMP/hook.sh
197 running hook txnclose-bookmark.test: sh $TESTTMP/hook.sh
198 test-hook-bookmark: W: 0000000000000000000000000000000000000000 ->
198 test-hook-bookmark: W: 0000000000000000000000000000000000000000 ->
199 bundle2-output-bundle: "HG20", 1 parts total
199 bundle2-output-bundle: "HG20", 1 parts total
200 bundle2-output: start emission of HG20 stream
200 bundle2-output: start emission of HG20 stream
201 bundle2-output: bundle parameter:
201 bundle2-output: bundle parameter:
202 bundle2-output: start of parts
202 bundle2-output: start of parts
203 bundle2-output: bundle part: "reply:pushkey"
203 bundle2-output: bundle part: "reply:pushkey"
204 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
204 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
205 bundle2-output: part 0: "REPLY:PUSHKEY"
205 bundle2-output: part 0: "REPLY:PUSHKEY"
206 bundle2-output: header chunk size: 43
206 bundle2-output: header chunk size: 43
207 bundle2-output: closing payload chunk
207 bundle2-output: closing payload chunk
208 bundle2-output: end of bundle
208 bundle2-output: end of bundle
209 bundle2-input: start processing of HG20 stream
209 bundle2-input: start processing of HG20 stream
210 bundle2-input: reading bundle2 stream parameters
210 bundle2-input: reading bundle2 stream parameters
211 bundle2-input-bundle: no-transaction
211 bundle2-input-bundle: no-transaction
212 bundle2-input: start extraction of bundle2 parts
212 bundle2-input: start extraction of bundle2 parts
213 bundle2-input: part header size: 43
213 bundle2-input: part header size: 43
214 bundle2-input: part type: "REPLY:PUSHKEY"
214 bundle2-input: part type: "REPLY:PUSHKEY"
215 bundle2-input: part id: "0"
215 bundle2-input: part id: "0"
216 bundle2-input: part parameters: 2
216 bundle2-input: part parameters: 2
217 bundle2-input: found a handler for part reply:pushkey
217 bundle2-input: found a handler for part reply:pushkey
218 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
218 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
219 bundle2-input: payload chunk size: 0
219 bundle2-input: payload chunk size: 0
220 bundle2-input: part header size: 0
220 bundle2-input: part header size: 0
221 bundle2-input: end of bundle2 stream
221 bundle2-input: end of bundle2 stream
222 bundle2-input-bundle: 0 parts total
222 bundle2-input-bundle: 0 parts total
223 deleting remote bookmark W
223 deleting remote bookmark W
224 listing keys for "phases"
224 listing keys for "phases"
225 [1]
225 [1]
226
226
227 #endif
227 #endif
228 #if b2-binary
228 #if b2-binary
229
229
230 $ hg push -B W ../a --config "$TESTHOOK" --debug --config devel.bundle2.debug=yes
230 $ hg push -B W ../a --config "$TESTHOOK" --debug --config devel.bundle2.debug=yes
231 pushing to ../a
231 pushing to ../a
232 query 1; heads
232 query 1; heads
233 searching for changes
233 searching for changes
234 all remote heads known locally
234 all remote heads known locally
235 listing keys for "phases"
235 listing keys for "phases"
236 checking for updated bookmarks
236 checking for updated bookmarks
237 listing keys for "bookmarks"
237 listing keys for "bookmarks"
238 no changes found
238 no changes found
239 bundle2-output-bundle: "HG20", 4 parts total
239 bundle2-output-bundle: "HG20", 4 parts total
240 bundle2-output: start emission of HG20 stream
240 bundle2-output: start emission of HG20 stream
241 bundle2-output: bundle parameter:
241 bundle2-output: bundle parameter:
242 bundle2-output: start of parts
242 bundle2-output: start of parts
243 bundle2-output: bundle part: "replycaps"
243 bundle2-output: bundle part: "replycaps"
244 bundle2-output-part: "replycaps" 222 bytes payload
244 bundle2-output-part: "replycaps" 222 bytes payload
245 bundle2-output: part 0: "REPLYCAPS"
245 bundle2-output: part 0: "REPLYCAPS"
246 bundle2-output: header chunk size: 16
246 bundle2-output: header chunk size: 16
247 bundle2-output: payload chunk size: 222
247 bundle2-output: payload chunk size: 222
248 bundle2-output: closing payload chunk
248 bundle2-output: closing payload chunk
249 bundle2-output: bundle part: "check:bookmarks"
249 bundle2-output: bundle part: "check:bookmarks"
250 bundle2-output-part: "check:bookmarks" 23 bytes payload
250 bundle2-output-part: "check:bookmarks" 23 bytes payload
251 bundle2-output: part 1: "CHECK:BOOKMARKS"
251 bundle2-output: part 1: "CHECK:BOOKMARKS"
252 bundle2-output: header chunk size: 22
252 bundle2-output: header chunk size: 22
253 bundle2-output: payload chunk size: 23
253 bundle2-output: payload chunk size: 23
254 bundle2-output: closing payload chunk
254 bundle2-output: closing payload chunk
255 bundle2-output: bundle part: "check:phases"
255 bundle2-output: bundle part: "check:phases"
256 bundle2-output-part: "check:phases" 24 bytes payload
256 bundle2-output-part: "check:phases" 24 bytes payload
257 bundle2-output: part 2: "CHECK:PHASES"
257 bundle2-output: part 2: "CHECK:PHASES"
258 bundle2-output: header chunk size: 19
258 bundle2-output: header chunk size: 19
259 bundle2-output: payload chunk size: 24
259 bundle2-output: payload chunk size: 24
260 bundle2-output: closing payload chunk
260 bundle2-output: closing payload chunk
261 bundle2-output: bundle part: "bookmarks"
261 bundle2-output: bundle part: "bookmarks"
262 bundle2-output-part: "bookmarks" 23 bytes payload
262 bundle2-output-part: "bookmarks" 23 bytes payload
263 bundle2-output: part 3: "BOOKMARKS"
263 bundle2-output: part 3: "BOOKMARKS"
264 bundle2-output: header chunk size: 16
264 bundle2-output: header chunk size: 16
265 bundle2-output: payload chunk size: 23
265 bundle2-output: payload chunk size: 23
266 bundle2-output: closing payload chunk
266 bundle2-output: closing payload chunk
267 bundle2-output: end of bundle
267 bundle2-output: end of bundle
268 bundle2-input: start processing of HG20 stream
268 bundle2-input: start processing of HG20 stream
269 bundle2-input: reading bundle2 stream parameters
269 bundle2-input: reading bundle2 stream parameters
270 bundle2-input-bundle: with-transaction
270 bundle2-input-bundle: with-transaction
271 bundle2-input: start extraction of bundle2 parts
271 bundle2-input: start extraction of bundle2 parts
272 bundle2-input: part header size: 16
272 bundle2-input: part header size: 16
273 bundle2-input: part type: "REPLYCAPS"
273 bundle2-input: part type: "REPLYCAPS"
274 bundle2-input: part id: "0"
274 bundle2-input: part id: "0"
275 bundle2-input: part parameters: 0
275 bundle2-input: part parameters: 0
276 bundle2-input: found a handler for part replycaps
276 bundle2-input: found a handler for part replycaps
277 bundle2-input-part: "replycaps" supported
277 bundle2-input-part: "replycaps" supported
278 bundle2-input: payload chunk size: 222
278 bundle2-input: payload chunk size: 222
279 bundle2-input: payload chunk size: 0
279 bundle2-input: payload chunk size: 0
280 bundle2-input-part: total payload size 222
280 bundle2-input-part: total payload size 222
281 bundle2-input: part header size: 22
281 bundle2-input: part header size: 22
282 bundle2-input: part type: "CHECK:BOOKMARKS"
282 bundle2-input: part type: "CHECK:BOOKMARKS"
283 bundle2-input: part id: "1"
283 bundle2-input: part id: "1"
284 bundle2-input: part parameters: 0
284 bundle2-input: part parameters: 0
285 bundle2-input: found a handler for part check:bookmarks
285 bundle2-input: found a handler for part check:bookmarks
286 bundle2-input-part: "check:bookmarks" supported
286 bundle2-input-part: "check:bookmarks" supported
287 bundle2-input: payload chunk size: 23
287 bundle2-input: payload chunk size: 23
288 bundle2-input: payload chunk size: 0
288 bundle2-input: payload chunk size: 0
289 bundle2-input-part: total payload size 23
289 bundle2-input-part: total payload size 23
290 bundle2-input: part header size: 19
290 bundle2-input: part header size: 19
291 bundle2-input: part type: "CHECK:PHASES"
291 bundle2-input: part type: "CHECK:PHASES"
292 bundle2-input: part id: "2"
292 bundle2-input: part id: "2"
293 bundle2-input: part parameters: 0
293 bundle2-input: part parameters: 0
294 bundle2-input: found a handler for part check:phases
294 bundle2-input: found a handler for part check:phases
295 bundle2-input-part: "check:phases" supported
295 bundle2-input-part: "check:phases" supported
296 bundle2-input: payload chunk size: 24
296 bundle2-input: payload chunk size: 24
297 bundle2-input: payload chunk size: 0
297 bundle2-input: payload chunk size: 0
298 bundle2-input-part: total payload size 24
298 bundle2-input-part: total payload size 24
299 bundle2-input: part header size: 16
299 bundle2-input: part header size: 16
300 bundle2-input: part type: "BOOKMARKS"
300 bundle2-input: part type: "BOOKMARKS"
301 bundle2-input: part id: "3"
301 bundle2-input: part id: "3"
302 bundle2-input: part parameters: 0
302 bundle2-input: part parameters: 0
303 bundle2-input: found a handler for part bookmarks
303 bundle2-input: found a handler for part bookmarks
304 bundle2-input-part: "bookmarks" supported
304 bundle2-input-part: "bookmarks" supported
305 bundle2-input: payload chunk size: 23
305 bundle2-input: payload chunk size: 23
306 bundle2-input: payload chunk size: 0
306 bundle2-input: payload chunk size: 0
307 bundle2-input-part: total payload size 23
307 bundle2-input-part: total payload size 23
308 bundle2-input: part header size: 0
308 bundle2-input: part header size: 0
309 bundle2-input: end of bundle2 stream
309 bundle2-input: end of bundle2 stream
310 bundle2-input-bundle: 3 parts total
310 bundle2-input-bundle: 3 parts total
311 running hook txnclose-bookmark.test: sh $TESTTMP/hook.sh
311 running hook txnclose-bookmark.test: sh $TESTTMP/hook.sh
312 test-hook-bookmark: W: 0000000000000000000000000000000000000000 ->
312 test-hook-bookmark: W: 0000000000000000000000000000000000000000 ->
313 bundle2-output-bundle: "HG20", 0 parts total
313 bundle2-output-bundle: "HG20", 0 parts total
314 bundle2-output: start emission of HG20 stream
314 bundle2-output: start emission of HG20 stream
315 bundle2-output: bundle parameter:
315 bundle2-output: bundle parameter:
316 bundle2-output: start of parts
316 bundle2-output: start of parts
317 bundle2-output: end of bundle
317 bundle2-output: end of bundle
318 bundle2-input: start processing of HG20 stream
318 bundle2-input: start processing of HG20 stream
319 bundle2-input: reading bundle2 stream parameters
319 bundle2-input: reading bundle2 stream parameters
320 bundle2-input-bundle: no-transaction
320 bundle2-input-bundle: no-transaction
321 bundle2-input: start extraction of bundle2 parts
321 bundle2-input: start extraction of bundle2 parts
322 bundle2-input: part header size: 0
322 bundle2-input: part header size: 0
323 bundle2-input: end of bundle2 stream
323 bundle2-input: end of bundle2 stream
324 bundle2-input-bundle: 0 parts total
324 bundle2-input-bundle: 0 parts total
325 deleting remote bookmark W
325 deleting remote bookmark W
326 listing keys for "phases"
326 listing keys for "phases"
327 [1]
327 [1]
328
328
329 #endif
329 #endif
330
330
331 export the active bookmark
331 export the active bookmark
332
332
333 $ hg bookmark V
333 $ hg bookmark V
334 $ hg push -B . ../a
334 $ hg push -B . ../a
335 pushing to ../a
335 pushing to ../a
336 searching for changes
336 searching for changes
337 no changes found
337 no changes found
338 exporting bookmark V
338 exporting bookmark V
339 [1]
339 [1]
340
340
341 exporting the active bookmark with 'push -B .'
341 exporting the active bookmark with 'push -B .'
342 demand that one of the bookmarks is activated
342 demand that one of the bookmarks is activated
343
343
344 $ hg update -r default
344 $ hg update -r default
345 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
345 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
346 (leaving bookmark V)
346 (leaving bookmark V)
347 $ hg push -B . ../a
347 $ hg push -B . ../a
348 abort: no active bookmark!
348 abort: no active bookmark!
349 [255]
349 [255]
350 $ hg update -r V
350 $ hg update -r V
351 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
351 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
352 (activating bookmark V)
352 (activating bookmark V)
353
353
354 delete the bookmark
354 delete the bookmark
355
355
356 $ hg book -d V
356 $ hg book -d V
357 $ hg push -B V ../a
357 $ hg push -B V ../a
358 pushing to ../a
358 pushing to ../a
359 searching for changes
359 searching for changes
360 no changes found
360 no changes found
361 deleting remote bookmark V
361 deleting remote bookmark V
362 [1]
362 [1]
363 $ hg up foobar
363 $ hg up foobar
364 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
364 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
365 (activating bookmark foobar)
365 (activating bookmark foobar)
366
366
367 push/pull name that doesn't exist
367 push/pull name that doesn't exist
368
368
369 $ hg push -B badname ../a
369 $ hg push -B badname ../a
370 pushing to ../a
370 pushing to ../a
371 searching for changes
371 searching for changes
372 bookmark badname does not exist on the local or remote repository!
372 bookmark badname does not exist on the local or remote repository!
373 no changes found
373 no changes found
374 [2]
374 [2]
375 $ hg pull -B anotherbadname ../a
375 $ hg pull -B anotherbadname ../a
376 pulling from ../a
376 pulling from ../a
377 abort: remote bookmark anotherbadname not found!
377 abort: remote bookmark anotherbadname not found!
378 [255]
378 [255]
379
379
380 divergent bookmarks
380 divergent bookmarks
381
381
382 $ cd ../a
382 $ cd ../a
383 $ echo c1 > f1
383 $ echo c1 > f1
384 $ hg ci -Am1
384 $ hg ci -Am1
385 adding f1
385 adding f1
386 $ hg book -f @
386 $ hg book -f @
387 $ hg book -f X
387 $ hg book -f X
388 $ hg book
388 $ hg book
389 @ 1:0d2164f0ce0d
389 @ 1:0d2164f0ce0d
390 * X 1:0d2164f0ce0d
390 * X 1:0d2164f0ce0d
391 Y 0:4e3505fd9583
391 Y 0:4e3505fd9583
392 Z 1:0d2164f0ce0d
392 Z 1:0d2164f0ce0d
393
393
394 $ cd ../b
394 $ cd ../b
395 $ hg up
395 $ hg up
396 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
396 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
397 updating bookmark foobar
397 updating bookmark foobar
398 $ echo c2 > f2
398 $ echo c2 > f2
399 $ hg ci -Am2
399 $ hg ci -Am2
400 adding f2
400 adding f2
401 $ hg book -if @
401 $ hg book -if @
402 $ hg book -if X
402 $ hg book -if X
403 $ hg book
403 $ hg book
404 @ 1:9b140be10808
404 @ 1:9b140be10808
405 X 1:9b140be10808
405 X 1:9b140be10808
406 Y 0:4e3505fd9583
406 Y 0:4e3505fd9583
407 Z 0:4e3505fd9583
407 Z 0:4e3505fd9583
408 foo -1:000000000000
408 foo -1:000000000000
409 * foobar 1:9b140be10808
409 * foobar 1:9b140be10808
410
410
411 $ hg pull --config paths.foo=../a foo --config "$TESTHOOK"
411 $ hg pull --config paths.foo=../a foo --config "$TESTHOOK"
412 pulling from $TESTTMP/a
412 pulling from $TESTTMP/a
413 searching for changes
413 searching for changes
414 adding changesets
414 adding changesets
415 adding manifests
415 adding manifests
416 adding file changes
416 adding file changes
417 added 1 changesets with 1 changes to 1 files (+1 heads)
417 added 1 changesets with 1 changes to 1 files (+1 heads)
418 divergent bookmark @ stored as @foo
418 divergent bookmark @ stored as @foo
419 divergent bookmark X stored as X@foo
419 divergent bookmark X stored as X@foo
420 updating bookmark Z
420 updating bookmark Z
421 new changesets 0d2164f0ce0d (1 drafts)
421 new changesets 0d2164f0ce0d (1 drafts)
422 test-hook-bookmark: @foo: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
422 test-hook-bookmark: @foo: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
423 test-hook-bookmark: X@foo: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
423 test-hook-bookmark: X@foo: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
424 test-hook-bookmark: Z: 4e3505fd95835d721066b76e75dbb8cc554d7f77 -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
424 test-hook-bookmark: Z: 4e3505fd95835d721066b76e75dbb8cc554d7f77 -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
425 (run 'hg heads' to see heads, 'hg merge' to merge)
425 (run 'hg heads' to see heads, 'hg merge' to merge)
426 $ hg book
426 $ hg book
427 @ 1:9b140be10808
427 @ 1:9b140be10808
428 @foo 2:0d2164f0ce0d
428 @foo 2:0d2164f0ce0d
429 X 1:9b140be10808
429 X 1:9b140be10808
430 X@foo 2:0d2164f0ce0d
430 X@foo 2:0d2164f0ce0d
431 Y 0:4e3505fd9583
431 Y 0:4e3505fd9583
432 Z 2:0d2164f0ce0d
432 Z 2:0d2164f0ce0d
433 foo -1:000000000000
433 foo -1:000000000000
434 * foobar 1:9b140be10808
434 * foobar 1:9b140be10808
435
435
436 (test that too many divergence of bookmark)
436 (test that too many divergence of bookmark)
437
437
438 $ "$PYTHON" $TESTDIR/seq.py 1 100 | while read i; do hg bookmarks -r 000000000000 "X@${i}"; done
438 $ "$PYTHON" $TESTDIR/seq.py 1 100 | while read i; do hg bookmarks -r 000000000000 "X@${i}"; done
439 $ hg pull ../a
439 $ hg pull ../a
440 pulling from ../a
440 pulling from ../a
441 searching for changes
441 searching for changes
442 no changes found
442 no changes found
443 warning: failed to assign numbered name to divergent bookmark X
443 warning: failed to assign numbered name to divergent bookmark X
444 divergent bookmark @ stored as @1
444 divergent bookmark @ stored as @1
445 $ hg bookmarks | grep '^ X' | grep -v ':000000000000'
445 $ hg bookmarks | grep '^ X' | grep -v ':000000000000'
446 X 1:9b140be10808
446 X 1:9b140be10808
447 X@foo 2:0d2164f0ce0d
447 X@foo 2:0d2164f0ce0d
448
448
449 (test that remotely diverged bookmarks are reused if they aren't changed)
449 (test that remotely diverged bookmarks are reused if they aren't changed)
450
450
451 $ hg bookmarks | grep '^ @'
451 $ hg bookmarks | grep '^ @'
452 @ 1:9b140be10808
452 @ 1:9b140be10808
453 @1 2:0d2164f0ce0d
453 @1 2:0d2164f0ce0d
454 @foo 2:0d2164f0ce0d
454 @foo 2:0d2164f0ce0d
455 $ hg pull ../a
455 $ hg pull ../a
456 pulling from ../a
456 pulling from ../a
457 searching for changes
457 searching for changes
458 no changes found
458 no changes found
459 warning: failed to assign numbered name to divergent bookmark X
459 warning: failed to assign numbered name to divergent bookmark X
460 divergent bookmark @ stored as @1
460 divergent bookmark @ stored as @1
461 $ hg bookmarks | grep '^ @'
461 $ hg bookmarks | grep '^ @'
462 @ 1:9b140be10808
462 @ 1:9b140be10808
463 @1 2:0d2164f0ce0d
463 @1 2:0d2164f0ce0d
464 @foo 2:0d2164f0ce0d
464 @foo 2:0d2164f0ce0d
465
465
466 $ "$PYTHON" $TESTDIR/seq.py 1 100 | while read i; do hg bookmarks -d "X@${i}"; done
466 $ "$PYTHON" $TESTDIR/seq.py 1 100 | while read i; do hg bookmarks -d "X@${i}"; done
467 $ hg bookmarks -d "@1"
467 $ hg bookmarks -d "@1"
468
468
469 $ hg push -f ../a
469 $ hg push -f ../a
470 pushing to ../a
470 pushing to ../a
471 searching for changes
471 searching for changes
472 adding changesets
472 adding changesets
473 adding manifests
473 adding manifests
474 adding file changes
474 adding file changes
475 added 1 changesets with 1 changes to 1 files (+1 heads)
475 added 1 changesets with 1 changes to 1 files (+1 heads)
476 $ hg -R ../a book
476 $ hg -R ../a book
477 @ 1:0d2164f0ce0d
477 @ 1:0d2164f0ce0d
478 * X 1:0d2164f0ce0d
478 * X 1:0d2164f0ce0d
479 Y 0:4e3505fd9583
479 Y 0:4e3505fd9583
480 Z 1:0d2164f0ce0d
480 Z 1:0d2164f0ce0d
481
481
482 explicit pull should overwrite the local version (issue4439)
482 explicit pull should overwrite the local version (issue4439)
483
483
484 $ hg update -r X
484 $ hg update -r X
485 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
485 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
486 (activating bookmark X)
486 (activating bookmark X)
487 $ hg pull --config paths.foo=../a foo -B . --config "$TESTHOOK"
487 $ hg pull --config paths.foo=../a foo -B . --config "$TESTHOOK"
488 pulling from $TESTTMP/a
488 pulling from $TESTTMP/a
489 no changes found
489 no changes found
490 divergent bookmark @ stored as @foo
490 divergent bookmark @ stored as @foo
491 importing bookmark X
491 importing bookmark X
492 test-hook-bookmark: @foo: 0d2164f0ce0d8f1d6f94351eba04b794909be66c -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
492 test-hook-bookmark: @foo: 0d2164f0ce0d8f1d6f94351eba04b794909be66c -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
493 test-hook-bookmark: X: 9b140be1080824d768c5a4691a564088eede71f9 -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
493 test-hook-bookmark: X: 9b140be1080824d768c5a4691a564088eede71f9 -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
494
494
495 reinstall state for further testing:
495 reinstall state for further testing:
496
496
497 $ hg book -fr 9b140be10808 X
497 $ hg book -fr 9b140be10808 X
498
498
499 revsets should not ignore divergent bookmarks
499 revsets should not ignore divergent bookmarks
500
500
501 $ hg bookmark -fr 1 Z
501 $ hg bookmark -fr 1 Z
502 $ hg log -r 'bookmark()' --template '{rev}:{node|short} {bookmarks}\n'
502 $ hg log -r 'bookmark()' --template '{rev}:{node|short} {bookmarks}\n'
503 0:4e3505fd9583 Y
503 0:4e3505fd9583 Y
504 1:9b140be10808 @ X Z foobar
504 1:9b140be10808 @ X Z foobar
505 2:0d2164f0ce0d @foo X@foo
505 2:0d2164f0ce0d @foo X@foo
506 $ hg log -r 'bookmark("X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
506 $ hg log -r 'bookmark("X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
507 2:0d2164f0ce0d @foo X@foo
507 2:0d2164f0ce0d @foo X@foo
508 $ hg log -r 'bookmark("re:X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
508 $ hg log -r 'bookmark("re:X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
509 2:0d2164f0ce0d @foo X@foo
509 2:0d2164f0ce0d @foo X@foo
510
510
511 update a remote bookmark from a non-head to a head
511 update a remote bookmark from a non-head to a head
512
512
513 $ hg up -q Y
513 $ hg up -q Y
514 $ echo c3 > f2
514 $ echo c3 > f2
515 $ hg ci -Am3
515 $ hg ci -Am3
516 adding f2
516 adding f2
517 created new head
517 created new head
518 $ hg push ../a --config "$TESTHOOK"
518 $ hg push ../a --config "$TESTHOOK"
519 pushing to ../a
519 pushing to ../a
520 searching for changes
520 searching for changes
521 adding changesets
521 adding changesets
522 adding manifests
522 adding manifests
523 adding file changes
523 adding file changes
524 added 1 changesets with 1 changes to 1 files (+1 heads)
524 added 1 changesets with 1 changes to 1 files (+1 heads)
525 test-hook-bookmark: Y: 4e3505fd95835d721066b76e75dbb8cc554d7f77 -> f6fc62dde3c0771e29704af56ba4d8af77abcc2f
525 test-hook-bookmark: Y: 4e3505fd95835d721066b76e75dbb8cc554d7f77 -> f6fc62dde3c0771e29704af56ba4d8af77abcc2f
526 updating bookmark Y
526 updating bookmark Y
527 $ hg -R ../a book
527 $ hg -R ../a book
528 @ 1:0d2164f0ce0d
528 @ 1:0d2164f0ce0d
529 * X 1:0d2164f0ce0d
529 * X 1:0d2164f0ce0d
530 Y 3:f6fc62dde3c0
530 Y 3:f6fc62dde3c0
531 Z 1:0d2164f0ce0d
531 Z 1:0d2164f0ce0d
532
532
533 update a bookmark in the middle of a client pulling changes
533 update a bookmark in the middle of a client pulling changes
534
534
535 $ cd ..
535 $ cd ..
536 $ hg clone -q a pull-race
536 $ hg clone -q a pull-race
537
537
538 We want to use http because it is stateless and therefore more susceptible to
538 We want to use http because it is stateless and therefore more susceptible to
539 race conditions
539 race conditions
540
540
541 $ hg serve -R pull-race -p $HGPORT -d --pid-file=pull-race.pid -E main-error.log
541 $ hg serve -R pull-race -p $HGPORT -d --pid-file=pull-race.pid -E main-error.log
542 $ cat pull-race.pid >> $DAEMON_PIDS
542 $ cat pull-race.pid >> $DAEMON_PIDS
543
543
544 $ cat <<EOF > $TESTTMP/out_makecommit.sh
544 $ cat <<EOF > $TESTTMP/out_makecommit.sh
545 > #!/bin/sh
545 > #!/bin/sh
546 > hg ci -Am5
546 > hg ci -Am5
547 > echo committed in pull-race
547 > echo committed in pull-race
548 > EOF
548 > EOF
549
549
550 $ hg clone -q http://localhost:$HGPORT/ pull-race2 --config "$TESTHOOK"
550 $ hg clone -q http://localhost:$HGPORT/ pull-race2 --config "$TESTHOOK"
551 test-hook-bookmark: @: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
551 test-hook-bookmark: @: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
552 test-hook-bookmark: X: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
552 test-hook-bookmark: X: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
553 test-hook-bookmark: Y: -> f6fc62dde3c0771e29704af56ba4d8af77abcc2f
553 test-hook-bookmark: Y: -> f6fc62dde3c0771e29704af56ba4d8af77abcc2f
554 test-hook-bookmark: Z: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
554 test-hook-bookmark: Z: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
555 $ cd pull-race
555 $ cd pull-race
556 $ hg up -q Y
556 $ hg up -q Y
557 $ echo c4 > f2
557 $ echo c4 > f2
558 $ hg ci -Am4
558 $ hg ci -Am4
559 $ echo c5 > f3
559 $ echo c5 > f3
560 $ cat <<EOF > .hg/hgrc
560 $ cat <<EOF > .hg/hgrc
561 > [hooks]
561 > [hooks]
562 > outgoing.makecommit = sh $TESTTMP/out_makecommit.sh
562 > outgoing.makecommit = sh $TESTTMP/out_makecommit.sh
563 > EOF
563 > EOF
564
564
565 (new config needs a server restart)
565 (new config needs a server restart)
566
566
567 $ cd ..
567 $ cd ..
568 $ killdaemons.py
568 $ killdaemons.py
569 $ hg serve -R pull-race -p $HGPORT -d --pid-file=pull-race.pid -E main-error.log
569 $ hg serve -R pull-race -p $HGPORT -d --pid-file=pull-race.pid -E main-error.log
570 $ cat pull-race.pid >> $DAEMON_PIDS
570 $ cat pull-race.pid >> $DAEMON_PIDS
571 $ cd pull-race2
571 $ cd pull-race2
572 $ hg -R $TESTTMP/pull-race book
572 $ hg -R $TESTTMP/pull-race book
573 @ 1:0d2164f0ce0d
573 @ 1:0d2164f0ce0d
574 X 1:0d2164f0ce0d
574 X 1:0d2164f0ce0d
575 * Y 4:b0a5eff05604
575 * Y 4:b0a5eff05604
576 Z 1:0d2164f0ce0d
576 Z 1:0d2164f0ce0d
577 $ hg pull
577 $ hg pull
578 pulling from http://localhost:$HGPORT/
578 pulling from http://localhost:$HGPORT/
579 searching for changes
579 searching for changes
580 adding changesets
580 adding changesets
581 adding manifests
581 adding manifests
582 adding file changes
582 adding file changes
583 added 1 changesets with 1 changes to 1 files
583 added 1 changesets with 1 changes to 1 files
584 updating bookmark Y
584 updating bookmark Y
585 new changesets b0a5eff05604 (1 drafts)
585 new changesets b0a5eff05604 (1 drafts)
586 (run 'hg update' to get a working copy)
586 (run 'hg update' to get a working copy)
587 $ hg book
587 $ hg book
588 * @ 1:0d2164f0ce0d
588 * @ 1:0d2164f0ce0d
589 X 1:0d2164f0ce0d
589 X 1:0d2164f0ce0d
590 Y 4:b0a5eff05604
590 Y 4:b0a5eff05604
591 Z 1:0d2164f0ce0d
591 Z 1:0d2164f0ce0d
592
592
593 Update a bookmark right after the initial lookup -B (issue4689)
593 Update a bookmark right after the initial lookup -B (issue4689)
594
594
595 $ echo c6 > ../pull-race/f3 # to be committed during the race
595 $ echo c6 > ../pull-race/f3 # to be committed during the race
596 $ cat <<EOF > $TESTTMP/listkeys_makecommit.sh
596 $ cat <<EOF > $TESTTMP/listkeys_makecommit.sh
597 > #!/bin/sh
597 > #!/bin/sh
598 > if hg st | grep -q M; then
598 > if hg st | grep -q M; then
599 > hg commit -m race
599 > hg commit -m race
600 > echo committed in pull-race
600 > echo committed in pull-race
601 > else
601 > else
602 > exit 0
602 > exit 0
603 > fi
603 > fi
604 > EOF
604 > EOF
605 $ cat <<EOF > ../pull-race/.hg/hgrc
605 $ cat <<EOF > ../pull-race/.hg/hgrc
606 > [hooks]
606 > [hooks]
607 > # If anything to commit, commit it right after the first key listing used
607 > # If anything to commit, commit it right after the first key listing used
608 > # during lookup. This makes the commit appear before the actual getbundle
608 > # during lookup. This makes the commit appear before the actual getbundle
609 > # call.
609 > # call.
610 > listkeys.makecommit= sh $TESTTMP/listkeys_makecommit.sh
610 > listkeys.makecommit= sh $TESTTMP/listkeys_makecommit.sh
611 > EOF
611 > EOF
612 $ restart_server() {
612 $ restart_server() {
613 > "$TESTDIR/killdaemons.py" $DAEMON_PIDS
613 > "$TESTDIR/killdaemons.py" $DAEMON_PIDS
614 > hg serve -R ../pull-race -p $HGPORT -d --pid-file=../pull-race.pid -E main-error.log
614 > hg serve -R ../pull-race -p $HGPORT -d --pid-file=../pull-race.pid -E main-error.log
615 > cat ../pull-race.pid >> $DAEMON_PIDS
615 > cat ../pull-race.pid >> $DAEMON_PIDS
616 > }
616 > }
617 $ restart_server # new config need server restart
617 $ restart_server # new config need server restart
618 $ hg -R $TESTTMP/pull-race book
618 $ hg -R $TESTTMP/pull-race book
619 @ 1:0d2164f0ce0d
619 @ 1:0d2164f0ce0d
620 X 1:0d2164f0ce0d
620 X 1:0d2164f0ce0d
621 * Y 5:35d1ef0a8d1b
621 * Y 5:35d1ef0a8d1b
622 Z 1:0d2164f0ce0d
622 Z 1:0d2164f0ce0d
623 $ hg update -r Y
623 $ hg update -r Y
624 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
624 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
625 (activating bookmark Y)
625 (activating bookmark Y)
626 $ hg pull -B .
626 $ hg pull -B .
627 pulling from http://localhost:$HGPORT/
627 pulling from http://localhost:$HGPORT/
628 searching for changes
628 searching for changes
629 adding changesets
629 adding changesets
630 adding manifests
630 adding manifests
631 adding file changes
631 adding file changes
632 added 1 changesets with 1 changes to 1 files
632 added 1 changesets with 1 changes to 1 files
633 updating bookmark Y
633 updating bookmark Y
634 new changesets 35d1ef0a8d1b (1 drafts)
634 new changesets 35d1ef0a8d1b (1 drafts)
635 (run 'hg update' to get a working copy)
635 (run 'hg update' to get a working copy)
636 $ hg book
636 $ hg book
637 @ 1:0d2164f0ce0d
637 @ 1:0d2164f0ce0d
638 X 1:0d2164f0ce0d
638 X 1:0d2164f0ce0d
639 * Y 5:35d1ef0a8d1b
639 * Y 5:35d1ef0a8d1b
640 Z 1:0d2164f0ce0d
640 Z 1:0d2164f0ce0d
641
641
642 Update a bookmark right after the initial lookup -r (issue4700)
642 Update a bookmark right after the initial lookup -r (issue4700)
643
643
644 $ echo c7 > ../pull-race/f3 # to be committed during the race
644 $ echo c7 > ../pull-race/f3 # to be committed during the race
645 $ cat <<EOF > ../lookuphook.py
645 $ cat <<EOF > ../lookuphook.py
646 > """small extensions adding a hook after wireprotocol lookup to test race"""
646 > """small extensions adding a hook after wireprotocol lookup to test race"""
647 > import functools
647 > import functools
648 > from mercurial import wireprotov1server, wireprotov2server
648 > from mercurial import wireprotov1server, wireprotov2server
649 >
649 >
650 > def wrappedlookup(orig, repo, *args, **kwargs):
650 > def wrappedlookup(orig, repo, *args, **kwargs):
651 > ret = orig(repo, *args, **kwargs)
651 > ret = orig(repo, *args, **kwargs)
652 > repo.hook(b'lookup')
652 > repo.hook(b'lookup')
653 > return ret
653 > return ret
654 > for table in [wireprotov1server.commands, wireprotov2server.COMMANDS]:
654 > for table in [wireprotov1server.commands, wireprotov2server.COMMANDS]:
655 > table[b'lookup'].func = functools.partial(wrappedlookup, table[b'lookup'].func)
655 > table[b'lookup'].func = functools.partial(wrappedlookup, table[b'lookup'].func)
656 > EOF
656 > EOF
657 $ cat <<EOF > ../pull-race/.hg/hgrc
657 $ cat <<EOF > ../pull-race/.hg/hgrc
658 > [extensions]
658 > [extensions]
659 > lookuphook=$TESTTMP/lookuphook.py
659 > lookuphook=$TESTTMP/lookuphook.py
660 > [hooks]
660 > [hooks]
661 > lookup.makecommit= sh $TESTTMP/listkeys_makecommit.sh
661 > lookup.makecommit= sh $TESTTMP/listkeys_makecommit.sh
662 > EOF
662 > EOF
663 $ restart_server # new config need server restart
663 $ restart_server # new config need server restart
664 $ hg -R $TESTTMP/pull-race book
664 $ hg -R $TESTTMP/pull-race book
665 @ 1:0d2164f0ce0d
665 @ 1:0d2164f0ce0d
666 X 1:0d2164f0ce0d
666 X 1:0d2164f0ce0d
667 * Y 6:0d60821d2197
667 * Y 6:0d60821d2197
668 Z 1:0d2164f0ce0d
668 Z 1:0d2164f0ce0d
669 $ hg pull -r Y
669 $ hg pull -r Y
670 pulling from http://localhost:$HGPORT/
670 pulling from http://localhost:$HGPORT/
671 searching for changes
671 searching for changes
672 adding changesets
672 adding changesets
673 adding manifests
673 adding manifests
674 adding file changes
674 adding file changes
675 added 1 changesets with 1 changes to 1 files
675 added 1 changesets with 1 changes to 1 files
676 updating bookmark Y
676 new changesets 0d60821d2197 (1 drafts)
677 new changesets 0d60821d2197 (1 drafts)
677 (run 'hg update' to get a working copy)
678 (run 'hg update' to get a working copy)
678 $ hg book
679 $ hg book
679 @ 1:0d2164f0ce0d
680 @ 1:0d2164f0ce0d
680 X 1:0d2164f0ce0d
681 X 1:0d2164f0ce0d
681 * Y 5:35d1ef0a8d1b
682 * Y 6:0d60821d2197
682 Z 1:0d2164f0ce0d
683 Z 1:0d2164f0ce0d
683 $ hg -R $TESTTMP/pull-race book
684 $ hg -R $TESTTMP/pull-race book
684 @ 1:0d2164f0ce0d
685 @ 1:0d2164f0ce0d
685 X 1:0d2164f0ce0d
686 X 1:0d2164f0ce0d
686 * Y 7:714424d9e8b8
687 * Y 7:714424d9e8b8
687 Z 1:0d2164f0ce0d
688 Z 1:0d2164f0ce0d
688
689
689 (done with this section of the test)
690 (done with this section of the test)
690
691
691 $ killdaemons.py
692 $ killdaemons.py
692 $ cd ../b
693 $ cd ../b
693
694
694 diverging a remote bookmark fails
695 diverging a remote bookmark fails
695
696
696 $ hg up -q 4e3505fd9583
697 $ hg up -q 4e3505fd9583
697 $ echo c4 > f2
698 $ echo c4 > f2
698 $ hg ci -Am4
699 $ hg ci -Am4
699 adding f2
700 adding f2
700 created new head
701 created new head
701 $ echo c5 > f2
702 $ echo c5 > f2
702 $ hg ci -Am5
703 $ hg ci -Am5
703 $ hg log -G
704 $ hg log -G
704 @ 5:c922c0139ca0 5
705 @ 5:c922c0139ca0 5
705 |
706 |
706 o 4:4efff6d98829 4
707 o 4:4efff6d98829 4
707 |
708 |
708 | o 3:f6fc62dde3c0 3
709 | o 3:f6fc62dde3c0 3
709 |/
710 |/
710 | o 2:0d2164f0ce0d 1
711 | o 2:0d2164f0ce0d 1
711 |/
712 |/
712 | o 1:9b140be10808 2
713 | o 1:9b140be10808 2
713 |/
714 |/
714 o 0:4e3505fd9583 test
715 o 0:4e3505fd9583 test
715
716
716
717
717 $ hg book -f Y
718 $ hg book -f Y
718
719
719 $ cat <<EOF > ../a/.hg/hgrc
720 $ cat <<EOF > ../a/.hg/hgrc
720 > [web]
721 > [web]
721 > push_ssl = false
722 > push_ssl = false
722 > allow_push = *
723 > allow_push = *
723 > EOF
724 > EOF
724
725
725 $ hg serve -R ../a -p $HGPORT2 -d --pid-file=../hg2.pid
726 $ hg serve -R ../a -p $HGPORT2 -d --pid-file=../hg2.pid
726 $ cat ../hg2.pid >> $DAEMON_PIDS
727 $ cat ../hg2.pid >> $DAEMON_PIDS
727
728
728 $ hg push http://localhost:$HGPORT2/
729 $ hg push http://localhost:$HGPORT2/
729 pushing to http://localhost:$HGPORT2/
730 pushing to http://localhost:$HGPORT2/
730 searching for changes
731 searching for changes
731 abort: push creates new remote head c922c0139ca0 with bookmark 'Y'!
732 abort: push creates new remote head c922c0139ca0 with bookmark 'Y'!
732 (merge or see 'hg help push' for details about pushing new heads)
733 (merge or see 'hg help push' for details about pushing new heads)
733 [255]
734 [255]
734 $ hg -R ../a book
735 $ hg -R ../a book
735 @ 1:0d2164f0ce0d
736 @ 1:0d2164f0ce0d
736 * X 1:0d2164f0ce0d
737 * X 1:0d2164f0ce0d
737 Y 3:f6fc62dde3c0
738 Y 3:f6fc62dde3c0
738 Z 1:0d2164f0ce0d
739 Z 1:0d2164f0ce0d
739
740
740
741
741 Unrelated marker does not alter the decision
742 Unrelated marker does not alter the decision
742
743
743 $ hg debugobsolete aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
744 $ hg debugobsolete aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
744 $ hg push http://localhost:$HGPORT2/
745 $ hg push http://localhost:$HGPORT2/
745 pushing to http://localhost:$HGPORT2/
746 pushing to http://localhost:$HGPORT2/
746 searching for changes
747 searching for changes
747 abort: push creates new remote head c922c0139ca0 with bookmark 'Y'!
748 abort: push creates new remote head c922c0139ca0 with bookmark 'Y'!
748 (merge or see 'hg help push' for details about pushing new heads)
749 (merge or see 'hg help push' for details about pushing new heads)
749 [255]
750 [255]
750 $ hg -R ../a book
751 $ hg -R ../a book
751 @ 1:0d2164f0ce0d
752 @ 1:0d2164f0ce0d
752 * X 1:0d2164f0ce0d
753 * X 1:0d2164f0ce0d
753 Y 3:f6fc62dde3c0
754 Y 3:f6fc62dde3c0
754 Z 1:0d2164f0ce0d
755 Z 1:0d2164f0ce0d
755
756
756 Update to a successor works
757 Update to a successor works
757
758
758 $ hg id --debug -r 3
759 $ hg id --debug -r 3
759 f6fc62dde3c0771e29704af56ba4d8af77abcc2f
760 f6fc62dde3c0771e29704af56ba4d8af77abcc2f
760 $ hg id --debug -r 4
761 $ hg id --debug -r 4
761 4efff6d98829d9c824c621afd6e3f01865f5439f
762 4efff6d98829d9c824c621afd6e3f01865f5439f
762 $ hg id --debug -r 5
763 $ hg id --debug -r 5
763 c922c0139ca03858f655e4a2af4dd02796a63969 tip Y
764 c922c0139ca03858f655e4a2af4dd02796a63969 tip Y
764 $ hg debugobsolete f6fc62dde3c0771e29704af56ba4d8af77abcc2f cccccccccccccccccccccccccccccccccccccccc
765 $ hg debugobsolete f6fc62dde3c0771e29704af56ba4d8af77abcc2f cccccccccccccccccccccccccccccccccccccccc
765 obsoleted 1 changesets
766 obsoleted 1 changesets
766 $ hg debugobsolete cccccccccccccccccccccccccccccccccccccccc 4efff6d98829d9c824c621afd6e3f01865f5439f
767 $ hg debugobsolete cccccccccccccccccccccccccccccccccccccccc 4efff6d98829d9c824c621afd6e3f01865f5439f
767 $ hg push http://localhost:$HGPORT2/
768 $ hg push http://localhost:$HGPORT2/
768 pushing to http://localhost:$HGPORT2/
769 pushing to http://localhost:$HGPORT2/
769 searching for changes
770 searching for changes
770 remote: adding changesets
771 remote: adding changesets
771 remote: adding manifests
772 remote: adding manifests
772 remote: adding file changes
773 remote: adding file changes
773 remote: added 2 changesets with 2 changes to 1 files (+1 heads)
774 remote: added 2 changesets with 2 changes to 1 files (+1 heads)
774 remote: 2 new obsolescence markers
775 remote: 2 new obsolescence markers
775 remote: obsoleted 1 changesets
776 remote: obsoleted 1 changesets
776 updating bookmark Y
777 updating bookmark Y
777 $ hg -R ../a book
778 $ hg -R ../a book
778 @ 1:0d2164f0ce0d
779 @ 1:0d2164f0ce0d
779 * X 1:0d2164f0ce0d
780 * X 1:0d2164f0ce0d
780 Y 5:c922c0139ca0
781 Y 5:c922c0139ca0
781 Z 1:0d2164f0ce0d
782 Z 1:0d2164f0ce0d
782
783
783 hgweb
784 hgweb
784
785
785 $ cat <<EOF > .hg/hgrc
786 $ cat <<EOF > .hg/hgrc
786 > [web]
787 > [web]
787 > push_ssl = false
788 > push_ssl = false
788 > allow_push = *
789 > allow_push = *
789 > EOF
790 > EOF
790
791
791 $ hg serve -p $HGPORT -d --pid-file=../hg.pid -E errors.log
792 $ hg serve -p $HGPORT -d --pid-file=../hg.pid -E errors.log
792 $ cat ../hg.pid >> $DAEMON_PIDS
793 $ cat ../hg.pid >> $DAEMON_PIDS
793 $ cd ../a
794 $ cd ../a
794
795
795 $ hg debugpushkey http://localhost:$HGPORT/ namespaces
796 $ hg debugpushkey http://localhost:$HGPORT/ namespaces
796 bookmarks
797 bookmarks
797 namespaces
798 namespaces
798 obsolete
799 obsolete
799 phases
800 phases
800 $ hg debugpushkey http://localhost:$HGPORT/ bookmarks
801 $ hg debugpushkey http://localhost:$HGPORT/ bookmarks
801 @ 9b140be1080824d768c5a4691a564088eede71f9
802 @ 9b140be1080824d768c5a4691a564088eede71f9
802 X 9b140be1080824d768c5a4691a564088eede71f9
803 X 9b140be1080824d768c5a4691a564088eede71f9
803 Y c922c0139ca03858f655e4a2af4dd02796a63969
804 Y c922c0139ca03858f655e4a2af4dd02796a63969
804 Z 9b140be1080824d768c5a4691a564088eede71f9
805 Z 9b140be1080824d768c5a4691a564088eede71f9
805 foo 0000000000000000000000000000000000000000
806 foo 0000000000000000000000000000000000000000
806 foobar 9b140be1080824d768c5a4691a564088eede71f9
807 foobar 9b140be1080824d768c5a4691a564088eede71f9
807 $ hg out -B http://localhost:$HGPORT/
808 $ hg out -B http://localhost:$HGPORT/
808 comparing with http://localhost:$HGPORT/
809 comparing with http://localhost:$HGPORT/
809 searching for changed bookmarks
810 searching for changed bookmarks
810 @ 0d2164f0ce0d
811 @ 0d2164f0ce0d
811 X 0d2164f0ce0d
812 X 0d2164f0ce0d
812 Z 0d2164f0ce0d
813 Z 0d2164f0ce0d
813 foo
814 foo
814 foobar
815 foobar
815 $ hg push -B Z http://localhost:$HGPORT/
816 $ hg push -B Z http://localhost:$HGPORT/
816 pushing to http://localhost:$HGPORT/
817 pushing to http://localhost:$HGPORT/
817 searching for changes
818 searching for changes
818 no changes found
819 no changes found
819 updating bookmark Z
820 updating bookmark Z
820 [1]
821 [1]
821 $ hg book -d Z
822 $ hg book -d Z
822 $ hg in -B http://localhost:$HGPORT/
823 $ hg in -B http://localhost:$HGPORT/
823 comparing with http://localhost:$HGPORT/
824 comparing with http://localhost:$HGPORT/
824 searching for changed bookmarks
825 searching for changed bookmarks
825 @ 9b140be10808
826 @ 9b140be10808
826 X 9b140be10808
827 X 9b140be10808
827 Z 0d2164f0ce0d
828 Z 0d2164f0ce0d
828 foo 000000000000
829 foo 000000000000
829 foobar 9b140be10808
830 foobar 9b140be10808
830 $ hg pull -B Z http://localhost:$HGPORT/
831 $ hg pull -B Z http://localhost:$HGPORT/
831 pulling from http://localhost:$HGPORT/
832 pulling from http://localhost:$HGPORT/
832 no changes found
833 no changes found
833 divergent bookmark @ stored as @1
834 divergent bookmark @ stored as @1
834 divergent bookmark X stored as X@1
835 divergent bookmark X stored as X@1
835 adding remote bookmark Z
836 adding remote bookmark Z
836 adding remote bookmark foo
837 adding remote bookmark foo
837 adding remote bookmark foobar
838 adding remote bookmark foobar
838 $ hg clone http://localhost:$HGPORT/ cloned-bookmarks
839 $ hg clone http://localhost:$HGPORT/ cloned-bookmarks
839 requesting all changes
840 requesting all changes
840 adding changesets
841 adding changesets
841 adding manifests
842 adding manifests
842 adding file changes
843 adding file changes
843 added 5 changesets with 5 changes to 3 files (+2 heads)
844 added 5 changesets with 5 changes to 3 files (+2 heads)
844 2 new obsolescence markers
845 2 new obsolescence markers
845 new changesets 4e3505fd9583:c922c0139ca0 (5 drafts)
846 new changesets 4e3505fd9583:c922c0139ca0 (5 drafts)
846 updating to bookmark @
847 updating to bookmark @
847 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
848 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
848 $ hg -R cloned-bookmarks bookmarks
849 $ hg -R cloned-bookmarks bookmarks
849 * @ 1:9b140be10808
850 * @ 1:9b140be10808
850 X 1:9b140be10808
851 X 1:9b140be10808
851 Y 4:c922c0139ca0
852 Y 4:c922c0139ca0
852 Z 2:0d2164f0ce0d
853 Z 2:0d2164f0ce0d
853 foo -1:000000000000
854 foo -1:000000000000
854 foobar 1:9b140be10808
855 foobar 1:9b140be10808
855
856
856 $ cd ..
857 $ cd ..
857
858
858 Test to show result of bookmarks comparison
859 Test to show result of bookmarks comparison
859
860
860 $ mkdir bmcomparison
861 $ mkdir bmcomparison
861 $ cd bmcomparison
862 $ cd bmcomparison
862
863
863 $ hg init source
864 $ hg init source
864 $ hg -R source debugbuilddag '+2*2*3*4'
865 $ hg -R source debugbuilddag '+2*2*3*4'
865 $ hg -R source log -G --template '{rev}:{node|short}'
866 $ hg -R source log -G --template '{rev}:{node|short}'
866 o 4:e7bd5218ca15
867 o 4:e7bd5218ca15
867 |
868 |
868 | o 3:6100d3090acf
869 | o 3:6100d3090acf
869 |/
870 |/
870 | o 2:fa942426a6fd
871 | o 2:fa942426a6fd
871 |/
872 |/
872 | o 1:66f7d451a68b
873 | o 1:66f7d451a68b
873 |/
874 |/
874 o 0:1ea73414a91b
875 o 0:1ea73414a91b
875
876
876 $ hg -R source bookmarks -r 0 SAME
877 $ hg -R source bookmarks -r 0 SAME
877 $ hg -R source bookmarks -r 0 ADV_ON_REPO1
878 $ hg -R source bookmarks -r 0 ADV_ON_REPO1
878 $ hg -R source bookmarks -r 0 ADV_ON_REPO2
879 $ hg -R source bookmarks -r 0 ADV_ON_REPO2
879 $ hg -R source bookmarks -r 0 DIFF_ADV_ON_REPO1
880 $ hg -R source bookmarks -r 0 DIFF_ADV_ON_REPO1
880 $ hg -R source bookmarks -r 0 DIFF_ADV_ON_REPO2
881 $ hg -R source bookmarks -r 0 DIFF_ADV_ON_REPO2
881 $ hg -R source bookmarks -r 1 DIVERGED
882 $ hg -R source bookmarks -r 1 DIVERGED
882
883
883 $ hg clone -U source repo1
884 $ hg clone -U source repo1
884
885
885 (test that incoming/outgoing exit with 1, if there is no bookmark to
886 (test that incoming/outgoing exit with 1, if there is no bookmark to
886 be exchanged)
887 be exchanged)
887
888
888 $ hg -R repo1 incoming -B
889 $ hg -R repo1 incoming -B
889 comparing with $TESTTMP/bmcomparison/source
890 comparing with $TESTTMP/bmcomparison/source
890 searching for changed bookmarks
891 searching for changed bookmarks
891 no changed bookmarks found
892 no changed bookmarks found
892 [1]
893 [1]
893 $ hg -R repo1 outgoing -B
894 $ hg -R repo1 outgoing -B
894 comparing with $TESTTMP/bmcomparison/source
895 comparing with $TESTTMP/bmcomparison/source
895 searching for changed bookmarks
896 searching for changed bookmarks
896 no changed bookmarks found
897 no changed bookmarks found
897 [1]
898 [1]
898
899
899 $ hg -R repo1 bookmarks -f -r 1 ADD_ON_REPO1
900 $ hg -R repo1 bookmarks -f -r 1 ADD_ON_REPO1
900 $ hg -R repo1 bookmarks -f -r 2 ADV_ON_REPO1
901 $ hg -R repo1 bookmarks -f -r 2 ADV_ON_REPO1
901 $ hg -R repo1 bookmarks -f -r 3 DIFF_ADV_ON_REPO1
902 $ hg -R repo1 bookmarks -f -r 3 DIFF_ADV_ON_REPO1
902 $ hg -R repo1 bookmarks -f -r 3 DIFF_DIVERGED
903 $ hg -R repo1 bookmarks -f -r 3 DIFF_DIVERGED
903 $ hg -R repo1 -q --config extensions.mq= strip 4
904 $ hg -R repo1 -q --config extensions.mq= strip 4
904 $ hg -R repo1 log -G --template '{node|short} ({bookmarks})'
905 $ hg -R repo1 log -G --template '{node|short} ({bookmarks})'
905 o 6100d3090acf (DIFF_ADV_ON_REPO1 DIFF_DIVERGED)
906 o 6100d3090acf (DIFF_ADV_ON_REPO1 DIFF_DIVERGED)
906 |
907 |
907 | o fa942426a6fd (ADV_ON_REPO1)
908 | o fa942426a6fd (ADV_ON_REPO1)
908 |/
909 |/
909 | o 66f7d451a68b (ADD_ON_REPO1 DIVERGED)
910 | o 66f7d451a68b (ADD_ON_REPO1 DIVERGED)
910 |/
911 |/
911 o 1ea73414a91b (ADV_ON_REPO2 DIFF_ADV_ON_REPO2 SAME)
912 o 1ea73414a91b (ADV_ON_REPO2 DIFF_ADV_ON_REPO2 SAME)
912
913
913
914
914 $ hg clone -U source repo2
915 $ hg clone -U source repo2
915 $ hg -R repo2 bookmarks -f -r 1 ADD_ON_REPO2
916 $ hg -R repo2 bookmarks -f -r 1 ADD_ON_REPO2
916 $ hg -R repo2 bookmarks -f -r 1 ADV_ON_REPO2
917 $ hg -R repo2 bookmarks -f -r 1 ADV_ON_REPO2
917 $ hg -R repo2 bookmarks -f -r 2 DIVERGED
918 $ hg -R repo2 bookmarks -f -r 2 DIVERGED
918 $ hg -R repo2 bookmarks -f -r 4 DIFF_ADV_ON_REPO2
919 $ hg -R repo2 bookmarks -f -r 4 DIFF_ADV_ON_REPO2
919 $ hg -R repo2 bookmarks -f -r 4 DIFF_DIVERGED
920 $ hg -R repo2 bookmarks -f -r 4 DIFF_DIVERGED
920 $ hg -R repo2 -q --config extensions.mq= strip 3
921 $ hg -R repo2 -q --config extensions.mq= strip 3
921 $ hg -R repo2 log -G --template '{node|short} ({bookmarks})'
922 $ hg -R repo2 log -G --template '{node|short} ({bookmarks})'
922 o e7bd5218ca15 (DIFF_ADV_ON_REPO2 DIFF_DIVERGED)
923 o e7bd5218ca15 (DIFF_ADV_ON_REPO2 DIFF_DIVERGED)
923 |
924 |
924 | o fa942426a6fd (DIVERGED)
925 | o fa942426a6fd (DIVERGED)
925 |/
926 |/
926 | o 66f7d451a68b (ADD_ON_REPO2 ADV_ON_REPO2)
927 | o 66f7d451a68b (ADD_ON_REPO2 ADV_ON_REPO2)
927 |/
928 |/
928 o 1ea73414a91b (ADV_ON_REPO1 DIFF_ADV_ON_REPO1 SAME)
929 o 1ea73414a91b (ADV_ON_REPO1 DIFF_ADV_ON_REPO1 SAME)
929
930
930
931
931 (test that difference of bookmarks between repositories are fully shown)
932 (test that difference of bookmarks between repositories are fully shown)
932
933
933 $ hg -R repo1 incoming -B repo2 -v
934 $ hg -R repo1 incoming -B repo2 -v
934 comparing with repo2
935 comparing with repo2
935 searching for changed bookmarks
936 searching for changed bookmarks
936 ADD_ON_REPO2 66f7d451a68b added
937 ADD_ON_REPO2 66f7d451a68b added
937 ADV_ON_REPO2 66f7d451a68b advanced
938 ADV_ON_REPO2 66f7d451a68b advanced
938 DIFF_ADV_ON_REPO2 e7bd5218ca15 changed
939 DIFF_ADV_ON_REPO2 e7bd5218ca15 changed
939 DIFF_DIVERGED e7bd5218ca15 changed
940 DIFF_DIVERGED e7bd5218ca15 changed
940 DIVERGED fa942426a6fd diverged
941 DIVERGED fa942426a6fd diverged
941 $ hg -R repo1 outgoing -B repo2 -v
942 $ hg -R repo1 outgoing -B repo2 -v
942 comparing with repo2
943 comparing with repo2
943 searching for changed bookmarks
944 searching for changed bookmarks
944 ADD_ON_REPO1 66f7d451a68b added
945 ADD_ON_REPO1 66f7d451a68b added
945 ADD_ON_REPO2 deleted
946 ADD_ON_REPO2 deleted
946 ADV_ON_REPO1 fa942426a6fd advanced
947 ADV_ON_REPO1 fa942426a6fd advanced
947 DIFF_ADV_ON_REPO1 6100d3090acf advanced
948 DIFF_ADV_ON_REPO1 6100d3090acf advanced
948 DIFF_ADV_ON_REPO2 1ea73414a91b changed
949 DIFF_ADV_ON_REPO2 1ea73414a91b changed
949 DIFF_DIVERGED 6100d3090acf changed
950 DIFF_DIVERGED 6100d3090acf changed
950 DIVERGED 66f7d451a68b diverged
951 DIVERGED 66f7d451a68b diverged
951
952
952 $ hg -R repo2 incoming -B repo1 -v
953 $ hg -R repo2 incoming -B repo1 -v
953 comparing with repo1
954 comparing with repo1
954 searching for changed bookmarks
955 searching for changed bookmarks
955 ADD_ON_REPO1 66f7d451a68b added
956 ADD_ON_REPO1 66f7d451a68b added
956 ADV_ON_REPO1 fa942426a6fd advanced
957 ADV_ON_REPO1 fa942426a6fd advanced
957 DIFF_ADV_ON_REPO1 6100d3090acf changed
958 DIFF_ADV_ON_REPO1 6100d3090acf changed
958 DIFF_DIVERGED 6100d3090acf changed
959 DIFF_DIVERGED 6100d3090acf changed
959 DIVERGED 66f7d451a68b diverged
960 DIVERGED 66f7d451a68b diverged
960 $ hg -R repo2 outgoing -B repo1 -v
961 $ hg -R repo2 outgoing -B repo1 -v
961 comparing with repo1
962 comparing with repo1
962 searching for changed bookmarks
963 searching for changed bookmarks
963 ADD_ON_REPO1 deleted
964 ADD_ON_REPO1 deleted
964 ADD_ON_REPO2 66f7d451a68b added
965 ADD_ON_REPO2 66f7d451a68b added
965 ADV_ON_REPO2 66f7d451a68b advanced
966 ADV_ON_REPO2 66f7d451a68b advanced
966 DIFF_ADV_ON_REPO1 1ea73414a91b changed
967 DIFF_ADV_ON_REPO1 1ea73414a91b changed
967 DIFF_ADV_ON_REPO2 e7bd5218ca15 advanced
968 DIFF_ADV_ON_REPO2 e7bd5218ca15 advanced
968 DIFF_DIVERGED e7bd5218ca15 changed
969 DIFF_DIVERGED e7bd5218ca15 changed
969 DIVERGED fa942426a6fd diverged
970 DIVERGED fa942426a6fd diverged
970
971
971 $ cd ..
972 $ cd ..
972
973
973 Pushing a bookmark should only push the changes required by that
974 Pushing a bookmark should only push the changes required by that
974 bookmark, not all outgoing changes:
975 bookmark, not all outgoing changes:
975 $ hg clone http://localhost:$HGPORT/ addmarks
976 $ hg clone http://localhost:$HGPORT/ addmarks
976 requesting all changes
977 requesting all changes
977 adding changesets
978 adding changesets
978 adding manifests
979 adding manifests
979 adding file changes
980 adding file changes
980 added 5 changesets with 5 changes to 3 files (+2 heads)
981 added 5 changesets with 5 changes to 3 files (+2 heads)
981 2 new obsolescence markers
982 2 new obsolescence markers
982 new changesets 4e3505fd9583:c922c0139ca0 (5 drafts)
983 new changesets 4e3505fd9583:c922c0139ca0 (5 drafts)
983 updating to bookmark @
984 updating to bookmark @
984 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
985 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
985 $ cd addmarks
986 $ cd addmarks
986 $ echo foo > foo
987 $ echo foo > foo
987 $ hg add foo
988 $ hg add foo
988 $ hg commit -m 'add foo'
989 $ hg commit -m 'add foo'
989 $ echo bar > bar
990 $ echo bar > bar
990 $ hg add bar
991 $ hg add bar
991 $ hg commit -m 'add bar'
992 $ hg commit -m 'add bar'
992 $ hg co "tip^"
993 $ hg co "tip^"
993 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
994 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
994 (leaving bookmark @)
995 (leaving bookmark @)
995 $ hg book add-foo
996 $ hg book add-foo
996 $ hg book -r tip add-bar
997 $ hg book -r tip add-bar
997 Note: this push *must* push only a single changeset, as that's the point
998 Note: this push *must* push only a single changeset, as that's the point
998 of this test.
999 of this test.
999 $ hg push -B add-foo --traceback
1000 $ hg push -B add-foo --traceback
1000 pushing to http://localhost:$HGPORT/
1001 pushing to http://localhost:$HGPORT/
1001 searching for changes
1002 searching for changes
1002 remote: adding changesets
1003 remote: adding changesets
1003 remote: adding manifests
1004 remote: adding manifests
1004 remote: adding file changes
1005 remote: adding file changes
1005 remote: added 1 changesets with 1 changes to 1 files
1006 remote: added 1 changesets with 1 changes to 1 files
1006 exporting bookmark add-foo
1007 exporting bookmark add-foo
1007
1008
1008 pushing a new bookmark on a new head does not require -f if -B is specified
1009 pushing a new bookmark on a new head does not require -f if -B is specified
1009
1010
1010 $ hg up -q X
1011 $ hg up -q X
1011 $ hg book W
1012 $ hg book W
1012 $ echo c5 > f2
1013 $ echo c5 > f2
1013 $ hg ci -Am5
1014 $ hg ci -Am5
1014 created new head
1015 created new head
1015 $ hg push -B .
1016 $ hg push -B .
1016 pushing to http://localhost:$HGPORT/
1017 pushing to http://localhost:$HGPORT/
1017 searching for changes
1018 searching for changes
1018 remote: adding changesets
1019 remote: adding changesets
1019 remote: adding manifests
1020 remote: adding manifests
1020 remote: adding file changes
1021 remote: adding file changes
1021 remote: added 1 changesets with 1 changes to 1 files (+1 heads)
1022 remote: added 1 changesets with 1 changes to 1 files (+1 heads)
1022 exporting bookmark W
1023 exporting bookmark W
1023 $ hg -R ../b id -r W
1024 $ hg -R ../b id -r W
1024 cc978a373a53 tip W
1025 cc978a373a53 tip W
1025
1026
1026 pushing an existing but divergent bookmark with -B still requires -f
1027 pushing an existing but divergent bookmark with -B still requires -f
1027
1028
1028 $ hg clone -q . ../r
1029 $ hg clone -q . ../r
1029 $ hg up -q X
1030 $ hg up -q X
1030 $ echo 1 > f2
1031 $ echo 1 > f2
1031 $ hg ci -qAml
1032 $ hg ci -qAml
1032
1033
1033 $ cd ../r
1034 $ cd ../r
1034 $ hg up -q X
1035 $ hg up -q X
1035 $ echo 2 > f2
1036 $ echo 2 > f2
1036 $ hg ci -qAmr
1037 $ hg ci -qAmr
1037 $ hg push -B X
1038 $ hg push -B X
1038 pushing to $TESTTMP/addmarks
1039 pushing to $TESTTMP/addmarks
1039 searching for changes
1040 searching for changes
1040 remote has heads on branch 'default' that are not known locally: a2a606d9ff1b
1041 remote has heads on branch 'default' that are not known locally: a2a606d9ff1b
1041 abort: push creates new remote head 54694f811df9 with bookmark 'X'!
1042 abort: push creates new remote head 54694f811df9 with bookmark 'X'!
1042 (pull and merge or see 'hg help push' for details about pushing new heads)
1043 (pull and merge or see 'hg help push' for details about pushing new heads)
1043 [255]
1044 [255]
1044 $ cd ../addmarks
1045 $ cd ../addmarks
1045
1046
1046 Check summary output for incoming/outgoing bookmarks
1047 Check summary output for incoming/outgoing bookmarks
1047
1048
1048 $ hg bookmarks -d X
1049 $ hg bookmarks -d X
1049 $ hg bookmarks -d Y
1050 $ hg bookmarks -d Y
1050 $ hg summary --remote | grep '^remote:'
1051 $ hg summary --remote | grep '^remote:'
1051 remote: *, 2 incoming bookmarks, 1 outgoing bookmarks (glob)
1052 remote: *, 2 incoming bookmarks, 1 outgoing bookmarks (glob)
1052
1053
1053 $ cd ..
1054 $ cd ..
1054
1055
1055 pushing an unchanged bookmark should result in no changes
1056 pushing an unchanged bookmark should result in no changes
1056
1057
1057 $ hg init unchanged-a
1058 $ hg init unchanged-a
1058 $ hg init unchanged-b
1059 $ hg init unchanged-b
1059 $ cd unchanged-a
1060 $ cd unchanged-a
1060 $ echo initial > foo
1061 $ echo initial > foo
1061 $ hg commit -A -m initial
1062 $ hg commit -A -m initial
1062 adding foo
1063 adding foo
1063 $ hg bookmark @
1064 $ hg bookmark @
1064 $ hg push -B @ ../unchanged-b
1065 $ hg push -B @ ../unchanged-b
1065 pushing to ../unchanged-b
1066 pushing to ../unchanged-b
1066 searching for changes
1067 searching for changes
1067 adding changesets
1068 adding changesets
1068 adding manifests
1069 adding manifests
1069 adding file changes
1070 adding file changes
1070 added 1 changesets with 1 changes to 1 files
1071 added 1 changesets with 1 changes to 1 files
1071 exporting bookmark @
1072 exporting bookmark @
1072
1073
1073 $ hg push -B @ ../unchanged-b
1074 $ hg push -B @ ../unchanged-b
1074 pushing to ../unchanged-b
1075 pushing to ../unchanged-b
1075 searching for changes
1076 searching for changes
1076 no changes found
1077 no changes found
1077 [1]
1078 [1]
1078
1079
1079 Pushing a really long bookmark should work fine (issue5165)
1080 Pushing a really long bookmark should work fine (issue5165)
1080 ===============================================
1081 ===============================================
1081
1082
1082 #if b2-binary
1083 #if b2-binary
1083 >>> with open('longname', 'w') as f:
1084 >>> with open('longname', 'w') as f:
1084 ... f.write('wat' * 100) and None
1085 ... f.write('wat' * 100) and None
1085 $ hg book `cat longname`
1086 $ hg book `cat longname`
1086 $ hg push -B `cat longname` ../unchanged-b
1087 $ hg push -B `cat longname` ../unchanged-b
1087 pushing to ../unchanged-b
1088 pushing to ../unchanged-b
1088 searching for changes
1089 searching for changes
1089 no changes found
1090 no changes found
1090 exporting bookmark (wat){100} (re)
1091 exporting bookmark (wat){100} (re)
1091 [1]
1092 [1]
1092 $ hg -R ../unchanged-b book --delete `cat longname`
1093 $ hg -R ../unchanged-b book --delete `cat longname`
1093
1094
1094 Test again but forcing bundle2 exchange to make sure that doesn't regress.
1095 Test again but forcing bundle2 exchange to make sure that doesn't regress.
1095
1096
1096 $ hg push -B `cat longname` ../unchanged-b --config devel.legacy.exchange=bundle1
1097 $ hg push -B `cat longname` ../unchanged-b --config devel.legacy.exchange=bundle1
1097 pushing to ../unchanged-b
1098 pushing to ../unchanged-b
1098 searching for changes
1099 searching for changes
1099 no changes found
1100 no changes found
1100 exporting bookmark (wat){100} (re)
1101 exporting bookmark (wat){100} (re)
1101 [1]
1102 [1]
1102 $ hg -R ../unchanged-b book --delete `cat longname`
1103 $ hg -R ../unchanged-b book --delete `cat longname`
1103 $ hg book --delete `cat longname`
1104 $ hg book --delete `cat longname`
1104 $ hg co @
1105 $ hg co @
1105 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1106 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1106 (activating bookmark @)
1107 (activating bookmark @)
1107 #endif
1108 #endif
1108
1109
1109 Check hook preventing push (issue4455)
1110 Check hook preventing push (issue4455)
1110 ======================================
1111 ======================================
1111
1112
1112 $ hg bookmarks
1113 $ hg bookmarks
1113 * @ 0:55482a6fb4b1
1114 * @ 0:55482a6fb4b1
1114 $ hg log -G
1115 $ hg log -G
1115 @ 0:55482a6fb4b1 initial
1116 @ 0:55482a6fb4b1 initial
1116
1117
1117 $ hg init ../issue4455-dest
1118 $ hg init ../issue4455-dest
1118 $ hg push ../issue4455-dest # changesets only
1119 $ hg push ../issue4455-dest # changesets only
1119 pushing to ../issue4455-dest
1120 pushing to ../issue4455-dest
1120 searching for changes
1121 searching for changes
1121 adding changesets
1122 adding changesets
1122 adding manifests
1123 adding manifests
1123 adding file changes
1124 adding file changes
1124 added 1 changesets with 1 changes to 1 files
1125 added 1 changesets with 1 changes to 1 files
1125 $ cat >> .hg/hgrc << EOF
1126 $ cat >> .hg/hgrc << EOF
1126 > [paths]
1127 > [paths]
1127 > local=../issue4455-dest/
1128 > local=../issue4455-dest/
1128 > ssh=ssh://user@dummy/issue4455-dest
1129 > ssh=ssh://user@dummy/issue4455-dest
1129 > http=http://localhost:$HGPORT/
1130 > http=http://localhost:$HGPORT/
1130 > [ui]
1131 > [ui]
1131 > ssh="$PYTHON" "$TESTDIR/dummyssh"
1132 > ssh="$PYTHON" "$TESTDIR/dummyssh"
1132 > EOF
1133 > EOF
1133 $ cat >> ../issue4455-dest/.hg/hgrc << EOF
1134 $ cat >> ../issue4455-dest/.hg/hgrc << EOF
1134 > [hooks]
1135 > [hooks]
1135 > prepushkey=false
1136 > prepushkey=false
1136 > [web]
1137 > [web]
1137 > push_ssl = false
1138 > push_ssl = false
1138 > allow_push = *
1139 > allow_push = *
1139 > EOF
1140 > EOF
1140 $ killdaemons.py
1141 $ killdaemons.py
1141 $ hg serve -R ../issue4455-dest -p $HGPORT -d --pid-file=../issue4455.pid -E ../issue4455-error.log
1142 $ hg serve -R ../issue4455-dest -p $HGPORT -d --pid-file=../issue4455.pid -E ../issue4455-error.log
1142 $ cat ../issue4455.pid >> $DAEMON_PIDS
1143 $ cat ../issue4455.pid >> $DAEMON_PIDS
1143
1144
1144 Local push
1145 Local push
1145 ----------
1146 ----------
1146
1147
1147 #if b2-pushkey
1148 #if b2-pushkey
1148
1149
1149 $ hg push -B @ local
1150 $ hg push -B @ local
1150 pushing to $TESTTMP/issue4455-dest
1151 pushing to $TESTTMP/issue4455-dest
1151 searching for changes
1152 searching for changes
1152 no changes found
1153 no changes found
1153 pushkey-abort: prepushkey hook exited with status 1
1154 pushkey-abort: prepushkey hook exited with status 1
1154 abort: exporting bookmark @ failed!
1155 abort: exporting bookmark @ failed!
1155 [255]
1156 [255]
1156
1157
1157 #endif
1158 #endif
1158 #if b2-binary
1159 #if b2-binary
1159
1160
1160 $ hg push -B @ local
1161 $ hg push -B @ local
1161 pushing to $TESTTMP/issue4455-dest
1162 pushing to $TESTTMP/issue4455-dest
1162 searching for changes
1163 searching for changes
1163 no changes found
1164 no changes found
1164 abort: prepushkey hook exited with status 1
1165 abort: prepushkey hook exited with status 1
1165 [255]
1166 [255]
1166
1167
1167 #endif
1168 #endif
1168
1169
1169 $ hg -R ../issue4455-dest/ bookmarks
1170 $ hg -R ../issue4455-dest/ bookmarks
1170 no bookmarks set
1171 no bookmarks set
1171
1172
1172 Using ssh
1173 Using ssh
1173 ---------
1174 ---------
1174
1175
1175 #if b2-pushkey
1176 #if b2-pushkey
1176
1177
1177 $ hg push -B @ ssh # bundle2+
1178 $ hg push -B @ ssh # bundle2+
1178 pushing to ssh://user@dummy/issue4455-dest
1179 pushing to ssh://user@dummy/issue4455-dest
1179 searching for changes
1180 searching for changes
1180 no changes found
1181 no changes found
1181 remote: pushkey-abort: prepushkey hook exited with status 1
1182 remote: pushkey-abort: prepushkey hook exited with status 1
1182 abort: exporting bookmark @ failed!
1183 abort: exporting bookmark @ failed!
1183 [255]
1184 [255]
1184
1185
1185 $ hg -R ../issue4455-dest/ bookmarks
1186 $ hg -R ../issue4455-dest/ bookmarks
1186 no bookmarks set
1187 no bookmarks set
1187
1188
1188 $ hg push -B @ ssh --config devel.legacy.exchange=bundle1
1189 $ hg push -B @ ssh --config devel.legacy.exchange=bundle1
1189 pushing to ssh://user@dummy/issue4455-dest
1190 pushing to ssh://user@dummy/issue4455-dest
1190 searching for changes
1191 searching for changes
1191 no changes found
1192 no changes found
1192 remote: pushkey-abort: prepushkey hook exited with status 1
1193 remote: pushkey-abort: prepushkey hook exited with status 1
1193 exporting bookmark @ failed!
1194 exporting bookmark @ failed!
1194 [1]
1195 [1]
1195
1196
1196 #endif
1197 #endif
1197 #if b2-binary
1198 #if b2-binary
1198
1199
1199 $ hg push -B @ ssh # bundle2+
1200 $ hg push -B @ ssh # bundle2+
1200 pushing to ssh://user@dummy/issue4455-dest
1201 pushing to ssh://user@dummy/issue4455-dest
1201 searching for changes
1202 searching for changes
1202 no changes found
1203 no changes found
1203 remote: prepushkey hook exited with status 1
1204 remote: prepushkey hook exited with status 1
1204 abort: push failed on remote
1205 abort: push failed on remote
1205 [255]
1206 [255]
1206
1207
1207 #endif
1208 #endif
1208
1209
1209 $ hg -R ../issue4455-dest/ bookmarks
1210 $ hg -R ../issue4455-dest/ bookmarks
1210 no bookmarks set
1211 no bookmarks set
1211
1212
1212 Using http
1213 Using http
1213 ----------
1214 ----------
1214
1215
1215 #if b2-pushkey
1216 #if b2-pushkey
1216 $ hg push -B @ http # bundle2+
1217 $ hg push -B @ http # bundle2+
1217 pushing to http://localhost:$HGPORT/
1218 pushing to http://localhost:$HGPORT/
1218 searching for changes
1219 searching for changes
1219 no changes found
1220 no changes found
1220 remote: pushkey-abort: prepushkey hook exited with status 1
1221 remote: pushkey-abort: prepushkey hook exited with status 1
1221 abort: exporting bookmark @ failed!
1222 abort: exporting bookmark @ failed!
1222 [255]
1223 [255]
1223
1224
1224 $ hg -R ../issue4455-dest/ bookmarks
1225 $ hg -R ../issue4455-dest/ bookmarks
1225 no bookmarks set
1226 no bookmarks set
1226
1227
1227 $ hg push -B @ http --config devel.legacy.exchange=bundle1
1228 $ hg push -B @ http --config devel.legacy.exchange=bundle1
1228 pushing to http://localhost:$HGPORT/
1229 pushing to http://localhost:$HGPORT/
1229 searching for changes
1230 searching for changes
1230 no changes found
1231 no changes found
1231 remote: pushkey-abort: prepushkey hook exited with status 1
1232 remote: pushkey-abort: prepushkey hook exited with status 1
1232 exporting bookmark @ failed!
1233 exporting bookmark @ failed!
1233 [1]
1234 [1]
1234
1235
1235 #endif
1236 #endif
1236
1237
1237 #if b2-binary
1238 #if b2-binary
1238
1239
1239 $ hg push -B @ ssh # bundle2+
1240 $ hg push -B @ ssh # bundle2+
1240 pushing to ssh://user@dummy/issue4455-dest
1241 pushing to ssh://user@dummy/issue4455-dest
1241 searching for changes
1242 searching for changes
1242 no changes found
1243 no changes found
1243 remote: prepushkey hook exited with status 1
1244 remote: prepushkey hook exited with status 1
1244 abort: push failed on remote
1245 abort: push failed on remote
1245 [255]
1246 [255]
1246
1247
1247 #endif
1248 #endif
1248
1249
1249 $ hg -R ../issue4455-dest/ bookmarks
1250 $ hg -R ../issue4455-dest/ bookmarks
1250 no bookmarks set
1251 no bookmarks set
1251
1252
1252 $ cd ..
1253 $ cd ..
1253
1254
1254 Test that pre-pushkey compat for bookmark works as expected (issue5777)
1255 Test that pre-pushkey compat for bookmark works as expected (issue5777)
1255
1256
1256 $ cat << EOF >> $HGRCPATH
1257 $ cat << EOF >> $HGRCPATH
1257 > [ui]
1258 > [ui]
1258 > ssh="$PYTHON" "$TESTDIR/dummyssh"
1259 > ssh="$PYTHON" "$TESTDIR/dummyssh"
1259 > [server]
1260 > [server]
1260 > bookmarks-pushkey-compat = yes
1261 > bookmarks-pushkey-compat = yes
1261 > EOF
1262 > EOF
1262
1263
1263 $ hg init server
1264 $ hg init server
1264 $ echo foo > server/a
1265 $ echo foo > server/a
1265 $ hg -R server book foo
1266 $ hg -R server book foo
1266 $ hg -R server commit -Am a
1267 $ hg -R server commit -Am a
1267 adding a
1268 adding a
1268 $ hg clone ssh://user@dummy/server client
1269 $ hg clone ssh://user@dummy/server client
1269 requesting all changes
1270 requesting all changes
1270 adding changesets
1271 adding changesets
1271 adding manifests
1272 adding manifests
1272 adding file changes
1273 adding file changes
1273 added 1 changesets with 1 changes to 1 files
1274 added 1 changesets with 1 changes to 1 files
1274 new changesets 79513d0d7716 (1 drafts)
1275 new changesets 79513d0d7716 (1 drafts)
1275 updating to branch default
1276 updating to branch default
1276 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1277 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1277
1278
1278 Forbid bookmark move on the server
1279 Forbid bookmark move on the server
1279
1280
1280 $ cat << EOF >> $TESTTMP/no-bm-move.sh
1281 $ cat << EOF >> $TESTTMP/no-bm-move.sh
1281 > #!/bin/sh
1282 > #!/bin/sh
1282 > echo \$HG_NAMESPACE | grep -v bookmarks
1283 > echo \$HG_NAMESPACE | grep -v bookmarks
1283 > EOF
1284 > EOF
1284 $ cat << EOF >> server/.hg/hgrc
1285 $ cat << EOF >> server/.hg/hgrc
1285 > [hooks]
1286 > [hooks]
1286 > prepushkey.no-bm-move= sh $TESTTMP/no-bm-move.sh
1287 > prepushkey.no-bm-move= sh $TESTTMP/no-bm-move.sh
1287 > EOF
1288 > EOF
1288
1289
1289 pushing changeset is okay
1290 pushing changeset is okay
1290
1291
1291 $ echo bar >> client/a
1292 $ echo bar >> client/a
1292 $ hg -R client commit -m b
1293 $ hg -R client commit -m b
1293 $ hg -R client push
1294 $ hg -R client push
1294 pushing to ssh://user@dummy/server
1295 pushing to ssh://user@dummy/server
1295 searching for changes
1296 searching for changes
1296 remote: adding changesets
1297 remote: adding changesets
1297 remote: adding manifests
1298 remote: adding manifests
1298 remote: adding file changes
1299 remote: adding file changes
1299 remote: added 1 changesets with 1 changes to 1 files
1300 remote: added 1 changesets with 1 changes to 1 files
1300
1301
1301 attempt to move the bookmark is rejected
1302 attempt to move the bookmark is rejected
1302
1303
1303 $ hg -R client book foo -r .
1304 $ hg -R client book foo -r .
1304 moving bookmark 'foo' forward from 79513d0d7716
1305 moving bookmark 'foo' forward from 79513d0d7716
1305
1306
1306 #if b2-pushkey
1307 #if b2-pushkey
1307 $ hg -R client push
1308 $ hg -R client push
1308 pushing to ssh://user@dummy/server
1309 pushing to ssh://user@dummy/server
1309 searching for changes
1310 searching for changes
1310 no changes found
1311 no changes found
1311 remote: pushkey-abort: prepushkey.no-bm-move hook exited with status 1
1312 remote: pushkey-abort: prepushkey.no-bm-move hook exited with status 1
1312 abort: updating bookmark foo failed!
1313 abort: updating bookmark foo failed!
1313 [255]
1314 [255]
1314 #endif
1315 #endif
1315 #if b2-binary
1316 #if b2-binary
1316 $ hg -R client push
1317 $ hg -R client push
1317 pushing to ssh://user@dummy/server
1318 pushing to ssh://user@dummy/server
1318 searching for changes
1319 searching for changes
1319 no changes found
1320 no changes found
1320 remote: prepushkey.no-bm-move hook exited with status 1
1321 remote: prepushkey.no-bm-move hook exited with status 1
1321 abort: push failed on remote
1322 abort: push failed on remote
1322 [255]
1323 [255]
1323 #endif
1324 #endif
General Comments 0
You need to be logged in to leave comments. Login now