##// END OF EJS Templates
push: add --publish flag to change phase of pushed changesets...
av6 -
r40722:9b8d1ad8 default
parent child Browse files
Show More
@@ -1,6204 +1,6206 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 rev = opts.get('rev')
323 rev = opts.get('rev')
324 if rev:
324 if rev:
325 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
325 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
326 ctx = scmutil.revsingle(repo, rev)
326 ctx = scmutil.revsingle(repo, rev)
327
327
328 rootfm = ui.formatter('annotate', opts)
328 rootfm = ui.formatter('annotate', opts)
329 if ui.debugflag:
329 if ui.debugflag:
330 shorthex = pycompat.identity
330 shorthex = pycompat.identity
331 else:
331 else:
332 def shorthex(h):
332 def shorthex(h):
333 return h[:12]
333 return h[:12]
334 if ui.quiet:
334 if ui.quiet:
335 datefunc = dateutil.shortdate
335 datefunc = dateutil.shortdate
336 else:
336 else:
337 datefunc = dateutil.datestr
337 datefunc = dateutil.datestr
338 if ctx.rev() is None:
338 if ctx.rev() is None:
339 if opts.get('changeset'):
339 if opts.get('changeset'):
340 # omit "+" suffix which is appended to node hex
340 # omit "+" suffix which is appended to node hex
341 def formatrev(rev):
341 def formatrev(rev):
342 if rev == wdirrev:
342 if rev == wdirrev:
343 return '%d' % ctx.p1().rev()
343 return '%d' % ctx.p1().rev()
344 else:
344 else:
345 return '%d' % rev
345 return '%d' % rev
346 else:
346 else:
347 def formatrev(rev):
347 def formatrev(rev):
348 if rev == wdirrev:
348 if rev == wdirrev:
349 return '%d+' % ctx.p1().rev()
349 return '%d+' % ctx.p1().rev()
350 else:
350 else:
351 return '%d ' % rev
351 return '%d ' % rev
352 def formathex(h):
352 def formathex(h):
353 if h == wdirhex:
353 if h == wdirhex:
354 return '%s+' % shorthex(hex(ctx.p1().node()))
354 return '%s+' % shorthex(hex(ctx.p1().node()))
355 else:
355 else:
356 return '%s ' % shorthex(h)
356 return '%s ' % shorthex(h)
357 else:
357 else:
358 formatrev = b'%d'.__mod__
358 formatrev = b'%d'.__mod__
359 formathex = shorthex
359 formathex = shorthex
360
360
361 opmap = [('user', ' ', lambda x: x.fctx.user(), ui.shortuser),
361 opmap = [('user', ' ', lambda x: x.fctx.user(), ui.shortuser),
362 ('rev', ' ', lambda x: scmutil.intrev(x.fctx), formatrev),
362 ('rev', ' ', lambda x: scmutil.intrev(x.fctx), formatrev),
363 ('node', ' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
363 ('node', ' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
364 ('date', ' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
364 ('date', ' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
365 ('path', ' ', lambda x: x.fctx.path(), pycompat.bytestr),
365 ('path', ' ', lambda x: x.fctx.path(), pycompat.bytestr),
366 ('lineno', ':', lambda x: x.lineno, pycompat.bytestr),
366 ('lineno', ':', lambda x: x.lineno, pycompat.bytestr),
367 ]
367 ]
368 opnamemap = {'rev': 'number', 'node': 'changeset', 'path': 'file',
368 opnamemap = {'rev': 'number', 'node': 'changeset', 'path': 'file',
369 'lineno': 'line_number'}
369 'lineno': 'line_number'}
370
370
371 if (not opts.get('user') and not opts.get('changeset')
371 if (not opts.get('user') and not opts.get('changeset')
372 and not opts.get('date') and not opts.get('file')):
372 and not opts.get('date') and not opts.get('file')):
373 opts['number'] = True
373 opts['number'] = True
374
374
375 linenumber = opts.get('line_number') is not None
375 linenumber = opts.get('line_number') is not None
376 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
376 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
377 raise error.Abort(_('at least one of -n/-c is required for -l'))
377 raise error.Abort(_('at least one of -n/-c is required for -l'))
378
378
379 ui.pager('annotate')
379 ui.pager('annotate')
380
380
381 if rootfm.isplain():
381 if rootfm.isplain():
382 def makefunc(get, fmt):
382 def makefunc(get, fmt):
383 return lambda x: fmt(get(x))
383 return lambda x: fmt(get(x))
384 else:
384 else:
385 def makefunc(get, fmt):
385 def makefunc(get, fmt):
386 return get
386 return get
387 datahint = rootfm.datahint()
387 datahint = rootfm.datahint()
388 funcmap = [(makefunc(get, fmt), sep) for fn, sep, get, fmt in opmap
388 funcmap = [(makefunc(get, fmt), sep) for fn, sep, get, fmt in opmap
389 if opts.get(opnamemap.get(fn, fn)) or fn in datahint]
389 if opts.get(opnamemap.get(fn, fn)) or fn in datahint]
390 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
390 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
391 fields = ' '.join(fn for fn, sep, get, fmt in opmap
391 fields = ' '.join(fn for fn, sep, get, fmt in opmap
392 if opts.get(opnamemap.get(fn, fn)) or fn in datahint)
392 if opts.get(opnamemap.get(fn, fn)) or fn in datahint)
393
393
394 def bad(x, y):
394 def bad(x, y):
395 raise error.Abort("%s: %s" % (x, y))
395 raise error.Abort("%s: %s" % (x, y))
396
396
397 m = scmutil.match(ctx, pats, opts, badfn=bad)
397 m = scmutil.match(ctx, pats, opts, badfn=bad)
398
398
399 follow = not opts.get('no_follow')
399 follow = not opts.get('no_follow')
400 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
400 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
401 whitespace=True)
401 whitespace=True)
402 skiprevs = opts.get('skip')
402 skiprevs = opts.get('skip')
403 if skiprevs:
403 if skiprevs:
404 skiprevs = scmutil.revrange(repo, skiprevs)
404 skiprevs = scmutil.revrange(repo, skiprevs)
405
405
406 for abs in ctx.walk(m):
406 for abs in ctx.walk(m):
407 fctx = ctx[abs]
407 fctx = ctx[abs]
408 rootfm.startitem()
408 rootfm.startitem()
409 rootfm.data(path=abs)
409 rootfm.data(path=abs)
410 if not opts.get('text') and fctx.isbinary():
410 if not opts.get('text') and fctx.isbinary():
411 rootfm.plain(_("%s: binary file\n")
411 rootfm.plain(_("%s: binary file\n")
412 % ((pats and m.rel(abs)) or abs))
412 % ((pats and m.rel(abs)) or abs))
413 continue
413 continue
414
414
415 fm = rootfm.nested('lines', tmpl='{rev}: {line}')
415 fm = rootfm.nested('lines', tmpl='{rev}: {line}')
416 lines = fctx.annotate(follow=follow, skiprevs=skiprevs,
416 lines = fctx.annotate(follow=follow, skiprevs=skiprevs,
417 diffopts=diffopts)
417 diffopts=diffopts)
418 if not lines:
418 if not lines:
419 fm.end()
419 fm.end()
420 continue
420 continue
421 formats = []
421 formats = []
422 pieces = []
422 pieces = []
423
423
424 for f, sep in funcmap:
424 for f, sep in funcmap:
425 l = [f(n) for n in lines]
425 l = [f(n) for n in lines]
426 if fm.isplain():
426 if fm.isplain():
427 sizes = [encoding.colwidth(x) for x in l]
427 sizes = [encoding.colwidth(x) for x in l]
428 ml = max(sizes)
428 ml = max(sizes)
429 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
429 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
430 else:
430 else:
431 formats.append(['%s' for x in l])
431 formats.append(['%s' for x in l])
432 pieces.append(l)
432 pieces.append(l)
433
433
434 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
434 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
435 fm.startitem()
435 fm.startitem()
436 fm.context(fctx=n.fctx)
436 fm.context(fctx=n.fctx)
437 fm.write(fields, "".join(f), *p)
437 fm.write(fields, "".join(f), *p)
438 if n.skip:
438 if n.skip:
439 fmt = "* %s"
439 fmt = "* %s"
440 else:
440 else:
441 fmt = ": %s"
441 fmt = ": %s"
442 fm.write('line', fmt, n.text)
442 fm.write('line', fmt, n.text)
443
443
444 if not lines[-1].text.endswith('\n'):
444 if not lines[-1].text.endswith('\n'):
445 fm.plain('\n')
445 fm.plain('\n')
446 fm.end()
446 fm.end()
447
447
448 rootfm.end()
448 rootfm.end()
449
449
450 @command('archive',
450 @command('archive',
451 [('', 'no-decode', None, _('do not pass files through decoders')),
451 [('', 'no-decode', None, _('do not pass files through decoders')),
452 ('p', 'prefix', '', _('directory prefix for files in archive'),
452 ('p', 'prefix', '', _('directory prefix for files in archive'),
453 _('PREFIX')),
453 _('PREFIX')),
454 ('r', 'rev', '', _('revision to distribute'), _('REV')),
454 ('r', 'rev', '', _('revision to distribute'), _('REV')),
455 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
455 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
456 ] + subrepoopts + walkopts,
456 ] + subrepoopts + walkopts,
457 _('[OPTION]... DEST'),
457 _('[OPTION]... DEST'),
458 helpcategory=command.CATEGORY_IMPORT_EXPORT)
458 helpcategory=command.CATEGORY_IMPORT_EXPORT)
459 def archive(ui, repo, dest, **opts):
459 def archive(ui, repo, dest, **opts):
460 '''create an unversioned archive of a repository revision
460 '''create an unversioned archive of a repository revision
461
461
462 By default, the revision used is the parent of the working
462 By default, the revision used is the parent of the working
463 directory; use -r/--rev to specify a different revision.
463 directory; use -r/--rev to specify a different revision.
464
464
465 The archive type is automatically detected based on file
465 The archive type is automatically detected based on file
466 extension (to override, use -t/--type).
466 extension (to override, use -t/--type).
467
467
468 .. container:: verbose
468 .. container:: verbose
469
469
470 Examples:
470 Examples:
471
471
472 - create a zip file containing the 1.0 release::
472 - create a zip file containing the 1.0 release::
473
473
474 hg archive -r 1.0 project-1.0.zip
474 hg archive -r 1.0 project-1.0.zip
475
475
476 - create a tarball excluding .hg files::
476 - create a tarball excluding .hg files::
477
477
478 hg archive project.tar.gz -X ".hg*"
478 hg archive project.tar.gz -X ".hg*"
479
479
480 Valid types are:
480 Valid types are:
481
481
482 :``files``: a directory full of files (default)
482 :``files``: a directory full of files (default)
483 :``tar``: tar archive, uncompressed
483 :``tar``: tar archive, uncompressed
484 :``tbz2``: tar archive, compressed using bzip2
484 :``tbz2``: tar archive, compressed using bzip2
485 :``tgz``: tar archive, compressed using gzip
485 :``tgz``: tar archive, compressed using gzip
486 :``uzip``: zip archive, uncompressed
486 :``uzip``: zip archive, uncompressed
487 :``zip``: zip archive, compressed using deflate
487 :``zip``: zip archive, compressed using deflate
488
488
489 The exact name of the destination archive or directory is given
489 The exact name of the destination archive or directory is given
490 using a format string; see :hg:`help export` for details.
490 using a format string; see :hg:`help export` for details.
491
491
492 Each member added to an archive file has a directory prefix
492 Each member added to an archive file has a directory prefix
493 prepended. Use -p/--prefix to specify a format string for the
493 prepended. Use -p/--prefix to specify a format string for the
494 prefix. The default is the basename of the archive, with suffixes
494 prefix. The default is the basename of the archive, with suffixes
495 removed.
495 removed.
496
496
497 Returns 0 on success.
497 Returns 0 on success.
498 '''
498 '''
499
499
500 opts = pycompat.byteskwargs(opts)
500 opts = pycompat.byteskwargs(opts)
501 rev = opts.get('rev')
501 rev = opts.get('rev')
502 if rev:
502 if rev:
503 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
503 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
504 ctx = scmutil.revsingle(repo, rev)
504 ctx = scmutil.revsingle(repo, rev)
505 if not ctx:
505 if not ctx:
506 raise error.Abort(_('no working directory: please specify a revision'))
506 raise error.Abort(_('no working directory: please specify a revision'))
507 node = ctx.node()
507 node = ctx.node()
508 dest = cmdutil.makefilename(ctx, dest)
508 dest = cmdutil.makefilename(ctx, dest)
509 if os.path.realpath(dest) == repo.root:
509 if os.path.realpath(dest) == repo.root:
510 raise error.Abort(_('repository root cannot be destination'))
510 raise error.Abort(_('repository root cannot be destination'))
511
511
512 kind = opts.get('type') or archival.guesskind(dest) or 'files'
512 kind = opts.get('type') or archival.guesskind(dest) or 'files'
513 prefix = opts.get('prefix')
513 prefix = opts.get('prefix')
514
514
515 if dest == '-':
515 if dest == '-':
516 if kind == 'files':
516 if kind == 'files':
517 raise error.Abort(_('cannot archive plain files to stdout'))
517 raise error.Abort(_('cannot archive plain files to stdout'))
518 dest = cmdutil.makefileobj(ctx, dest)
518 dest = cmdutil.makefileobj(ctx, dest)
519 if not prefix:
519 if not prefix:
520 prefix = os.path.basename(repo.root) + '-%h'
520 prefix = os.path.basename(repo.root) + '-%h'
521
521
522 prefix = cmdutil.makefilename(ctx, prefix)
522 prefix = cmdutil.makefilename(ctx, prefix)
523 match = scmutil.match(ctx, [], opts)
523 match = scmutil.match(ctx, [], opts)
524 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
524 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
525 match, prefix, subrepos=opts.get('subrepos'))
525 match, prefix, subrepos=opts.get('subrepos'))
526
526
527 @command('backout',
527 @command('backout',
528 [('', 'merge', None, _('merge with old dirstate parent after backout')),
528 [('', 'merge', None, _('merge with old dirstate parent after backout')),
529 ('', 'commit', None,
529 ('', 'commit', None,
530 _('commit if no conflicts were encountered (DEPRECATED)')),
530 _('commit if no conflicts were encountered (DEPRECATED)')),
531 ('', 'no-commit', None, _('do not commit')),
531 ('', 'no-commit', None, _('do not commit')),
532 ('', 'parent', '',
532 ('', 'parent', '',
533 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
533 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
534 ('r', 'rev', '', _('revision to backout'), _('REV')),
534 ('r', 'rev', '', _('revision to backout'), _('REV')),
535 ('e', 'edit', False, _('invoke editor on commit messages')),
535 ('e', 'edit', False, _('invoke editor on commit messages')),
536 ] + mergetoolopts + walkopts + commitopts + commitopts2,
536 ] + mergetoolopts + walkopts + commitopts + commitopts2,
537 _('[OPTION]... [-r] REV'),
537 _('[OPTION]... [-r] REV'),
538 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
538 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
539 def backout(ui, repo, node=None, rev=None, **opts):
539 def backout(ui, repo, node=None, rev=None, **opts):
540 '''reverse effect of earlier changeset
540 '''reverse effect of earlier changeset
541
541
542 Prepare a new changeset with the effect of REV undone in the
542 Prepare a new changeset with the effect of REV undone in the
543 current working directory. If no conflicts were encountered,
543 current working directory. If no conflicts were encountered,
544 it will be committed immediately.
544 it will be committed immediately.
545
545
546 If REV is the parent of the working directory, then this new changeset
546 If REV is the parent of the working directory, then this new changeset
547 is committed automatically (unless --no-commit is specified).
547 is committed automatically (unless --no-commit is specified).
548
548
549 .. note::
549 .. note::
550
550
551 :hg:`backout` cannot be used to fix either an unwanted or
551 :hg:`backout` cannot be used to fix either an unwanted or
552 incorrect merge.
552 incorrect merge.
553
553
554 .. container:: verbose
554 .. container:: verbose
555
555
556 Examples:
556 Examples:
557
557
558 - Reverse the effect of the parent of the working directory.
558 - Reverse the effect of the parent of the working directory.
559 This backout will be committed immediately::
559 This backout will be committed immediately::
560
560
561 hg backout -r .
561 hg backout -r .
562
562
563 - Reverse the effect of previous bad revision 23::
563 - Reverse the effect of previous bad revision 23::
564
564
565 hg backout -r 23
565 hg backout -r 23
566
566
567 - Reverse the effect of previous bad revision 23 and
567 - Reverse the effect of previous bad revision 23 and
568 leave changes uncommitted::
568 leave changes uncommitted::
569
569
570 hg backout -r 23 --no-commit
570 hg backout -r 23 --no-commit
571 hg commit -m "Backout revision 23"
571 hg commit -m "Backout revision 23"
572
572
573 By default, the pending changeset will have one parent,
573 By default, the pending changeset will have one parent,
574 maintaining a linear history. With --merge, the pending
574 maintaining a linear history. With --merge, the pending
575 changeset will instead have two parents: the old parent of the
575 changeset will instead have two parents: the old parent of the
576 working directory and a new child of REV that simply undoes REV.
576 working directory and a new child of REV that simply undoes REV.
577
577
578 Before version 1.7, the behavior without --merge was equivalent
578 Before version 1.7, the behavior without --merge was equivalent
579 to specifying --merge followed by :hg:`update --clean .` to
579 to specifying --merge followed by :hg:`update --clean .` to
580 cancel the merge and leave the child of REV as a head to be
580 cancel the merge and leave the child of REV as a head to be
581 merged separately.
581 merged separately.
582
582
583 See :hg:`help dates` for a list of formats valid for -d/--date.
583 See :hg:`help dates` for a list of formats valid for -d/--date.
584
584
585 See :hg:`help revert` for a way to restore files to the state
585 See :hg:`help revert` for a way to restore files to the state
586 of another revision.
586 of another revision.
587
587
588 Returns 0 on success, 1 if nothing to backout or there are unresolved
588 Returns 0 on success, 1 if nothing to backout or there are unresolved
589 files.
589 files.
590 '''
590 '''
591 with repo.wlock(), repo.lock():
591 with repo.wlock(), repo.lock():
592 return _dobackout(ui, repo, node, rev, **opts)
592 return _dobackout(ui, repo, node, rev, **opts)
593
593
594 def _dobackout(ui, repo, node=None, rev=None, **opts):
594 def _dobackout(ui, repo, node=None, rev=None, **opts):
595 opts = pycompat.byteskwargs(opts)
595 opts = pycompat.byteskwargs(opts)
596 if opts.get('commit') and opts.get('no_commit'):
596 if opts.get('commit') and opts.get('no_commit'):
597 raise error.Abort(_("cannot use --commit with --no-commit"))
597 raise error.Abort(_("cannot use --commit with --no-commit"))
598 if opts.get('merge') and opts.get('no_commit'):
598 if opts.get('merge') and opts.get('no_commit'):
599 raise error.Abort(_("cannot use --merge with --no-commit"))
599 raise error.Abort(_("cannot use --merge with --no-commit"))
600
600
601 if rev and node:
601 if rev and node:
602 raise error.Abort(_("please specify just one revision"))
602 raise error.Abort(_("please specify just one revision"))
603
603
604 if not rev:
604 if not rev:
605 rev = node
605 rev = node
606
606
607 if not rev:
607 if not rev:
608 raise error.Abort(_("please specify a revision to backout"))
608 raise error.Abort(_("please specify a revision to backout"))
609
609
610 date = opts.get('date')
610 date = opts.get('date')
611 if date:
611 if date:
612 opts['date'] = dateutil.parsedate(date)
612 opts['date'] = dateutil.parsedate(date)
613
613
614 cmdutil.checkunfinished(repo)
614 cmdutil.checkunfinished(repo)
615 cmdutil.bailifchanged(repo)
615 cmdutil.bailifchanged(repo)
616 node = scmutil.revsingle(repo, rev).node()
616 node = scmutil.revsingle(repo, rev).node()
617
617
618 op1, op2 = repo.dirstate.parents()
618 op1, op2 = repo.dirstate.parents()
619 if not repo.changelog.isancestor(node, op1):
619 if not repo.changelog.isancestor(node, op1):
620 raise error.Abort(_('cannot backout change that is not an ancestor'))
620 raise error.Abort(_('cannot backout change that is not an ancestor'))
621
621
622 p1, p2 = repo.changelog.parents(node)
622 p1, p2 = repo.changelog.parents(node)
623 if p1 == nullid:
623 if p1 == nullid:
624 raise error.Abort(_('cannot backout a change with no parents'))
624 raise error.Abort(_('cannot backout a change with no parents'))
625 if p2 != nullid:
625 if p2 != nullid:
626 if not opts.get('parent'):
626 if not opts.get('parent'):
627 raise error.Abort(_('cannot backout a merge changeset'))
627 raise error.Abort(_('cannot backout a merge changeset'))
628 p = repo.lookup(opts['parent'])
628 p = repo.lookup(opts['parent'])
629 if p not in (p1, p2):
629 if p not in (p1, p2):
630 raise error.Abort(_('%s is not a parent of %s') %
630 raise error.Abort(_('%s is not a parent of %s') %
631 (short(p), short(node)))
631 (short(p), short(node)))
632 parent = p
632 parent = p
633 else:
633 else:
634 if opts.get('parent'):
634 if opts.get('parent'):
635 raise error.Abort(_('cannot use --parent on non-merge changeset'))
635 raise error.Abort(_('cannot use --parent on non-merge changeset'))
636 parent = p1
636 parent = p1
637
637
638 # the backout should appear on the same branch
638 # the backout should appear on the same branch
639 branch = repo.dirstate.branch()
639 branch = repo.dirstate.branch()
640 bheads = repo.branchheads(branch)
640 bheads = repo.branchheads(branch)
641 rctx = scmutil.revsingle(repo, hex(parent))
641 rctx = scmutil.revsingle(repo, hex(parent))
642 if not opts.get('merge') and op1 != node:
642 if not opts.get('merge') and op1 != node:
643 with dirstateguard.dirstateguard(repo, 'backout'):
643 with dirstateguard.dirstateguard(repo, 'backout'):
644 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
644 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
645 with ui.configoverride(overrides, 'backout'):
645 with ui.configoverride(overrides, 'backout'):
646 stats = mergemod.update(repo, parent, branchmerge=True,
646 stats = mergemod.update(repo, parent, branchmerge=True,
647 force=True, ancestor=node,
647 force=True, ancestor=node,
648 mergeancestor=False)
648 mergeancestor=False)
649 repo.setparents(op1, op2)
649 repo.setparents(op1, op2)
650 hg._showstats(repo, stats)
650 hg._showstats(repo, stats)
651 if stats.unresolvedcount:
651 if stats.unresolvedcount:
652 repo.ui.status(_("use 'hg resolve' to retry unresolved "
652 repo.ui.status(_("use 'hg resolve' to retry unresolved "
653 "file merges\n"))
653 "file merges\n"))
654 return 1
654 return 1
655 else:
655 else:
656 hg.clean(repo, node, show_stats=False)
656 hg.clean(repo, node, show_stats=False)
657 repo.dirstate.setbranch(branch)
657 repo.dirstate.setbranch(branch)
658 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
658 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
659
659
660 if opts.get('no_commit'):
660 if opts.get('no_commit'):
661 msg = _("changeset %s backed out, "
661 msg = _("changeset %s backed out, "
662 "don't forget to commit.\n")
662 "don't forget to commit.\n")
663 ui.status(msg % short(node))
663 ui.status(msg % short(node))
664 return 0
664 return 0
665
665
666 def commitfunc(ui, repo, message, match, opts):
666 def commitfunc(ui, repo, message, match, opts):
667 editform = 'backout'
667 editform = 'backout'
668 e = cmdutil.getcommiteditor(editform=editform,
668 e = cmdutil.getcommiteditor(editform=editform,
669 **pycompat.strkwargs(opts))
669 **pycompat.strkwargs(opts))
670 if not message:
670 if not message:
671 # we don't translate commit messages
671 # we don't translate commit messages
672 message = "Backed out changeset %s" % short(node)
672 message = "Backed out changeset %s" % short(node)
673 e = cmdutil.getcommiteditor(edit=True, editform=editform)
673 e = cmdutil.getcommiteditor(edit=True, editform=editform)
674 return repo.commit(message, opts.get('user'), opts.get('date'),
674 return repo.commit(message, opts.get('user'), opts.get('date'),
675 match, editor=e)
675 match, editor=e)
676 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
676 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
677 if not newnode:
677 if not newnode:
678 ui.status(_("nothing changed\n"))
678 ui.status(_("nothing changed\n"))
679 return 1
679 return 1
680 cmdutil.commitstatus(repo, newnode, branch, bheads)
680 cmdutil.commitstatus(repo, newnode, branch, bheads)
681
681
682 def nice(node):
682 def nice(node):
683 return '%d:%s' % (repo.changelog.rev(node), short(node))
683 return '%d:%s' % (repo.changelog.rev(node), short(node))
684 ui.status(_('changeset %s backs out changeset %s\n') %
684 ui.status(_('changeset %s backs out changeset %s\n') %
685 (nice(repo.changelog.tip()), nice(node)))
685 (nice(repo.changelog.tip()), nice(node)))
686 if opts.get('merge') and op1 != node:
686 if opts.get('merge') and op1 != node:
687 hg.clean(repo, op1, show_stats=False)
687 hg.clean(repo, op1, show_stats=False)
688 ui.status(_('merging with changeset %s\n')
688 ui.status(_('merging with changeset %s\n')
689 % nice(repo.changelog.tip()))
689 % nice(repo.changelog.tip()))
690 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
690 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
691 with ui.configoverride(overrides, 'backout'):
691 with ui.configoverride(overrides, 'backout'):
692 return hg.merge(repo, hex(repo.changelog.tip()))
692 return hg.merge(repo, hex(repo.changelog.tip()))
693 return 0
693 return 0
694
694
695 @command('bisect',
695 @command('bisect',
696 [('r', 'reset', False, _('reset bisect state')),
696 [('r', 'reset', False, _('reset bisect state')),
697 ('g', 'good', False, _('mark changeset good')),
697 ('g', 'good', False, _('mark changeset good')),
698 ('b', 'bad', False, _('mark changeset bad')),
698 ('b', 'bad', False, _('mark changeset bad')),
699 ('s', 'skip', False, _('skip testing changeset')),
699 ('s', 'skip', False, _('skip testing changeset')),
700 ('e', 'extend', False, _('extend the bisect range')),
700 ('e', 'extend', False, _('extend the bisect range')),
701 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
701 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
702 ('U', 'noupdate', False, _('do not update to target'))],
702 ('U', 'noupdate', False, _('do not update to target'))],
703 _("[-gbsr] [-U] [-c CMD] [REV]"),
703 _("[-gbsr] [-U] [-c CMD] [REV]"),
704 helpcategory=command.CATEGORY_CHANGE_NAVIGATION)
704 helpcategory=command.CATEGORY_CHANGE_NAVIGATION)
705 def bisect(ui, repo, rev=None, extra=None, command=None,
705 def bisect(ui, repo, rev=None, extra=None, command=None,
706 reset=None, good=None, bad=None, skip=None, extend=None,
706 reset=None, good=None, bad=None, skip=None, extend=None,
707 noupdate=None):
707 noupdate=None):
708 """subdivision search of changesets
708 """subdivision search of changesets
709
709
710 This command helps to find changesets which introduce problems. To
710 This command helps to find changesets which introduce problems. To
711 use, mark the earliest changeset you know exhibits the problem as
711 use, mark the earliest changeset you know exhibits the problem as
712 bad, then mark the latest changeset which is free from the problem
712 bad, then mark the latest changeset which is free from the problem
713 as good. Bisect will update your working directory to a revision
713 as good. Bisect will update your working directory to a revision
714 for testing (unless the -U/--noupdate option is specified). Once
714 for testing (unless the -U/--noupdate option is specified). Once
715 you have performed tests, mark the working directory as good or
715 you have performed tests, mark the working directory as good or
716 bad, and bisect will either update to another candidate changeset
716 bad, and bisect will either update to another candidate changeset
717 or announce that it has found the bad revision.
717 or announce that it has found the bad revision.
718
718
719 As a shortcut, you can also use the revision argument to mark a
719 As a shortcut, you can also use the revision argument to mark a
720 revision as good or bad without checking it out first.
720 revision as good or bad without checking it out first.
721
721
722 If you supply a command, it will be used for automatic bisection.
722 If you supply a command, it will be used for automatic bisection.
723 The environment variable HG_NODE will contain the ID of the
723 The environment variable HG_NODE will contain the ID of the
724 changeset being tested. The exit status of the command will be
724 changeset being tested. The exit status of the command will be
725 used to mark revisions as good or bad: status 0 means good, 125
725 used to mark revisions as good or bad: status 0 means good, 125
726 means to skip the revision, 127 (command not found) will abort the
726 means to skip the revision, 127 (command not found) will abort the
727 bisection, and any other non-zero exit status means the revision
727 bisection, and any other non-zero exit status means the revision
728 is bad.
728 is bad.
729
729
730 .. container:: verbose
730 .. container:: verbose
731
731
732 Some examples:
732 Some examples:
733
733
734 - start a bisection with known bad revision 34, and good revision 12::
734 - start a bisection with known bad revision 34, and good revision 12::
735
735
736 hg bisect --bad 34
736 hg bisect --bad 34
737 hg bisect --good 12
737 hg bisect --good 12
738
738
739 - advance the current bisection by marking current revision as good or
739 - advance the current bisection by marking current revision as good or
740 bad::
740 bad::
741
741
742 hg bisect --good
742 hg bisect --good
743 hg bisect --bad
743 hg bisect --bad
744
744
745 - mark the current revision, or a known revision, to be skipped (e.g. if
745 - mark the current revision, or a known revision, to be skipped (e.g. if
746 that revision is not usable because of another issue)::
746 that revision is not usable because of another issue)::
747
747
748 hg bisect --skip
748 hg bisect --skip
749 hg bisect --skip 23
749 hg bisect --skip 23
750
750
751 - skip all revisions that do not touch directories ``foo`` or ``bar``::
751 - skip all revisions that do not touch directories ``foo`` or ``bar``::
752
752
753 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
753 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
754
754
755 - forget the current bisection::
755 - forget the current bisection::
756
756
757 hg bisect --reset
757 hg bisect --reset
758
758
759 - use 'make && make tests' to automatically find the first broken
759 - use 'make && make tests' to automatically find the first broken
760 revision::
760 revision::
761
761
762 hg bisect --reset
762 hg bisect --reset
763 hg bisect --bad 34
763 hg bisect --bad 34
764 hg bisect --good 12
764 hg bisect --good 12
765 hg bisect --command "make && make tests"
765 hg bisect --command "make && make tests"
766
766
767 - see all changesets whose states are already known in the current
767 - see all changesets whose states are already known in the current
768 bisection::
768 bisection::
769
769
770 hg log -r "bisect(pruned)"
770 hg log -r "bisect(pruned)"
771
771
772 - see the changeset currently being bisected (especially useful
772 - see the changeset currently being bisected (especially useful
773 if running with -U/--noupdate)::
773 if running with -U/--noupdate)::
774
774
775 hg log -r "bisect(current)"
775 hg log -r "bisect(current)"
776
776
777 - see all changesets that took part in the current bisection::
777 - see all changesets that took part in the current bisection::
778
778
779 hg log -r "bisect(range)"
779 hg log -r "bisect(range)"
780
780
781 - you can even get a nice graph::
781 - you can even get a nice graph::
782
782
783 hg log --graph -r "bisect(range)"
783 hg log --graph -r "bisect(range)"
784
784
785 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
785 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
786
786
787 Returns 0 on success.
787 Returns 0 on success.
788 """
788 """
789 # backward compatibility
789 # backward compatibility
790 if rev in "good bad reset init".split():
790 if rev in "good bad reset init".split():
791 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
791 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
792 cmd, rev, extra = rev, extra, None
792 cmd, rev, extra = rev, extra, None
793 if cmd == "good":
793 if cmd == "good":
794 good = True
794 good = True
795 elif cmd == "bad":
795 elif cmd == "bad":
796 bad = True
796 bad = True
797 else:
797 else:
798 reset = True
798 reset = True
799 elif extra:
799 elif extra:
800 raise error.Abort(_('incompatible arguments'))
800 raise error.Abort(_('incompatible arguments'))
801
801
802 incompatibles = {
802 incompatibles = {
803 '--bad': bad,
803 '--bad': bad,
804 '--command': bool(command),
804 '--command': bool(command),
805 '--extend': extend,
805 '--extend': extend,
806 '--good': good,
806 '--good': good,
807 '--reset': reset,
807 '--reset': reset,
808 '--skip': skip,
808 '--skip': skip,
809 }
809 }
810
810
811 enabled = [x for x in incompatibles if incompatibles[x]]
811 enabled = [x for x in incompatibles if incompatibles[x]]
812
812
813 if len(enabled) > 1:
813 if len(enabled) > 1:
814 raise error.Abort(_('%s and %s are incompatible') %
814 raise error.Abort(_('%s and %s are incompatible') %
815 tuple(sorted(enabled)[0:2]))
815 tuple(sorted(enabled)[0:2]))
816
816
817 if reset:
817 if reset:
818 hbisect.resetstate(repo)
818 hbisect.resetstate(repo)
819 return
819 return
820
820
821 state = hbisect.load_state(repo)
821 state = hbisect.load_state(repo)
822
822
823 # update state
823 # update state
824 if good or bad or skip:
824 if good or bad or skip:
825 if rev:
825 if rev:
826 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
826 nodes = [repo[i].node() for i in scmutil.revrange(repo, [rev])]
827 else:
827 else:
828 nodes = [repo.lookup('.')]
828 nodes = [repo.lookup('.')]
829 if good:
829 if good:
830 state['good'] += nodes
830 state['good'] += nodes
831 elif bad:
831 elif bad:
832 state['bad'] += nodes
832 state['bad'] += nodes
833 elif skip:
833 elif skip:
834 state['skip'] += nodes
834 state['skip'] += nodes
835 hbisect.save_state(repo, state)
835 hbisect.save_state(repo, state)
836 if not (state['good'] and state['bad']):
836 if not (state['good'] and state['bad']):
837 return
837 return
838
838
839 def mayupdate(repo, node, show_stats=True):
839 def mayupdate(repo, node, show_stats=True):
840 """common used update sequence"""
840 """common used update sequence"""
841 if noupdate:
841 if noupdate:
842 return
842 return
843 cmdutil.checkunfinished(repo)
843 cmdutil.checkunfinished(repo)
844 cmdutil.bailifchanged(repo)
844 cmdutil.bailifchanged(repo)
845 return hg.clean(repo, node, show_stats=show_stats)
845 return hg.clean(repo, node, show_stats=show_stats)
846
846
847 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
847 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
848
848
849 if command:
849 if command:
850 changesets = 1
850 changesets = 1
851 if noupdate:
851 if noupdate:
852 try:
852 try:
853 node = state['current'][0]
853 node = state['current'][0]
854 except LookupError:
854 except LookupError:
855 raise error.Abort(_('current bisect revision is unknown - '
855 raise error.Abort(_('current bisect revision is unknown - '
856 'start a new bisect to fix'))
856 'start a new bisect to fix'))
857 else:
857 else:
858 node, p2 = repo.dirstate.parents()
858 node, p2 = repo.dirstate.parents()
859 if p2 != nullid:
859 if p2 != nullid:
860 raise error.Abort(_('current bisect revision is a merge'))
860 raise error.Abort(_('current bisect revision is a merge'))
861 if rev:
861 if rev:
862 node = repo[scmutil.revsingle(repo, rev, node)].node()
862 node = repo[scmutil.revsingle(repo, rev, node)].node()
863 try:
863 try:
864 while changesets:
864 while changesets:
865 # update state
865 # update state
866 state['current'] = [node]
866 state['current'] = [node]
867 hbisect.save_state(repo, state)
867 hbisect.save_state(repo, state)
868 status = ui.system(command, environ={'HG_NODE': hex(node)},
868 status = ui.system(command, environ={'HG_NODE': hex(node)},
869 blockedtag='bisect_check')
869 blockedtag='bisect_check')
870 if status == 125:
870 if status == 125:
871 transition = "skip"
871 transition = "skip"
872 elif status == 0:
872 elif status == 0:
873 transition = "good"
873 transition = "good"
874 # status < 0 means process was killed
874 # status < 0 means process was killed
875 elif status == 127:
875 elif status == 127:
876 raise error.Abort(_("failed to execute %s") % command)
876 raise error.Abort(_("failed to execute %s") % command)
877 elif status < 0:
877 elif status < 0:
878 raise error.Abort(_("%s killed") % command)
878 raise error.Abort(_("%s killed") % command)
879 else:
879 else:
880 transition = "bad"
880 transition = "bad"
881 state[transition].append(node)
881 state[transition].append(node)
882 ctx = repo[node]
882 ctx = repo[node]
883 ui.status(_('changeset %d:%s: %s\n') % (ctx.rev(), ctx,
883 ui.status(_('changeset %d:%s: %s\n') % (ctx.rev(), ctx,
884 transition))
884 transition))
885 hbisect.checkstate(state)
885 hbisect.checkstate(state)
886 # bisect
886 # bisect
887 nodes, changesets, bgood = hbisect.bisect(repo, state)
887 nodes, changesets, bgood = hbisect.bisect(repo, state)
888 # update to next check
888 # update to next check
889 node = nodes[0]
889 node = nodes[0]
890 mayupdate(repo, node, show_stats=False)
890 mayupdate(repo, node, show_stats=False)
891 finally:
891 finally:
892 state['current'] = [node]
892 state['current'] = [node]
893 hbisect.save_state(repo, state)
893 hbisect.save_state(repo, state)
894 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
894 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
895 return
895 return
896
896
897 hbisect.checkstate(state)
897 hbisect.checkstate(state)
898
898
899 # actually bisect
899 # actually bisect
900 nodes, changesets, good = hbisect.bisect(repo, state)
900 nodes, changesets, good = hbisect.bisect(repo, state)
901 if extend:
901 if extend:
902 if not changesets:
902 if not changesets:
903 extendnode = hbisect.extendrange(repo, state, nodes, good)
903 extendnode = hbisect.extendrange(repo, state, nodes, good)
904 if extendnode is not None:
904 if extendnode is not None:
905 ui.write(_("Extending search to changeset %d:%s\n")
905 ui.write(_("Extending search to changeset %d:%s\n")
906 % (extendnode.rev(), extendnode))
906 % (extendnode.rev(), extendnode))
907 state['current'] = [extendnode.node()]
907 state['current'] = [extendnode.node()]
908 hbisect.save_state(repo, state)
908 hbisect.save_state(repo, state)
909 return mayupdate(repo, extendnode.node())
909 return mayupdate(repo, extendnode.node())
910 raise error.Abort(_("nothing to extend"))
910 raise error.Abort(_("nothing to extend"))
911
911
912 if changesets == 0:
912 if changesets == 0:
913 hbisect.printresult(ui, repo, state, displayer, nodes, good)
913 hbisect.printresult(ui, repo, state, displayer, nodes, good)
914 else:
914 else:
915 assert len(nodes) == 1 # only a single node can be tested next
915 assert len(nodes) == 1 # only a single node can be tested next
916 node = nodes[0]
916 node = nodes[0]
917 # compute the approximate number of remaining tests
917 # compute the approximate number of remaining tests
918 tests, size = 0, 2
918 tests, size = 0, 2
919 while size <= changesets:
919 while size <= changesets:
920 tests, size = tests + 1, size * 2
920 tests, size = tests + 1, size * 2
921 rev = repo.changelog.rev(node)
921 rev = repo.changelog.rev(node)
922 ui.write(_("Testing changeset %d:%s "
922 ui.write(_("Testing changeset %d:%s "
923 "(%d changesets remaining, ~%d tests)\n")
923 "(%d changesets remaining, ~%d tests)\n")
924 % (rev, short(node), changesets, tests))
924 % (rev, short(node), changesets, tests))
925 state['current'] = [node]
925 state['current'] = [node]
926 hbisect.save_state(repo, state)
926 hbisect.save_state(repo, state)
927 return mayupdate(repo, node)
927 return mayupdate(repo, node)
928
928
929 @command('bookmarks|bookmark',
929 @command('bookmarks|bookmark',
930 [('f', 'force', False, _('force')),
930 [('f', 'force', False, _('force')),
931 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
931 ('r', 'rev', '', _('revision for bookmark action'), _('REV')),
932 ('d', 'delete', False, _('delete a given bookmark')),
932 ('d', 'delete', False, _('delete a given bookmark')),
933 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
933 ('m', 'rename', '', _('rename a given bookmark'), _('OLD')),
934 ('i', 'inactive', False, _('mark a bookmark inactive')),
934 ('i', 'inactive', False, _('mark a bookmark inactive')),
935 ('l', 'list', False, _('list existing bookmarks')),
935 ('l', 'list', False, _('list existing bookmarks')),
936 ] + formatteropts,
936 ] + formatteropts,
937 _('hg bookmarks [OPTIONS]... [NAME]...'),
937 _('hg bookmarks [OPTIONS]... [NAME]...'),
938 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
938 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
939 def bookmark(ui, repo, *names, **opts):
939 def bookmark(ui, repo, *names, **opts):
940 '''create a new bookmark or list existing bookmarks
940 '''create a new bookmark or list existing bookmarks
941
941
942 Bookmarks are labels on changesets to help track lines of development.
942 Bookmarks are labels on changesets to help track lines of development.
943 Bookmarks are unversioned and can be moved, renamed and deleted.
943 Bookmarks are unversioned and can be moved, renamed and deleted.
944 Deleting or moving a bookmark has no effect on the associated changesets.
944 Deleting or moving a bookmark has no effect on the associated changesets.
945
945
946 Creating or updating to a bookmark causes it to be marked as 'active'.
946 Creating or updating to a bookmark causes it to be marked as 'active'.
947 The active bookmark is indicated with a '*'.
947 The active bookmark is indicated with a '*'.
948 When a commit is made, the active bookmark will advance to the new commit.
948 When a commit is made, the active bookmark will advance to the new commit.
949 A plain :hg:`update` will also advance an active bookmark, if possible.
949 A plain :hg:`update` will also advance an active bookmark, if possible.
950 Updating away from a bookmark will cause it to be deactivated.
950 Updating away from a bookmark will cause it to be deactivated.
951
951
952 Bookmarks can be pushed and pulled between repositories (see
952 Bookmarks can be pushed and pulled between repositories (see
953 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
953 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
954 diverged, a new 'divergent bookmark' of the form 'name@path' will
954 diverged, a new 'divergent bookmark' of the form 'name@path' will
955 be created. Using :hg:`merge` will resolve the divergence.
955 be created. Using :hg:`merge` will resolve the divergence.
956
956
957 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
957 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
958 the active bookmark's name.
958 the active bookmark's name.
959
959
960 A bookmark named '@' has the special property that :hg:`clone` will
960 A bookmark named '@' has the special property that :hg:`clone` will
961 check it out by default if it exists.
961 check it out by default if it exists.
962
962
963 .. container:: verbose
963 .. container:: verbose
964
964
965 Template:
965 Template:
966
966
967 The following keywords are supported in addition to the common template
967 The following keywords are supported in addition to the common template
968 keywords and functions such as ``{bookmark}``. See also
968 keywords and functions such as ``{bookmark}``. See also
969 :hg:`help templates`.
969 :hg:`help templates`.
970
970
971 :active: Boolean. True if the bookmark is active.
971 :active: Boolean. True if the bookmark is active.
972
972
973 Examples:
973 Examples:
974
974
975 - create an active bookmark for a new line of development::
975 - create an active bookmark for a new line of development::
976
976
977 hg book new-feature
977 hg book new-feature
978
978
979 - create an inactive bookmark as a place marker::
979 - create an inactive bookmark as a place marker::
980
980
981 hg book -i reviewed
981 hg book -i reviewed
982
982
983 - create an inactive bookmark on another changeset::
983 - create an inactive bookmark on another changeset::
984
984
985 hg book -r .^ tested
985 hg book -r .^ tested
986
986
987 - rename bookmark turkey to dinner::
987 - rename bookmark turkey to dinner::
988
988
989 hg book -m turkey dinner
989 hg book -m turkey dinner
990
990
991 - move the '@' bookmark from another branch::
991 - move the '@' bookmark from another branch::
992
992
993 hg book -f @
993 hg book -f @
994
994
995 - print only the active bookmark name::
995 - print only the active bookmark name::
996
996
997 hg book -ql .
997 hg book -ql .
998 '''
998 '''
999 opts = pycompat.byteskwargs(opts)
999 opts = pycompat.byteskwargs(opts)
1000 force = opts.get('force')
1000 force = opts.get('force')
1001 rev = opts.get('rev')
1001 rev = opts.get('rev')
1002 inactive = opts.get('inactive') # meaning add/rename to inactive bookmark
1002 inactive = opts.get('inactive') # meaning add/rename to inactive bookmark
1003
1003
1004 selactions = [k for k in ['delete', 'rename', 'list'] if opts.get(k)]
1004 selactions = [k for k in ['delete', 'rename', 'list'] if opts.get(k)]
1005 if len(selactions) > 1:
1005 if len(selactions) > 1:
1006 raise error.Abort(_('--%s and --%s are incompatible')
1006 raise error.Abort(_('--%s and --%s are incompatible')
1007 % tuple(selactions[:2]))
1007 % tuple(selactions[:2]))
1008 if selactions:
1008 if selactions:
1009 action = selactions[0]
1009 action = selactions[0]
1010 elif names or rev:
1010 elif names or rev:
1011 action = 'add'
1011 action = 'add'
1012 elif inactive:
1012 elif inactive:
1013 action = 'inactive' # meaning deactivate
1013 action = 'inactive' # meaning deactivate
1014 else:
1014 else:
1015 action = 'list'
1015 action = 'list'
1016
1016
1017 if rev and action in {'delete', 'rename', 'list'}:
1017 if rev and action in {'delete', 'rename', 'list'}:
1018 raise error.Abort(_("--rev is incompatible with --%s") % action)
1018 raise error.Abort(_("--rev is incompatible with --%s") % action)
1019 if inactive and action in {'delete', 'list'}:
1019 if inactive and action in {'delete', 'list'}:
1020 raise error.Abort(_("--inactive is incompatible with --%s") % action)
1020 raise error.Abort(_("--inactive is incompatible with --%s") % action)
1021 if not names and action in {'add', 'delete'}:
1021 if not names and action in {'add', 'delete'}:
1022 raise error.Abort(_("bookmark name required"))
1022 raise error.Abort(_("bookmark name required"))
1023
1023
1024 if action in {'add', 'delete', 'rename', 'inactive'}:
1024 if action in {'add', 'delete', 'rename', 'inactive'}:
1025 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
1025 with repo.wlock(), repo.lock(), repo.transaction('bookmark') as tr:
1026 if action == 'delete':
1026 if action == 'delete':
1027 names = pycompat.maplist(repo._bookmarks.expandname, names)
1027 names = pycompat.maplist(repo._bookmarks.expandname, names)
1028 bookmarks.delete(repo, tr, names)
1028 bookmarks.delete(repo, tr, names)
1029 elif action == 'rename':
1029 elif action == 'rename':
1030 if not names:
1030 if not names:
1031 raise error.Abort(_("new bookmark name required"))
1031 raise error.Abort(_("new bookmark name required"))
1032 elif len(names) > 1:
1032 elif len(names) > 1:
1033 raise error.Abort(_("only one new bookmark name allowed"))
1033 raise error.Abort(_("only one new bookmark name allowed"))
1034 oldname = repo._bookmarks.expandname(opts['rename'])
1034 oldname = repo._bookmarks.expandname(opts['rename'])
1035 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1035 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1036 elif action == 'add':
1036 elif action == 'add':
1037 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1037 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1038 elif action == 'inactive':
1038 elif action == 'inactive':
1039 if len(repo._bookmarks) == 0:
1039 if len(repo._bookmarks) == 0:
1040 ui.status(_("no bookmarks set\n"))
1040 ui.status(_("no bookmarks set\n"))
1041 elif not repo._activebookmark:
1041 elif not repo._activebookmark:
1042 ui.status(_("no active bookmark\n"))
1042 ui.status(_("no active bookmark\n"))
1043 else:
1043 else:
1044 bookmarks.deactivate(repo)
1044 bookmarks.deactivate(repo)
1045 elif action == 'list':
1045 elif action == 'list':
1046 names = pycompat.maplist(repo._bookmarks.expandname, names)
1046 names = pycompat.maplist(repo._bookmarks.expandname, names)
1047 with ui.formatter('bookmarks', opts) as fm:
1047 with ui.formatter('bookmarks', opts) as fm:
1048 bookmarks.printbookmarks(ui, repo, fm, names)
1048 bookmarks.printbookmarks(ui, repo, fm, names)
1049 else:
1049 else:
1050 raise error.ProgrammingError('invalid action: %s' % action)
1050 raise error.ProgrammingError('invalid action: %s' % action)
1051
1051
1052 @command('branch',
1052 @command('branch',
1053 [('f', 'force', None,
1053 [('f', 'force', None,
1054 _('set branch name even if it shadows an existing branch')),
1054 _('set branch name even if it shadows an existing branch')),
1055 ('C', 'clean', None, _('reset branch name to parent branch name')),
1055 ('C', 'clean', None, _('reset branch name to parent branch name')),
1056 ('r', 'rev', [], _('change branches of the given revs (EXPERIMENTAL)')),
1056 ('r', 'rev', [], _('change branches of the given revs (EXPERIMENTAL)')),
1057 ],
1057 ],
1058 _('[-fC] [NAME]'),
1058 _('[-fC] [NAME]'),
1059 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
1059 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
1060 def branch(ui, repo, label=None, **opts):
1060 def branch(ui, repo, label=None, **opts):
1061 """set or show the current branch name
1061 """set or show the current branch name
1062
1062
1063 .. note::
1063 .. note::
1064
1064
1065 Branch names are permanent and global. Use :hg:`bookmark` to create a
1065 Branch names are permanent and global. Use :hg:`bookmark` to create a
1066 light-weight bookmark instead. See :hg:`help glossary` for more
1066 light-weight bookmark instead. See :hg:`help glossary` for more
1067 information about named branches and bookmarks.
1067 information about named branches and bookmarks.
1068
1068
1069 With no argument, show the current branch name. With one argument,
1069 With no argument, show the current branch name. With one argument,
1070 set the working directory branch name (the branch will not exist
1070 set the working directory branch name (the branch will not exist
1071 in the repository until the next commit). Standard practice
1071 in the repository until the next commit). Standard practice
1072 recommends that primary development take place on the 'default'
1072 recommends that primary development take place on the 'default'
1073 branch.
1073 branch.
1074
1074
1075 Unless -f/--force is specified, branch will not let you set a
1075 Unless -f/--force is specified, branch will not let you set a
1076 branch name that already exists.
1076 branch name that already exists.
1077
1077
1078 Use -C/--clean to reset the working directory branch to that of
1078 Use -C/--clean to reset the working directory branch to that of
1079 the parent of the working directory, negating a previous branch
1079 the parent of the working directory, negating a previous branch
1080 change.
1080 change.
1081
1081
1082 Use the command :hg:`update` to switch to an existing branch. Use
1082 Use the command :hg:`update` to switch to an existing branch. Use
1083 :hg:`commit --close-branch` to mark this branch head as closed.
1083 :hg:`commit --close-branch` to mark this branch head as closed.
1084 When all heads of a branch are closed, the branch will be
1084 When all heads of a branch are closed, the branch will be
1085 considered closed.
1085 considered closed.
1086
1086
1087 Returns 0 on success.
1087 Returns 0 on success.
1088 """
1088 """
1089 opts = pycompat.byteskwargs(opts)
1089 opts = pycompat.byteskwargs(opts)
1090 revs = opts.get('rev')
1090 revs = opts.get('rev')
1091 if label:
1091 if label:
1092 label = label.strip()
1092 label = label.strip()
1093
1093
1094 if not opts.get('clean') and not label:
1094 if not opts.get('clean') and not label:
1095 if revs:
1095 if revs:
1096 raise error.Abort(_("no branch name specified for the revisions"))
1096 raise error.Abort(_("no branch name specified for the revisions"))
1097 ui.write("%s\n" % repo.dirstate.branch())
1097 ui.write("%s\n" % repo.dirstate.branch())
1098 return
1098 return
1099
1099
1100 with repo.wlock():
1100 with repo.wlock():
1101 if opts.get('clean'):
1101 if opts.get('clean'):
1102 label = repo[None].p1().branch()
1102 label = repo[None].p1().branch()
1103 repo.dirstate.setbranch(label)
1103 repo.dirstate.setbranch(label)
1104 ui.status(_('reset working directory to branch %s\n') % label)
1104 ui.status(_('reset working directory to branch %s\n') % label)
1105 elif label:
1105 elif label:
1106
1106
1107 scmutil.checknewlabel(repo, label, 'branch')
1107 scmutil.checknewlabel(repo, label, 'branch')
1108 if revs:
1108 if revs:
1109 return cmdutil.changebranch(ui, repo, revs, label)
1109 return cmdutil.changebranch(ui, repo, revs, label)
1110
1110
1111 if not opts.get('force') and label in repo.branchmap():
1111 if not opts.get('force') and label in repo.branchmap():
1112 if label not in [p.branch() for p in repo[None].parents()]:
1112 if label not in [p.branch() for p in repo[None].parents()]:
1113 raise error.Abort(_('a branch of the same name already'
1113 raise error.Abort(_('a branch of the same name already'
1114 ' exists'),
1114 ' exists'),
1115 # i18n: "it" refers to an existing branch
1115 # i18n: "it" refers to an existing branch
1116 hint=_("use 'hg update' to switch to it"))
1116 hint=_("use 'hg update' to switch to it"))
1117
1117
1118 repo.dirstate.setbranch(label)
1118 repo.dirstate.setbranch(label)
1119 ui.status(_('marked working directory as branch %s\n') % label)
1119 ui.status(_('marked working directory as branch %s\n') % label)
1120
1120
1121 # find any open named branches aside from default
1121 # find any open named branches aside from default
1122 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1122 others = [n for n, h, t, c in repo.branchmap().iterbranches()
1123 if n != "default" and not c]
1123 if n != "default" and not c]
1124 if not others:
1124 if not others:
1125 ui.status(_('(branches are permanent and global, '
1125 ui.status(_('(branches are permanent and global, '
1126 'did you want a bookmark?)\n'))
1126 'did you want a bookmark?)\n'))
1127
1127
1128 @command('branches',
1128 @command('branches',
1129 [('a', 'active', False,
1129 [('a', 'active', False,
1130 _('show only branches that have unmerged heads (DEPRECATED)')),
1130 _('show only branches that have unmerged heads (DEPRECATED)')),
1131 ('c', 'closed', False, _('show normal and closed branches')),
1131 ('c', 'closed', False, _('show normal and closed branches')),
1132 ] + formatteropts,
1132 ] + formatteropts,
1133 _('[-c]'),
1133 _('[-c]'),
1134 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1134 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1135 intents={INTENT_READONLY})
1135 intents={INTENT_READONLY})
1136 def branches(ui, repo, active=False, closed=False, **opts):
1136 def branches(ui, repo, active=False, closed=False, **opts):
1137 """list repository named branches
1137 """list repository named branches
1138
1138
1139 List the repository's named branches, indicating which ones are
1139 List the repository's named branches, indicating which ones are
1140 inactive. If -c/--closed is specified, also list branches which have
1140 inactive. If -c/--closed is specified, also list branches which have
1141 been marked closed (see :hg:`commit --close-branch`).
1141 been marked closed (see :hg:`commit --close-branch`).
1142
1142
1143 Use the command :hg:`update` to switch to an existing branch.
1143 Use the command :hg:`update` to switch to an existing branch.
1144
1144
1145 .. container:: verbose
1145 .. container:: verbose
1146
1146
1147 Template:
1147 Template:
1148
1148
1149 The following keywords are supported in addition to the common template
1149 The following keywords are supported in addition to the common template
1150 keywords and functions such as ``{branch}``. See also
1150 keywords and functions such as ``{branch}``. See also
1151 :hg:`help templates`.
1151 :hg:`help templates`.
1152
1152
1153 :active: Boolean. True if the branch is active.
1153 :active: Boolean. True if the branch is active.
1154 :closed: Boolean. True if the branch is closed.
1154 :closed: Boolean. True if the branch is closed.
1155 :current: Boolean. True if it is the current branch.
1155 :current: Boolean. True if it is the current branch.
1156
1156
1157 Returns 0.
1157 Returns 0.
1158 """
1158 """
1159
1159
1160 opts = pycompat.byteskwargs(opts)
1160 opts = pycompat.byteskwargs(opts)
1161 ui.pager('branches')
1161 ui.pager('branches')
1162 fm = ui.formatter('branches', opts)
1162 fm = ui.formatter('branches', opts)
1163 hexfunc = fm.hexfunc
1163 hexfunc = fm.hexfunc
1164
1164
1165 allheads = set(repo.heads())
1165 allheads = set(repo.heads())
1166 branches = []
1166 branches = []
1167 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1167 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1168 isactive = False
1168 isactive = False
1169 if not isclosed:
1169 if not isclosed:
1170 openheads = set(repo.branchmap().iteropen(heads))
1170 openheads = set(repo.branchmap().iteropen(heads))
1171 isactive = bool(openheads & allheads)
1171 isactive = bool(openheads & allheads)
1172 branches.append((tag, repo[tip], isactive, not isclosed))
1172 branches.append((tag, repo[tip], isactive, not isclosed))
1173 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1173 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1174 reverse=True)
1174 reverse=True)
1175
1175
1176 for tag, ctx, isactive, isopen in branches:
1176 for tag, ctx, isactive, isopen in branches:
1177 if active and not isactive:
1177 if active and not isactive:
1178 continue
1178 continue
1179 if isactive:
1179 if isactive:
1180 label = 'branches.active'
1180 label = 'branches.active'
1181 notice = ''
1181 notice = ''
1182 elif not isopen:
1182 elif not isopen:
1183 if not closed:
1183 if not closed:
1184 continue
1184 continue
1185 label = 'branches.closed'
1185 label = 'branches.closed'
1186 notice = _(' (closed)')
1186 notice = _(' (closed)')
1187 else:
1187 else:
1188 label = 'branches.inactive'
1188 label = 'branches.inactive'
1189 notice = _(' (inactive)')
1189 notice = _(' (inactive)')
1190 current = (tag == repo.dirstate.branch())
1190 current = (tag == repo.dirstate.branch())
1191 if current:
1191 if current:
1192 label = 'branches.current'
1192 label = 'branches.current'
1193
1193
1194 fm.startitem()
1194 fm.startitem()
1195 fm.write('branch', '%s', tag, label=label)
1195 fm.write('branch', '%s', tag, label=label)
1196 rev = ctx.rev()
1196 rev = ctx.rev()
1197 padsize = max(31 - len("%d" % rev) - encoding.colwidth(tag), 0)
1197 padsize = max(31 - len("%d" % rev) - encoding.colwidth(tag), 0)
1198 fmt = ' ' * padsize + ' %d:%s'
1198 fmt = ' ' * padsize + ' %d:%s'
1199 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1199 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1200 label='log.changeset changeset.%s' % ctx.phasestr())
1200 label='log.changeset changeset.%s' % ctx.phasestr())
1201 fm.context(ctx=ctx)
1201 fm.context(ctx=ctx)
1202 fm.data(active=isactive, closed=not isopen, current=current)
1202 fm.data(active=isactive, closed=not isopen, current=current)
1203 if not ui.quiet:
1203 if not ui.quiet:
1204 fm.plain(notice)
1204 fm.plain(notice)
1205 fm.plain('\n')
1205 fm.plain('\n')
1206 fm.end()
1206 fm.end()
1207
1207
1208 @command('bundle',
1208 @command('bundle',
1209 [('f', 'force', None, _('run even when the destination is unrelated')),
1209 [('f', 'force', None, _('run even when the destination is unrelated')),
1210 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1210 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1211 _('REV')),
1211 _('REV')),
1212 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1212 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1213 _('BRANCH')),
1213 _('BRANCH')),
1214 ('', 'base', [],
1214 ('', 'base', [],
1215 _('a base changeset assumed to be available at the destination'),
1215 _('a base changeset assumed to be available at the destination'),
1216 _('REV')),
1216 _('REV')),
1217 ('a', 'all', None, _('bundle all changesets in the repository')),
1217 ('a', 'all', None, _('bundle all changesets in the repository')),
1218 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1218 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1219 ] + remoteopts,
1219 ] + remoteopts,
1220 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1220 _('[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]'),
1221 helpcategory=command.CATEGORY_IMPORT_EXPORT)
1221 helpcategory=command.CATEGORY_IMPORT_EXPORT)
1222 def bundle(ui, repo, fname, dest=None, **opts):
1222 def bundle(ui, repo, fname, dest=None, **opts):
1223 """create a bundle file
1223 """create a bundle file
1224
1224
1225 Generate a bundle file containing data to be transferred to another
1225 Generate a bundle file containing data to be transferred to another
1226 repository.
1226 repository.
1227
1227
1228 To create a bundle containing all changesets, use -a/--all
1228 To create a bundle containing all changesets, use -a/--all
1229 (or --base null). Otherwise, hg assumes the destination will have
1229 (or --base null). Otherwise, hg assumes the destination will have
1230 all the nodes you specify with --base parameters. Otherwise, hg
1230 all the nodes you specify with --base parameters. Otherwise, hg
1231 will assume the repository has all the nodes in destination, or
1231 will assume the repository has all the nodes in destination, or
1232 default-push/default if no destination is specified, where destination
1232 default-push/default if no destination is specified, where destination
1233 is the repository you provide through DEST option.
1233 is the repository you provide through DEST option.
1234
1234
1235 You can change bundle format with the -t/--type option. See
1235 You can change bundle format with the -t/--type option. See
1236 :hg:`help bundlespec` for documentation on this format. By default,
1236 :hg:`help bundlespec` for documentation on this format. By default,
1237 the most appropriate format is used and compression defaults to
1237 the most appropriate format is used and compression defaults to
1238 bzip2.
1238 bzip2.
1239
1239
1240 The bundle file can then be transferred using conventional means
1240 The bundle file can then be transferred using conventional means
1241 and applied to another repository with the unbundle or pull
1241 and applied to another repository with the unbundle or pull
1242 command. This is useful when direct push and pull are not
1242 command. This is useful when direct push and pull are not
1243 available or when exporting an entire repository is undesirable.
1243 available or when exporting an entire repository is undesirable.
1244
1244
1245 Applying bundles preserves all changeset contents including
1245 Applying bundles preserves all changeset contents including
1246 permissions, copy/rename information, and revision history.
1246 permissions, copy/rename information, and revision history.
1247
1247
1248 Returns 0 on success, 1 if no changes found.
1248 Returns 0 on success, 1 if no changes found.
1249 """
1249 """
1250 opts = pycompat.byteskwargs(opts)
1250 opts = pycompat.byteskwargs(opts)
1251 revs = None
1251 revs = None
1252 if 'rev' in opts:
1252 if 'rev' in opts:
1253 revstrings = opts['rev']
1253 revstrings = opts['rev']
1254 revs = scmutil.revrange(repo, revstrings)
1254 revs = scmutil.revrange(repo, revstrings)
1255 if revstrings and not revs:
1255 if revstrings and not revs:
1256 raise error.Abort(_('no commits to bundle'))
1256 raise error.Abort(_('no commits to bundle'))
1257
1257
1258 bundletype = opts.get('type', 'bzip2').lower()
1258 bundletype = opts.get('type', 'bzip2').lower()
1259 try:
1259 try:
1260 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1260 bundlespec = exchange.parsebundlespec(repo, bundletype, strict=False)
1261 except error.UnsupportedBundleSpecification as e:
1261 except error.UnsupportedBundleSpecification as e:
1262 raise error.Abort(pycompat.bytestr(e),
1262 raise error.Abort(pycompat.bytestr(e),
1263 hint=_("see 'hg help bundlespec' for supported "
1263 hint=_("see 'hg help bundlespec' for supported "
1264 "values for --type"))
1264 "values for --type"))
1265 cgversion = bundlespec.contentopts["cg.version"]
1265 cgversion = bundlespec.contentopts["cg.version"]
1266
1266
1267 # Packed bundles are a pseudo bundle format for now.
1267 # Packed bundles are a pseudo bundle format for now.
1268 if cgversion == 's1':
1268 if cgversion == 's1':
1269 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1269 raise error.Abort(_('packed bundles cannot be produced by "hg bundle"'),
1270 hint=_("use 'hg debugcreatestreamclonebundle'"))
1270 hint=_("use 'hg debugcreatestreamclonebundle'"))
1271
1271
1272 if opts.get('all'):
1272 if opts.get('all'):
1273 if dest:
1273 if dest:
1274 raise error.Abort(_("--all is incompatible with specifying "
1274 raise error.Abort(_("--all is incompatible with specifying "
1275 "a destination"))
1275 "a destination"))
1276 if opts.get('base'):
1276 if opts.get('base'):
1277 ui.warn(_("ignoring --base because --all was specified\n"))
1277 ui.warn(_("ignoring --base because --all was specified\n"))
1278 base = [nullrev]
1278 base = [nullrev]
1279 else:
1279 else:
1280 base = scmutil.revrange(repo, opts.get('base'))
1280 base = scmutil.revrange(repo, opts.get('base'))
1281 if cgversion not in changegroup.supportedoutgoingversions(repo):
1281 if cgversion not in changegroup.supportedoutgoingversions(repo):
1282 raise error.Abort(_("repository does not support bundle version %s") %
1282 raise error.Abort(_("repository does not support bundle version %s") %
1283 cgversion)
1283 cgversion)
1284
1284
1285 if base:
1285 if base:
1286 if dest:
1286 if dest:
1287 raise error.Abort(_("--base is incompatible with specifying "
1287 raise error.Abort(_("--base is incompatible with specifying "
1288 "a destination"))
1288 "a destination"))
1289 common = [repo[rev].node() for rev in base]
1289 common = [repo[rev].node() for rev in base]
1290 heads = [repo[r].node() for r in revs] if revs else None
1290 heads = [repo[r].node() for r in revs] if revs else None
1291 outgoing = discovery.outgoing(repo, common, heads)
1291 outgoing = discovery.outgoing(repo, common, heads)
1292 else:
1292 else:
1293 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1293 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1294 dest, branches = hg.parseurl(dest, opts.get('branch'))
1294 dest, branches = hg.parseurl(dest, opts.get('branch'))
1295 other = hg.peer(repo, opts, dest)
1295 other = hg.peer(repo, opts, dest)
1296 revs = [repo[r].hex() for r in revs]
1296 revs = [repo[r].hex() for r in revs]
1297 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1297 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1298 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1298 heads = revs and pycompat.maplist(repo.lookup, revs) or revs
1299 outgoing = discovery.findcommonoutgoing(repo, other,
1299 outgoing = discovery.findcommonoutgoing(repo, other,
1300 onlyheads=heads,
1300 onlyheads=heads,
1301 force=opts.get('force'),
1301 force=opts.get('force'),
1302 portable=True)
1302 portable=True)
1303
1303
1304 if not outgoing.missing:
1304 if not outgoing.missing:
1305 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1305 scmutil.nochangesfound(ui, repo, not base and outgoing.excluded)
1306 return 1
1306 return 1
1307
1307
1308 if cgversion == '01': #bundle1
1308 if cgversion == '01': #bundle1
1309 bversion = 'HG10' + bundlespec.wirecompression
1309 bversion = 'HG10' + bundlespec.wirecompression
1310 bcompression = None
1310 bcompression = None
1311 elif cgversion in ('02', '03'):
1311 elif cgversion in ('02', '03'):
1312 bversion = 'HG20'
1312 bversion = 'HG20'
1313 bcompression = bundlespec.wirecompression
1313 bcompression = bundlespec.wirecompression
1314 else:
1314 else:
1315 raise error.ProgrammingError(
1315 raise error.ProgrammingError(
1316 'bundle: unexpected changegroup version %s' % cgversion)
1316 'bundle: unexpected changegroup version %s' % cgversion)
1317
1317
1318 # TODO compression options should be derived from bundlespec parsing.
1318 # TODO compression options should be derived from bundlespec parsing.
1319 # This is a temporary hack to allow adjusting bundle compression
1319 # This is a temporary hack to allow adjusting bundle compression
1320 # level without a) formalizing the bundlespec changes to declare it
1320 # level without a) formalizing the bundlespec changes to declare it
1321 # b) introducing a command flag.
1321 # b) introducing a command flag.
1322 compopts = {}
1322 compopts = {}
1323 complevel = ui.configint('experimental',
1323 complevel = ui.configint('experimental',
1324 'bundlecomplevel.' + bundlespec.compression)
1324 'bundlecomplevel.' + bundlespec.compression)
1325 if complevel is None:
1325 if complevel is None:
1326 complevel = ui.configint('experimental', 'bundlecomplevel')
1326 complevel = ui.configint('experimental', 'bundlecomplevel')
1327 if complevel is not None:
1327 if complevel is not None:
1328 compopts['level'] = complevel
1328 compopts['level'] = complevel
1329
1329
1330 # Allow overriding the bundling of obsmarker in phases through
1330 # Allow overriding the bundling of obsmarker in phases through
1331 # configuration while we don't have a bundle version that include them
1331 # configuration while we don't have a bundle version that include them
1332 if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker'):
1332 if repo.ui.configbool('experimental', 'evolution.bundle-obsmarker'):
1333 bundlespec.contentopts['obsolescence'] = True
1333 bundlespec.contentopts['obsolescence'] = True
1334 if repo.ui.configbool('experimental', 'bundle-phases'):
1334 if repo.ui.configbool('experimental', 'bundle-phases'):
1335 bundlespec.contentopts['phases'] = True
1335 bundlespec.contentopts['phases'] = True
1336
1336
1337 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1337 bundle2.writenewbundle(ui, repo, 'bundle', fname, bversion, outgoing,
1338 bundlespec.contentopts, compression=bcompression,
1338 bundlespec.contentopts, compression=bcompression,
1339 compopts=compopts)
1339 compopts=compopts)
1340
1340
1341 @command('cat',
1341 @command('cat',
1342 [('o', 'output', '',
1342 [('o', 'output', '',
1343 _('print output to file with formatted name'), _('FORMAT')),
1343 _('print output to file with formatted name'), _('FORMAT')),
1344 ('r', 'rev', '', _('print the given revision'), _('REV')),
1344 ('r', 'rev', '', _('print the given revision'), _('REV')),
1345 ('', 'decode', None, _('apply any matching decode filter')),
1345 ('', 'decode', None, _('apply any matching decode filter')),
1346 ] + walkopts + formatteropts,
1346 ] + walkopts + formatteropts,
1347 _('[OPTION]... FILE...'),
1347 _('[OPTION]... FILE...'),
1348 helpcategory=command.CATEGORY_FILE_CONTENTS,
1348 helpcategory=command.CATEGORY_FILE_CONTENTS,
1349 inferrepo=True,
1349 inferrepo=True,
1350 intents={INTENT_READONLY})
1350 intents={INTENT_READONLY})
1351 def cat(ui, repo, file1, *pats, **opts):
1351 def cat(ui, repo, file1, *pats, **opts):
1352 """output the current or given revision of files
1352 """output the current or given revision of files
1353
1353
1354 Print the specified files as they were at the given revision. If
1354 Print the specified files as they were at the given revision. If
1355 no revision is given, the parent of the working directory is used.
1355 no revision is given, the parent of the working directory is used.
1356
1356
1357 Output may be to a file, in which case the name of the file is
1357 Output may be to a file, in which case the name of the file is
1358 given using a template string. See :hg:`help templates`. In addition
1358 given using a template string. See :hg:`help templates`. In addition
1359 to the common template keywords, the following formatting rules are
1359 to the common template keywords, the following formatting rules are
1360 supported:
1360 supported:
1361
1361
1362 :``%%``: literal "%" character
1362 :``%%``: literal "%" character
1363 :``%s``: basename of file being printed
1363 :``%s``: basename of file being printed
1364 :``%d``: dirname of file being printed, or '.' if in repository root
1364 :``%d``: dirname of file being printed, or '.' if in repository root
1365 :``%p``: root-relative path name of file being printed
1365 :``%p``: root-relative path name of file being printed
1366 :``%H``: changeset hash (40 hexadecimal digits)
1366 :``%H``: changeset hash (40 hexadecimal digits)
1367 :``%R``: changeset revision number
1367 :``%R``: changeset revision number
1368 :``%h``: short-form changeset hash (12 hexadecimal digits)
1368 :``%h``: short-form changeset hash (12 hexadecimal digits)
1369 :``%r``: zero-padded changeset revision number
1369 :``%r``: zero-padded changeset revision number
1370 :``%b``: basename of the exporting repository
1370 :``%b``: basename of the exporting repository
1371 :``\\``: literal "\\" character
1371 :``\\``: literal "\\" character
1372
1372
1373 .. container:: verbose
1373 .. container:: verbose
1374
1374
1375 Template:
1375 Template:
1376
1376
1377 The following keywords are supported in addition to the common template
1377 The following keywords are supported in addition to the common template
1378 keywords and functions. See also :hg:`help templates`.
1378 keywords and functions. See also :hg:`help templates`.
1379
1379
1380 :data: String. File content.
1380 :data: String. File content.
1381 :path: String. Repository-absolute path of the file.
1381 :path: String. Repository-absolute path of the file.
1382
1382
1383 Returns 0 on success.
1383 Returns 0 on success.
1384 """
1384 """
1385 opts = pycompat.byteskwargs(opts)
1385 opts = pycompat.byteskwargs(opts)
1386 rev = opts.get('rev')
1386 rev = opts.get('rev')
1387 if rev:
1387 if rev:
1388 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
1388 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
1389 ctx = scmutil.revsingle(repo, rev)
1389 ctx = scmutil.revsingle(repo, rev)
1390 m = scmutil.match(ctx, (file1,) + pats, opts)
1390 m = scmutil.match(ctx, (file1,) + pats, opts)
1391 fntemplate = opts.pop('output', '')
1391 fntemplate = opts.pop('output', '')
1392 if cmdutil.isstdiofilename(fntemplate):
1392 if cmdutil.isstdiofilename(fntemplate):
1393 fntemplate = ''
1393 fntemplate = ''
1394
1394
1395 if fntemplate:
1395 if fntemplate:
1396 fm = formatter.nullformatter(ui, 'cat', opts)
1396 fm = formatter.nullformatter(ui, 'cat', opts)
1397 else:
1397 else:
1398 ui.pager('cat')
1398 ui.pager('cat')
1399 fm = ui.formatter('cat', opts)
1399 fm = ui.formatter('cat', opts)
1400 with fm:
1400 with fm:
1401 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '',
1401 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '',
1402 **pycompat.strkwargs(opts))
1402 **pycompat.strkwargs(opts))
1403
1403
1404 @command('clone',
1404 @command('clone',
1405 [('U', 'noupdate', None, _('the clone will include an empty working '
1405 [('U', 'noupdate', None, _('the clone will include an empty working '
1406 'directory (only a repository)')),
1406 'directory (only a repository)')),
1407 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1407 ('u', 'updaterev', '', _('revision, tag, or branch to check out'),
1408 _('REV')),
1408 _('REV')),
1409 ('r', 'rev', [], _('do not clone everything, but include this changeset'
1409 ('r', 'rev', [], _('do not clone everything, but include this changeset'
1410 ' and its ancestors'), _('REV')),
1410 ' and its ancestors'), _('REV')),
1411 ('b', 'branch', [], _('do not clone everything, but include this branch\'s'
1411 ('b', 'branch', [], _('do not clone everything, but include this branch\'s'
1412 ' changesets and their ancestors'), _('BRANCH')),
1412 ' changesets and their ancestors'), _('BRANCH')),
1413 ('', 'pull', None, _('use pull protocol to copy metadata')),
1413 ('', 'pull', None, _('use pull protocol to copy metadata')),
1414 ('', 'uncompressed', None,
1414 ('', 'uncompressed', None,
1415 _('an alias to --stream (DEPRECATED)')),
1415 _('an alias to --stream (DEPRECATED)')),
1416 ('', 'stream', None,
1416 ('', 'stream', None,
1417 _('clone with minimal data processing')),
1417 _('clone with minimal data processing')),
1418 ] + remoteopts,
1418 ] + remoteopts,
1419 _('[OPTION]... SOURCE [DEST]'),
1419 _('[OPTION]... SOURCE [DEST]'),
1420 helpcategory=command.CATEGORY_REPO_CREATION,
1420 helpcategory=command.CATEGORY_REPO_CREATION,
1421 helpbasic=True, norepo=True)
1421 helpbasic=True, norepo=True)
1422 def clone(ui, source, dest=None, **opts):
1422 def clone(ui, source, dest=None, **opts):
1423 """make a copy of an existing repository
1423 """make a copy of an existing repository
1424
1424
1425 Create a copy of an existing repository in a new directory.
1425 Create a copy of an existing repository in a new directory.
1426
1426
1427 If no destination directory name is specified, it defaults to the
1427 If no destination directory name is specified, it defaults to the
1428 basename of the source.
1428 basename of the source.
1429
1429
1430 The location of the source is added to the new repository's
1430 The location of the source is added to the new repository's
1431 ``.hg/hgrc`` file, as the default to be used for future pulls.
1431 ``.hg/hgrc`` file, as the default to be used for future pulls.
1432
1432
1433 Only local paths and ``ssh://`` URLs are supported as
1433 Only local paths and ``ssh://`` URLs are supported as
1434 destinations. For ``ssh://`` destinations, no working directory or
1434 destinations. For ``ssh://`` destinations, no working directory or
1435 ``.hg/hgrc`` will be created on the remote side.
1435 ``.hg/hgrc`` will be created on the remote side.
1436
1436
1437 If the source repository has a bookmark called '@' set, that
1437 If the source repository has a bookmark called '@' set, that
1438 revision will be checked out in the new repository by default.
1438 revision will be checked out in the new repository by default.
1439
1439
1440 To check out a particular version, use -u/--update, or
1440 To check out a particular version, use -u/--update, or
1441 -U/--noupdate to create a clone with no working directory.
1441 -U/--noupdate to create a clone with no working directory.
1442
1442
1443 To pull only a subset of changesets, specify one or more revisions
1443 To pull only a subset of changesets, specify one or more revisions
1444 identifiers with -r/--rev or branches with -b/--branch. The
1444 identifiers with -r/--rev or branches with -b/--branch. The
1445 resulting clone will contain only the specified changesets and
1445 resulting clone will contain only the specified changesets and
1446 their ancestors. These options (or 'clone src#rev dest') imply
1446 their ancestors. These options (or 'clone src#rev dest') imply
1447 --pull, even for local source repositories.
1447 --pull, even for local source repositories.
1448
1448
1449 In normal clone mode, the remote normalizes repository data into a common
1449 In normal clone mode, the remote normalizes repository data into a common
1450 exchange format and the receiving end translates this data into its local
1450 exchange format and the receiving end translates this data into its local
1451 storage format. --stream activates a different clone mode that essentially
1451 storage format. --stream activates a different clone mode that essentially
1452 copies repository files from the remote with minimal data processing. This
1452 copies repository files from the remote with minimal data processing. This
1453 significantly reduces the CPU cost of a clone both remotely and locally.
1453 significantly reduces the CPU cost of a clone both remotely and locally.
1454 However, it often increases the transferred data size by 30-40%. This can
1454 However, it often increases the transferred data size by 30-40%. This can
1455 result in substantially faster clones where I/O throughput is plentiful,
1455 result in substantially faster clones where I/O throughput is plentiful,
1456 especially for larger repositories. A side-effect of --stream clones is
1456 especially for larger repositories. A side-effect of --stream clones is
1457 that storage settings and requirements on the remote are applied locally:
1457 that storage settings and requirements on the remote are applied locally:
1458 a modern client may inherit legacy or inefficient storage used by the
1458 a modern client may inherit legacy or inefficient storage used by the
1459 remote or a legacy Mercurial client may not be able to clone from a
1459 remote or a legacy Mercurial client may not be able to clone from a
1460 modern Mercurial remote.
1460 modern Mercurial remote.
1461
1461
1462 .. note::
1462 .. note::
1463
1463
1464 Specifying a tag will include the tagged changeset but not the
1464 Specifying a tag will include the tagged changeset but not the
1465 changeset containing the tag.
1465 changeset containing the tag.
1466
1466
1467 .. container:: verbose
1467 .. container:: verbose
1468
1468
1469 For efficiency, hardlinks are used for cloning whenever the
1469 For efficiency, hardlinks are used for cloning whenever the
1470 source and destination are on the same filesystem (note this
1470 source and destination are on the same filesystem (note this
1471 applies only to the repository data, not to the working
1471 applies only to the repository data, not to the working
1472 directory). Some filesystems, such as AFS, implement hardlinking
1472 directory). Some filesystems, such as AFS, implement hardlinking
1473 incorrectly, but do not report errors. In these cases, use the
1473 incorrectly, but do not report errors. In these cases, use the
1474 --pull option to avoid hardlinking.
1474 --pull option to avoid hardlinking.
1475
1475
1476 Mercurial will update the working directory to the first applicable
1476 Mercurial will update the working directory to the first applicable
1477 revision from this list:
1477 revision from this list:
1478
1478
1479 a) null if -U or the source repository has no changesets
1479 a) null if -U or the source repository has no changesets
1480 b) if -u . and the source repository is local, the first parent of
1480 b) if -u . and the source repository is local, the first parent of
1481 the source repository's working directory
1481 the source repository's working directory
1482 c) the changeset specified with -u (if a branch name, this means the
1482 c) the changeset specified with -u (if a branch name, this means the
1483 latest head of that branch)
1483 latest head of that branch)
1484 d) the changeset specified with -r
1484 d) the changeset specified with -r
1485 e) the tipmost head specified with -b
1485 e) the tipmost head specified with -b
1486 f) the tipmost head specified with the url#branch source syntax
1486 f) the tipmost head specified with the url#branch source syntax
1487 g) the revision marked with the '@' bookmark, if present
1487 g) the revision marked with the '@' bookmark, if present
1488 h) the tipmost head of the default branch
1488 h) the tipmost head of the default branch
1489 i) tip
1489 i) tip
1490
1490
1491 When cloning from servers that support it, Mercurial may fetch
1491 When cloning from servers that support it, Mercurial may fetch
1492 pre-generated data from a server-advertised URL or inline from the
1492 pre-generated data from a server-advertised URL or inline from the
1493 same stream. When this is done, hooks operating on incoming changesets
1493 same stream. When this is done, hooks operating on incoming changesets
1494 and changegroups may fire more than once, once for each pre-generated
1494 and changegroups may fire more than once, once for each pre-generated
1495 bundle and as well as for any additional remaining data. In addition,
1495 bundle and as well as for any additional remaining data. In addition,
1496 if an error occurs, the repository may be rolled back to a partial
1496 if an error occurs, the repository may be rolled back to a partial
1497 clone. This behavior may change in future releases.
1497 clone. This behavior may change in future releases.
1498 See :hg:`help -e clonebundles` for more.
1498 See :hg:`help -e clonebundles` for more.
1499
1499
1500 Examples:
1500 Examples:
1501
1501
1502 - clone a remote repository to a new directory named hg/::
1502 - clone a remote repository to a new directory named hg/::
1503
1503
1504 hg clone https://www.mercurial-scm.org/repo/hg/
1504 hg clone https://www.mercurial-scm.org/repo/hg/
1505
1505
1506 - create a lightweight local clone::
1506 - create a lightweight local clone::
1507
1507
1508 hg clone project/ project-feature/
1508 hg clone project/ project-feature/
1509
1509
1510 - clone from an absolute path on an ssh server (note double-slash)::
1510 - clone from an absolute path on an ssh server (note double-slash)::
1511
1511
1512 hg clone ssh://user@server//home/projects/alpha/
1512 hg clone ssh://user@server//home/projects/alpha/
1513
1513
1514 - do a streaming clone while checking out a specified version::
1514 - do a streaming clone while checking out a specified version::
1515
1515
1516 hg clone --stream http://server/repo -u 1.5
1516 hg clone --stream http://server/repo -u 1.5
1517
1517
1518 - create a repository without changesets after a particular revision::
1518 - create a repository without changesets after a particular revision::
1519
1519
1520 hg clone -r 04e544 experimental/ good/
1520 hg clone -r 04e544 experimental/ good/
1521
1521
1522 - clone (and track) a particular named branch::
1522 - clone (and track) a particular named branch::
1523
1523
1524 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1524 hg clone https://www.mercurial-scm.org/repo/hg/#stable
1525
1525
1526 See :hg:`help urls` for details on specifying URLs.
1526 See :hg:`help urls` for details on specifying URLs.
1527
1527
1528 Returns 0 on success.
1528 Returns 0 on success.
1529 """
1529 """
1530 opts = pycompat.byteskwargs(opts)
1530 opts = pycompat.byteskwargs(opts)
1531 if opts.get('noupdate') and opts.get('updaterev'):
1531 if opts.get('noupdate') and opts.get('updaterev'):
1532 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1532 raise error.Abort(_("cannot specify both --noupdate and --updaterev"))
1533
1533
1534 # --include/--exclude can come from narrow or sparse.
1534 # --include/--exclude can come from narrow or sparse.
1535 includepats, excludepats = None, None
1535 includepats, excludepats = None, None
1536
1536
1537 # hg.clone() differentiates between None and an empty set. So make sure
1537 # hg.clone() differentiates between None and an empty set. So make sure
1538 # patterns are sets if narrow is requested without patterns.
1538 # patterns are sets if narrow is requested without patterns.
1539 if opts.get('narrow'):
1539 if opts.get('narrow'):
1540 includepats = set()
1540 includepats = set()
1541 excludepats = set()
1541 excludepats = set()
1542
1542
1543 if opts.get('include'):
1543 if opts.get('include'):
1544 includepats = narrowspec.parsepatterns(opts.get('include'))
1544 includepats = narrowspec.parsepatterns(opts.get('include'))
1545 if opts.get('exclude'):
1545 if opts.get('exclude'):
1546 excludepats = narrowspec.parsepatterns(opts.get('exclude'))
1546 excludepats = narrowspec.parsepatterns(opts.get('exclude'))
1547
1547
1548 r = hg.clone(ui, opts, source, dest,
1548 r = hg.clone(ui, opts, source, dest,
1549 pull=opts.get('pull'),
1549 pull=opts.get('pull'),
1550 stream=opts.get('stream') or opts.get('uncompressed'),
1550 stream=opts.get('stream') or opts.get('uncompressed'),
1551 revs=opts.get('rev'),
1551 revs=opts.get('rev'),
1552 update=opts.get('updaterev') or not opts.get('noupdate'),
1552 update=opts.get('updaterev') or not opts.get('noupdate'),
1553 branch=opts.get('branch'),
1553 branch=opts.get('branch'),
1554 shareopts=opts.get('shareopts'),
1554 shareopts=opts.get('shareopts'),
1555 storeincludepats=includepats,
1555 storeincludepats=includepats,
1556 storeexcludepats=excludepats,
1556 storeexcludepats=excludepats,
1557 depth=opts.get('depth') or None)
1557 depth=opts.get('depth') or None)
1558
1558
1559 return r is None
1559 return r is None
1560
1560
1561 @command('commit|ci',
1561 @command('commit|ci',
1562 [('A', 'addremove', None,
1562 [('A', 'addremove', None,
1563 _('mark new/missing files as added/removed before committing')),
1563 _('mark new/missing files as added/removed before committing')),
1564 ('', 'close-branch', None,
1564 ('', 'close-branch', None,
1565 _('mark a branch head as closed')),
1565 _('mark a branch head as closed')),
1566 ('', 'amend', None, _('amend the parent of the working directory')),
1566 ('', 'amend', None, _('amend the parent of the working directory')),
1567 ('s', 'secret', None, _('use the secret phase for committing')),
1567 ('s', 'secret', None, _('use the secret phase for committing')),
1568 ('e', 'edit', None, _('invoke editor on commit messages')),
1568 ('e', 'edit', None, _('invoke editor on commit messages')),
1569 ('i', 'interactive', None, _('use interactive mode')),
1569 ('i', 'interactive', None, _('use interactive mode')),
1570 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1570 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1571 _('[OPTION]... [FILE]...'),
1571 _('[OPTION]... [FILE]...'),
1572 helpcategory=command.CATEGORY_COMMITTING, helpbasic=True,
1572 helpcategory=command.CATEGORY_COMMITTING, helpbasic=True,
1573 inferrepo=True)
1573 inferrepo=True)
1574 def commit(ui, repo, *pats, **opts):
1574 def commit(ui, repo, *pats, **opts):
1575 """commit the specified files or all outstanding changes
1575 """commit the specified files or all outstanding changes
1576
1576
1577 Commit changes to the given files into the repository. Unlike a
1577 Commit changes to the given files into the repository. Unlike a
1578 centralized SCM, this operation is a local operation. See
1578 centralized SCM, this operation is a local operation. See
1579 :hg:`push` for a way to actively distribute your changes.
1579 :hg:`push` for a way to actively distribute your changes.
1580
1580
1581 If a list of files is omitted, all changes reported by :hg:`status`
1581 If a list of files is omitted, all changes reported by :hg:`status`
1582 will be committed.
1582 will be committed.
1583
1583
1584 If you are committing the result of a merge, do not provide any
1584 If you are committing the result of a merge, do not provide any
1585 filenames or -I/-X filters.
1585 filenames or -I/-X filters.
1586
1586
1587 If no commit message is specified, Mercurial starts your
1587 If no commit message is specified, Mercurial starts your
1588 configured editor where you can enter a message. In case your
1588 configured editor where you can enter a message. In case your
1589 commit fails, you will find a backup of your message in
1589 commit fails, you will find a backup of your message in
1590 ``.hg/last-message.txt``.
1590 ``.hg/last-message.txt``.
1591
1591
1592 The --close-branch flag can be used to mark the current branch
1592 The --close-branch flag can be used to mark the current branch
1593 head closed. When all heads of a branch are closed, the branch
1593 head closed. When all heads of a branch are closed, the branch
1594 will be considered closed and no longer listed.
1594 will be considered closed and no longer listed.
1595
1595
1596 The --amend flag can be used to amend the parent of the
1596 The --amend flag can be used to amend the parent of the
1597 working directory with a new commit that contains the changes
1597 working directory with a new commit that contains the changes
1598 in the parent in addition to those currently reported by :hg:`status`,
1598 in the parent in addition to those currently reported by :hg:`status`,
1599 if there are any. The old commit is stored in a backup bundle in
1599 if there are any. The old commit is stored in a backup bundle in
1600 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1600 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1601 on how to restore it).
1601 on how to restore it).
1602
1602
1603 Message, user and date are taken from the amended commit unless
1603 Message, user and date are taken from the amended commit unless
1604 specified. When a message isn't specified on the command line,
1604 specified. When a message isn't specified on the command line,
1605 the editor will open with the message of the amended commit.
1605 the editor will open with the message of the amended commit.
1606
1606
1607 It is not possible to amend public changesets (see :hg:`help phases`)
1607 It is not possible to amend public changesets (see :hg:`help phases`)
1608 or changesets that have children.
1608 or changesets that have children.
1609
1609
1610 See :hg:`help dates` for a list of formats valid for -d/--date.
1610 See :hg:`help dates` for a list of formats valid for -d/--date.
1611
1611
1612 Returns 0 on success, 1 if nothing changed.
1612 Returns 0 on success, 1 if nothing changed.
1613
1613
1614 .. container:: verbose
1614 .. container:: verbose
1615
1615
1616 Examples:
1616 Examples:
1617
1617
1618 - commit all files ending in .py::
1618 - commit all files ending in .py::
1619
1619
1620 hg commit --include "set:**.py"
1620 hg commit --include "set:**.py"
1621
1621
1622 - commit all non-binary files::
1622 - commit all non-binary files::
1623
1623
1624 hg commit --exclude "set:binary()"
1624 hg commit --exclude "set:binary()"
1625
1625
1626 - amend the current commit and set the date to now::
1626 - amend the current commit and set the date to now::
1627
1627
1628 hg commit --amend --date now
1628 hg commit --amend --date now
1629 """
1629 """
1630 with repo.wlock(), repo.lock():
1630 with repo.wlock(), repo.lock():
1631 return _docommit(ui, repo, *pats, **opts)
1631 return _docommit(ui, repo, *pats, **opts)
1632
1632
1633 def _docommit(ui, repo, *pats, **opts):
1633 def _docommit(ui, repo, *pats, **opts):
1634 if opts.get(r'interactive'):
1634 if opts.get(r'interactive'):
1635 opts.pop(r'interactive')
1635 opts.pop(r'interactive')
1636 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1636 ret = cmdutil.dorecord(ui, repo, commit, None, False,
1637 cmdutil.recordfilter, *pats,
1637 cmdutil.recordfilter, *pats,
1638 **opts)
1638 **opts)
1639 # ret can be 0 (no changes to record) or the value returned by
1639 # ret can be 0 (no changes to record) or the value returned by
1640 # commit(), 1 if nothing changed or None on success.
1640 # commit(), 1 if nothing changed or None on success.
1641 return 1 if ret == 0 else ret
1641 return 1 if ret == 0 else ret
1642
1642
1643 opts = pycompat.byteskwargs(opts)
1643 opts = pycompat.byteskwargs(opts)
1644 if opts.get('subrepos'):
1644 if opts.get('subrepos'):
1645 if opts.get('amend'):
1645 if opts.get('amend'):
1646 raise error.Abort(_('cannot amend with --subrepos'))
1646 raise error.Abort(_('cannot amend with --subrepos'))
1647 # Let --subrepos on the command line override config setting.
1647 # Let --subrepos on the command line override config setting.
1648 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1648 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1649
1649
1650 cmdutil.checkunfinished(repo, commit=True)
1650 cmdutil.checkunfinished(repo, commit=True)
1651
1651
1652 branch = repo[None].branch()
1652 branch = repo[None].branch()
1653 bheads = repo.branchheads(branch)
1653 bheads = repo.branchheads(branch)
1654
1654
1655 extra = {}
1655 extra = {}
1656 if opts.get('close_branch'):
1656 if opts.get('close_branch'):
1657 extra['close'] = '1'
1657 extra['close'] = '1'
1658
1658
1659 if not bheads:
1659 if not bheads:
1660 raise error.Abort(_('can only close branch heads'))
1660 raise error.Abort(_('can only close branch heads'))
1661 elif opts.get('amend'):
1661 elif opts.get('amend'):
1662 if repo[None].parents()[0].p1().branch() != branch and \
1662 if repo[None].parents()[0].p1().branch() != branch and \
1663 repo[None].parents()[0].p2().branch() != branch:
1663 repo[None].parents()[0].p2().branch() != branch:
1664 raise error.Abort(_('can only close branch heads'))
1664 raise error.Abort(_('can only close branch heads'))
1665
1665
1666 if opts.get('amend'):
1666 if opts.get('amend'):
1667 if ui.configbool('ui', 'commitsubrepos'):
1667 if ui.configbool('ui', 'commitsubrepos'):
1668 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1668 raise error.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1669
1669
1670 old = repo['.']
1670 old = repo['.']
1671 rewriteutil.precheck(repo, [old.rev()], 'amend')
1671 rewriteutil.precheck(repo, [old.rev()], 'amend')
1672
1672
1673 # Currently histedit gets confused if an amend happens while histedit
1673 # Currently histedit gets confused if an amend happens while histedit
1674 # is in progress. Since we have a checkunfinished command, we are
1674 # is in progress. Since we have a checkunfinished command, we are
1675 # temporarily honoring it.
1675 # temporarily honoring it.
1676 #
1676 #
1677 # Note: eventually this guard will be removed. Please do not expect
1677 # Note: eventually this guard will be removed. Please do not expect
1678 # this behavior to remain.
1678 # this behavior to remain.
1679 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1679 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1680 cmdutil.checkunfinished(repo)
1680 cmdutil.checkunfinished(repo)
1681
1681
1682 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
1682 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
1683 if node == old.node():
1683 if node == old.node():
1684 ui.status(_("nothing changed\n"))
1684 ui.status(_("nothing changed\n"))
1685 return 1
1685 return 1
1686 else:
1686 else:
1687 def commitfunc(ui, repo, message, match, opts):
1687 def commitfunc(ui, repo, message, match, opts):
1688 overrides = {}
1688 overrides = {}
1689 if opts.get('secret'):
1689 if opts.get('secret'):
1690 overrides[('phases', 'new-commit')] = 'secret'
1690 overrides[('phases', 'new-commit')] = 'secret'
1691
1691
1692 baseui = repo.baseui
1692 baseui = repo.baseui
1693 with baseui.configoverride(overrides, 'commit'):
1693 with baseui.configoverride(overrides, 'commit'):
1694 with ui.configoverride(overrides, 'commit'):
1694 with ui.configoverride(overrides, 'commit'):
1695 editform = cmdutil.mergeeditform(repo[None],
1695 editform = cmdutil.mergeeditform(repo[None],
1696 'commit.normal')
1696 'commit.normal')
1697 editor = cmdutil.getcommiteditor(
1697 editor = cmdutil.getcommiteditor(
1698 editform=editform, **pycompat.strkwargs(opts))
1698 editform=editform, **pycompat.strkwargs(opts))
1699 return repo.commit(message,
1699 return repo.commit(message,
1700 opts.get('user'),
1700 opts.get('user'),
1701 opts.get('date'),
1701 opts.get('date'),
1702 match,
1702 match,
1703 editor=editor,
1703 editor=editor,
1704 extra=extra)
1704 extra=extra)
1705
1705
1706 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1706 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1707
1707
1708 if not node:
1708 if not node:
1709 stat = cmdutil.postcommitstatus(repo, pats, opts)
1709 stat = cmdutil.postcommitstatus(repo, pats, opts)
1710 if stat[3]:
1710 if stat[3]:
1711 ui.status(_("nothing changed (%d missing files, see "
1711 ui.status(_("nothing changed (%d missing files, see "
1712 "'hg status')\n") % len(stat[3]))
1712 "'hg status')\n") % len(stat[3]))
1713 else:
1713 else:
1714 ui.status(_("nothing changed\n"))
1714 ui.status(_("nothing changed\n"))
1715 return 1
1715 return 1
1716
1716
1717 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1717 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1718
1718
1719 @command('config|showconfig|debugconfig',
1719 @command('config|showconfig|debugconfig',
1720 [('u', 'untrusted', None, _('show untrusted configuration options')),
1720 [('u', 'untrusted', None, _('show untrusted configuration options')),
1721 ('e', 'edit', None, _('edit user config')),
1721 ('e', 'edit', None, _('edit user config')),
1722 ('l', 'local', None, _('edit repository config')),
1722 ('l', 'local', None, _('edit repository config')),
1723 ('g', 'global', None, _('edit global config'))] + formatteropts,
1723 ('g', 'global', None, _('edit global config'))] + formatteropts,
1724 _('[-u] [NAME]...'),
1724 _('[-u] [NAME]...'),
1725 helpcategory=command.CATEGORY_HELP,
1725 helpcategory=command.CATEGORY_HELP,
1726 optionalrepo=True,
1726 optionalrepo=True,
1727 intents={INTENT_READONLY})
1727 intents={INTENT_READONLY})
1728 def config(ui, repo, *values, **opts):
1728 def config(ui, repo, *values, **opts):
1729 """show combined config settings from all hgrc files
1729 """show combined config settings from all hgrc files
1730
1730
1731 With no arguments, print names and values of all config items.
1731 With no arguments, print names and values of all config items.
1732
1732
1733 With one argument of the form section.name, print just the value
1733 With one argument of the form section.name, print just the value
1734 of that config item.
1734 of that config item.
1735
1735
1736 With multiple arguments, print names and values of all config
1736 With multiple arguments, print names and values of all config
1737 items with matching section names or section.names.
1737 items with matching section names or section.names.
1738
1738
1739 With --edit, start an editor on the user-level config file. With
1739 With --edit, start an editor on the user-level config file. With
1740 --global, edit the system-wide config file. With --local, edit the
1740 --global, edit the system-wide config file. With --local, edit the
1741 repository-level config file.
1741 repository-level config file.
1742
1742
1743 With --debug, the source (filename and line number) is printed
1743 With --debug, the source (filename and line number) is printed
1744 for each config item.
1744 for each config item.
1745
1745
1746 See :hg:`help config` for more information about config files.
1746 See :hg:`help config` for more information about config files.
1747
1747
1748 .. container:: verbose
1748 .. container:: verbose
1749
1749
1750 Template:
1750 Template:
1751
1751
1752 The following keywords are supported. See also :hg:`help templates`.
1752 The following keywords are supported. See also :hg:`help templates`.
1753
1753
1754 :name: String. Config name.
1754 :name: String. Config name.
1755 :source: String. Filename and line number where the item is defined.
1755 :source: String. Filename and line number where the item is defined.
1756 :value: String. Config value.
1756 :value: String. Config value.
1757
1757
1758 Returns 0 on success, 1 if NAME does not exist.
1758 Returns 0 on success, 1 if NAME does not exist.
1759
1759
1760 """
1760 """
1761
1761
1762 opts = pycompat.byteskwargs(opts)
1762 opts = pycompat.byteskwargs(opts)
1763 if opts.get('edit') or opts.get('local') or opts.get('global'):
1763 if opts.get('edit') or opts.get('local') or opts.get('global'):
1764 if opts.get('local') and opts.get('global'):
1764 if opts.get('local') and opts.get('global'):
1765 raise error.Abort(_("can't use --local and --global together"))
1765 raise error.Abort(_("can't use --local and --global together"))
1766
1766
1767 if opts.get('local'):
1767 if opts.get('local'):
1768 if not repo:
1768 if not repo:
1769 raise error.Abort(_("can't use --local outside a repository"))
1769 raise error.Abort(_("can't use --local outside a repository"))
1770 paths = [repo.vfs.join('hgrc')]
1770 paths = [repo.vfs.join('hgrc')]
1771 elif opts.get('global'):
1771 elif opts.get('global'):
1772 paths = rcutil.systemrcpath()
1772 paths = rcutil.systemrcpath()
1773 else:
1773 else:
1774 paths = rcutil.userrcpath()
1774 paths = rcutil.userrcpath()
1775
1775
1776 for f in paths:
1776 for f in paths:
1777 if os.path.exists(f):
1777 if os.path.exists(f):
1778 break
1778 break
1779 else:
1779 else:
1780 if opts.get('global'):
1780 if opts.get('global'):
1781 samplehgrc = uimod.samplehgrcs['global']
1781 samplehgrc = uimod.samplehgrcs['global']
1782 elif opts.get('local'):
1782 elif opts.get('local'):
1783 samplehgrc = uimod.samplehgrcs['local']
1783 samplehgrc = uimod.samplehgrcs['local']
1784 else:
1784 else:
1785 samplehgrc = uimod.samplehgrcs['user']
1785 samplehgrc = uimod.samplehgrcs['user']
1786
1786
1787 f = paths[0]
1787 f = paths[0]
1788 fp = open(f, "wb")
1788 fp = open(f, "wb")
1789 fp.write(util.tonativeeol(samplehgrc))
1789 fp.write(util.tonativeeol(samplehgrc))
1790 fp.close()
1790 fp.close()
1791
1791
1792 editor = ui.geteditor()
1792 editor = ui.geteditor()
1793 ui.system("%s \"%s\"" % (editor, f),
1793 ui.system("%s \"%s\"" % (editor, f),
1794 onerr=error.Abort, errprefix=_("edit failed"),
1794 onerr=error.Abort, errprefix=_("edit failed"),
1795 blockedtag='config_edit')
1795 blockedtag='config_edit')
1796 return
1796 return
1797 ui.pager('config')
1797 ui.pager('config')
1798 fm = ui.formatter('config', opts)
1798 fm = ui.formatter('config', opts)
1799 for t, f in rcutil.rccomponents():
1799 for t, f in rcutil.rccomponents():
1800 if t == 'path':
1800 if t == 'path':
1801 ui.debug('read config from: %s\n' % f)
1801 ui.debug('read config from: %s\n' % f)
1802 elif t == 'items':
1802 elif t == 'items':
1803 for section, name, value, source in f:
1803 for section, name, value, source in f:
1804 ui.debug('set config by: %s\n' % source)
1804 ui.debug('set config by: %s\n' % source)
1805 else:
1805 else:
1806 raise error.ProgrammingError('unknown rctype: %s' % t)
1806 raise error.ProgrammingError('unknown rctype: %s' % t)
1807 untrusted = bool(opts.get('untrusted'))
1807 untrusted = bool(opts.get('untrusted'))
1808
1808
1809 selsections = selentries = []
1809 selsections = selentries = []
1810 if values:
1810 if values:
1811 selsections = [v for v in values if '.' not in v]
1811 selsections = [v for v in values if '.' not in v]
1812 selentries = [v for v in values if '.' in v]
1812 selentries = [v for v in values if '.' in v]
1813 uniquesel = (len(selentries) == 1 and not selsections)
1813 uniquesel = (len(selentries) == 1 and not selsections)
1814 selsections = set(selsections)
1814 selsections = set(selsections)
1815 selentries = set(selentries)
1815 selentries = set(selentries)
1816
1816
1817 matched = False
1817 matched = False
1818 for section, name, value in ui.walkconfig(untrusted=untrusted):
1818 for section, name, value in ui.walkconfig(untrusted=untrusted):
1819 source = ui.configsource(section, name, untrusted)
1819 source = ui.configsource(section, name, untrusted)
1820 value = pycompat.bytestr(value)
1820 value = pycompat.bytestr(value)
1821 if fm.isplain():
1821 if fm.isplain():
1822 source = source or 'none'
1822 source = source or 'none'
1823 value = value.replace('\n', '\\n')
1823 value = value.replace('\n', '\\n')
1824 entryname = section + '.' + name
1824 entryname = section + '.' + name
1825 if values and not (section in selsections or entryname in selentries):
1825 if values and not (section in selsections or entryname in selentries):
1826 continue
1826 continue
1827 fm.startitem()
1827 fm.startitem()
1828 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1828 fm.condwrite(ui.debugflag, 'source', '%s: ', source)
1829 if uniquesel:
1829 if uniquesel:
1830 fm.data(name=entryname)
1830 fm.data(name=entryname)
1831 fm.write('value', '%s\n', value)
1831 fm.write('value', '%s\n', value)
1832 else:
1832 else:
1833 fm.write('name value', '%s=%s\n', entryname, value)
1833 fm.write('name value', '%s=%s\n', entryname, value)
1834 matched = True
1834 matched = True
1835 fm.end()
1835 fm.end()
1836 if matched:
1836 if matched:
1837 return 0
1837 return 0
1838 return 1
1838 return 1
1839
1839
1840 @command('copy|cp',
1840 @command('copy|cp',
1841 [('A', 'after', None, _('record a copy that has already occurred')),
1841 [('A', 'after', None, _('record a copy that has already occurred')),
1842 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1842 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1843 ] + walkopts + dryrunopts,
1843 ] + walkopts + dryrunopts,
1844 _('[OPTION]... [SOURCE]... DEST'),
1844 _('[OPTION]... [SOURCE]... DEST'),
1845 helpcategory=command.CATEGORY_FILE_CONTENTS)
1845 helpcategory=command.CATEGORY_FILE_CONTENTS)
1846 def copy(ui, repo, *pats, **opts):
1846 def copy(ui, repo, *pats, **opts):
1847 """mark files as copied for the next commit
1847 """mark files as copied for the next commit
1848
1848
1849 Mark dest as having copies of source files. If dest is a
1849 Mark dest as having copies of source files. If dest is a
1850 directory, copies are put in that directory. If dest is a file,
1850 directory, copies are put in that directory. If dest is a file,
1851 the source must be a single file.
1851 the source must be a single file.
1852
1852
1853 By default, this command copies the contents of files as they
1853 By default, this command copies the contents of files as they
1854 exist in the working directory. If invoked with -A/--after, the
1854 exist in the working directory. If invoked with -A/--after, the
1855 operation is recorded, but no copying is performed.
1855 operation is recorded, but no copying is performed.
1856
1856
1857 This command takes effect with the next commit. To undo a copy
1857 This command takes effect with the next commit. To undo a copy
1858 before that, see :hg:`revert`.
1858 before that, see :hg:`revert`.
1859
1859
1860 Returns 0 on success, 1 if errors are encountered.
1860 Returns 0 on success, 1 if errors are encountered.
1861 """
1861 """
1862 opts = pycompat.byteskwargs(opts)
1862 opts = pycompat.byteskwargs(opts)
1863 with repo.wlock(False):
1863 with repo.wlock(False):
1864 return cmdutil.copy(ui, repo, pats, opts)
1864 return cmdutil.copy(ui, repo, pats, opts)
1865
1865
1866 @command(
1866 @command(
1867 'debugcommands', [], _('[COMMAND]'),
1867 'debugcommands', [], _('[COMMAND]'),
1868 helpcategory=command.CATEGORY_HELP,
1868 helpcategory=command.CATEGORY_HELP,
1869 norepo=True)
1869 norepo=True)
1870 def debugcommands(ui, cmd='', *args):
1870 def debugcommands(ui, cmd='', *args):
1871 """list all available commands and options"""
1871 """list all available commands and options"""
1872 for cmd, vals in sorted(table.iteritems()):
1872 for cmd, vals in sorted(table.iteritems()):
1873 cmd = cmd.split('|')[0]
1873 cmd = cmd.split('|')[0]
1874 opts = ', '.join([i[1] for i in vals[1]])
1874 opts = ', '.join([i[1] for i in vals[1]])
1875 ui.write('%s: %s\n' % (cmd, opts))
1875 ui.write('%s: %s\n' % (cmd, opts))
1876
1876
1877 @command('debugcomplete',
1877 @command('debugcomplete',
1878 [('o', 'options', None, _('show the command options'))],
1878 [('o', 'options', None, _('show the command options'))],
1879 _('[-o] CMD'),
1879 _('[-o] CMD'),
1880 helpcategory=command.CATEGORY_HELP,
1880 helpcategory=command.CATEGORY_HELP,
1881 norepo=True)
1881 norepo=True)
1882 def debugcomplete(ui, cmd='', **opts):
1882 def debugcomplete(ui, cmd='', **opts):
1883 """returns the completion list associated with the given command"""
1883 """returns the completion list associated with the given command"""
1884
1884
1885 if opts.get(r'options'):
1885 if opts.get(r'options'):
1886 options = []
1886 options = []
1887 otables = [globalopts]
1887 otables = [globalopts]
1888 if cmd:
1888 if cmd:
1889 aliases, entry = cmdutil.findcmd(cmd, table, False)
1889 aliases, entry = cmdutil.findcmd(cmd, table, False)
1890 otables.append(entry[1])
1890 otables.append(entry[1])
1891 for t in otables:
1891 for t in otables:
1892 for o in t:
1892 for o in t:
1893 if "(DEPRECATED)" in o[3]:
1893 if "(DEPRECATED)" in o[3]:
1894 continue
1894 continue
1895 if o[0]:
1895 if o[0]:
1896 options.append('-%s' % o[0])
1896 options.append('-%s' % o[0])
1897 options.append('--%s' % o[1])
1897 options.append('--%s' % o[1])
1898 ui.write("%s\n" % "\n".join(options))
1898 ui.write("%s\n" % "\n".join(options))
1899 return
1899 return
1900
1900
1901 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1901 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
1902 if ui.verbose:
1902 if ui.verbose:
1903 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1903 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1904 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1904 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1905
1905
1906 @command('diff',
1906 @command('diff',
1907 [('r', 'rev', [], _('revision'), _('REV')),
1907 [('r', 'rev', [], _('revision'), _('REV')),
1908 ('c', 'change', '', _('change made by revision'), _('REV'))
1908 ('c', 'change', '', _('change made by revision'), _('REV'))
1909 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1909 ] + diffopts + diffopts2 + walkopts + subrepoopts,
1910 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1910 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
1911 helpcategory=command.CATEGORY_FILE_CONTENTS,
1911 helpcategory=command.CATEGORY_FILE_CONTENTS,
1912 helpbasic=True, inferrepo=True, intents={INTENT_READONLY})
1912 helpbasic=True, inferrepo=True, intents={INTENT_READONLY})
1913 def diff(ui, repo, *pats, **opts):
1913 def diff(ui, repo, *pats, **opts):
1914 """diff repository (or selected files)
1914 """diff repository (or selected files)
1915
1915
1916 Show differences between revisions for the specified files.
1916 Show differences between revisions for the specified files.
1917
1917
1918 Differences between files are shown using the unified diff format.
1918 Differences between files are shown using the unified diff format.
1919
1919
1920 .. note::
1920 .. note::
1921
1921
1922 :hg:`diff` may generate unexpected results for merges, as it will
1922 :hg:`diff` may generate unexpected results for merges, as it will
1923 default to comparing against the working directory's first
1923 default to comparing against the working directory's first
1924 parent changeset if no revisions are specified.
1924 parent changeset if no revisions are specified.
1925
1925
1926 When two revision arguments are given, then changes are shown
1926 When two revision arguments are given, then changes are shown
1927 between those revisions. If only one revision is specified then
1927 between those revisions. If only one revision is specified then
1928 that revision is compared to the working directory, and, when no
1928 that revision is compared to the working directory, and, when no
1929 revisions are specified, the working directory files are compared
1929 revisions are specified, the working directory files are compared
1930 to its first parent.
1930 to its first parent.
1931
1931
1932 Alternatively you can specify -c/--change with a revision to see
1932 Alternatively you can specify -c/--change with a revision to see
1933 the changes in that changeset relative to its first parent.
1933 the changes in that changeset relative to its first parent.
1934
1934
1935 Without the -a/--text option, diff will avoid generating diffs of
1935 Without the -a/--text option, diff will avoid generating diffs of
1936 files it detects as binary. With -a, diff will generate a diff
1936 files it detects as binary. With -a, diff will generate a diff
1937 anyway, probably with undesirable results.
1937 anyway, probably with undesirable results.
1938
1938
1939 Use the -g/--git option to generate diffs in the git extended diff
1939 Use the -g/--git option to generate diffs in the git extended diff
1940 format. For more information, read :hg:`help diffs`.
1940 format. For more information, read :hg:`help diffs`.
1941
1941
1942 .. container:: verbose
1942 .. container:: verbose
1943
1943
1944 Examples:
1944 Examples:
1945
1945
1946 - compare a file in the current working directory to its parent::
1946 - compare a file in the current working directory to its parent::
1947
1947
1948 hg diff foo.c
1948 hg diff foo.c
1949
1949
1950 - compare two historical versions of a directory, with rename info::
1950 - compare two historical versions of a directory, with rename info::
1951
1951
1952 hg diff --git -r 1.0:1.2 lib/
1952 hg diff --git -r 1.0:1.2 lib/
1953
1953
1954 - get change stats relative to the last change on some date::
1954 - get change stats relative to the last change on some date::
1955
1955
1956 hg diff --stat -r "date('may 2')"
1956 hg diff --stat -r "date('may 2')"
1957
1957
1958 - diff all newly-added files that contain a keyword::
1958 - diff all newly-added files that contain a keyword::
1959
1959
1960 hg diff "set:added() and grep(GNU)"
1960 hg diff "set:added() and grep(GNU)"
1961
1961
1962 - compare a revision and its parents::
1962 - compare a revision and its parents::
1963
1963
1964 hg diff -c 9353 # compare against first parent
1964 hg diff -c 9353 # compare against first parent
1965 hg diff -r 9353^:9353 # same using revset syntax
1965 hg diff -r 9353^:9353 # same using revset syntax
1966 hg diff -r 9353^2:9353 # compare against the second parent
1966 hg diff -r 9353^2:9353 # compare against the second parent
1967
1967
1968 Returns 0 on success.
1968 Returns 0 on success.
1969 """
1969 """
1970
1970
1971 opts = pycompat.byteskwargs(opts)
1971 opts = pycompat.byteskwargs(opts)
1972 revs = opts.get('rev')
1972 revs = opts.get('rev')
1973 change = opts.get('change')
1973 change = opts.get('change')
1974 stat = opts.get('stat')
1974 stat = opts.get('stat')
1975 reverse = opts.get('reverse')
1975 reverse = opts.get('reverse')
1976
1976
1977 if revs and change:
1977 if revs and change:
1978 msg = _('cannot specify --rev and --change at the same time')
1978 msg = _('cannot specify --rev and --change at the same time')
1979 raise error.Abort(msg)
1979 raise error.Abort(msg)
1980 elif change:
1980 elif change:
1981 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
1981 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
1982 ctx2 = scmutil.revsingle(repo, change, None)
1982 ctx2 = scmutil.revsingle(repo, change, None)
1983 ctx1 = ctx2.p1()
1983 ctx1 = ctx2.p1()
1984 else:
1984 else:
1985 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
1985 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
1986 ctx1, ctx2 = scmutil.revpair(repo, revs)
1986 ctx1, ctx2 = scmutil.revpair(repo, revs)
1987 node1, node2 = ctx1.node(), ctx2.node()
1987 node1, node2 = ctx1.node(), ctx2.node()
1988
1988
1989 if reverse:
1989 if reverse:
1990 node1, node2 = node2, node1
1990 node1, node2 = node2, node1
1991
1991
1992 diffopts = patch.diffallopts(ui, opts)
1992 diffopts = patch.diffallopts(ui, opts)
1993 m = scmutil.match(ctx2, pats, opts)
1993 m = scmutil.match(ctx2, pats, opts)
1994 m = repo.narrowmatch(m)
1994 m = repo.narrowmatch(m)
1995 ui.pager('diff')
1995 ui.pager('diff')
1996 logcmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1996 logcmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1997 listsubrepos=opts.get('subrepos'),
1997 listsubrepos=opts.get('subrepos'),
1998 root=opts.get('root'))
1998 root=opts.get('root'))
1999
1999
2000 @command('export',
2000 @command('export',
2001 [('B', 'bookmark', '',
2001 [('B', 'bookmark', '',
2002 _('export changes only reachable by given bookmark'), _('BOOKMARK')),
2002 _('export changes only reachable by given bookmark'), _('BOOKMARK')),
2003 ('o', 'output', '',
2003 ('o', 'output', '',
2004 _('print output to file with formatted name'), _('FORMAT')),
2004 _('print output to file with formatted name'), _('FORMAT')),
2005 ('', 'switch-parent', None, _('diff against the second parent')),
2005 ('', 'switch-parent', None, _('diff against the second parent')),
2006 ('r', 'rev', [], _('revisions to export'), _('REV')),
2006 ('r', 'rev', [], _('revisions to export'), _('REV')),
2007 ] + diffopts + formatteropts,
2007 ] + diffopts + formatteropts,
2008 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2008 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2009 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2009 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2010 helpbasic=True, intents={INTENT_READONLY})
2010 helpbasic=True, intents={INTENT_READONLY})
2011 def export(ui, repo, *changesets, **opts):
2011 def export(ui, repo, *changesets, **opts):
2012 """dump the header and diffs for one or more changesets
2012 """dump the header and diffs for one or more changesets
2013
2013
2014 Print the changeset header and diffs for one or more revisions.
2014 Print the changeset header and diffs for one or more revisions.
2015 If no revision is given, the parent of the working directory is used.
2015 If no revision is given, the parent of the working directory is used.
2016
2016
2017 The information shown in the changeset header is: author, date,
2017 The information shown in the changeset header is: author, date,
2018 branch name (if non-default), changeset hash, parent(s) and commit
2018 branch name (if non-default), changeset hash, parent(s) and commit
2019 comment.
2019 comment.
2020
2020
2021 .. note::
2021 .. note::
2022
2022
2023 :hg:`export` may generate unexpected diff output for merge
2023 :hg:`export` may generate unexpected diff output for merge
2024 changesets, as it will compare the merge changeset against its
2024 changesets, as it will compare the merge changeset against its
2025 first parent only.
2025 first parent only.
2026
2026
2027 Output may be to a file, in which case the name of the file is
2027 Output may be to a file, in which case the name of the file is
2028 given using a template string. See :hg:`help templates`. In addition
2028 given using a template string. See :hg:`help templates`. In addition
2029 to the common template keywords, the following formatting rules are
2029 to the common template keywords, the following formatting rules are
2030 supported:
2030 supported:
2031
2031
2032 :``%%``: literal "%" character
2032 :``%%``: literal "%" character
2033 :``%H``: changeset hash (40 hexadecimal digits)
2033 :``%H``: changeset hash (40 hexadecimal digits)
2034 :``%N``: number of patches being generated
2034 :``%N``: number of patches being generated
2035 :``%R``: changeset revision number
2035 :``%R``: changeset revision number
2036 :``%b``: basename of the exporting repository
2036 :``%b``: basename of the exporting repository
2037 :``%h``: short-form changeset hash (12 hexadecimal digits)
2037 :``%h``: short-form changeset hash (12 hexadecimal digits)
2038 :``%m``: first line of the commit message (only alphanumeric characters)
2038 :``%m``: first line of the commit message (only alphanumeric characters)
2039 :``%n``: zero-padded sequence number, starting at 1
2039 :``%n``: zero-padded sequence number, starting at 1
2040 :``%r``: zero-padded changeset revision number
2040 :``%r``: zero-padded changeset revision number
2041 :``\\``: literal "\\" character
2041 :``\\``: literal "\\" character
2042
2042
2043 Without the -a/--text option, export will avoid generating diffs
2043 Without the -a/--text option, export will avoid generating diffs
2044 of files it detects as binary. With -a, export will generate a
2044 of files it detects as binary. With -a, export will generate a
2045 diff anyway, probably with undesirable results.
2045 diff anyway, probably with undesirable results.
2046
2046
2047 With -B/--bookmark changesets reachable by the given bookmark are
2047 With -B/--bookmark changesets reachable by the given bookmark are
2048 selected.
2048 selected.
2049
2049
2050 Use the -g/--git option to generate diffs in the git extended diff
2050 Use the -g/--git option to generate diffs in the git extended diff
2051 format. See :hg:`help diffs` for more information.
2051 format. See :hg:`help diffs` for more information.
2052
2052
2053 With the --switch-parent option, the diff will be against the
2053 With the --switch-parent option, the diff will be against the
2054 second parent. It can be useful to review a merge.
2054 second parent. It can be useful to review a merge.
2055
2055
2056 .. container:: verbose
2056 .. container:: verbose
2057
2057
2058 Template:
2058 Template:
2059
2059
2060 The following keywords are supported in addition to the common template
2060 The following keywords are supported in addition to the common template
2061 keywords and functions. See also :hg:`help templates`.
2061 keywords and functions. See also :hg:`help templates`.
2062
2062
2063 :diff: String. Diff content.
2063 :diff: String. Diff content.
2064 :parents: List of strings. Parent nodes of the changeset.
2064 :parents: List of strings. Parent nodes of the changeset.
2065
2065
2066 Examples:
2066 Examples:
2067
2067
2068 - use export and import to transplant a bugfix to the current
2068 - use export and import to transplant a bugfix to the current
2069 branch::
2069 branch::
2070
2070
2071 hg export -r 9353 | hg import -
2071 hg export -r 9353 | hg import -
2072
2072
2073 - export all the changesets between two revisions to a file with
2073 - export all the changesets between two revisions to a file with
2074 rename information::
2074 rename information::
2075
2075
2076 hg export --git -r 123:150 > changes.txt
2076 hg export --git -r 123:150 > changes.txt
2077
2077
2078 - split outgoing changes into a series of patches with
2078 - split outgoing changes into a series of patches with
2079 descriptive names::
2079 descriptive names::
2080
2080
2081 hg export -r "outgoing()" -o "%n-%m.patch"
2081 hg export -r "outgoing()" -o "%n-%m.patch"
2082
2082
2083 Returns 0 on success.
2083 Returns 0 on success.
2084 """
2084 """
2085 opts = pycompat.byteskwargs(opts)
2085 opts = pycompat.byteskwargs(opts)
2086 bookmark = opts.get('bookmark')
2086 bookmark = opts.get('bookmark')
2087 changesets += tuple(opts.get('rev', []))
2087 changesets += tuple(opts.get('rev', []))
2088
2088
2089 if bookmark and changesets:
2089 if bookmark and changesets:
2090 raise error.Abort(_("-r and -B are mutually exclusive"))
2090 raise error.Abort(_("-r and -B are mutually exclusive"))
2091
2091
2092 if bookmark:
2092 if bookmark:
2093 if bookmark not in repo._bookmarks:
2093 if bookmark not in repo._bookmarks:
2094 raise error.Abort(_("bookmark '%s' not found") % bookmark)
2094 raise error.Abort(_("bookmark '%s' not found") % bookmark)
2095
2095
2096 revs = scmutil.bookmarkrevs(repo, bookmark)
2096 revs = scmutil.bookmarkrevs(repo, bookmark)
2097 else:
2097 else:
2098 if not changesets:
2098 if not changesets:
2099 changesets = ['.']
2099 changesets = ['.']
2100
2100
2101 repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn')
2101 repo = scmutil.unhidehashlikerevs(repo, changesets, 'nowarn')
2102 revs = scmutil.revrange(repo, changesets)
2102 revs = scmutil.revrange(repo, changesets)
2103
2103
2104 if not revs:
2104 if not revs:
2105 raise error.Abort(_("export requires at least one changeset"))
2105 raise error.Abort(_("export requires at least one changeset"))
2106 if len(revs) > 1:
2106 if len(revs) > 1:
2107 ui.note(_('exporting patches:\n'))
2107 ui.note(_('exporting patches:\n'))
2108 else:
2108 else:
2109 ui.note(_('exporting patch:\n'))
2109 ui.note(_('exporting patch:\n'))
2110
2110
2111 fntemplate = opts.get('output')
2111 fntemplate = opts.get('output')
2112 if cmdutil.isstdiofilename(fntemplate):
2112 if cmdutil.isstdiofilename(fntemplate):
2113 fntemplate = ''
2113 fntemplate = ''
2114
2114
2115 if fntemplate:
2115 if fntemplate:
2116 fm = formatter.nullformatter(ui, 'export', opts)
2116 fm = formatter.nullformatter(ui, 'export', opts)
2117 else:
2117 else:
2118 ui.pager('export')
2118 ui.pager('export')
2119 fm = ui.formatter('export', opts)
2119 fm = ui.formatter('export', opts)
2120 with fm:
2120 with fm:
2121 cmdutil.export(repo, revs, fm, fntemplate=fntemplate,
2121 cmdutil.export(repo, revs, fm, fntemplate=fntemplate,
2122 switch_parent=opts.get('switch_parent'),
2122 switch_parent=opts.get('switch_parent'),
2123 opts=patch.diffallopts(ui, opts))
2123 opts=patch.diffallopts(ui, opts))
2124
2124
2125 @command('files',
2125 @command('files',
2126 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
2126 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
2127 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2127 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2128 ] + walkopts + formatteropts + subrepoopts,
2128 ] + walkopts + formatteropts + subrepoopts,
2129 _('[OPTION]... [FILE]...'),
2129 _('[OPTION]... [FILE]...'),
2130 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2130 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2131 intents={INTENT_READONLY})
2131 intents={INTENT_READONLY})
2132 def files(ui, repo, *pats, **opts):
2132 def files(ui, repo, *pats, **opts):
2133 """list tracked files
2133 """list tracked files
2134
2134
2135 Print files under Mercurial control in the working directory or
2135 Print files under Mercurial control in the working directory or
2136 specified revision for given files (excluding removed files).
2136 specified revision for given files (excluding removed files).
2137 Files can be specified as filenames or filesets.
2137 Files can be specified as filenames or filesets.
2138
2138
2139 If no files are given to match, this command prints the names
2139 If no files are given to match, this command prints the names
2140 of all files under Mercurial control.
2140 of all files under Mercurial control.
2141
2141
2142 .. container:: verbose
2142 .. container:: verbose
2143
2143
2144 Template:
2144 Template:
2145
2145
2146 The following keywords are supported in addition to the common template
2146 The following keywords are supported in addition to the common template
2147 keywords and functions. See also :hg:`help templates`.
2147 keywords and functions. See also :hg:`help templates`.
2148
2148
2149 :flags: String. Character denoting file's symlink and executable bits.
2149 :flags: String. Character denoting file's symlink and executable bits.
2150 :path: String. Repository-absolute path of the file.
2150 :path: String. Repository-absolute path of the file.
2151 :size: Integer. Size of the file in bytes.
2151 :size: Integer. Size of the file in bytes.
2152
2152
2153 Examples:
2153 Examples:
2154
2154
2155 - list all files under the current directory::
2155 - list all files under the current directory::
2156
2156
2157 hg files .
2157 hg files .
2158
2158
2159 - shows sizes and flags for current revision::
2159 - shows sizes and flags for current revision::
2160
2160
2161 hg files -vr .
2161 hg files -vr .
2162
2162
2163 - list all files named README::
2163 - list all files named README::
2164
2164
2165 hg files -I "**/README"
2165 hg files -I "**/README"
2166
2166
2167 - list all binary files::
2167 - list all binary files::
2168
2168
2169 hg files "set:binary()"
2169 hg files "set:binary()"
2170
2170
2171 - find files containing a regular expression::
2171 - find files containing a regular expression::
2172
2172
2173 hg files "set:grep('bob')"
2173 hg files "set:grep('bob')"
2174
2174
2175 - search tracked file contents with xargs and grep::
2175 - search tracked file contents with xargs and grep::
2176
2176
2177 hg files -0 | xargs -0 grep foo
2177 hg files -0 | xargs -0 grep foo
2178
2178
2179 See :hg:`help patterns` and :hg:`help filesets` for more information
2179 See :hg:`help patterns` and :hg:`help filesets` for more information
2180 on specifying file patterns.
2180 on specifying file patterns.
2181
2181
2182 Returns 0 if a match is found, 1 otherwise.
2182 Returns 0 if a match is found, 1 otherwise.
2183
2183
2184 """
2184 """
2185
2185
2186 opts = pycompat.byteskwargs(opts)
2186 opts = pycompat.byteskwargs(opts)
2187 rev = opts.get('rev')
2187 rev = opts.get('rev')
2188 if rev:
2188 if rev:
2189 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2189 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
2190 ctx = scmutil.revsingle(repo, rev, None)
2190 ctx = scmutil.revsingle(repo, rev, None)
2191
2191
2192 end = '\n'
2192 end = '\n'
2193 if opts.get('print0'):
2193 if opts.get('print0'):
2194 end = '\0'
2194 end = '\0'
2195 fmt = '%s' + end
2195 fmt = '%s' + end
2196
2196
2197 m = scmutil.match(ctx, pats, opts)
2197 m = scmutil.match(ctx, pats, opts)
2198 ui.pager('files')
2198 ui.pager('files')
2199 with ui.formatter('files', opts) as fm:
2199 with ui.formatter('files', opts) as fm:
2200 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2200 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
2201
2201
2202 @command(
2202 @command(
2203 'forget',
2203 'forget',
2204 [('i', 'interactive', None, _('use interactive mode')),
2204 [('i', 'interactive', None, _('use interactive mode')),
2205 ] + walkopts + dryrunopts,
2205 ] + walkopts + dryrunopts,
2206 _('[OPTION]... FILE...'),
2206 _('[OPTION]... FILE...'),
2207 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2207 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2208 helpbasic=True, inferrepo=True)
2208 helpbasic=True, inferrepo=True)
2209 def forget(ui, repo, *pats, **opts):
2209 def forget(ui, repo, *pats, **opts):
2210 """forget the specified files on the next commit
2210 """forget the specified files on the next commit
2211
2211
2212 Mark the specified files so they will no longer be tracked
2212 Mark the specified files so they will no longer be tracked
2213 after the next commit.
2213 after the next commit.
2214
2214
2215 This only removes files from the current branch, not from the
2215 This only removes files from the current branch, not from the
2216 entire project history, and it does not delete them from the
2216 entire project history, and it does not delete them from the
2217 working directory.
2217 working directory.
2218
2218
2219 To delete the file from the working directory, see :hg:`remove`.
2219 To delete the file from the working directory, see :hg:`remove`.
2220
2220
2221 To undo a forget before the next commit, see :hg:`add`.
2221 To undo a forget before the next commit, see :hg:`add`.
2222
2222
2223 .. container:: verbose
2223 .. container:: verbose
2224
2224
2225 Examples:
2225 Examples:
2226
2226
2227 - forget newly-added binary files::
2227 - forget newly-added binary files::
2228
2228
2229 hg forget "set:added() and binary()"
2229 hg forget "set:added() and binary()"
2230
2230
2231 - forget files that would be excluded by .hgignore::
2231 - forget files that would be excluded by .hgignore::
2232
2232
2233 hg forget "set:hgignore()"
2233 hg forget "set:hgignore()"
2234
2234
2235 Returns 0 on success.
2235 Returns 0 on success.
2236 """
2236 """
2237
2237
2238 opts = pycompat.byteskwargs(opts)
2238 opts = pycompat.byteskwargs(opts)
2239 if not pats:
2239 if not pats:
2240 raise error.Abort(_('no files specified'))
2240 raise error.Abort(_('no files specified'))
2241
2241
2242 m = scmutil.match(repo[None], pats, opts)
2242 m = scmutil.match(repo[None], pats, opts)
2243 dryrun, interactive = opts.get('dry_run'), opts.get('interactive')
2243 dryrun, interactive = opts.get('dry_run'), opts.get('interactive')
2244 rejected = cmdutil.forget(ui, repo, m, prefix="",
2244 rejected = cmdutil.forget(ui, repo, m, prefix="",
2245 explicitonly=False, dryrun=dryrun,
2245 explicitonly=False, dryrun=dryrun,
2246 interactive=interactive)[0]
2246 interactive=interactive)[0]
2247 return rejected and 1 or 0
2247 return rejected and 1 or 0
2248
2248
2249 @command(
2249 @command(
2250 'graft',
2250 'graft',
2251 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2251 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2252 ('', 'base', '',
2252 ('', 'base', '',
2253 _('base revision when doing the graft merge (ADVANCED)'), _('REV')),
2253 _('base revision when doing the graft merge (ADVANCED)'), _('REV')),
2254 ('c', 'continue', False, _('resume interrupted graft')),
2254 ('c', 'continue', False, _('resume interrupted graft')),
2255 ('', 'stop', False, _('stop interrupted graft')),
2255 ('', 'stop', False, _('stop interrupted graft')),
2256 ('', 'abort', False, _('abort interrupted graft')),
2256 ('', 'abort', False, _('abort interrupted graft')),
2257 ('e', 'edit', False, _('invoke editor on commit messages')),
2257 ('e', 'edit', False, _('invoke editor on commit messages')),
2258 ('', 'log', None, _('append graft info to log message')),
2258 ('', 'log', None, _('append graft info to log message')),
2259 ('', 'no-commit', None,
2259 ('', 'no-commit', None,
2260 _("don't commit, just apply the changes in working directory")),
2260 _("don't commit, just apply the changes in working directory")),
2261 ('f', 'force', False, _('force graft')),
2261 ('f', 'force', False, _('force graft')),
2262 ('D', 'currentdate', False,
2262 ('D', 'currentdate', False,
2263 _('record the current date as commit date')),
2263 _('record the current date as commit date')),
2264 ('U', 'currentuser', False,
2264 ('U', 'currentuser', False,
2265 _('record the current user as committer'))]
2265 _('record the current user as committer'))]
2266 + commitopts2 + mergetoolopts + dryrunopts,
2266 + commitopts2 + mergetoolopts + dryrunopts,
2267 _('[OPTION]... [-r REV]... REV...'),
2267 _('[OPTION]... [-r REV]... REV...'),
2268 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
2268 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT)
2269 def graft(ui, repo, *revs, **opts):
2269 def graft(ui, repo, *revs, **opts):
2270 '''copy changes from other branches onto the current branch
2270 '''copy changes from other branches onto the current branch
2271
2271
2272 This command uses Mercurial's merge logic to copy individual
2272 This command uses Mercurial's merge logic to copy individual
2273 changes from other branches without merging branches in the
2273 changes from other branches without merging branches in the
2274 history graph. This is sometimes known as 'backporting' or
2274 history graph. This is sometimes known as 'backporting' or
2275 'cherry-picking'. By default, graft will copy user, date, and
2275 'cherry-picking'. By default, graft will copy user, date, and
2276 description from the source changesets.
2276 description from the source changesets.
2277
2277
2278 Changesets that are ancestors of the current revision, that have
2278 Changesets that are ancestors of the current revision, that have
2279 already been grafted, or that are merges will be skipped.
2279 already been grafted, or that are merges will be skipped.
2280
2280
2281 If --log is specified, log messages will have a comment appended
2281 If --log is specified, log messages will have a comment appended
2282 of the form::
2282 of the form::
2283
2283
2284 (grafted from CHANGESETHASH)
2284 (grafted from CHANGESETHASH)
2285
2285
2286 If --force is specified, revisions will be grafted even if they
2286 If --force is specified, revisions will be grafted even if they
2287 are already ancestors of, or have been grafted to, the destination.
2287 are already ancestors of, or have been grafted to, the destination.
2288 This is useful when the revisions have since been backed out.
2288 This is useful when the revisions have since been backed out.
2289
2289
2290 If a graft merge results in conflicts, the graft process is
2290 If a graft merge results in conflicts, the graft process is
2291 interrupted so that the current merge can be manually resolved.
2291 interrupted so that the current merge can be manually resolved.
2292 Once all conflicts are addressed, the graft process can be
2292 Once all conflicts are addressed, the graft process can be
2293 continued with the -c/--continue option.
2293 continued with the -c/--continue option.
2294
2294
2295 The -c/--continue option reapplies all the earlier options.
2295 The -c/--continue option reapplies all the earlier options.
2296
2296
2297 .. container:: verbose
2297 .. container:: verbose
2298
2298
2299 The --base option exposes more of how graft internally uses merge with a
2299 The --base option exposes more of how graft internally uses merge with a
2300 custom base revision. --base can be used to specify another ancestor than
2300 custom base revision. --base can be used to specify another ancestor than
2301 the first and only parent.
2301 the first and only parent.
2302
2302
2303 The command::
2303 The command::
2304
2304
2305 hg graft -r 345 --base 234
2305 hg graft -r 345 --base 234
2306
2306
2307 is thus pretty much the same as::
2307 is thus pretty much the same as::
2308
2308
2309 hg diff -r 234 -r 345 | hg import
2309 hg diff -r 234 -r 345 | hg import
2310
2310
2311 but using merge to resolve conflicts and track moved files.
2311 but using merge to resolve conflicts and track moved files.
2312
2312
2313 The result of a merge can thus be backported as a single commit by
2313 The result of a merge can thus be backported as a single commit by
2314 specifying one of the merge parents as base, and thus effectively
2314 specifying one of the merge parents as base, and thus effectively
2315 grafting the changes from the other side.
2315 grafting the changes from the other side.
2316
2316
2317 It is also possible to collapse multiple changesets and clean up history
2317 It is also possible to collapse multiple changesets and clean up history
2318 by specifying another ancestor as base, much like rebase --collapse
2318 by specifying another ancestor as base, much like rebase --collapse
2319 --keep.
2319 --keep.
2320
2320
2321 The commit message can be tweaked after the fact using commit --amend .
2321 The commit message can be tweaked after the fact using commit --amend .
2322
2322
2323 For using non-ancestors as the base to backout changes, see the backout
2323 For using non-ancestors as the base to backout changes, see the backout
2324 command and the hidden --parent option.
2324 command and the hidden --parent option.
2325
2325
2326 .. container:: verbose
2326 .. container:: verbose
2327
2327
2328 Examples:
2328 Examples:
2329
2329
2330 - copy a single change to the stable branch and edit its description::
2330 - copy a single change to the stable branch and edit its description::
2331
2331
2332 hg update stable
2332 hg update stable
2333 hg graft --edit 9393
2333 hg graft --edit 9393
2334
2334
2335 - graft a range of changesets with one exception, updating dates::
2335 - graft a range of changesets with one exception, updating dates::
2336
2336
2337 hg graft -D "2085::2093 and not 2091"
2337 hg graft -D "2085::2093 and not 2091"
2338
2338
2339 - continue a graft after resolving conflicts::
2339 - continue a graft after resolving conflicts::
2340
2340
2341 hg graft -c
2341 hg graft -c
2342
2342
2343 - show the source of a grafted changeset::
2343 - show the source of a grafted changeset::
2344
2344
2345 hg log --debug -r .
2345 hg log --debug -r .
2346
2346
2347 - show revisions sorted by date::
2347 - show revisions sorted by date::
2348
2348
2349 hg log -r "sort(all(), date)"
2349 hg log -r "sort(all(), date)"
2350
2350
2351 - backport the result of a merge as a single commit::
2351 - backport the result of a merge as a single commit::
2352
2352
2353 hg graft -r 123 --base 123^
2353 hg graft -r 123 --base 123^
2354
2354
2355 - land a feature branch as one changeset::
2355 - land a feature branch as one changeset::
2356
2356
2357 hg up -cr default
2357 hg up -cr default
2358 hg graft -r featureX --base "ancestor('featureX', 'default')"
2358 hg graft -r featureX --base "ancestor('featureX', 'default')"
2359
2359
2360 See :hg:`help revisions` for more about specifying revisions.
2360 See :hg:`help revisions` for more about specifying revisions.
2361
2361
2362 Returns 0 on successful completion.
2362 Returns 0 on successful completion.
2363 '''
2363 '''
2364 with repo.wlock():
2364 with repo.wlock():
2365 return _dograft(ui, repo, *revs, **opts)
2365 return _dograft(ui, repo, *revs, **opts)
2366
2366
2367 def _dograft(ui, repo, *revs, **opts):
2367 def _dograft(ui, repo, *revs, **opts):
2368 opts = pycompat.byteskwargs(opts)
2368 opts = pycompat.byteskwargs(opts)
2369 if revs and opts.get('rev'):
2369 if revs and opts.get('rev'):
2370 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2370 ui.warn(_('warning: inconsistent use of --rev might give unexpected '
2371 'revision ordering!\n'))
2371 'revision ordering!\n'))
2372
2372
2373 revs = list(revs)
2373 revs = list(revs)
2374 revs.extend(opts.get('rev'))
2374 revs.extend(opts.get('rev'))
2375 basectx = None
2375 basectx = None
2376 if opts.get('base'):
2376 if opts.get('base'):
2377 basectx = scmutil.revsingle(repo, opts['base'], None)
2377 basectx = scmutil.revsingle(repo, opts['base'], None)
2378 # a dict of data to be stored in state file
2378 # a dict of data to be stored in state file
2379 statedata = {}
2379 statedata = {}
2380 # list of new nodes created by ongoing graft
2380 # list of new nodes created by ongoing graft
2381 statedata['newnodes'] = []
2381 statedata['newnodes'] = []
2382
2382
2383 if not opts.get('user') and opts.get('currentuser'):
2383 if not opts.get('user') and opts.get('currentuser'):
2384 opts['user'] = ui.username()
2384 opts['user'] = ui.username()
2385 if not opts.get('date') and opts.get('currentdate'):
2385 if not opts.get('date') and opts.get('currentdate'):
2386 opts['date'] = "%d %d" % dateutil.makedate()
2386 opts['date'] = "%d %d" % dateutil.makedate()
2387
2387
2388 editor = cmdutil.getcommiteditor(editform='graft',
2388 editor = cmdutil.getcommiteditor(editform='graft',
2389 **pycompat.strkwargs(opts))
2389 **pycompat.strkwargs(opts))
2390
2390
2391 cont = False
2391 cont = False
2392 if opts.get('no_commit'):
2392 if opts.get('no_commit'):
2393 if opts.get('edit'):
2393 if opts.get('edit'):
2394 raise error.Abort(_("cannot specify --no-commit and "
2394 raise error.Abort(_("cannot specify --no-commit and "
2395 "--edit together"))
2395 "--edit together"))
2396 if opts.get('currentuser'):
2396 if opts.get('currentuser'):
2397 raise error.Abort(_("cannot specify --no-commit and "
2397 raise error.Abort(_("cannot specify --no-commit and "
2398 "--currentuser together"))
2398 "--currentuser together"))
2399 if opts.get('currentdate'):
2399 if opts.get('currentdate'):
2400 raise error.Abort(_("cannot specify --no-commit and "
2400 raise error.Abort(_("cannot specify --no-commit and "
2401 "--currentdate together"))
2401 "--currentdate together"))
2402 if opts.get('log'):
2402 if opts.get('log'):
2403 raise error.Abort(_("cannot specify --no-commit and "
2403 raise error.Abort(_("cannot specify --no-commit and "
2404 "--log together"))
2404 "--log together"))
2405
2405
2406 graftstate = statemod.cmdstate(repo, 'graftstate')
2406 graftstate = statemod.cmdstate(repo, 'graftstate')
2407
2407
2408 if opts.get('stop'):
2408 if opts.get('stop'):
2409 if opts.get('continue'):
2409 if opts.get('continue'):
2410 raise error.Abort(_("cannot use '--continue' and "
2410 raise error.Abort(_("cannot use '--continue' and "
2411 "'--stop' together"))
2411 "'--stop' together"))
2412 if opts.get('abort'):
2412 if opts.get('abort'):
2413 raise error.Abort(_("cannot use '--abort' and '--stop' together"))
2413 raise error.Abort(_("cannot use '--abort' and '--stop' together"))
2414
2414
2415 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2415 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2416 opts.get('date'), opts.get('currentdate'),
2416 opts.get('date'), opts.get('currentdate'),
2417 opts.get('currentuser'), opts.get('rev'))):
2417 opts.get('currentuser'), opts.get('rev'))):
2418 raise error.Abort(_("cannot specify any other flag with '--stop'"))
2418 raise error.Abort(_("cannot specify any other flag with '--stop'"))
2419 return _stopgraft(ui, repo, graftstate)
2419 return _stopgraft(ui, repo, graftstate)
2420 elif opts.get('abort'):
2420 elif opts.get('abort'):
2421 if opts.get('continue'):
2421 if opts.get('continue'):
2422 raise error.Abort(_("cannot use '--continue' and "
2422 raise error.Abort(_("cannot use '--continue' and "
2423 "'--abort' together"))
2423 "'--abort' together"))
2424 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2424 if any((opts.get('edit'), opts.get('log'), opts.get('user'),
2425 opts.get('date'), opts.get('currentdate'),
2425 opts.get('date'), opts.get('currentdate'),
2426 opts.get('currentuser'), opts.get('rev'))):
2426 opts.get('currentuser'), opts.get('rev'))):
2427 raise error.Abort(_("cannot specify any other flag with '--abort'"))
2427 raise error.Abort(_("cannot specify any other flag with '--abort'"))
2428
2428
2429 return _abortgraft(ui, repo, graftstate)
2429 return _abortgraft(ui, repo, graftstate)
2430 elif opts.get('continue'):
2430 elif opts.get('continue'):
2431 cont = True
2431 cont = True
2432 if revs:
2432 if revs:
2433 raise error.Abort(_("can't specify --continue and revisions"))
2433 raise error.Abort(_("can't specify --continue and revisions"))
2434 # read in unfinished revisions
2434 # read in unfinished revisions
2435 if graftstate.exists():
2435 if graftstate.exists():
2436 statedata = _readgraftstate(repo, graftstate)
2436 statedata = _readgraftstate(repo, graftstate)
2437 if statedata.get('date'):
2437 if statedata.get('date'):
2438 opts['date'] = statedata['date']
2438 opts['date'] = statedata['date']
2439 if statedata.get('user'):
2439 if statedata.get('user'):
2440 opts['user'] = statedata['user']
2440 opts['user'] = statedata['user']
2441 if statedata.get('log'):
2441 if statedata.get('log'):
2442 opts['log'] = True
2442 opts['log'] = True
2443 if statedata.get('no_commit'):
2443 if statedata.get('no_commit'):
2444 opts['no_commit'] = statedata.get('no_commit')
2444 opts['no_commit'] = statedata.get('no_commit')
2445 nodes = statedata['nodes']
2445 nodes = statedata['nodes']
2446 revs = [repo[node].rev() for node in nodes]
2446 revs = [repo[node].rev() for node in nodes]
2447 else:
2447 else:
2448 cmdutil.wrongtooltocontinue(repo, _('graft'))
2448 cmdutil.wrongtooltocontinue(repo, _('graft'))
2449 else:
2449 else:
2450 if not revs:
2450 if not revs:
2451 raise error.Abort(_('no revisions specified'))
2451 raise error.Abort(_('no revisions specified'))
2452 cmdutil.checkunfinished(repo)
2452 cmdutil.checkunfinished(repo)
2453 cmdutil.bailifchanged(repo)
2453 cmdutil.bailifchanged(repo)
2454 revs = scmutil.revrange(repo, revs)
2454 revs = scmutil.revrange(repo, revs)
2455
2455
2456 skipped = set()
2456 skipped = set()
2457 if basectx is None:
2457 if basectx is None:
2458 # check for merges
2458 # check for merges
2459 for rev in repo.revs('%ld and merge()', revs):
2459 for rev in repo.revs('%ld and merge()', revs):
2460 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2460 ui.warn(_('skipping ungraftable merge revision %d\n') % rev)
2461 skipped.add(rev)
2461 skipped.add(rev)
2462 revs = [r for r in revs if r not in skipped]
2462 revs = [r for r in revs if r not in skipped]
2463 if not revs:
2463 if not revs:
2464 return -1
2464 return -1
2465 if basectx is not None and len(revs) != 1:
2465 if basectx is not None and len(revs) != 1:
2466 raise error.Abort(_('only one revision allowed with --base '))
2466 raise error.Abort(_('only one revision allowed with --base '))
2467
2467
2468 # Don't check in the --continue case, in effect retaining --force across
2468 # Don't check in the --continue case, in effect retaining --force across
2469 # --continues. That's because without --force, any revisions we decided to
2469 # --continues. That's because without --force, any revisions we decided to
2470 # skip would have been filtered out here, so they wouldn't have made their
2470 # skip would have been filtered out here, so they wouldn't have made their
2471 # way to the graftstate. With --force, any revisions we would have otherwise
2471 # way to the graftstate. With --force, any revisions we would have otherwise
2472 # skipped would not have been filtered out, and if they hadn't been applied
2472 # skipped would not have been filtered out, and if they hadn't been applied
2473 # already, they'd have been in the graftstate.
2473 # already, they'd have been in the graftstate.
2474 if not (cont or opts.get('force')) and basectx is None:
2474 if not (cont or opts.get('force')) and basectx is None:
2475 # check for ancestors of dest branch
2475 # check for ancestors of dest branch
2476 crev = repo['.'].rev()
2476 crev = repo['.'].rev()
2477 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2477 ancestors = repo.changelog.ancestors([crev], inclusive=True)
2478 # XXX make this lazy in the future
2478 # XXX make this lazy in the future
2479 # don't mutate while iterating, create a copy
2479 # don't mutate while iterating, create a copy
2480 for rev in list(revs):
2480 for rev in list(revs):
2481 if rev in ancestors:
2481 if rev in ancestors:
2482 ui.warn(_('skipping ancestor revision %d:%s\n') %
2482 ui.warn(_('skipping ancestor revision %d:%s\n') %
2483 (rev, repo[rev]))
2483 (rev, repo[rev]))
2484 # XXX remove on list is slow
2484 # XXX remove on list is slow
2485 revs.remove(rev)
2485 revs.remove(rev)
2486 if not revs:
2486 if not revs:
2487 return -1
2487 return -1
2488
2488
2489 # analyze revs for earlier grafts
2489 # analyze revs for earlier grafts
2490 ids = {}
2490 ids = {}
2491 for ctx in repo.set("%ld", revs):
2491 for ctx in repo.set("%ld", revs):
2492 ids[ctx.hex()] = ctx.rev()
2492 ids[ctx.hex()] = ctx.rev()
2493 n = ctx.extra().get('source')
2493 n = ctx.extra().get('source')
2494 if n:
2494 if n:
2495 ids[n] = ctx.rev()
2495 ids[n] = ctx.rev()
2496
2496
2497 # check ancestors for earlier grafts
2497 # check ancestors for earlier grafts
2498 ui.debug('scanning for duplicate grafts\n')
2498 ui.debug('scanning for duplicate grafts\n')
2499
2499
2500 # The only changesets we can be sure doesn't contain grafts of any
2500 # The only changesets we can be sure doesn't contain grafts of any
2501 # revs, are the ones that are common ancestors of *all* revs:
2501 # revs, are the ones that are common ancestors of *all* revs:
2502 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2502 for rev in repo.revs('only(%d,ancestor(%ld))', crev, revs):
2503 ctx = repo[rev]
2503 ctx = repo[rev]
2504 n = ctx.extra().get('source')
2504 n = ctx.extra().get('source')
2505 if n in ids:
2505 if n in ids:
2506 try:
2506 try:
2507 r = repo[n].rev()
2507 r = repo[n].rev()
2508 except error.RepoLookupError:
2508 except error.RepoLookupError:
2509 r = None
2509 r = None
2510 if r in revs:
2510 if r in revs:
2511 ui.warn(_('skipping revision %d:%s '
2511 ui.warn(_('skipping revision %d:%s '
2512 '(already grafted to %d:%s)\n')
2512 '(already grafted to %d:%s)\n')
2513 % (r, repo[r], rev, ctx))
2513 % (r, repo[r], rev, ctx))
2514 revs.remove(r)
2514 revs.remove(r)
2515 elif ids[n] in revs:
2515 elif ids[n] in revs:
2516 if r is None:
2516 if r is None:
2517 ui.warn(_('skipping already grafted revision %d:%s '
2517 ui.warn(_('skipping already grafted revision %d:%s '
2518 '(%d:%s also has unknown origin %s)\n')
2518 '(%d:%s also has unknown origin %s)\n')
2519 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2519 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
2520 else:
2520 else:
2521 ui.warn(_('skipping already grafted revision %d:%s '
2521 ui.warn(_('skipping already grafted revision %d:%s '
2522 '(%d:%s also has origin %d:%s)\n')
2522 '(%d:%s also has origin %d:%s)\n')
2523 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2523 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
2524 revs.remove(ids[n])
2524 revs.remove(ids[n])
2525 elif ctx.hex() in ids:
2525 elif ctx.hex() in ids:
2526 r = ids[ctx.hex()]
2526 r = ids[ctx.hex()]
2527 if r in revs:
2527 if r in revs:
2528 ui.warn(_('skipping already grafted revision %d:%s '
2528 ui.warn(_('skipping already grafted revision %d:%s '
2529 '(was grafted from %d:%s)\n') %
2529 '(was grafted from %d:%s)\n') %
2530 (r, repo[r], rev, ctx))
2530 (r, repo[r], rev, ctx))
2531 revs.remove(r)
2531 revs.remove(r)
2532 if not revs:
2532 if not revs:
2533 return -1
2533 return -1
2534
2534
2535 if opts.get('no_commit'):
2535 if opts.get('no_commit'):
2536 statedata['no_commit'] = True
2536 statedata['no_commit'] = True
2537 for pos, ctx in enumerate(repo.set("%ld", revs)):
2537 for pos, ctx in enumerate(repo.set("%ld", revs)):
2538 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2538 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
2539 ctx.description().split('\n', 1)[0])
2539 ctx.description().split('\n', 1)[0])
2540 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2540 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
2541 if names:
2541 if names:
2542 desc += ' (%s)' % ' '.join(names)
2542 desc += ' (%s)' % ' '.join(names)
2543 ui.status(_('grafting %s\n') % desc)
2543 ui.status(_('grafting %s\n') % desc)
2544 if opts.get('dry_run'):
2544 if opts.get('dry_run'):
2545 continue
2545 continue
2546
2546
2547 source = ctx.extra().get('source')
2547 source = ctx.extra().get('source')
2548 extra = {}
2548 extra = {}
2549 if source:
2549 if source:
2550 extra['source'] = source
2550 extra['source'] = source
2551 extra['intermediate-source'] = ctx.hex()
2551 extra['intermediate-source'] = ctx.hex()
2552 else:
2552 else:
2553 extra['source'] = ctx.hex()
2553 extra['source'] = ctx.hex()
2554 user = ctx.user()
2554 user = ctx.user()
2555 if opts.get('user'):
2555 if opts.get('user'):
2556 user = opts['user']
2556 user = opts['user']
2557 statedata['user'] = user
2557 statedata['user'] = user
2558 date = ctx.date()
2558 date = ctx.date()
2559 if opts.get('date'):
2559 if opts.get('date'):
2560 date = opts['date']
2560 date = opts['date']
2561 statedata['date'] = date
2561 statedata['date'] = date
2562 message = ctx.description()
2562 message = ctx.description()
2563 if opts.get('log'):
2563 if opts.get('log'):
2564 message += '\n(grafted from %s)' % ctx.hex()
2564 message += '\n(grafted from %s)' % ctx.hex()
2565 statedata['log'] = True
2565 statedata['log'] = True
2566
2566
2567 # we don't merge the first commit when continuing
2567 # we don't merge the first commit when continuing
2568 if not cont:
2568 if not cont:
2569 # perform the graft merge with p1(rev) as 'ancestor'
2569 # perform the graft merge with p1(rev) as 'ancestor'
2570 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
2570 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
2571 base = ctx.p1() if basectx is None else basectx
2571 base = ctx.p1() if basectx is None else basectx
2572 with ui.configoverride(overrides, 'graft'):
2572 with ui.configoverride(overrides, 'graft'):
2573 stats = mergemod.graft(repo, ctx, base, ['local', 'graft'])
2573 stats = mergemod.graft(repo, ctx, base, ['local', 'graft'])
2574 # report any conflicts
2574 # report any conflicts
2575 if stats.unresolvedcount > 0:
2575 if stats.unresolvedcount > 0:
2576 # write out state for --continue
2576 # write out state for --continue
2577 nodes = [repo[rev].hex() for rev in revs[pos:]]
2577 nodes = [repo[rev].hex() for rev in revs[pos:]]
2578 statedata['nodes'] = nodes
2578 statedata['nodes'] = nodes
2579 stateversion = 1
2579 stateversion = 1
2580 graftstate.save(stateversion, statedata)
2580 graftstate.save(stateversion, statedata)
2581 hint = _("use 'hg resolve' and 'hg graft --continue'")
2581 hint = _("use 'hg resolve' and 'hg graft --continue'")
2582 raise error.Abort(
2582 raise error.Abort(
2583 _("unresolved conflicts, can't continue"),
2583 _("unresolved conflicts, can't continue"),
2584 hint=hint)
2584 hint=hint)
2585 else:
2585 else:
2586 cont = False
2586 cont = False
2587
2587
2588 # commit if --no-commit is false
2588 # commit if --no-commit is false
2589 if not opts.get('no_commit'):
2589 if not opts.get('no_commit'):
2590 node = repo.commit(text=message, user=user, date=date, extra=extra,
2590 node = repo.commit(text=message, user=user, date=date, extra=extra,
2591 editor=editor)
2591 editor=editor)
2592 if node is None:
2592 if node is None:
2593 ui.warn(
2593 ui.warn(
2594 _('note: graft of %d:%s created no changes to commit\n') %
2594 _('note: graft of %d:%s created no changes to commit\n') %
2595 (ctx.rev(), ctx))
2595 (ctx.rev(), ctx))
2596 # checking that newnodes exist because old state files won't have it
2596 # checking that newnodes exist because old state files won't have it
2597 elif statedata.get('newnodes') is not None:
2597 elif statedata.get('newnodes') is not None:
2598 statedata['newnodes'].append(node)
2598 statedata['newnodes'].append(node)
2599
2599
2600 # remove state when we complete successfully
2600 # remove state when we complete successfully
2601 if not opts.get('dry_run'):
2601 if not opts.get('dry_run'):
2602 graftstate.delete()
2602 graftstate.delete()
2603
2603
2604 return 0
2604 return 0
2605
2605
2606 def _abortgraft(ui, repo, graftstate):
2606 def _abortgraft(ui, repo, graftstate):
2607 """abort the interrupted graft and rollbacks to the state before interrupted
2607 """abort the interrupted graft and rollbacks to the state before interrupted
2608 graft"""
2608 graft"""
2609 if not graftstate.exists():
2609 if not graftstate.exists():
2610 raise error.Abort(_("no interrupted graft to abort"))
2610 raise error.Abort(_("no interrupted graft to abort"))
2611 statedata = _readgraftstate(repo, graftstate)
2611 statedata = _readgraftstate(repo, graftstate)
2612 newnodes = statedata.get('newnodes')
2612 newnodes = statedata.get('newnodes')
2613 if newnodes is None:
2613 if newnodes is None:
2614 # and old graft state which does not have all the data required to abort
2614 # and old graft state which does not have all the data required to abort
2615 # the graft
2615 # the graft
2616 raise error.Abort(_("cannot abort using an old graftstate"))
2616 raise error.Abort(_("cannot abort using an old graftstate"))
2617
2617
2618 # changeset from which graft operation was started
2618 # changeset from which graft operation was started
2619 startctx = None
2619 startctx = None
2620 if len(newnodes) > 0:
2620 if len(newnodes) > 0:
2621 startctx = repo[newnodes[0]].p1()
2621 startctx = repo[newnodes[0]].p1()
2622 else:
2622 else:
2623 startctx = repo['.']
2623 startctx = repo['.']
2624 # whether to strip or not
2624 # whether to strip or not
2625 cleanup = False
2625 cleanup = False
2626 if newnodes:
2626 if newnodes:
2627 newnodes = [repo[r].rev() for r in newnodes]
2627 newnodes = [repo[r].rev() for r in newnodes]
2628 cleanup = True
2628 cleanup = True
2629 # checking that none of the newnodes turned public or is public
2629 # checking that none of the newnodes turned public or is public
2630 immutable = [c for c in newnodes if not repo[c].mutable()]
2630 immutable = [c for c in newnodes if not repo[c].mutable()]
2631 if immutable:
2631 if immutable:
2632 repo.ui.warn(_("cannot clean up public changesets %s\n")
2632 repo.ui.warn(_("cannot clean up public changesets %s\n")
2633 % ', '.join(bytes(repo[r]) for r in immutable),
2633 % ', '.join(bytes(repo[r]) for r in immutable),
2634 hint=_("see 'hg help phases' for details"))
2634 hint=_("see 'hg help phases' for details"))
2635 cleanup = False
2635 cleanup = False
2636
2636
2637 # checking that no new nodes are created on top of grafted revs
2637 # checking that no new nodes are created on top of grafted revs
2638 desc = set(repo.changelog.descendants(newnodes))
2638 desc = set(repo.changelog.descendants(newnodes))
2639 if desc - set(newnodes):
2639 if desc - set(newnodes):
2640 repo.ui.warn(_("new changesets detected on destination "
2640 repo.ui.warn(_("new changesets detected on destination "
2641 "branch, can't strip\n"))
2641 "branch, can't strip\n"))
2642 cleanup = False
2642 cleanup = False
2643
2643
2644 if cleanup:
2644 if cleanup:
2645 with repo.wlock(), repo.lock():
2645 with repo.wlock(), repo.lock():
2646 hg.updaterepo(repo, startctx.node(), overwrite=True)
2646 hg.updaterepo(repo, startctx.node(), overwrite=True)
2647 # stripping the new nodes created
2647 # stripping the new nodes created
2648 strippoints = [c.node() for c in repo.set("roots(%ld)",
2648 strippoints = [c.node() for c in repo.set("roots(%ld)",
2649 newnodes)]
2649 newnodes)]
2650 repair.strip(repo.ui, repo, strippoints, backup=False)
2650 repair.strip(repo.ui, repo, strippoints, backup=False)
2651
2651
2652 if not cleanup:
2652 if not cleanup:
2653 # we don't update to the startnode if we can't strip
2653 # we don't update to the startnode if we can't strip
2654 startctx = repo['.']
2654 startctx = repo['.']
2655 hg.updaterepo(repo, startctx.node(), overwrite=True)
2655 hg.updaterepo(repo, startctx.node(), overwrite=True)
2656
2656
2657 ui.status(_("graft aborted\n"))
2657 ui.status(_("graft aborted\n"))
2658 ui.status(_("working directory is now at %s\n") % startctx.hex()[:12])
2658 ui.status(_("working directory is now at %s\n") % startctx.hex()[:12])
2659 graftstate.delete()
2659 graftstate.delete()
2660 return 0
2660 return 0
2661
2661
2662 def _readgraftstate(repo, graftstate):
2662 def _readgraftstate(repo, graftstate):
2663 """read the graft state file and return a dict of the data stored in it"""
2663 """read the graft state file and return a dict of the data stored in it"""
2664 try:
2664 try:
2665 return graftstate.read()
2665 return graftstate.read()
2666 except error.CorruptedState:
2666 except error.CorruptedState:
2667 nodes = repo.vfs.read('graftstate').splitlines()
2667 nodes = repo.vfs.read('graftstate').splitlines()
2668 return {'nodes': nodes}
2668 return {'nodes': nodes}
2669
2669
2670 def _stopgraft(ui, repo, graftstate):
2670 def _stopgraft(ui, repo, graftstate):
2671 """stop the interrupted graft"""
2671 """stop the interrupted graft"""
2672 if not graftstate.exists():
2672 if not graftstate.exists():
2673 raise error.Abort(_("no interrupted graft found"))
2673 raise error.Abort(_("no interrupted graft found"))
2674 pctx = repo['.']
2674 pctx = repo['.']
2675 hg.updaterepo(repo, pctx.node(), overwrite=True)
2675 hg.updaterepo(repo, pctx.node(), overwrite=True)
2676 graftstate.delete()
2676 graftstate.delete()
2677 ui.status(_("stopped the interrupted graft\n"))
2677 ui.status(_("stopped the interrupted graft\n"))
2678 ui.status(_("working directory is now at %s\n") % pctx.hex()[:12])
2678 ui.status(_("working directory is now at %s\n") % pctx.hex()[:12])
2679 return 0
2679 return 0
2680
2680
2681 @command('grep',
2681 @command('grep',
2682 [('0', 'print0', None, _('end fields with NUL')),
2682 [('0', 'print0', None, _('end fields with NUL')),
2683 ('', 'all', None, _('print all revisions that match (DEPRECATED) ')),
2683 ('', 'all', None, _('print all revisions that match (DEPRECATED) ')),
2684 ('', 'diff', None, _('print all revisions when the term was introduced '
2684 ('', 'diff', None, _('print all revisions when the term was introduced '
2685 'or removed')),
2685 'or removed')),
2686 ('a', 'text', None, _('treat all files as text')),
2686 ('a', 'text', None, _('treat all files as text')),
2687 ('f', 'follow', None,
2687 ('f', 'follow', None,
2688 _('follow changeset history,'
2688 _('follow changeset history,'
2689 ' or file history across copies and renames')),
2689 ' or file history across copies and renames')),
2690 ('i', 'ignore-case', None, _('ignore case when matching')),
2690 ('i', 'ignore-case', None, _('ignore case when matching')),
2691 ('l', 'files-with-matches', None,
2691 ('l', 'files-with-matches', None,
2692 _('print only filenames and revisions that match')),
2692 _('print only filenames and revisions that match')),
2693 ('n', 'line-number', None, _('print matching line numbers')),
2693 ('n', 'line-number', None, _('print matching line numbers')),
2694 ('r', 'rev', [],
2694 ('r', 'rev', [],
2695 _('only search files changed within revision range'), _('REV')),
2695 _('only search files changed within revision range'), _('REV')),
2696 ('', 'all-files', None,
2696 ('', 'all-files', None,
2697 _('include all files in the changeset while grepping (EXPERIMENTAL)')),
2697 _('include all files in the changeset while grepping (EXPERIMENTAL)')),
2698 ('u', 'user', None, _('list the author (long with -v)')),
2698 ('u', 'user', None, _('list the author (long with -v)')),
2699 ('d', 'date', None, _('list the date (short with -q)')),
2699 ('d', 'date', None, _('list the date (short with -q)')),
2700 ] + formatteropts + walkopts,
2700 ] + formatteropts + walkopts,
2701 _('[OPTION]... PATTERN [FILE]...'),
2701 _('[OPTION]... PATTERN [FILE]...'),
2702 helpcategory=command.CATEGORY_FILE_CONTENTS,
2702 helpcategory=command.CATEGORY_FILE_CONTENTS,
2703 inferrepo=True,
2703 inferrepo=True,
2704 intents={INTENT_READONLY})
2704 intents={INTENT_READONLY})
2705 def grep(ui, repo, pattern, *pats, **opts):
2705 def grep(ui, repo, pattern, *pats, **opts):
2706 """search revision history for a pattern in specified files
2706 """search revision history for a pattern in specified files
2707
2707
2708 Search revision history for a regular expression in the specified
2708 Search revision history for a regular expression in the specified
2709 files or the entire project.
2709 files or the entire project.
2710
2710
2711 By default, grep prints the most recent revision number for each
2711 By default, grep prints the most recent revision number for each
2712 file in which it finds a match. To get it to print every revision
2712 file in which it finds a match. To get it to print every revision
2713 that contains a change in match status ("-" for a match that becomes
2713 that contains a change in match status ("-" for a match that becomes
2714 a non-match, or "+" for a non-match that becomes a match), use the
2714 a non-match, or "+" for a non-match that becomes a match), use the
2715 --diff flag.
2715 --diff flag.
2716
2716
2717 PATTERN can be any Python (roughly Perl-compatible) regular
2717 PATTERN can be any Python (roughly Perl-compatible) regular
2718 expression.
2718 expression.
2719
2719
2720 If no FILEs are specified (and -f/--follow isn't set), all files in
2720 If no FILEs are specified (and -f/--follow isn't set), all files in
2721 the repository are searched, including those that don't exist in the
2721 the repository are searched, including those that don't exist in the
2722 current branch or have been deleted in a prior changeset.
2722 current branch or have been deleted in a prior changeset.
2723
2723
2724 .. container:: verbose
2724 .. container:: verbose
2725
2725
2726 Template:
2726 Template:
2727
2727
2728 The following keywords are supported in addition to the common template
2728 The following keywords are supported in addition to the common template
2729 keywords and functions. See also :hg:`help templates`.
2729 keywords and functions. See also :hg:`help templates`.
2730
2730
2731 :change: String. Character denoting insertion ``+`` or removal ``-``.
2731 :change: String. Character denoting insertion ``+`` or removal ``-``.
2732 Available if ``--diff`` is specified.
2732 Available if ``--diff`` is specified.
2733 :lineno: Integer. Line number of the match.
2733 :lineno: Integer. Line number of the match.
2734 :path: String. Repository-absolute path of the file.
2734 :path: String. Repository-absolute path of the file.
2735 :texts: List of text chunks.
2735 :texts: List of text chunks.
2736
2736
2737 And each entry of ``{texts}`` provides the following sub-keywords.
2737 And each entry of ``{texts}`` provides the following sub-keywords.
2738
2738
2739 :matched: Boolean. True if the chunk matches the specified pattern.
2739 :matched: Boolean. True if the chunk matches the specified pattern.
2740 :text: String. Chunk content.
2740 :text: String. Chunk content.
2741
2741
2742 See :hg:`help templates.operators` for the list expansion syntax.
2742 See :hg:`help templates.operators` for the list expansion syntax.
2743
2743
2744 Returns 0 if a match is found, 1 otherwise.
2744 Returns 0 if a match is found, 1 otherwise.
2745 """
2745 """
2746 opts = pycompat.byteskwargs(opts)
2746 opts = pycompat.byteskwargs(opts)
2747 diff = opts.get('all') or opts.get('diff')
2747 diff = opts.get('all') or opts.get('diff')
2748 all_files = opts.get('all_files')
2748 all_files = opts.get('all_files')
2749 if diff and opts.get('all_files'):
2749 if diff and opts.get('all_files'):
2750 raise error.Abort(_('--diff and --all-files are mutually exclusive'))
2750 raise error.Abort(_('--diff and --all-files are mutually exclusive'))
2751 # TODO: remove "not opts.get('rev')" if --all-files -rMULTIREV gets working
2751 # TODO: remove "not opts.get('rev')" if --all-files -rMULTIREV gets working
2752 if opts.get('all_files') is None and not opts.get('rev') and not diff:
2752 if opts.get('all_files') is None and not opts.get('rev') and not diff:
2753 # experimental config: commands.grep.all-files
2753 # experimental config: commands.grep.all-files
2754 opts['all_files'] = ui.configbool('commands', 'grep.all-files')
2754 opts['all_files'] = ui.configbool('commands', 'grep.all-files')
2755 plaingrep = opts.get('all_files') and not opts.get('rev')
2755 plaingrep = opts.get('all_files') and not opts.get('rev')
2756 if plaingrep:
2756 if plaingrep:
2757 opts['rev'] = ['wdir()']
2757 opts['rev'] = ['wdir()']
2758
2758
2759 reflags = re.M
2759 reflags = re.M
2760 if opts.get('ignore_case'):
2760 if opts.get('ignore_case'):
2761 reflags |= re.I
2761 reflags |= re.I
2762 try:
2762 try:
2763 regexp = util.re.compile(pattern, reflags)
2763 regexp = util.re.compile(pattern, reflags)
2764 except re.error as inst:
2764 except re.error as inst:
2765 ui.warn(_("grep: invalid match pattern: %s\n") % pycompat.bytestr(inst))
2765 ui.warn(_("grep: invalid match pattern: %s\n") % pycompat.bytestr(inst))
2766 return 1
2766 return 1
2767 sep, eol = ':', '\n'
2767 sep, eol = ':', '\n'
2768 if opts.get('print0'):
2768 if opts.get('print0'):
2769 sep = eol = '\0'
2769 sep = eol = '\0'
2770
2770
2771 getfile = util.lrucachefunc(repo.file)
2771 getfile = util.lrucachefunc(repo.file)
2772
2772
2773 def matchlines(body):
2773 def matchlines(body):
2774 begin = 0
2774 begin = 0
2775 linenum = 0
2775 linenum = 0
2776 while begin < len(body):
2776 while begin < len(body):
2777 match = regexp.search(body, begin)
2777 match = regexp.search(body, begin)
2778 if not match:
2778 if not match:
2779 break
2779 break
2780 mstart, mend = match.span()
2780 mstart, mend = match.span()
2781 linenum += body.count('\n', begin, mstart) + 1
2781 linenum += body.count('\n', begin, mstart) + 1
2782 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2782 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2783 begin = body.find('\n', mend) + 1 or len(body) + 1
2783 begin = body.find('\n', mend) + 1 or len(body) + 1
2784 lend = begin - 1
2784 lend = begin - 1
2785 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2785 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2786
2786
2787 class linestate(object):
2787 class linestate(object):
2788 def __init__(self, line, linenum, colstart, colend):
2788 def __init__(self, line, linenum, colstart, colend):
2789 self.line = line
2789 self.line = line
2790 self.linenum = linenum
2790 self.linenum = linenum
2791 self.colstart = colstart
2791 self.colstart = colstart
2792 self.colend = colend
2792 self.colend = colend
2793
2793
2794 def __hash__(self):
2794 def __hash__(self):
2795 return hash((self.linenum, self.line))
2795 return hash((self.linenum, self.line))
2796
2796
2797 def __eq__(self, other):
2797 def __eq__(self, other):
2798 return self.line == other.line
2798 return self.line == other.line
2799
2799
2800 def findpos(self):
2800 def findpos(self):
2801 """Iterate all (start, end) indices of matches"""
2801 """Iterate all (start, end) indices of matches"""
2802 yield self.colstart, self.colend
2802 yield self.colstart, self.colend
2803 p = self.colend
2803 p = self.colend
2804 while p < len(self.line):
2804 while p < len(self.line):
2805 m = regexp.search(self.line, p)
2805 m = regexp.search(self.line, p)
2806 if not m:
2806 if not m:
2807 break
2807 break
2808 yield m.span()
2808 yield m.span()
2809 p = m.end()
2809 p = m.end()
2810
2810
2811 matches = {}
2811 matches = {}
2812 copies = {}
2812 copies = {}
2813 def grepbody(fn, rev, body):
2813 def grepbody(fn, rev, body):
2814 matches[rev].setdefault(fn, [])
2814 matches[rev].setdefault(fn, [])
2815 m = matches[rev][fn]
2815 m = matches[rev][fn]
2816 for lnum, cstart, cend, line in matchlines(body):
2816 for lnum, cstart, cend, line in matchlines(body):
2817 s = linestate(line, lnum, cstart, cend)
2817 s = linestate(line, lnum, cstart, cend)
2818 m.append(s)
2818 m.append(s)
2819
2819
2820 def difflinestates(a, b):
2820 def difflinestates(a, b):
2821 sm = difflib.SequenceMatcher(None, a, b)
2821 sm = difflib.SequenceMatcher(None, a, b)
2822 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2822 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2823 if tag == r'insert':
2823 if tag == r'insert':
2824 for i in pycompat.xrange(blo, bhi):
2824 for i in pycompat.xrange(blo, bhi):
2825 yield ('+', b[i])
2825 yield ('+', b[i])
2826 elif tag == r'delete':
2826 elif tag == r'delete':
2827 for i in pycompat.xrange(alo, ahi):
2827 for i in pycompat.xrange(alo, ahi):
2828 yield ('-', a[i])
2828 yield ('-', a[i])
2829 elif tag == r'replace':
2829 elif tag == r'replace':
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 for i in pycompat.xrange(blo, bhi):
2832 for i in pycompat.xrange(blo, bhi):
2833 yield ('+', b[i])
2833 yield ('+', b[i])
2834
2834
2835 def display(fm, fn, ctx, pstates, states):
2835 def display(fm, fn, ctx, pstates, states):
2836 rev = scmutil.intrev(ctx)
2836 rev = scmutil.intrev(ctx)
2837 if fm.isplain():
2837 if fm.isplain():
2838 formatuser = ui.shortuser
2838 formatuser = ui.shortuser
2839 else:
2839 else:
2840 formatuser = pycompat.bytestr
2840 formatuser = pycompat.bytestr
2841 if ui.quiet:
2841 if ui.quiet:
2842 datefmt = '%Y-%m-%d'
2842 datefmt = '%Y-%m-%d'
2843 else:
2843 else:
2844 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2844 datefmt = '%a %b %d %H:%M:%S %Y %1%2'
2845 found = False
2845 found = False
2846 @util.cachefunc
2846 @util.cachefunc
2847 def binary():
2847 def binary():
2848 flog = getfile(fn)
2848 flog = getfile(fn)
2849 try:
2849 try:
2850 return stringutil.binary(flog.read(ctx.filenode(fn)))
2850 return stringutil.binary(flog.read(ctx.filenode(fn)))
2851 except error.WdirUnsupported:
2851 except error.WdirUnsupported:
2852 return ctx[fn].isbinary()
2852 return ctx[fn].isbinary()
2853
2853
2854 fieldnamemap = {'filename': 'path', 'linenumber': 'lineno'}
2854 fieldnamemap = {'filename': 'path', 'linenumber': 'lineno'}
2855 if diff:
2855 if diff:
2856 iter = difflinestates(pstates, states)
2856 iter = difflinestates(pstates, states)
2857 else:
2857 else:
2858 iter = [('', l) for l in states]
2858 iter = [('', l) for l in states]
2859 for change, l in iter:
2859 for change, l in iter:
2860 fm.startitem()
2860 fm.startitem()
2861 fm.context(ctx=ctx)
2861 fm.context(ctx=ctx)
2862 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)))
2862 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)))
2863
2863
2864 cols = [
2864 cols = [
2865 ('filename', '%s', fn, True),
2865 ('filename', '%s', fn, True),
2866 ('rev', '%d', rev, not plaingrep),
2866 ('rev', '%d', rev, not plaingrep),
2867 ('linenumber', '%d', l.linenum, opts.get('line_number')),
2867 ('linenumber', '%d', l.linenum, opts.get('line_number')),
2868 ]
2868 ]
2869 if diff:
2869 if diff:
2870 cols.append(('change', '%s', change, True))
2870 cols.append(('change', '%s', change, True))
2871 cols.extend([
2871 cols.extend([
2872 ('user', '%s', formatuser(ctx.user()), opts.get('user')),
2872 ('user', '%s', formatuser(ctx.user()), opts.get('user')),
2873 ('date', '%s', fm.formatdate(ctx.date(), datefmt),
2873 ('date', '%s', fm.formatdate(ctx.date(), datefmt),
2874 opts.get('date')),
2874 opts.get('date')),
2875 ])
2875 ])
2876 lastcol = next(
2876 lastcol = next(
2877 name for name, fmt, data, cond in reversed(cols) if cond)
2877 name for name, fmt, data, cond in reversed(cols) if cond)
2878 for name, fmt, data, cond in cols:
2878 for name, fmt, data, cond in cols:
2879 field = fieldnamemap.get(name, name)
2879 field = fieldnamemap.get(name, name)
2880 fm.condwrite(cond, field, fmt, data, label='grep.%s' % name)
2880 fm.condwrite(cond, field, fmt, data, label='grep.%s' % name)
2881 if cond and name != lastcol:
2881 if cond and name != lastcol:
2882 fm.plain(sep, label='grep.sep')
2882 fm.plain(sep, label='grep.sep')
2883 if not opts.get('files_with_matches'):
2883 if not opts.get('files_with_matches'):
2884 fm.plain(sep, label='grep.sep')
2884 fm.plain(sep, label='grep.sep')
2885 if not opts.get('text') and binary():
2885 if not opts.get('text') and binary():
2886 fm.plain(_(" Binary file matches"))
2886 fm.plain(_(" Binary file matches"))
2887 else:
2887 else:
2888 displaymatches(fm.nested('texts', tmpl='{text}'), l)
2888 displaymatches(fm.nested('texts', tmpl='{text}'), l)
2889 fm.plain(eol)
2889 fm.plain(eol)
2890 found = True
2890 found = True
2891 if opts.get('files_with_matches'):
2891 if opts.get('files_with_matches'):
2892 break
2892 break
2893 return found
2893 return found
2894
2894
2895 def displaymatches(fm, l):
2895 def displaymatches(fm, l):
2896 p = 0
2896 p = 0
2897 for s, e in l.findpos():
2897 for s, e in l.findpos():
2898 if p < s:
2898 if p < s:
2899 fm.startitem()
2899 fm.startitem()
2900 fm.write('text', '%s', l.line[p:s])
2900 fm.write('text', '%s', l.line[p:s])
2901 fm.data(matched=False)
2901 fm.data(matched=False)
2902 fm.startitem()
2902 fm.startitem()
2903 fm.write('text', '%s', l.line[s:e], label='grep.match')
2903 fm.write('text', '%s', l.line[s:e], label='grep.match')
2904 fm.data(matched=True)
2904 fm.data(matched=True)
2905 p = e
2905 p = e
2906 if p < len(l.line):
2906 if p < len(l.line):
2907 fm.startitem()
2907 fm.startitem()
2908 fm.write('text', '%s', l.line[p:])
2908 fm.write('text', '%s', l.line[p:])
2909 fm.data(matched=False)
2909 fm.data(matched=False)
2910 fm.end()
2910 fm.end()
2911
2911
2912 skip = {}
2912 skip = {}
2913 revfiles = {}
2913 revfiles = {}
2914 match = scmutil.match(repo[None], pats, opts)
2914 match = scmutil.match(repo[None], pats, opts)
2915 found = False
2915 found = False
2916 follow = opts.get('follow')
2916 follow = opts.get('follow')
2917
2917
2918 def prep(ctx, fns):
2918 def prep(ctx, fns):
2919 rev = ctx.rev()
2919 rev = ctx.rev()
2920 pctx = ctx.p1()
2920 pctx = ctx.p1()
2921 parent = pctx.rev()
2921 parent = pctx.rev()
2922 matches.setdefault(rev, {})
2922 matches.setdefault(rev, {})
2923 matches.setdefault(parent, {})
2923 matches.setdefault(parent, {})
2924 files = revfiles.setdefault(rev, [])
2924 files = revfiles.setdefault(rev, [])
2925 for fn in fns:
2925 for fn in fns:
2926 flog = getfile(fn)
2926 flog = getfile(fn)
2927 try:
2927 try:
2928 fnode = ctx.filenode(fn)
2928 fnode = ctx.filenode(fn)
2929 except error.LookupError:
2929 except error.LookupError:
2930 continue
2930 continue
2931 try:
2931 try:
2932 copied = flog.renamed(fnode)
2932 copied = flog.renamed(fnode)
2933 except error.WdirUnsupported:
2933 except error.WdirUnsupported:
2934 copied = ctx[fn].renamed()
2934 copied = ctx[fn].renamed()
2935 copy = follow and copied and copied[0]
2935 copy = follow and copied and copied[0]
2936 if copy:
2936 if copy:
2937 copies.setdefault(rev, {})[fn] = copy
2937 copies.setdefault(rev, {})[fn] = copy
2938 if fn in skip:
2938 if fn in skip:
2939 if copy:
2939 if copy:
2940 skip[copy] = True
2940 skip[copy] = True
2941 continue
2941 continue
2942 files.append(fn)
2942 files.append(fn)
2943
2943
2944 if fn not in matches[rev]:
2944 if fn not in matches[rev]:
2945 try:
2945 try:
2946 content = flog.read(fnode)
2946 content = flog.read(fnode)
2947 except error.WdirUnsupported:
2947 except error.WdirUnsupported:
2948 content = ctx[fn].data()
2948 content = ctx[fn].data()
2949 grepbody(fn, rev, content)
2949 grepbody(fn, rev, content)
2950
2950
2951 pfn = copy or fn
2951 pfn = copy or fn
2952 if pfn not in matches[parent]:
2952 if pfn not in matches[parent]:
2953 try:
2953 try:
2954 fnode = pctx.filenode(pfn)
2954 fnode = pctx.filenode(pfn)
2955 grepbody(pfn, parent, flog.read(fnode))
2955 grepbody(pfn, parent, flog.read(fnode))
2956 except error.LookupError:
2956 except error.LookupError:
2957 pass
2957 pass
2958
2958
2959 ui.pager('grep')
2959 ui.pager('grep')
2960 fm = ui.formatter('grep', opts)
2960 fm = ui.formatter('grep', opts)
2961 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2961 for ctx in cmdutil.walkchangerevs(repo, match, opts, prep):
2962 rev = ctx.rev()
2962 rev = ctx.rev()
2963 parent = ctx.p1().rev()
2963 parent = ctx.p1().rev()
2964 for fn in sorted(revfiles.get(rev, [])):
2964 for fn in sorted(revfiles.get(rev, [])):
2965 states = matches[rev][fn]
2965 states = matches[rev][fn]
2966 copy = copies.get(rev, {}).get(fn)
2966 copy = copies.get(rev, {}).get(fn)
2967 if fn in skip:
2967 if fn in skip:
2968 if copy:
2968 if copy:
2969 skip[copy] = True
2969 skip[copy] = True
2970 continue
2970 continue
2971 pstates = matches.get(parent, {}).get(copy or fn, [])
2971 pstates = matches.get(parent, {}).get(copy or fn, [])
2972 if pstates or states:
2972 if pstates or states:
2973 r = display(fm, fn, ctx, pstates, states)
2973 r = display(fm, fn, ctx, pstates, states)
2974 found = found or r
2974 found = found or r
2975 if r and not diff and not all_files:
2975 if r and not diff and not all_files:
2976 skip[fn] = True
2976 skip[fn] = True
2977 if copy:
2977 if copy:
2978 skip[copy] = True
2978 skip[copy] = True
2979 del revfiles[rev]
2979 del revfiles[rev]
2980 # We will keep the matches dict for the duration of the window
2980 # We will keep the matches dict for the duration of the window
2981 # clear the matches dict once the window is over
2981 # clear the matches dict once the window is over
2982 if not revfiles:
2982 if not revfiles:
2983 matches.clear()
2983 matches.clear()
2984 fm.end()
2984 fm.end()
2985
2985
2986 return not found
2986 return not found
2987
2987
2988 @command('heads',
2988 @command('heads',
2989 [('r', 'rev', '',
2989 [('r', 'rev', '',
2990 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2990 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2991 ('t', 'topo', False, _('show topological heads only')),
2991 ('t', 'topo', False, _('show topological heads only')),
2992 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2992 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2993 ('c', 'closed', False, _('show normal and closed branch heads')),
2993 ('c', 'closed', False, _('show normal and closed branch heads')),
2994 ] + templateopts,
2994 ] + templateopts,
2995 _('[-ct] [-r STARTREV] [REV]...'),
2995 _('[-ct] [-r STARTREV] [REV]...'),
2996 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
2996 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
2997 intents={INTENT_READONLY})
2997 intents={INTENT_READONLY})
2998 def heads(ui, repo, *branchrevs, **opts):
2998 def heads(ui, repo, *branchrevs, **opts):
2999 """show branch heads
2999 """show branch heads
3000
3000
3001 With no arguments, show all open branch heads in the repository.
3001 With no arguments, show all open branch heads in the repository.
3002 Branch heads are changesets that have no descendants on the
3002 Branch heads are changesets that have no descendants on the
3003 same branch. They are where development generally takes place and
3003 same branch. They are where development generally takes place and
3004 are the usual targets for update and merge operations.
3004 are the usual targets for update and merge operations.
3005
3005
3006 If one or more REVs are given, only open branch heads on the
3006 If one or more REVs are given, only open branch heads on the
3007 branches associated with the specified changesets are shown. This
3007 branches associated with the specified changesets are shown. This
3008 means that you can use :hg:`heads .` to see the heads on the
3008 means that you can use :hg:`heads .` to see the heads on the
3009 currently checked-out branch.
3009 currently checked-out branch.
3010
3010
3011 If -c/--closed is specified, also show branch heads marked closed
3011 If -c/--closed is specified, also show branch heads marked closed
3012 (see :hg:`commit --close-branch`).
3012 (see :hg:`commit --close-branch`).
3013
3013
3014 If STARTREV is specified, only those heads that are descendants of
3014 If STARTREV is specified, only those heads that are descendants of
3015 STARTREV will be displayed.
3015 STARTREV will be displayed.
3016
3016
3017 If -t/--topo is specified, named branch mechanics will be ignored and only
3017 If -t/--topo is specified, named branch mechanics will be ignored and only
3018 topological heads (changesets with no children) will be shown.
3018 topological heads (changesets with no children) will be shown.
3019
3019
3020 Returns 0 if matching heads are found, 1 if not.
3020 Returns 0 if matching heads are found, 1 if not.
3021 """
3021 """
3022
3022
3023 opts = pycompat.byteskwargs(opts)
3023 opts = pycompat.byteskwargs(opts)
3024 start = None
3024 start = None
3025 rev = opts.get('rev')
3025 rev = opts.get('rev')
3026 if rev:
3026 if rev:
3027 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3027 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3028 start = scmutil.revsingle(repo, rev, None).node()
3028 start = scmutil.revsingle(repo, rev, None).node()
3029
3029
3030 if opts.get('topo'):
3030 if opts.get('topo'):
3031 heads = [repo[h] for h in repo.heads(start)]
3031 heads = [repo[h] for h in repo.heads(start)]
3032 else:
3032 else:
3033 heads = []
3033 heads = []
3034 for branch in repo.branchmap():
3034 for branch in repo.branchmap():
3035 heads += repo.branchheads(branch, start, opts.get('closed'))
3035 heads += repo.branchheads(branch, start, opts.get('closed'))
3036 heads = [repo[h] for h in heads]
3036 heads = [repo[h] for h in heads]
3037
3037
3038 if branchrevs:
3038 if branchrevs:
3039 branches = set(repo[r].branch()
3039 branches = set(repo[r].branch()
3040 for r in scmutil.revrange(repo, branchrevs))
3040 for r in scmutil.revrange(repo, branchrevs))
3041 heads = [h for h in heads if h.branch() in branches]
3041 heads = [h for h in heads if h.branch() in branches]
3042
3042
3043 if opts.get('active') and branchrevs:
3043 if opts.get('active') and branchrevs:
3044 dagheads = repo.heads(start)
3044 dagheads = repo.heads(start)
3045 heads = [h for h in heads if h.node() in dagheads]
3045 heads = [h for h in heads if h.node() in dagheads]
3046
3046
3047 if branchrevs:
3047 if branchrevs:
3048 haveheads = set(h.branch() for h in heads)
3048 haveheads = set(h.branch() for h in heads)
3049 if branches - haveheads:
3049 if branches - haveheads:
3050 headless = ', '.join(b for b in branches - haveheads)
3050 headless = ', '.join(b for b in branches - haveheads)
3051 msg = _('no open branch heads found on branches %s')
3051 msg = _('no open branch heads found on branches %s')
3052 if opts.get('rev'):
3052 if opts.get('rev'):
3053 msg += _(' (started at %s)') % opts['rev']
3053 msg += _(' (started at %s)') % opts['rev']
3054 ui.warn((msg + '\n') % headless)
3054 ui.warn((msg + '\n') % headless)
3055
3055
3056 if not heads:
3056 if not heads:
3057 return 1
3057 return 1
3058
3058
3059 ui.pager('heads')
3059 ui.pager('heads')
3060 heads = sorted(heads, key=lambda x: -x.rev())
3060 heads = sorted(heads, key=lambda x: -x.rev())
3061 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3061 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3062 for ctx in heads:
3062 for ctx in heads:
3063 displayer.show(ctx)
3063 displayer.show(ctx)
3064 displayer.close()
3064 displayer.close()
3065
3065
3066 @command('help',
3066 @command('help',
3067 [('e', 'extension', None, _('show only help for extensions')),
3067 [('e', 'extension', None, _('show only help for extensions')),
3068 ('c', 'command', None, _('show only help for commands')),
3068 ('c', 'command', None, _('show only help for commands')),
3069 ('k', 'keyword', None, _('show topics matching keyword')),
3069 ('k', 'keyword', None, _('show topics matching keyword')),
3070 ('s', 'system', [],
3070 ('s', 'system', [],
3071 _('show help for specific platform(s)'), _('PLATFORM')),
3071 _('show help for specific platform(s)'), _('PLATFORM')),
3072 ],
3072 ],
3073 _('[-eck] [-s PLATFORM] [TOPIC]'),
3073 _('[-eck] [-s PLATFORM] [TOPIC]'),
3074 helpcategory=command.CATEGORY_HELP,
3074 helpcategory=command.CATEGORY_HELP,
3075 norepo=True,
3075 norepo=True,
3076 intents={INTENT_READONLY})
3076 intents={INTENT_READONLY})
3077 def help_(ui, name=None, **opts):
3077 def help_(ui, name=None, **opts):
3078 """show help for a given topic or a help overview
3078 """show help for a given topic or a help overview
3079
3079
3080 With no arguments, print a list of commands with short help messages.
3080 With no arguments, print a list of commands with short help messages.
3081
3081
3082 Given a topic, extension, or command name, print help for that
3082 Given a topic, extension, or command name, print help for that
3083 topic.
3083 topic.
3084
3084
3085 Returns 0 if successful.
3085 Returns 0 if successful.
3086 """
3086 """
3087
3087
3088 keep = opts.get(r'system') or []
3088 keep = opts.get(r'system') or []
3089 if len(keep) == 0:
3089 if len(keep) == 0:
3090 if pycompat.sysplatform.startswith('win'):
3090 if pycompat.sysplatform.startswith('win'):
3091 keep.append('windows')
3091 keep.append('windows')
3092 elif pycompat.sysplatform == 'OpenVMS':
3092 elif pycompat.sysplatform == 'OpenVMS':
3093 keep.append('vms')
3093 keep.append('vms')
3094 elif pycompat.sysplatform == 'plan9':
3094 elif pycompat.sysplatform == 'plan9':
3095 keep.append('plan9')
3095 keep.append('plan9')
3096 else:
3096 else:
3097 keep.append('unix')
3097 keep.append('unix')
3098 keep.append(pycompat.sysplatform.lower())
3098 keep.append(pycompat.sysplatform.lower())
3099 if ui.verbose:
3099 if ui.verbose:
3100 keep.append('verbose')
3100 keep.append('verbose')
3101
3101
3102 commands = sys.modules[__name__]
3102 commands = sys.modules[__name__]
3103 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3103 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3104 ui.pager('help')
3104 ui.pager('help')
3105 ui.write(formatted)
3105 ui.write(formatted)
3106
3106
3107
3107
3108 @command('identify|id',
3108 @command('identify|id',
3109 [('r', 'rev', '',
3109 [('r', 'rev', '',
3110 _('identify the specified revision'), _('REV')),
3110 _('identify the specified revision'), _('REV')),
3111 ('n', 'num', None, _('show local revision number')),
3111 ('n', 'num', None, _('show local revision number')),
3112 ('i', 'id', None, _('show global revision id')),
3112 ('i', 'id', None, _('show global revision id')),
3113 ('b', 'branch', None, _('show branch')),
3113 ('b', 'branch', None, _('show branch')),
3114 ('t', 'tags', None, _('show tags')),
3114 ('t', 'tags', None, _('show tags')),
3115 ('B', 'bookmarks', None, _('show bookmarks')),
3115 ('B', 'bookmarks', None, _('show bookmarks')),
3116 ] + remoteopts + formatteropts,
3116 ] + remoteopts + formatteropts,
3117 _('[-nibtB] [-r REV] [SOURCE]'),
3117 _('[-nibtB] [-r REV] [SOURCE]'),
3118 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3118 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3119 optionalrepo=True,
3119 optionalrepo=True,
3120 intents={INTENT_READONLY})
3120 intents={INTENT_READONLY})
3121 def identify(ui, repo, source=None, rev=None,
3121 def identify(ui, repo, source=None, rev=None,
3122 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3122 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3123 """identify the working directory or specified revision
3123 """identify the working directory or specified revision
3124
3124
3125 Print a summary identifying the repository state at REV using one or
3125 Print a summary identifying the repository state at REV using one or
3126 two parent hash identifiers, followed by a "+" if the working
3126 two parent hash identifiers, followed by a "+" if the working
3127 directory has uncommitted changes, the branch name (if not default),
3127 directory has uncommitted changes, the branch name (if not default),
3128 a list of tags, and a list of bookmarks.
3128 a list of tags, and a list of bookmarks.
3129
3129
3130 When REV is not given, print a summary of the current state of the
3130 When REV is not given, print a summary of the current state of the
3131 repository including the working directory. Specify -r. to get information
3131 repository including the working directory. Specify -r. to get information
3132 of the working directory parent without scanning uncommitted changes.
3132 of the working directory parent without scanning uncommitted changes.
3133
3133
3134 Specifying a path to a repository root or Mercurial bundle will
3134 Specifying a path to a repository root or Mercurial bundle will
3135 cause lookup to operate on that repository/bundle.
3135 cause lookup to operate on that repository/bundle.
3136
3136
3137 .. container:: verbose
3137 .. container:: verbose
3138
3138
3139 Template:
3139 Template:
3140
3140
3141 The following keywords are supported in addition to the common template
3141 The following keywords are supported in addition to the common template
3142 keywords and functions. See also :hg:`help templates`.
3142 keywords and functions. See also :hg:`help templates`.
3143
3143
3144 :dirty: String. Character ``+`` denoting if the working directory has
3144 :dirty: String. Character ``+`` denoting if the working directory has
3145 uncommitted changes.
3145 uncommitted changes.
3146 :id: String. One or two nodes, optionally followed by ``+``.
3146 :id: String. One or two nodes, optionally followed by ``+``.
3147 :parents: List of strings. Parent nodes of the changeset.
3147 :parents: List of strings. Parent nodes of the changeset.
3148
3148
3149 Examples:
3149 Examples:
3150
3150
3151 - generate a build identifier for the working directory::
3151 - generate a build identifier for the working directory::
3152
3152
3153 hg id --id > build-id.dat
3153 hg id --id > build-id.dat
3154
3154
3155 - find the revision corresponding to a tag::
3155 - find the revision corresponding to a tag::
3156
3156
3157 hg id -n -r 1.3
3157 hg id -n -r 1.3
3158
3158
3159 - check the most recent revision of a remote repository::
3159 - check the most recent revision of a remote repository::
3160
3160
3161 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3161 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3162
3162
3163 See :hg:`log` for generating more information about specific revisions,
3163 See :hg:`log` for generating more information about specific revisions,
3164 including full hash identifiers.
3164 including full hash identifiers.
3165
3165
3166 Returns 0 if successful.
3166 Returns 0 if successful.
3167 """
3167 """
3168
3168
3169 opts = pycompat.byteskwargs(opts)
3169 opts = pycompat.byteskwargs(opts)
3170 if not repo and not source:
3170 if not repo and not source:
3171 raise error.Abort(_("there is no Mercurial repository here "
3171 raise error.Abort(_("there is no Mercurial repository here "
3172 "(.hg not found)"))
3172 "(.hg not found)"))
3173
3173
3174 default = not (num or id or branch or tags or bookmarks)
3174 default = not (num or id or branch or tags or bookmarks)
3175 output = []
3175 output = []
3176 revs = []
3176 revs = []
3177
3177
3178 if source:
3178 if source:
3179 source, branches = hg.parseurl(ui.expandpath(source))
3179 source, branches = hg.parseurl(ui.expandpath(source))
3180 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3180 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3181 repo = peer.local()
3181 repo = peer.local()
3182 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3182 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3183
3183
3184 fm = ui.formatter('identify', opts)
3184 fm = ui.formatter('identify', opts)
3185 fm.startitem()
3185 fm.startitem()
3186
3186
3187 if not repo:
3187 if not repo:
3188 if num or branch or tags:
3188 if num or branch or tags:
3189 raise error.Abort(
3189 raise error.Abort(
3190 _("can't query remote revision number, branch, or tags"))
3190 _("can't query remote revision number, branch, or tags"))
3191 if not rev and revs:
3191 if not rev and revs:
3192 rev = revs[0]
3192 rev = revs[0]
3193 if not rev:
3193 if not rev:
3194 rev = "tip"
3194 rev = "tip"
3195
3195
3196 remoterev = peer.lookup(rev)
3196 remoterev = peer.lookup(rev)
3197 hexrev = fm.hexfunc(remoterev)
3197 hexrev = fm.hexfunc(remoterev)
3198 if default or id:
3198 if default or id:
3199 output = [hexrev]
3199 output = [hexrev]
3200 fm.data(id=hexrev)
3200 fm.data(id=hexrev)
3201
3201
3202 @util.cachefunc
3202 @util.cachefunc
3203 def getbms():
3203 def getbms():
3204 bms = []
3204 bms = []
3205
3205
3206 if 'bookmarks' in peer.listkeys('namespaces'):
3206 if 'bookmarks' in peer.listkeys('namespaces'):
3207 hexremoterev = hex(remoterev)
3207 hexremoterev = hex(remoterev)
3208 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3208 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3209 if bmr == hexremoterev]
3209 if bmr == hexremoterev]
3210
3210
3211 return sorted(bms)
3211 return sorted(bms)
3212
3212
3213 if fm.isplain():
3213 if fm.isplain():
3214 if bookmarks:
3214 if bookmarks:
3215 output.extend(getbms())
3215 output.extend(getbms())
3216 elif default and not ui.quiet:
3216 elif default and not ui.quiet:
3217 # multiple bookmarks for a single parent separated by '/'
3217 # multiple bookmarks for a single parent separated by '/'
3218 bm = '/'.join(getbms())
3218 bm = '/'.join(getbms())
3219 if bm:
3219 if bm:
3220 output.append(bm)
3220 output.append(bm)
3221 else:
3221 else:
3222 fm.data(node=hex(remoterev))
3222 fm.data(node=hex(remoterev))
3223 if bookmarks or 'bookmarks' in fm.datahint():
3223 if bookmarks or 'bookmarks' in fm.datahint():
3224 fm.data(bookmarks=fm.formatlist(getbms(), name='bookmark'))
3224 fm.data(bookmarks=fm.formatlist(getbms(), name='bookmark'))
3225 else:
3225 else:
3226 if rev:
3226 if rev:
3227 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3227 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
3228 ctx = scmutil.revsingle(repo, rev, None)
3228 ctx = scmutil.revsingle(repo, rev, None)
3229
3229
3230 if ctx.rev() is None:
3230 if ctx.rev() is None:
3231 ctx = repo[None]
3231 ctx = repo[None]
3232 parents = ctx.parents()
3232 parents = ctx.parents()
3233 taglist = []
3233 taglist = []
3234 for p in parents:
3234 for p in parents:
3235 taglist.extend(p.tags())
3235 taglist.extend(p.tags())
3236
3236
3237 dirty = ""
3237 dirty = ""
3238 if ctx.dirty(missing=True, merge=False, branch=False):
3238 if ctx.dirty(missing=True, merge=False, branch=False):
3239 dirty = '+'
3239 dirty = '+'
3240 fm.data(dirty=dirty)
3240 fm.data(dirty=dirty)
3241
3241
3242 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3242 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3243 if default or id:
3243 if default or id:
3244 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
3244 output = ["%s%s" % ('+'.join(hexoutput), dirty)]
3245 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
3245 fm.data(id="%s%s" % ('+'.join(hexoutput), dirty))
3246
3246
3247 if num:
3247 if num:
3248 numoutput = ["%d" % p.rev() for p in parents]
3248 numoutput = ["%d" % p.rev() for p in parents]
3249 output.append("%s%s" % ('+'.join(numoutput), dirty))
3249 output.append("%s%s" % ('+'.join(numoutput), dirty))
3250
3250
3251 fm.data(parents=fm.formatlist([fm.hexfunc(p.node())
3251 fm.data(parents=fm.formatlist([fm.hexfunc(p.node())
3252 for p in parents], name='node'))
3252 for p in parents], name='node'))
3253 else:
3253 else:
3254 hexoutput = fm.hexfunc(ctx.node())
3254 hexoutput = fm.hexfunc(ctx.node())
3255 if default or id:
3255 if default or id:
3256 output = [hexoutput]
3256 output = [hexoutput]
3257 fm.data(id=hexoutput)
3257 fm.data(id=hexoutput)
3258
3258
3259 if num:
3259 if num:
3260 output.append(pycompat.bytestr(ctx.rev()))
3260 output.append(pycompat.bytestr(ctx.rev()))
3261 taglist = ctx.tags()
3261 taglist = ctx.tags()
3262
3262
3263 if default and not ui.quiet:
3263 if default and not ui.quiet:
3264 b = ctx.branch()
3264 b = ctx.branch()
3265 if b != 'default':
3265 if b != 'default':
3266 output.append("(%s)" % b)
3266 output.append("(%s)" % b)
3267
3267
3268 # multiple tags for a single parent separated by '/'
3268 # multiple tags for a single parent separated by '/'
3269 t = '/'.join(taglist)
3269 t = '/'.join(taglist)
3270 if t:
3270 if t:
3271 output.append(t)
3271 output.append(t)
3272
3272
3273 # multiple bookmarks for a single parent separated by '/'
3273 # multiple bookmarks for a single parent separated by '/'
3274 bm = '/'.join(ctx.bookmarks())
3274 bm = '/'.join(ctx.bookmarks())
3275 if bm:
3275 if bm:
3276 output.append(bm)
3276 output.append(bm)
3277 else:
3277 else:
3278 if branch:
3278 if branch:
3279 output.append(ctx.branch())
3279 output.append(ctx.branch())
3280
3280
3281 if tags:
3281 if tags:
3282 output.extend(taglist)
3282 output.extend(taglist)
3283
3283
3284 if bookmarks:
3284 if bookmarks:
3285 output.extend(ctx.bookmarks())
3285 output.extend(ctx.bookmarks())
3286
3286
3287 fm.data(node=ctx.hex())
3287 fm.data(node=ctx.hex())
3288 fm.data(branch=ctx.branch())
3288 fm.data(branch=ctx.branch())
3289 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
3289 fm.data(tags=fm.formatlist(taglist, name='tag', sep=':'))
3290 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
3290 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name='bookmark'))
3291 fm.context(ctx=ctx)
3291 fm.context(ctx=ctx)
3292
3292
3293 fm.plain("%s\n" % ' '.join(output))
3293 fm.plain("%s\n" % ' '.join(output))
3294 fm.end()
3294 fm.end()
3295
3295
3296 @command('import|patch',
3296 @command('import|patch',
3297 [('p', 'strip', 1,
3297 [('p', 'strip', 1,
3298 _('directory strip option for patch. This has the same '
3298 _('directory strip option for patch. This has the same '
3299 'meaning as the corresponding patch option'), _('NUM')),
3299 'meaning as the corresponding patch option'), _('NUM')),
3300 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3300 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3301 ('e', 'edit', False, _('invoke editor on commit messages')),
3301 ('e', 'edit', False, _('invoke editor on commit messages')),
3302 ('f', 'force', None,
3302 ('f', 'force', None,
3303 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
3303 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
3304 ('', 'no-commit', None,
3304 ('', 'no-commit', None,
3305 _("don't commit, just update the working directory")),
3305 _("don't commit, just update the working directory")),
3306 ('', 'bypass', None,
3306 ('', 'bypass', None,
3307 _("apply patch without touching the working directory")),
3307 _("apply patch without touching the working directory")),
3308 ('', 'partial', None,
3308 ('', 'partial', None,
3309 _('commit even if some hunks fail')),
3309 _('commit even if some hunks fail')),
3310 ('', 'exact', None,
3310 ('', 'exact', None,
3311 _('abort if patch would apply lossily')),
3311 _('abort if patch would apply lossily')),
3312 ('', 'prefix', '',
3312 ('', 'prefix', '',
3313 _('apply patch to subdirectory'), _('DIR')),
3313 _('apply patch to subdirectory'), _('DIR')),
3314 ('', 'import-branch', None,
3314 ('', 'import-branch', None,
3315 _('use any branch information in patch (implied by --exact)'))] +
3315 _('use any branch information in patch (implied by --exact)'))] +
3316 commitopts + commitopts2 + similarityopts,
3316 commitopts + commitopts2 + similarityopts,
3317 _('[OPTION]... PATCH...'),
3317 _('[OPTION]... PATCH...'),
3318 helpcategory=command.CATEGORY_IMPORT_EXPORT)
3318 helpcategory=command.CATEGORY_IMPORT_EXPORT)
3319 def import_(ui, repo, patch1=None, *patches, **opts):
3319 def import_(ui, repo, patch1=None, *patches, **opts):
3320 """import an ordered set of patches
3320 """import an ordered set of patches
3321
3321
3322 Import a list of patches and commit them individually (unless
3322 Import a list of patches and commit them individually (unless
3323 --no-commit is specified).
3323 --no-commit is specified).
3324
3324
3325 To read a patch from standard input (stdin), use "-" as the patch
3325 To read a patch from standard input (stdin), use "-" as the patch
3326 name. If a URL is specified, the patch will be downloaded from
3326 name. If a URL is specified, the patch will be downloaded from
3327 there.
3327 there.
3328
3328
3329 Import first applies changes to the working directory (unless
3329 Import first applies changes to the working directory (unless
3330 --bypass is specified), import will abort if there are outstanding
3330 --bypass is specified), import will abort if there are outstanding
3331 changes.
3331 changes.
3332
3332
3333 Use --bypass to apply and commit patches directly to the
3333 Use --bypass to apply and commit patches directly to the
3334 repository, without affecting the working directory. Without
3334 repository, without affecting the working directory. Without
3335 --exact, patches will be applied on top of the working directory
3335 --exact, patches will be applied on top of the working directory
3336 parent revision.
3336 parent revision.
3337
3337
3338 You can import a patch straight from a mail message. Even patches
3338 You can import a patch straight from a mail message. Even patches
3339 as attachments work (to use the body part, it must have type
3339 as attachments work (to use the body part, it must have type
3340 text/plain or text/x-patch). From and Subject headers of email
3340 text/plain or text/x-patch). From and Subject headers of email
3341 message are used as default committer and commit message. All
3341 message are used as default committer and commit message. All
3342 text/plain body parts before first diff are added to the commit
3342 text/plain body parts before first diff are added to the commit
3343 message.
3343 message.
3344
3344
3345 If the imported patch was generated by :hg:`export`, user and
3345 If the imported patch was generated by :hg:`export`, user and
3346 description from patch override values from message headers and
3346 description from patch override values from message headers and
3347 body. Values given on command line with -m/--message and -u/--user
3347 body. Values given on command line with -m/--message and -u/--user
3348 override these.
3348 override these.
3349
3349
3350 If --exact is specified, import will set the working directory to
3350 If --exact is specified, import will set the working directory to
3351 the parent of each patch before applying it, and will abort if the
3351 the parent of each patch before applying it, and will abort if the
3352 resulting changeset has a different ID than the one recorded in
3352 resulting changeset has a different ID than the one recorded in
3353 the patch. This will guard against various ways that portable
3353 the patch. This will guard against various ways that portable
3354 patch formats and mail systems might fail to transfer Mercurial
3354 patch formats and mail systems might fail to transfer Mercurial
3355 data or metadata. See :hg:`bundle` for lossless transmission.
3355 data or metadata. See :hg:`bundle` for lossless transmission.
3356
3356
3357 Use --partial to ensure a changeset will be created from the patch
3357 Use --partial to ensure a changeset will be created from the patch
3358 even if some hunks fail to apply. Hunks that fail to apply will be
3358 even if some hunks fail to apply. Hunks that fail to apply will be
3359 written to a <target-file>.rej file. Conflicts can then be resolved
3359 written to a <target-file>.rej file. Conflicts can then be resolved
3360 by hand before :hg:`commit --amend` is run to update the created
3360 by hand before :hg:`commit --amend` is run to update the created
3361 changeset. This flag exists to let people import patches that
3361 changeset. This flag exists to let people import patches that
3362 partially apply without losing the associated metadata (author,
3362 partially apply without losing the associated metadata (author,
3363 date, description, ...).
3363 date, description, ...).
3364
3364
3365 .. note::
3365 .. note::
3366
3366
3367 When no hunks apply cleanly, :hg:`import --partial` will create
3367 When no hunks apply cleanly, :hg:`import --partial` will create
3368 an empty changeset, importing only the patch metadata.
3368 an empty changeset, importing only the patch metadata.
3369
3369
3370 With -s/--similarity, hg will attempt to discover renames and
3370 With -s/--similarity, hg will attempt to discover renames and
3371 copies in the patch in the same way as :hg:`addremove`.
3371 copies in the patch in the same way as :hg:`addremove`.
3372
3372
3373 It is possible to use external patch programs to perform the patch
3373 It is possible to use external patch programs to perform the patch
3374 by setting the ``ui.patch`` configuration option. For the default
3374 by setting the ``ui.patch`` configuration option. For the default
3375 internal tool, the fuzz can also be configured via ``patch.fuzz``.
3375 internal tool, the fuzz can also be configured via ``patch.fuzz``.
3376 See :hg:`help config` for more information about configuration
3376 See :hg:`help config` for more information about configuration
3377 files and how to use these options.
3377 files and how to use these options.
3378
3378
3379 See :hg:`help dates` for a list of formats valid for -d/--date.
3379 See :hg:`help dates` for a list of formats valid for -d/--date.
3380
3380
3381 .. container:: verbose
3381 .. container:: verbose
3382
3382
3383 Examples:
3383 Examples:
3384
3384
3385 - import a traditional patch from a website and detect renames::
3385 - import a traditional patch from a website and detect renames::
3386
3386
3387 hg import -s 80 http://example.com/bugfix.patch
3387 hg import -s 80 http://example.com/bugfix.patch
3388
3388
3389 - import a changeset from an hgweb server::
3389 - import a changeset from an hgweb server::
3390
3390
3391 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3391 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3392
3392
3393 - import all the patches in an Unix-style mbox::
3393 - import all the patches in an Unix-style mbox::
3394
3394
3395 hg import incoming-patches.mbox
3395 hg import incoming-patches.mbox
3396
3396
3397 - import patches from stdin::
3397 - import patches from stdin::
3398
3398
3399 hg import -
3399 hg import -
3400
3400
3401 - attempt to exactly restore an exported changeset (not always
3401 - attempt to exactly restore an exported changeset (not always
3402 possible)::
3402 possible)::
3403
3403
3404 hg import --exact proposed-fix.patch
3404 hg import --exact proposed-fix.patch
3405
3405
3406 - use an external tool to apply a patch which is too fuzzy for
3406 - use an external tool to apply a patch which is too fuzzy for
3407 the default internal tool.
3407 the default internal tool.
3408
3408
3409 hg import --config ui.patch="patch --merge" fuzzy.patch
3409 hg import --config ui.patch="patch --merge" fuzzy.patch
3410
3410
3411 - change the default fuzzing from 2 to a less strict 7
3411 - change the default fuzzing from 2 to a less strict 7
3412
3412
3413 hg import --config ui.fuzz=7 fuzz.patch
3413 hg import --config ui.fuzz=7 fuzz.patch
3414
3414
3415 Returns 0 on success, 1 on partial success (see --partial).
3415 Returns 0 on success, 1 on partial success (see --partial).
3416 """
3416 """
3417
3417
3418 opts = pycompat.byteskwargs(opts)
3418 opts = pycompat.byteskwargs(opts)
3419 if not patch1:
3419 if not patch1:
3420 raise error.Abort(_('need at least one patch to import'))
3420 raise error.Abort(_('need at least one patch to import'))
3421
3421
3422 patches = (patch1,) + patches
3422 patches = (patch1,) + patches
3423
3423
3424 date = opts.get('date')
3424 date = opts.get('date')
3425 if date:
3425 if date:
3426 opts['date'] = dateutil.parsedate(date)
3426 opts['date'] = dateutil.parsedate(date)
3427
3427
3428 exact = opts.get('exact')
3428 exact = opts.get('exact')
3429 update = not opts.get('bypass')
3429 update = not opts.get('bypass')
3430 if not update and opts.get('no_commit'):
3430 if not update and opts.get('no_commit'):
3431 raise error.Abort(_('cannot use --no-commit with --bypass'))
3431 raise error.Abort(_('cannot use --no-commit with --bypass'))
3432 try:
3432 try:
3433 sim = float(opts.get('similarity') or 0)
3433 sim = float(opts.get('similarity') or 0)
3434 except ValueError:
3434 except ValueError:
3435 raise error.Abort(_('similarity must be a number'))
3435 raise error.Abort(_('similarity must be a number'))
3436 if sim < 0 or sim > 100:
3436 if sim < 0 or sim > 100:
3437 raise error.Abort(_('similarity must be between 0 and 100'))
3437 raise error.Abort(_('similarity must be between 0 and 100'))
3438 if sim and not update:
3438 if sim and not update:
3439 raise error.Abort(_('cannot use --similarity with --bypass'))
3439 raise error.Abort(_('cannot use --similarity with --bypass'))
3440 if exact:
3440 if exact:
3441 if opts.get('edit'):
3441 if opts.get('edit'):
3442 raise error.Abort(_('cannot use --exact with --edit'))
3442 raise error.Abort(_('cannot use --exact with --edit'))
3443 if opts.get('prefix'):
3443 if opts.get('prefix'):
3444 raise error.Abort(_('cannot use --exact with --prefix'))
3444 raise error.Abort(_('cannot use --exact with --prefix'))
3445
3445
3446 base = opts["base"]
3446 base = opts["base"]
3447 msgs = []
3447 msgs = []
3448 ret = 0
3448 ret = 0
3449
3449
3450 with repo.wlock():
3450 with repo.wlock():
3451 if update:
3451 if update:
3452 cmdutil.checkunfinished(repo)
3452 cmdutil.checkunfinished(repo)
3453 if (exact or not opts.get('force')):
3453 if (exact or not opts.get('force')):
3454 cmdutil.bailifchanged(repo)
3454 cmdutil.bailifchanged(repo)
3455
3455
3456 if not opts.get('no_commit'):
3456 if not opts.get('no_commit'):
3457 lock = repo.lock
3457 lock = repo.lock
3458 tr = lambda: repo.transaction('import')
3458 tr = lambda: repo.transaction('import')
3459 dsguard = util.nullcontextmanager
3459 dsguard = util.nullcontextmanager
3460 else:
3460 else:
3461 lock = util.nullcontextmanager
3461 lock = util.nullcontextmanager
3462 tr = util.nullcontextmanager
3462 tr = util.nullcontextmanager
3463 dsguard = lambda: dirstateguard.dirstateguard(repo, 'import')
3463 dsguard = lambda: dirstateguard.dirstateguard(repo, 'import')
3464 with lock(), tr(), dsguard():
3464 with lock(), tr(), dsguard():
3465 parents = repo[None].parents()
3465 parents = repo[None].parents()
3466 for patchurl in patches:
3466 for patchurl in patches:
3467 if patchurl == '-':
3467 if patchurl == '-':
3468 ui.status(_('applying patch from stdin\n'))
3468 ui.status(_('applying patch from stdin\n'))
3469 patchfile = ui.fin
3469 patchfile = ui.fin
3470 patchurl = 'stdin' # for error message
3470 patchurl = 'stdin' # for error message
3471 else:
3471 else:
3472 patchurl = os.path.join(base, patchurl)
3472 patchurl = os.path.join(base, patchurl)
3473 ui.status(_('applying %s\n') % patchurl)
3473 ui.status(_('applying %s\n') % patchurl)
3474 patchfile = hg.openpath(ui, patchurl)
3474 patchfile = hg.openpath(ui, patchurl)
3475
3475
3476 haspatch = False
3476 haspatch = False
3477 for hunk in patch.split(patchfile):
3477 for hunk in patch.split(patchfile):
3478 with patch.extract(ui, hunk) as patchdata:
3478 with patch.extract(ui, hunk) as patchdata:
3479 msg, node, rej = cmdutil.tryimportone(ui, repo,
3479 msg, node, rej = cmdutil.tryimportone(ui, repo,
3480 patchdata,
3480 patchdata,
3481 parents, opts,
3481 parents, opts,
3482 msgs, hg.clean)
3482 msgs, hg.clean)
3483 if msg:
3483 if msg:
3484 haspatch = True
3484 haspatch = True
3485 ui.note(msg + '\n')
3485 ui.note(msg + '\n')
3486 if update or exact:
3486 if update or exact:
3487 parents = repo[None].parents()
3487 parents = repo[None].parents()
3488 else:
3488 else:
3489 parents = [repo[node]]
3489 parents = [repo[node]]
3490 if rej:
3490 if rej:
3491 ui.write_err(_("patch applied partially\n"))
3491 ui.write_err(_("patch applied partially\n"))
3492 ui.write_err(_("(fix the .rej files and run "
3492 ui.write_err(_("(fix the .rej files and run "
3493 "`hg commit --amend`)\n"))
3493 "`hg commit --amend`)\n"))
3494 ret = 1
3494 ret = 1
3495 break
3495 break
3496
3496
3497 if not haspatch:
3497 if not haspatch:
3498 raise error.Abort(_('%s: no diffs found') % patchurl)
3498 raise error.Abort(_('%s: no diffs found') % patchurl)
3499
3499
3500 if msgs:
3500 if msgs:
3501 repo.savecommitmessage('\n* * *\n'.join(msgs))
3501 repo.savecommitmessage('\n* * *\n'.join(msgs))
3502 return ret
3502 return ret
3503
3503
3504 @command('incoming|in',
3504 @command('incoming|in',
3505 [('f', 'force', None,
3505 [('f', 'force', None,
3506 _('run even if remote repository is unrelated')),
3506 _('run even if remote repository is unrelated')),
3507 ('n', 'newest-first', None, _('show newest record first')),
3507 ('n', 'newest-first', None, _('show newest record first')),
3508 ('', 'bundle', '',
3508 ('', 'bundle', '',
3509 _('file to store the bundles into'), _('FILE')),
3509 _('file to store the bundles into'), _('FILE')),
3510 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3510 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3511 ('B', 'bookmarks', False, _("compare bookmarks")),
3511 ('B', 'bookmarks', False, _("compare bookmarks")),
3512 ('b', 'branch', [],
3512 ('b', 'branch', [],
3513 _('a specific branch you would like to pull'), _('BRANCH')),
3513 _('a specific branch you would like to pull'), _('BRANCH')),
3514 ] + logopts + remoteopts + subrepoopts,
3514 ] + logopts + remoteopts + subrepoopts,
3515 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
3515 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
3516 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
3516 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
3517 def incoming(ui, repo, source="default", **opts):
3517 def incoming(ui, repo, source="default", **opts):
3518 """show new changesets found in source
3518 """show new changesets found in source
3519
3519
3520 Show new changesets found in the specified path/URL or the default
3520 Show new changesets found in the specified path/URL or the default
3521 pull location. These are the changesets that would have been pulled
3521 pull location. These are the changesets that would have been pulled
3522 by :hg:`pull` at the time you issued this command.
3522 by :hg:`pull` at the time you issued this command.
3523
3523
3524 See pull for valid source format details.
3524 See pull for valid source format details.
3525
3525
3526 .. container:: verbose
3526 .. container:: verbose
3527
3527
3528 With -B/--bookmarks, the result of bookmark comparison between
3528 With -B/--bookmarks, the result of bookmark comparison between
3529 local and remote repositories is displayed. With -v/--verbose,
3529 local and remote repositories is displayed. With -v/--verbose,
3530 status is also displayed for each bookmark like below::
3530 status is also displayed for each bookmark like below::
3531
3531
3532 BM1 01234567890a added
3532 BM1 01234567890a added
3533 BM2 1234567890ab advanced
3533 BM2 1234567890ab advanced
3534 BM3 234567890abc diverged
3534 BM3 234567890abc diverged
3535 BM4 34567890abcd changed
3535 BM4 34567890abcd changed
3536
3536
3537 The action taken locally when pulling depends on the
3537 The action taken locally when pulling depends on the
3538 status of each bookmark:
3538 status of each bookmark:
3539
3539
3540 :``added``: pull will create it
3540 :``added``: pull will create it
3541 :``advanced``: pull will update it
3541 :``advanced``: pull will update it
3542 :``diverged``: pull will create a divergent bookmark
3542 :``diverged``: pull will create a divergent bookmark
3543 :``changed``: result depends on remote changesets
3543 :``changed``: result depends on remote changesets
3544
3544
3545 From the point of view of pulling behavior, bookmark
3545 From the point of view of pulling behavior, bookmark
3546 existing only in the remote repository are treated as ``added``,
3546 existing only in the remote repository are treated as ``added``,
3547 even if it is in fact locally deleted.
3547 even if it is in fact locally deleted.
3548
3548
3549 .. container:: verbose
3549 .. container:: verbose
3550
3550
3551 For remote repository, using --bundle avoids downloading the
3551 For remote repository, using --bundle avoids downloading the
3552 changesets twice if the incoming is followed by a pull.
3552 changesets twice if the incoming is followed by a pull.
3553
3553
3554 Examples:
3554 Examples:
3555
3555
3556 - show incoming changes with patches and full description::
3556 - show incoming changes with patches and full description::
3557
3557
3558 hg incoming -vp
3558 hg incoming -vp
3559
3559
3560 - show incoming changes excluding merges, store a bundle::
3560 - show incoming changes excluding merges, store a bundle::
3561
3561
3562 hg in -vpM --bundle incoming.hg
3562 hg in -vpM --bundle incoming.hg
3563 hg pull incoming.hg
3563 hg pull incoming.hg
3564
3564
3565 - briefly list changes inside a bundle::
3565 - briefly list changes inside a bundle::
3566
3566
3567 hg in changes.hg -T "{desc|firstline}\\n"
3567 hg in changes.hg -T "{desc|firstline}\\n"
3568
3568
3569 Returns 0 if there are incoming changes, 1 otherwise.
3569 Returns 0 if there are incoming changes, 1 otherwise.
3570 """
3570 """
3571 opts = pycompat.byteskwargs(opts)
3571 opts = pycompat.byteskwargs(opts)
3572 if opts.get('graph'):
3572 if opts.get('graph'):
3573 logcmdutil.checkunsupportedgraphflags([], opts)
3573 logcmdutil.checkunsupportedgraphflags([], opts)
3574 def display(other, chlist, displayer):
3574 def display(other, chlist, displayer):
3575 revdag = logcmdutil.graphrevs(other, chlist, opts)
3575 revdag = logcmdutil.graphrevs(other, chlist, opts)
3576 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3576 logcmdutil.displaygraph(ui, repo, revdag, displayer,
3577 graphmod.asciiedges)
3577 graphmod.asciiedges)
3578
3578
3579 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3579 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3580 return 0
3580 return 0
3581
3581
3582 if opts.get('bundle') and opts.get('subrepos'):
3582 if opts.get('bundle') and opts.get('subrepos'):
3583 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3583 raise error.Abort(_('cannot combine --bundle and --subrepos'))
3584
3584
3585 if opts.get('bookmarks'):
3585 if opts.get('bookmarks'):
3586 source, branches = hg.parseurl(ui.expandpath(source),
3586 source, branches = hg.parseurl(ui.expandpath(source),
3587 opts.get('branch'))
3587 opts.get('branch'))
3588 other = hg.peer(repo, opts, source)
3588 other = hg.peer(repo, opts, source)
3589 if 'bookmarks' not in other.listkeys('namespaces'):
3589 if 'bookmarks' not in other.listkeys('namespaces'):
3590 ui.warn(_("remote doesn't support bookmarks\n"))
3590 ui.warn(_("remote doesn't support bookmarks\n"))
3591 return 0
3591 return 0
3592 ui.pager('incoming')
3592 ui.pager('incoming')
3593 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3593 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3594 return bookmarks.incoming(ui, repo, other)
3594 return bookmarks.incoming(ui, repo, other)
3595
3595
3596 repo._subtoppath = ui.expandpath(source)
3596 repo._subtoppath = ui.expandpath(source)
3597 try:
3597 try:
3598 return hg.incoming(ui, repo, source, opts)
3598 return hg.incoming(ui, repo, source, opts)
3599 finally:
3599 finally:
3600 del repo._subtoppath
3600 del repo._subtoppath
3601
3601
3602
3602
3603 @command('init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3603 @command('init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
3604 helpcategory=command.CATEGORY_REPO_CREATION,
3604 helpcategory=command.CATEGORY_REPO_CREATION,
3605 helpbasic=True, norepo=True)
3605 helpbasic=True, norepo=True)
3606 def init(ui, dest=".", **opts):
3606 def init(ui, dest=".", **opts):
3607 """create a new repository in the given directory
3607 """create a new repository in the given directory
3608
3608
3609 Initialize a new repository in the given directory. If the given
3609 Initialize a new repository in the given directory. If the given
3610 directory does not exist, it will be created.
3610 directory does not exist, it will be created.
3611
3611
3612 If no directory is given, the current directory is used.
3612 If no directory is given, the current directory is used.
3613
3613
3614 It is possible to specify an ``ssh://`` URL as the destination.
3614 It is possible to specify an ``ssh://`` URL as the destination.
3615 See :hg:`help urls` for more information.
3615 See :hg:`help urls` for more information.
3616
3616
3617 Returns 0 on success.
3617 Returns 0 on success.
3618 """
3618 """
3619 opts = pycompat.byteskwargs(opts)
3619 opts = pycompat.byteskwargs(opts)
3620 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3620 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3621
3621
3622 @command('locate',
3622 @command('locate',
3623 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3623 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3624 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3624 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3625 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3625 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3626 ] + walkopts,
3626 ] + walkopts,
3627 _('[OPTION]... [PATTERN]...'),
3627 _('[OPTION]... [PATTERN]...'),
3628 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
3628 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
3629 def locate(ui, repo, *pats, **opts):
3629 def locate(ui, repo, *pats, **opts):
3630 """locate files matching specific patterns (DEPRECATED)
3630 """locate files matching specific patterns (DEPRECATED)
3631
3631
3632 Print files under Mercurial control in the working directory whose
3632 Print files under Mercurial control in the working directory whose
3633 names match the given patterns.
3633 names match the given patterns.
3634
3634
3635 By default, this command searches all directories in the working
3635 By default, this command searches all directories in the working
3636 directory. To search just the current directory and its
3636 directory. To search just the current directory and its
3637 subdirectories, use "--include .".
3637 subdirectories, use "--include .".
3638
3638
3639 If no patterns are given to match, this command prints the names
3639 If no patterns are given to match, this command prints the names
3640 of all files under Mercurial control in the working directory.
3640 of all files under Mercurial control in the working directory.
3641
3641
3642 If you want to feed the output of this command into the "xargs"
3642 If you want to feed the output of this command into the "xargs"
3643 command, use the -0 option to both this command and "xargs". This
3643 command, use the -0 option to both this command and "xargs". This
3644 will avoid the problem of "xargs" treating single filenames that
3644 will avoid the problem of "xargs" treating single filenames that
3645 contain whitespace as multiple filenames.
3645 contain whitespace as multiple filenames.
3646
3646
3647 See :hg:`help files` for a more versatile command.
3647 See :hg:`help files` for a more versatile command.
3648
3648
3649 Returns 0 if a match is found, 1 otherwise.
3649 Returns 0 if a match is found, 1 otherwise.
3650 """
3650 """
3651 opts = pycompat.byteskwargs(opts)
3651 opts = pycompat.byteskwargs(opts)
3652 if opts.get('print0'):
3652 if opts.get('print0'):
3653 end = '\0'
3653 end = '\0'
3654 else:
3654 else:
3655 end = '\n'
3655 end = '\n'
3656 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3656 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3657
3657
3658 ret = 1
3658 ret = 1
3659 m = scmutil.match(ctx, pats, opts, default='relglob',
3659 m = scmutil.match(ctx, pats, opts, default='relglob',
3660 badfn=lambda x, y: False)
3660 badfn=lambda x, y: False)
3661
3661
3662 ui.pager('locate')
3662 ui.pager('locate')
3663 if ctx.rev() is None:
3663 if ctx.rev() is None:
3664 # When run on the working copy, "locate" includes removed files, so
3664 # When run on the working copy, "locate" includes removed files, so
3665 # we get the list of files from the dirstate.
3665 # we get the list of files from the dirstate.
3666 filesgen = sorted(repo.dirstate.matches(m))
3666 filesgen = sorted(repo.dirstate.matches(m))
3667 else:
3667 else:
3668 filesgen = ctx.matches(m)
3668 filesgen = ctx.matches(m)
3669 for abs in filesgen:
3669 for abs in filesgen:
3670 if opts.get('fullpath'):
3670 if opts.get('fullpath'):
3671 ui.write(repo.wjoin(abs), end)
3671 ui.write(repo.wjoin(abs), end)
3672 else:
3672 else:
3673 ui.write(((pats and m.rel(abs)) or abs), end)
3673 ui.write(((pats and m.rel(abs)) or abs), end)
3674 ret = 0
3674 ret = 0
3675
3675
3676 return ret
3676 return ret
3677
3677
3678 @command('log|history',
3678 @command('log|history',
3679 [('f', 'follow', None,
3679 [('f', 'follow', None,
3680 _('follow changeset history, or file history across copies and renames')),
3680 _('follow changeset history, or file history across copies and renames')),
3681 ('', 'follow-first', None,
3681 ('', 'follow-first', None,
3682 _('only follow the first parent of merge changesets (DEPRECATED)')),
3682 _('only follow the first parent of merge changesets (DEPRECATED)')),
3683 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3683 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3684 ('C', 'copies', None, _('show copied files')),
3684 ('C', 'copies', None, _('show copied files')),
3685 ('k', 'keyword', [],
3685 ('k', 'keyword', [],
3686 _('do case-insensitive search for a given text'), _('TEXT')),
3686 _('do case-insensitive search for a given text'), _('TEXT')),
3687 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3687 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
3688 ('L', 'line-range', [],
3688 ('L', 'line-range', [],
3689 _('follow line range of specified file (EXPERIMENTAL)'),
3689 _('follow line range of specified file (EXPERIMENTAL)'),
3690 _('FILE,RANGE')),
3690 _('FILE,RANGE')),
3691 ('', 'removed', None, _('include revisions where files were removed')),
3691 ('', 'removed', None, _('include revisions where files were removed')),
3692 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3692 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3693 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3693 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3694 ('', 'only-branch', [],
3694 ('', 'only-branch', [],
3695 _('show only changesets within the given named branch (DEPRECATED)'),
3695 _('show only changesets within the given named branch (DEPRECATED)'),
3696 _('BRANCH')),
3696 _('BRANCH')),
3697 ('b', 'branch', [],
3697 ('b', 'branch', [],
3698 _('show changesets within the given named branch'), _('BRANCH')),
3698 _('show changesets within the given named branch'), _('BRANCH')),
3699 ('P', 'prune', [],
3699 ('P', 'prune', [],
3700 _('do not display revision or any of its ancestors'), _('REV')),
3700 _('do not display revision or any of its ancestors'), _('REV')),
3701 ] + logopts + walkopts,
3701 ] + logopts + walkopts,
3702 _('[OPTION]... [FILE]'),
3702 _('[OPTION]... [FILE]'),
3703 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3703 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3704 helpbasic=True, inferrepo=True,
3704 helpbasic=True, inferrepo=True,
3705 intents={INTENT_READONLY})
3705 intents={INTENT_READONLY})
3706 def log(ui, repo, *pats, **opts):
3706 def log(ui, repo, *pats, **opts):
3707 """show revision history of entire repository or files
3707 """show revision history of entire repository or files
3708
3708
3709 Print the revision history of the specified files or the entire
3709 Print the revision history of the specified files or the entire
3710 project.
3710 project.
3711
3711
3712 If no revision range is specified, the default is ``tip:0`` unless
3712 If no revision range is specified, the default is ``tip:0`` unless
3713 --follow is set, in which case the working directory parent is
3713 --follow is set, in which case the working directory parent is
3714 used as the starting revision.
3714 used as the starting revision.
3715
3715
3716 File history is shown without following rename or copy history of
3716 File history is shown without following rename or copy history of
3717 files. Use -f/--follow with a filename to follow history across
3717 files. Use -f/--follow with a filename to follow history across
3718 renames and copies. --follow without a filename will only show
3718 renames and copies. --follow without a filename will only show
3719 ancestors of the starting revision.
3719 ancestors of the starting revision.
3720
3720
3721 By default this command prints revision number and changeset id,
3721 By default this command prints revision number and changeset id,
3722 tags, non-trivial parents, user, date and time, and a summary for
3722 tags, non-trivial parents, user, date and time, and a summary for
3723 each commit. When the -v/--verbose switch is used, the list of
3723 each commit. When the -v/--verbose switch is used, the list of
3724 changed files and full commit message are shown.
3724 changed files and full commit message are shown.
3725
3725
3726 With --graph the revisions are shown as an ASCII art DAG with the most
3726 With --graph the revisions are shown as an ASCII art DAG with the most
3727 recent changeset at the top.
3727 recent changeset at the top.
3728 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
3728 'o' is a changeset, '@' is a working directory parent, '_' closes a branch,
3729 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
3729 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
3730 changeset from the lines below is a parent of the 'o' merge on the same
3730 changeset from the lines below is a parent of the 'o' merge on the same
3731 line.
3731 line.
3732 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3732 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
3733 of a '|' indicates one or more revisions in a path are omitted.
3733 of a '|' indicates one or more revisions in a path are omitted.
3734
3734
3735 .. container:: verbose
3735 .. container:: verbose
3736
3736
3737 Use -L/--line-range FILE,M:N options to follow the history of lines
3737 Use -L/--line-range FILE,M:N options to follow the history of lines
3738 from M to N in FILE. With -p/--patch only diff hunks affecting
3738 from M to N in FILE. With -p/--patch only diff hunks affecting
3739 specified line range will be shown. This option requires --follow;
3739 specified line range will be shown. This option requires --follow;
3740 it can be specified multiple times. Currently, this option is not
3740 it can be specified multiple times. Currently, this option is not
3741 compatible with --graph. This option is experimental.
3741 compatible with --graph. This option is experimental.
3742
3742
3743 .. note::
3743 .. note::
3744
3744
3745 :hg:`log --patch` may generate unexpected diff output for merge
3745 :hg:`log --patch` may generate unexpected diff output for merge
3746 changesets, as it will only compare the merge changeset against
3746 changesets, as it will only compare the merge changeset against
3747 its first parent. Also, only files different from BOTH parents
3747 its first parent. Also, only files different from BOTH parents
3748 will appear in files:.
3748 will appear in files:.
3749
3749
3750 .. note::
3750 .. note::
3751
3751
3752 For performance reasons, :hg:`log FILE` may omit duplicate changes
3752 For performance reasons, :hg:`log FILE` may omit duplicate changes
3753 made on branches and will not show removals or mode changes. To
3753 made on branches and will not show removals or mode changes. To
3754 see all such changes, use the --removed switch.
3754 see all such changes, use the --removed switch.
3755
3755
3756 .. container:: verbose
3756 .. container:: verbose
3757
3757
3758 .. note::
3758 .. note::
3759
3759
3760 The history resulting from -L/--line-range options depends on diff
3760 The history resulting from -L/--line-range options depends on diff
3761 options; for instance if white-spaces are ignored, respective changes
3761 options; for instance if white-spaces are ignored, respective changes
3762 with only white-spaces in specified line range will not be listed.
3762 with only white-spaces in specified line range will not be listed.
3763
3763
3764 .. container:: verbose
3764 .. container:: verbose
3765
3765
3766 Some examples:
3766 Some examples:
3767
3767
3768 - changesets with full descriptions and file lists::
3768 - changesets with full descriptions and file lists::
3769
3769
3770 hg log -v
3770 hg log -v
3771
3771
3772 - changesets ancestral to the working directory::
3772 - changesets ancestral to the working directory::
3773
3773
3774 hg log -f
3774 hg log -f
3775
3775
3776 - last 10 commits on the current branch::
3776 - last 10 commits on the current branch::
3777
3777
3778 hg log -l 10 -b .
3778 hg log -l 10 -b .
3779
3779
3780 - changesets showing all modifications of a file, including removals::
3780 - changesets showing all modifications of a file, including removals::
3781
3781
3782 hg log --removed file.c
3782 hg log --removed file.c
3783
3783
3784 - all changesets that touch a directory, with diffs, excluding merges::
3784 - all changesets that touch a directory, with diffs, excluding merges::
3785
3785
3786 hg log -Mp lib/
3786 hg log -Mp lib/
3787
3787
3788 - all revision numbers that match a keyword::
3788 - all revision numbers that match a keyword::
3789
3789
3790 hg log -k bug --template "{rev}\\n"
3790 hg log -k bug --template "{rev}\\n"
3791
3791
3792 - the full hash identifier of the working directory parent::
3792 - the full hash identifier of the working directory parent::
3793
3793
3794 hg log -r . --template "{node}\\n"
3794 hg log -r . --template "{node}\\n"
3795
3795
3796 - list available log templates::
3796 - list available log templates::
3797
3797
3798 hg log -T list
3798 hg log -T list
3799
3799
3800 - check if a given changeset is included in a tagged release::
3800 - check if a given changeset is included in a tagged release::
3801
3801
3802 hg log -r "a21ccf and ancestor(1.9)"
3802 hg log -r "a21ccf and ancestor(1.9)"
3803
3803
3804 - find all changesets by some user in a date range::
3804 - find all changesets by some user in a date range::
3805
3805
3806 hg log -k alice -d "may 2008 to jul 2008"
3806 hg log -k alice -d "may 2008 to jul 2008"
3807
3807
3808 - summary of all changesets after the last tag::
3808 - summary of all changesets after the last tag::
3809
3809
3810 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3810 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3811
3811
3812 - changesets touching lines 13 to 23 for file.c::
3812 - changesets touching lines 13 to 23 for file.c::
3813
3813
3814 hg log -L file.c,13:23
3814 hg log -L file.c,13:23
3815
3815
3816 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
3816 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
3817 main.c with patch::
3817 main.c with patch::
3818
3818
3819 hg log -L file.c,13:23 -L main.c,2:6 -p
3819 hg log -L file.c,13:23 -L main.c,2:6 -p
3820
3820
3821 See :hg:`help dates` for a list of formats valid for -d/--date.
3821 See :hg:`help dates` for a list of formats valid for -d/--date.
3822
3822
3823 See :hg:`help revisions` for more about specifying and ordering
3823 See :hg:`help revisions` for more about specifying and ordering
3824 revisions.
3824 revisions.
3825
3825
3826 See :hg:`help templates` for more about pre-packaged styles and
3826 See :hg:`help templates` for more about pre-packaged styles and
3827 specifying custom templates. The default template used by the log
3827 specifying custom templates. The default template used by the log
3828 command can be customized via the ``ui.logtemplate`` configuration
3828 command can be customized via the ``ui.logtemplate`` configuration
3829 setting.
3829 setting.
3830
3830
3831 Returns 0 on success.
3831 Returns 0 on success.
3832
3832
3833 """
3833 """
3834 opts = pycompat.byteskwargs(opts)
3834 opts = pycompat.byteskwargs(opts)
3835 linerange = opts.get('line_range')
3835 linerange = opts.get('line_range')
3836
3836
3837 if linerange and not opts.get('follow'):
3837 if linerange and not opts.get('follow'):
3838 raise error.Abort(_('--line-range requires --follow'))
3838 raise error.Abort(_('--line-range requires --follow'))
3839
3839
3840 if linerange and pats:
3840 if linerange and pats:
3841 # TODO: take pats as patterns with no line-range filter
3841 # TODO: take pats as patterns with no line-range filter
3842 raise error.Abort(
3842 raise error.Abort(
3843 _('FILE arguments are not compatible with --line-range option')
3843 _('FILE arguments are not compatible with --line-range option')
3844 )
3844 )
3845
3845
3846 repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
3846 repo = scmutil.unhidehashlikerevs(repo, opts.get('rev'), 'nowarn')
3847 revs, differ = logcmdutil.getrevs(repo, pats, opts)
3847 revs, differ = logcmdutil.getrevs(repo, pats, opts)
3848 if linerange:
3848 if linerange:
3849 # TODO: should follow file history from logcmdutil._initialrevs(),
3849 # TODO: should follow file history from logcmdutil._initialrevs(),
3850 # then filter the result by logcmdutil._makerevset() and --limit
3850 # then filter the result by logcmdutil._makerevset() and --limit
3851 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
3851 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
3852
3852
3853 getrenamed = None
3853 getrenamed = None
3854 if opts.get('copies'):
3854 if opts.get('copies'):
3855 endrev = None
3855 endrev = None
3856 if revs:
3856 if revs:
3857 endrev = revs.max() + 1
3857 endrev = revs.max() + 1
3858 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3858 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3859
3859
3860 ui.pager('log')
3860 ui.pager('log')
3861 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, differ,
3861 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, differ,
3862 buffered=True)
3862 buffered=True)
3863 if opts.get('graph'):
3863 if opts.get('graph'):
3864 displayfn = logcmdutil.displaygraphrevs
3864 displayfn = logcmdutil.displaygraphrevs
3865 else:
3865 else:
3866 displayfn = logcmdutil.displayrevs
3866 displayfn = logcmdutil.displayrevs
3867 displayfn(ui, repo, revs, displayer, getrenamed)
3867 displayfn(ui, repo, revs, displayer, getrenamed)
3868
3868
3869 @command('manifest',
3869 @command('manifest',
3870 [('r', 'rev', '', _('revision to display'), _('REV')),
3870 [('r', 'rev', '', _('revision to display'), _('REV')),
3871 ('', 'all', False, _("list files from all revisions"))]
3871 ('', 'all', False, _("list files from all revisions"))]
3872 + formatteropts,
3872 + formatteropts,
3873 _('[-r REV]'),
3873 _('[-r REV]'),
3874 helpcategory=command.CATEGORY_MAINTENANCE,
3874 helpcategory=command.CATEGORY_MAINTENANCE,
3875 intents={INTENT_READONLY})
3875 intents={INTENT_READONLY})
3876 def manifest(ui, repo, node=None, rev=None, **opts):
3876 def manifest(ui, repo, node=None, rev=None, **opts):
3877 """output the current or given revision of the project manifest
3877 """output the current or given revision of the project manifest
3878
3878
3879 Print a list of version controlled files for the given revision.
3879 Print a list of version controlled files for the given revision.
3880 If no revision is given, the first parent of the working directory
3880 If no revision is given, the first parent of the working directory
3881 is used, or the null revision if no revision is checked out.
3881 is used, or the null revision if no revision is checked out.
3882
3882
3883 With -v, print file permissions, symlink and executable bits.
3883 With -v, print file permissions, symlink and executable bits.
3884 With --debug, print file revision hashes.
3884 With --debug, print file revision hashes.
3885
3885
3886 If option --all is specified, the list of all files from all revisions
3886 If option --all is specified, the list of all files from all revisions
3887 is printed. This includes deleted and renamed files.
3887 is printed. This includes deleted and renamed files.
3888
3888
3889 Returns 0 on success.
3889 Returns 0 on success.
3890 """
3890 """
3891 opts = pycompat.byteskwargs(opts)
3891 opts = pycompat.byteskwargs(opts)
3892 fm = ui.formatter('manifest', opts)
3892 fm = ui.formatter('manifest', opts)
3893
3893
3894 if opts.get('all'):
3894 if opts.get('all'):
3895 if rev or node:
3895 if rev or node:
3896 raise error.Abort(_("can't specify a revision with --all"))
3896 raise error.Abort(_("can't specify a revision with --all"))
3897
3897
3898 res = set()
3898 res = set()
3899 for rev in repo:
3899 for rev in repo:
3900 ctx = repo[rev]
3900 ctx = repo[rev]
3901 res |= set(ctx.files())
3901 res |= set(ctx.files())
3902
3902
3903 ui.pager('manifest')
3903 ui.pager('manifest')
3904 for f in sorted(res):
3904 for f in sorted(res):
3905 fm.startitem()
3905 fm.startitem()
3906 fm.write("path", '%s\n', f)
3906 fm.write("path", '%s\n', f)
3907 fm.end()
3907 fm.end()
3908 return
3908 return
3909
3909
3910 if rev and node:
3910 if rev and node:
3911 raise error.Abort(_("please specify just one revision"))
3911 raise error.Abort(_("please specify just one revision"))
3912
3912
3913 if not node:
3913 if not node:
3914 node = rev
3914 node = rev
3915
3915
3916 char = {'l': '@', 'x': '*', '': '', 't': 'd'}
3916 char = {'l': '@', 'x': '*', '': '', 't': 'd'}
3917 mode = {'l': '644', 'x': '755', '': '644', 't': '755'}
3917 mode = {'l': '644', 'x': '755', '': '644', 't': '755'}
3918 if node:
3918 if node:
3919 repo = scmutil.unhidehashlikerevs(repo, [node], 'nowarn')
3919 repo = scmutil.unhidehashlikerevs(repo, [node], 'nowarn')
3920 ctx = scmutil.revsingle(repo, node)
3920 ctx = scmutil.revsingle(repo, node)
3921 mf = ctx.manifest()
3921 mf = ctx.manifest()
3922 ui.pager('manifest')
3922 ui.pager('manifest')
3923 for f in ctx:
3923 for f in ctx:
3924 fm.startitem()
3924 fm.startitem()
3925 fm.context(ctx=ctx)
3925 fm.context(ctx=ctx)
3926 fl = ctx[f].flags()
3926 fl = ctx[f].flags()
3927 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3927 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
3928 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3928 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
3929 fm.write('path', '%s\n', f)
3929 fm.write('path', '%s\n', f)
3930 fm.end()
3930 fm.end()
3931
3931
3932 @command('merge',
3932 @command('merge',
3933 [('f', 'force', None,
3933 [('f', 'force', None,
3934 _('force a merge including outstanding changes (DEPRECATED)')),
3934 _('force a merge including outstanding changes (DEPRECATED)')),
3935 ('r', 'rev', '', _('revision to merge'), _('REV')),
3935 ('r', 'rev', '', _('revision to merge'), _('REV')),
3936 ('P', 'preview', None,
3936 ('P', 'preview', None,
3937 _('review revisions to merge (no merge is performed)')),
3937 _('review revisions to merge (no merge is performed)')),
3938 ('', 'abort', None, _('abort the ongoing merge')),
3938 ('', 'abort', None, _('abort the ongoing merge')),
3939 ] + mergetoolopts,
3939 ] + mergetoolopts,
3940 _('[-P] [[-r] REV]'),
3940 _('[-P] [[-r] REV]'),
3941 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT, helpbasic=True)
3941 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT, helpbasic=True)
3942 def merge(ui, repo, node=None, **opts):
3942 def merge(ui, repo, node=None, **opts):
3943 """merge another revision into working directory
3943 """merge another revision into working directory
3944
3944
3945 The current working directory is updated with all changes made in
3945 The current working directory is updated with all changes made in
3946 the requested revision since the last common predecessor revision.
3946 the requested revision since the last common predecessor revision.
3947
3947
3948 Files that changed between either parent are marked as changed for
3948 Files that changed between either parent are marked as changed for
3949 the next commit and a commit must be performed before any further
3949 the next commit and a commit must be performed before any further
3950 updates to the repository are allowed. The next commit will have
3950 updates to the repository are allowed. The next commit will have
3951 two parents.
3951 two parents.
3952
3952
3953 ``--tool`` can be used to specify the merge tool used for file
3953 ``--tool`` can be used to specify the merge tool used for file
3954 merges. It overrides the HGMERGE environment variable and your
3954 merges. It overrides the HGMERGE environment variable and your
3955 configuration files. See :hg:`help merge-tools` for options.
3955 configuration files. See :hg:`help merge-tools` for options.
3956
3956
3957 If no revision is specified, the working directory's parent is a
3957 If no revision is specified, the working directory's parent is a
3958 head revision, and the current branch contains exactly one other
3958 head revision, and the current branch contains exactly one other
3959 head, the other head is merged with by default. Otherwise, an
3959 head, the other head is merged with by default. Otherwise, an
3960 explicit revision with which to merge with must be provided.
3960 explicit revision with which to merge with must be provided.
3961
3961
3962 See :hg:`help resolve` for information on handling file conflicts.
3962 See :hg:`help resolve` for information on handling file conflicts.
3963
3963
3964 To undo an uncommitted merge, use :hg:`merge --abort` which
3964 To undo an uncommitted merge, use :hg:`merge --abort` which
3965 will check out a clean copy of the original merge parent, losing
3965 will check out a clean copy of the original merge parent, losing
3966 all changes.
3966 all changes.
3967
3967
3968 Returns 0 on success, 1 if there are unresolved files.
3968 Returns 0 on success, 1 if there are unresolved files.
3969 """
3969 """
3970
3970
3971 opts = pycompat.byteskwargs(opts)
3971 opts = pycompat.byteskwargs(opts)
3972 abort = opts.get('abort')
3972 abort = opts.get('abort')
3973 if abort and repo.dirstate.p2() == nullid:
3973 if abort and repo.dirstate.p2() == nullid:
3974 cmdutil.wrongtooltocontinue(repo, _('merge'))
3974 cmdutil.wrongtooltocontinue(repo, _('merge'))
3975 if abort:
3975 if abort:
3976 if node:
3976 if node:
3977 raise error.Abort(_("cannot specify a node with --abort"))
3977 raise error.Abort(_("cannot specify a node with --abort"))
3978 if opts.get('rev'):
3978 if opts.get('rev'):
3979 raise error.Abort(_("cannot specify both --rev and --abort"))
3979 raise error.Abort(_("cannot specify both --rev and --abort"))
3980 if opts.get('preview'):
3980 if opts.get('preview'):
3981 raise error.Abort(_("cannot specify --preview with --abort"))
3981 raise error.Abort(_("cannot specify --preview with --abort"))
3982 if opts.get('rev') and node:
3982 if opts.get('rev') and node:
3983 raise error.Abort(_("please specify just one revision"))
3983 raise error.Abort(_("please specify just one revision"))
3984 if not node:
3984 if not node:
3985 node = opts.get('rev')
3985 node = opts.get('rev')
3986
3986
3987 if node:
3987 if node:
3988 node = scmutil.revsingle(repo, node).node()
3988 node = scmutil.revsingle(repo, node).node()
3989
3989
3990 if not node and not abort:
3990 if not node and not abort:
3991 node = repo[destutil.destmerge(repo)].node()
3991 node = repo[destutil.destmerge(repo)].node()
3992
3992
3993 if opts.get('preview'):
3993 if opts.get('preview'):
3994 # find nodes that are ancestors of p2 but not of p1
3994 # find nodes that are ancestors of p2 but not of p1
3995 p1 = repo.lookup('.')
3995 p1 = repo.lookup('.')
3996 p2 = node
3996 p2 = node
3997 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3997 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3998
3998
3999 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3999 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4000 for node in nodes:
4000 for node in nodes:
4001 displayer.show(repo[node])
4001 displayer.show(repo[node])
4002 displayer.close()
4002 displayer.close()
4003 return 0
4003 return 0
4004
4004
4005 # ui.forcemerge is an internal variable, do not document
4005 # ui.forcemerge is an internal variable, do not document
4006 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4006 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4007 with ui.configoverride(overrides, 'merge'):
4007 with ui.configoverride(overrides, 'merge'):
4008 force = opts.get('force')
4008 force = opts.get('force')
4009 labels = ['working copy', 'merge rev']
4009 labels = ['working copy', 'merge rev']
4010 return hg.merge(repo, node, force=force, mergeforce=force,
4010 return hg.merge(repo, node, force=force, mergeforce=force,
4011 labels=labels, abort=abort)
4011 labels=labels, abort=abort)
4012
4012
4013 @command('outgoing|out',
4013 @command('outgoing|out',
4014 [('f', 'force', None, _('run even when the destination is unrelated')),
4014 [('f', 'force', None, _('run even when the destination is unrelated')),
4015 ('r', 'rev', [],
4015 ('r', 'rev', [],
4016 _('a changeset intended to be included in the destination'), _('REV')),
4016 _('a changeset intended to be included in the destination'), _('REV')),
4017 ('n', 'newest-first', None, _('show newest record first')),
4017 ('n', 'newest-first', None, _('show newest record first')),
4018 ('B', 'bookmarks', False, _('compare bookmarks')),
4018 ('B', 'bookmarks', False, _('compare bookmarks')),
4019 ('b', 'branch', [], _('a specific branch you would like to push'),
4019 ('b', 'branch', [], _('a specific branch you would like to push'),
4020 _('BRANCH')),
4020 _('BRANCH')),
4021 ] + logopts + remoteopts + subrepoopts,
4021 ] + logopts + remoteopts + subrepoopts,
4022 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4022 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'),
4023 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
4023 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT)
4024 def outgoing(ui, repo, dest=None, **opts):
4024 def outgoing(ui, repo, dest=None, **opts):
4025 """show changesets not found in the destination
4025 """show changesets not found in the destination
4026
4026
4027 Show changesets not found in the specified destination repository
4027 Show changesets not found in the specified destination repository
4028 or the default push location. These are the changesets that would
4028 or the default push location. These are the changesets that would
4029 be pushed if a push was requested.
4029 be pushed if a push was requested.
4030
4030
4031 See pull for details of valid destination formats.
4031 See pull for details of valid destination formats.
4032
4032
4033 .. container:: verbose
4033 .. container:: verbose
4034
4034
4035 With -B/--bookmarks, the result of bookmark comparison between
4035 With -B/--bookmarks, the result of bookmark comparison between
4036 local and remote repositories is displayed. With -v/--verbose,
4036 local and remote repositories is displayed. With -v/--verbose,
4037 status is also displayed for each bookmark like below::
4037 status is also displayed for each bookmark like below::
4038
4038
4039 BM1 01234567890a added
4039 BM1 01234567890a added
4040 BM2 deleted
4040 BM2 deleted
4041 BM3 234567890abc advanced
4041 BM3 234567890abc advanced
4042 BM4 34567890abcd diverged
4042 BM4 34567890abcd diverged
4043 BM5 4567890abcde changed
4043 BM5 4567890abcde changed
4044
4044
4045 The action taken when pushing depends on the
4045 The action taken when pushing depends on the
4046 status of each bookmark:
4046 status of each bookmark:
4047
4047
4048 :``added``: push with ``-B`` will create it
4048 :``added``: push with ``-B`` will create it
4049 :``deleted``: push with ``-B`` will delete it
4049 :``deleted``: push with ``-B`` will delete it
4050 :``advanced``: push will update it
4050 :``advanced``: push will update it
4051 :``diverged``: push with ``-B`` will update it
4051 :``diverged``: push with ``-B`` will update it
4052 :``changed``: push with ``-B`` will update it
4052 :``changed``: push with ``-B`` will update it
4053
4053
4054 From the point of view of pushing behavior, bookmarks
4054 From the point of view of pushing behavior, bookmarks
4055 existing only in the remote repository are treated as
4055 existing only in the remote repository are treated as
4056 ``deleted``, even if it is in fact added remotely.
4056 ``deleted``, even if it is in fact added remotely.
4057
4057
4058 Returns 0 if there are outgoing changes, 1 otherwise.
4058 Returns 0 if there are outgoing changes, 1 otherwise.
4059 """
4059 """
4060 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4060 # hg._outgoing() needs to re-resolve the path in order to handle #branch
4061 # style URLs, so don't overwrite dest.
4061 # style URLs, so don't overwrite dest.
4062 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4062 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4063 if not path:
4063 if not path:
4064 raise error.Abort(_('default repository not configured!'),
4064 raise error.Abort(_('default repository not configured!'),
4065 hint=_("see 'hg help config.paths'"))
4065 hint=_("see 'hg help config.paths'"))
4066
4066
4067 opts = pycompat.byteskwargs(opts)
4067 opts = pycompat.byteskwargs(opts)
4068 if opts.get('graph'):
4068 if opts.get('graph'):
4069 logcmdutil.checkunsupportedgraphflags([], opts)
4069 logcmdutil.checkunsupportedgraphflags([], opts)
4070 o, other = hg._outgoing(ui, repo, dest, opts)
4070 o, other = hg._outgoing(ui, repo, dest, opts)
4071 if not o:
4071 if not o:
4072 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4072 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4073 return
4073 return
4074
4074
4075 revdag = logcmdutil.graphrevs(repo, o, opts)
4075 revdag = logcmdutil.graphrevs(repo, o, opts)
4076 ui.pager('outgoing')
4076 ui.pager('outgoing')
4077 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
4077 displayer = logcmdutil.changesetdisplayer(ui, repo, opts, buffered=True)
4078 logcmdutil.displaygraph(ui, repo, revdag, displayer,
4078 logcmdutil.displaygraph(ui, repo, revdag, displayer,
4079 graphmod.asciiedges)
4079 graphmod.asciiedges)
4080 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4080 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4081 return 0
4081 return 0
4082
4082
4083 if opts.get('bookmarks'):
4083 if opts.get('bookmarks'):
4084 dest = path.pushloc or path.loc
4084 dest = path.pushloc or path.loc
4085 other = hg.peer(repo, opts, dest)
4085 other = hg.peer(repo, opts, dest)
4086 if 'bookmarks' not in other.listkeys('namespaces'):
4086 if 'bookmarks' not in other.listkeys('namespaces'):
4087 ui.warn(_("remote doesn't support bookmarks\n"))
4087 ui.warn(_("remote doesn't support bookmarks\n"))
4088 return 0
4088 return 0
4089 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4089 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4090 ui.pager('outgoing')
4090 ui.pager('outgoing')
4091 return bookmarks.outgoing(ui, repo, other)
4091 return bookmarks.outgoing(ui, repo, other)
4092
4092
4093 repo._subtoppath = path.pushloc or path.loc
4093 repo._subtoppath = path.pushloc or path.loc
4094 try:
4094 try:
4095 return hg.outgoing(ui, repo, dest, opts)
4095 return hg.outgoing(ui, repo, dest, opts)
4096 finally:
4096 finally:
4097 del repo._subtoppath
4097 del repo._subtoppath
4098
4098
4099 @command('parents',
4099 @command('parents',
4100 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4100 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4101 ] + templateopts,
4101 ] + templateopts,
4102 _('[-r REV] [FILE]'),
4102 _('[-r REV] [FILE]'),
4103 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4103 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4104 inferrepo=True)
4104 inferrepo=True)
4105 def parents(ui, repo, file_=None, **opts):
4105 def parents(ui, repo, file_=None, **opts):
4106 """show the parents of the working directory or revision (DEPRECATED)
4106 """show the parents of the working directory or revision (DEPRECATED)
4107
4107
4108 Print the working directory's parent revisions. If a revision is
4108 Print the working directory's parent revisions. If a revision is
4109 given via -r/--rev, the parent of that revision will be printed.
4109 given via -r/--rev, the parent of that revision will be printed.
4110 If a file argument is given, the revision in which the file was
4110 If a file argument is given, the revision in which the file was
4111 last changed (before the working directory revision or the
4111 last changed (before the working directory revision or the
4112 argument to --rev if given) is printed.
4112 argument to --rev if given) is printed.
4113
4113
4114 This command is equivalent to::
4114 This command is equivalent to::
4115
4115
4116 hg log -r "p1()+p2()" or
4116 hg log -r "p1()+p2()" or
4117 hg log -r "p1(REV)+p2(REV)" or
4117 hg log -r "p1(REV)+p2(REV)" or
4118 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
4118 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
4119 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
4119 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
4120
4120
4121 See :hg:`summary` and :hg:`help revsets` for related information.
4121 See :hg:`summary` and :hg:`help revsets` for related information.
4122
4122
4123 Returns 0 on success.
4123 Returns 0 on success.
4124 """
4124 """
4125
4125
4126 opts = pycompat.byteskwargs(opts)
4126 opts = pycompat.byteskwargs(opts)
4127 rev = opts.get('rev')
4127 rev = opts.get('rev')
4128 if rev:
4128 if rev:
4129 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
4129 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
4130 ctx = scmutil.revsingle(repo, rev, None)
4130 ctx = scmutil.revsingle(repo, rev, None)
4131
4131
4132 if file_:
4132 if file_:
4133 m = scmutil.match(ctx, (file_,), opts)
4133 m = scmutil.match(ctx, (file_,), opts)
4134 if m.anypats() or len(m.files()) != 1:
4134 if m.anypats() or len(m.files()) != 1:
4135 raise error.Abort(_('can only specify an explicit filename'))
4135 raise error.Abort(_('can only specify an explicit filename'))
4136 file_ = m.files()[0]
4136 file_ = m.files()[0]
4137 filenodes = []
4137 filenodes = []
4138 for cp in ctx.parents():
4138 for cp in ctx.parents():
4139 if not cp:
4139 if not cp:
4140 continue
4140 continue
4141 try:
4141 try:
4142 filenodes.append(cp.filenode(file_))
4142 filenodes.append(cp.filenode(file_))
4143 except error.LookupError:
4143 except error.LookupError:
4144 pass
4144 pass
4145 if not filenodes:
4145 if not filenodes:
4146 raise error.Abort(_("'%s' not found in manifest!") % file_)
4146 raise error.Abort(_("'%s' not found in manifest!") % file_)
4147 p = []
4147 p = []
4148 for fn in filenodes:
4148 for fn in filenodes:
4149 fctx = repo.filectx(file_, fileid=fn)
4149 fctx = repo.filectx(file_, fileid=fn)
4150 p.append(fctx.node())
4150 p.append(fctx.node())
4151 else:
4151 else:
4152 p = [cp.node() for cp in ctx.parents()]
4152 p = [cp.node() for cp in ctx.parents()]
4153
4153
4154 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4154 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4155 for n in p:
4155 for n in p:
4156 if n != nullid:
4156 if n != nullid:
4157 displayer.show(repo[n])
4157 displayer.show(repo[n])
4158 displayer.close()
4158 displayer.close()
4159
4159
4160 @command('paths', formatteropts, _('[NAME]'),
4160 @command('paths', formatteropts, _('[NAME]'),
4161 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4161 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4162 optionalrepo=True, intents={INTENT_READONLY})
4162 optionalrepo=True, intents={INTENT_READONLY})
4163 def paths(ui, repo, search=None, **opts):
4163 def paths(ui, repo, search=None, **opts):
4164 """show aliases for remote repositories
4164 """show aliases for remote repositories
4165
4165
4166 Show definition of symbolic path name NAME. If no name is given,
4166 Show definition of symbolic path name NAME. If no name is given,
4167 show definition of all available names.
4167 show definition of all available names.
4168
4168
4169 Option -q/--quiet suppresses all output when searching for NAME
4169 Option -q/--quiet suppresses all output when searching for NAME
4170 and shows only the path names when listing all definitions.
4170 and shows only the path names when listing all definitions.
4171
4171
4172 Path names are defined in the [paths] section of your
4172 Path names are defined in the [paths] section of your
4173 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4173 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4174 repository, ``.hg/hgrc`` is used, too.
4174 repository, ``.hg/hgrc`` is used, too.
4175
4175
4176 The path names ``default`` and ``default-push`` have a special
4176 The path names ``default`` and ``default-push`` have a special
4177 meaning. When performing a push or pull operation, they are used
4177 meaning. When performing a push or pull operation, they are used
4178 as fallbacks if no location is specified on the command-line.
4178 as fallbacks if no location is specified on the command-line.
4179 When ``default-push`` is set, it will be used for push and
4179 When ``default-push`` is set, it will be used for push and
4180 ``default`` will be used for pull; otherwise ``default`` is used
4180 ``default`` will be used for pull; otherwise ``default`` is used
4181 as the fallback for both. When cloning a repository, the clone
4181 as the fallback for both. When cloning a repository, the clone
4182 source is written as ``default`` in ``.hg/hgrc``.
4182 source is written as ``default`` in ``.hg/hgrc``.
4183
4183
4184 .. note::
4184 .. note::
4185
4185
4186 ``default`` and ``default-push`` apply to all inbound (e.g.
4186 ``default`` and ``default-push`` apply to all inbound (e.g.
4187 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
4187 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
4188 and :hg:`bundle`) operations.
4188 and :hg:`bundle`) operations.
4189
4189
4190 See :hg:`help urls` for more information.
4190 See :hg:`help urls` for more information.
4191
4191
4192 .. container:: verbose
4192 .. container:: verbose
4193
4193
4194 Template:
4194 Template:
4195
4195
4196 The following keywords are supported. See also :hg:`help templates`.
4196 The following keywords are supported. See also :hg:`help templates`.
4197
4197
4198 :name: String. Symbolic name of the path alias.
4198 :name: String. Symbolic name of the path alias.
4199 :pushurl: String. URL for push operations.
4199 :pushurl: String. URL for push operations.
4200 :url: String. URL or directory path for the other operations.
4200 :url: String. URL or directory path for the other operations.
4201
4201
4202 Returns 0 on success.
4202 Returns 0 on success.
4203 """
4203 """
4204
4204
4205 opts = pycompat.byteskwargs(opts)
4205 opts = pycompat.byteskwargs(opts)
4206 ui.pager('paths')
4206 ui.pager('paths')
4207 if search:
4207 if search:
4208 pathitems = [(name, path) for name, path in ui.paths.iteritems()
4208 pathitems = [(name, path) for name, path in ui.paths.iteritems()
4209 if name == search]
4209 if name == search]
4210 else:
4210 else:
4211 pathitems = sorted(ui.paths.iteritems())
4211 pathitems = sorted(ui.paths.iteritems())
4212
4212
4213 fm = ui.formatter('paths', opts)
4213 fm = ui.formatter('paths', opts)
4214 if fm.isplain():
4214 if fm.isplain():
4215 hidepassword = util.hidepassword
4215 hidepassword = util.hidepassword
4216 else:
4216 else:
4217 hidepassword = bytes
4217 hidepassword = bytes
4218 if ui.quiet:
4218 if ui.quiet:
4219 namefmt = '%s\n'
4219 namefmt = '%s\n'
4220 else:
4220 else:
4221 namefmt = '%s = '
4221 namefmt = '%s = '
4222 showsubopts = not search and not ui.quiet
4222 showsubopts = not search and not ui.quiet
4223
4223
4224 for name, path in pathitems:
4224 for name, path in pathitems:
4225 fm.startitem()
4225 fm.startitem()
4226 fm.condwrite(not search, 'name', namefmt, name)
4226 fm.condwrite(not search, 'name', namefmt, name)
4227 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
4227 fm.condwrite(not ui.quiet, 'url', '%s\n', hidepassword(path.rawloc))
4228 for subopt, value in sorted(path.suboptions.items()):
4228 for subopt, value in sorted(path.suboptions.items()):
4229 assert subopt not in ('name', 'url')
4229 assert subopt not in ('name', 'url')
4230 if showsubopts:
4230 if showsubopts:
4231 fm.plain('%s:%s = ' % (name, subopt))
4231 fm.plain('%s:%s = ' % (name, subopt))
4232 fm.condwrite(showsubopts, subopt, '%s\n', value)
4232 fm.condwrite(showsubopts, subopt, '%s\n', value)
4233
4233
4234 fm.end()
4234 fm.end()
4235
4235
4236 if search and not pathitems:
4236 if search and not pathitems:
4237 if not ui.quiet:
4237 if not ui.quiet:
4238 ui.warn(_("not found!\n"))
4238 ui.warn(_("not found!\n"))
4239 return 1
4239 return 1
4240 else:
4240 else:
4241 return 0
4241 return 0
4242
4242
4243 @command('phase',
4243 @command('phase',
4244 [('p', 'public', False, _('set changeset phase to public')),
4244 [('p', 'public', False, _('set changeset phase to public')),
4245 ('d', 'draft', False, _('set changeset phase to draft')),
4245 ('d', 'draft', False, _('set changeset phase to draft')),
4246 ('s', 'secret', False, _('set changeset phase to secret')),
4246 ('s', 'secret', False, _('set changeset phase to secret')),
4247 ('f', 'force', False, _('allow to move boundary backward')),
4247 ('f', 'force', False, _('allow to move boundary backward')),
4248 ('r', 'rev', [], _('target revision'), _('REV')),
4248 ('r', 'rev', [], _('target revision'), _('REV')),
4249 ],
4249 ],
4250 _('[-p|-d|-s] [-f] [-r] [REV...]'),
4250 _('[-p|-d|-s] [-f] [-r] [REV...]'),
4251 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
4251 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
4252 def phase(ui, repo, *revs, **opts):
4252 def phase(ui, repo, *revs, **opts):
4253 """set or show the current phase name
4253 """set or show the current phase name
4254
4254
4255 With no argument, show the phase name of the current revision(s).
4255 With no argument, show the phase name of the current revision(s).
4256
4256
4257 With one of -p/--public, -d/--draft or -s/--secret, change the
4257 With one of -p/--public, -d/--draft or -s/--secret, change the
4258 phase value of the specified revisions.
4258 phase value of the specified revisions.
4259
4259
4260 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
4260 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
4261 lower phase to a higher phase. Phases are ordered as follows::
4261 lower phase to a higher phase. Phases are ordered as follows::
4262
4262
4263 public < draft < secret
4263 public < draft < secret
4264
4264
4265 Returns 0 on success, 1 if some phases could not be changed.
4265 Returns 0 on success, 1 if some phases could not be changed.
4266
4266
4267 (For more information about the phases concept, see :hg:`help phases`.)
4267 (For more information about the phases concept, see :hg:`help phases`.)
4268 """
4268 """
4269 opts = pycompat.byteskwargs(opts)
4269 opts = pycompat.byteskwargs(opts)
4270 # search for a unique phase argument
4270 # search for a unique phase argument
4271 targetphase = None
4271 targetphase = None
4272 for idx, name in enumerate(phases.cmdphasenames):
4272 for idx, name in enumerate(phases.cmdphasenames):
4273 if opts[name]:
4273 if opts[name]:
4274 if targetphase is not None:
4274 if targetphase is not None:
4275 raise error.Abort(_('only one phase can be specified'))
4275 raise error.Abort(_('only one phase can be specified'))
4276 targetphase = idx
4276 targetphase = idx
4277
4277
4278 # look for specified revision
4278 # look for specified revision
4279 revs = list(revs)
4279 revs = list(revs)
4280 revs.extend(opts['rev'])
4280 revs.extend(opts['rev'])
4281 if not revs:
4281 if not revs:
4282 # display both parents as the second parent phase can influence
4282 # display both parents as the second parent phase can influence
4283 # the phase of a merge commit
4283 # the phase of a merge commit
4284 revs = [c.rev() for c in repo[None].parents()]
4284 revs = [c.rev() for c in repo[None].parents()]
4285
4285
4286 revs = scmutil.revrange(repo, revs)
4286 revs = scmutil.revrange(repo, revs)
4287
4287
4288 ret = 0
4288 ret = 0
4289 if targetphase is None:
4289 if targetphase is None:
4290 # display
4290 # display
4291 for r in revs:
4291 for r in revs:
4292 ctx = repo[r]
4292 ctx = repo[r]
4293 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4293 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4294 else:
4294 else:
4295 with repo.lock(), repo.transaction("phase") as tr:
4295 with repo.lock(), repo.transaction("phase") as tr:
4296 # set phase
4296 # set phase
4297 if not revs:
4297 if not revs:
4298 raise error.Abort(_('empty revision set'))
4298 raise error.Abort(_('empty revision set'))
4299 nodes = [repo[r].node() for r in revs]
4299 nodes = [repo[r].node() for r in revs]
4300 # moving revision from public to draft may hide them
4300 # moving revision from public to draft may hide them
4301 # We have to check result on an unfiltered repository
4301 # We have to check result on an unfiltered repository
4302 unfi = repo.unfiltered()
4302 unfi = repo.unfiltered()
4303 getphase = unfi._phasecache.phase
4303 getphase = unfi._phasecache.phase
4304 olddata = [getphase(unfi, r) for r in unfi]
4304 olddata = [getphase(unfi, r) for r in unfi]
4305 phases.advanceboundary(repo, tr, targetphase, nodes)
4305 phases.advanceboundary(repo, tr, targetphase, nodes)
4306 if opts['force']:
4306 if opts['force']:
4307 phases.retractboundary(repo, tr, targetphase, nodes)
4307 phases.retractboundary(repo, tr, targetphase, nodes)
4308 getphase = unfi._phasecache.phase
4308 getphase = unfi._phasecache.phase
4309 newdata = [getphase(unfi, r) for r in unfi]
4309 newdata = [getphase(unfi, r) for r in unfi]
4310 changes = sum(newdata[r] != olddata[r] for r in unfi)
4310 changes = sum(newdata[r] != olddata[r] for r in unfi)
4311 cl = unfi.changelog
4311 cl = unfi.changelog
4312 rejected = [n for n in nodes
4312 rejected = [n for n in nodes
4313 if newdata[cl.rev(n)] < targetphase]
4313 if newdata[cl.rev(n)] < targetphase]
4314 if rejected:
4314 if rejected:
4315 ui.warn(_('cannot move %i changesets to a higher '
4315 ui.warn(_('cannot move %i changesets to a higher '
4316 'phase, use --force\n') % len(rejected))
4316 'phase, use --force\n') % len(rejected))
4317 ret = 1
4317 ret = 1
4318 if changes:
4318 if changes:
4319 msg = _('phase changed for %i changesets\n') % changes
4319 msg = _('phase changed for %i changesets\n') % changes
4320 if ret:
4320 if ret:
4321 ui.status(msg)
4321 ui.status(msg)
4322 else:
4322 else:
4323 ui.note(msg)
4323 ui.note(msg)
4324 else:
4324 else:
4325 ui.warn(_('no phases changed\n'))
4325 ui.warn(_('no phases changed\n'))
4326 return ret
4326 return ret
4327
4327
4328 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
4328 def postincoming(ui, repo, modheads, optupdate, checkout, brev):
4329 """Run after a changegroup has been added via pull/unbundle
4329 """Run after a changegroup has been added via pull/unbundle
4330
4330
4331 This takes arguments below:
4331 This takes arguments below:
4332
4332
4333 :modheads: change of heads by pull/unbundle
4333 :modheads: change of heads by pull/unbundle
4334 :optupdate: updating working directory is needed or not
4334 :optupdate: updating working directory is needed or not
4335 :checkout: update destination revision (or None to default destination)
4335 :checkout: update destination revision (or None to default destination)
4336 :brev: a name, which might be a bookmark to be activated after updating
4336 :brev: a name, which might be a bookmark to be activated after updating
4337 """
4337 """
4338 if modheads == 0:
4338 if modheads == 0:
4339 return
4339 return
4340 if optupdate:
4340 if optupdate:
4341 try:
4341 try:
4342 return hg.updatetotally(ui, repo, checkout, brev)
4342 return hg.updatetotally(ui, repo, checkout, brev)
4343 except error.UpdateAbort as inst:
4343 except error.UpdateAbort as inst:
4344 msg = _("not updating: %s") % stringutil.forcebytestr(inst)
4344 msg = _("not updating: %s") % stringutil.forcebytestr(inst)
4345 hint = inst.hint
4345 hint = inst.hint
4346 raise error.UpdateAbort(msg, hint=hint)
4346 raise error.UpdateAbort(msg, hint=hint)
4347 if modheads > 1:
4347 if modheads > 1:
4348 currentbranchheads = len(repo.branchheads())
4348 currentbranchheads = len(repo.branchheads())
4349 if currentbranchheads == modheads:
4349 if currentbranchheads == modheads:
4350 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4350 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4351 elif currentbranchheads > 1:
4351 elif currentbranchheads > 1:
4352 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4352 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4353 "merge)\n"))
4353 "merge)\n"))
4354 else:
4354 else:
4355 ui.status(_("(run 'hg heads' to see heads)\n"))
4355 ui.status(_("(run 'hg heads' to see heads)\n"))
4356 elif not ui.configbool('commands', 'update.requiredest'):
4356 elif not ui.configbool('commands', 'update.requiredest'):
4357 ui.status(_("(run 'hg update' to get a working copy)\n"))
4357 ui.status(_("(run 'hg update' to get a working copy)\n"))
4358
4358
4359 @command('pull',
4359 @command('pull',
4360 [('u', 'update', None,
4360 [('u', 'update', None,
4361 _('update to new branch head if new descendants were pulled')),
4361 _('update to new branch head if new descendants were pulled')),
4362 ('f', 'force', None, _('run even when remote repository is unrelated')),
4362 ('f', 'force', None, _('run even when remote repository is unrelated')),
4363 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4363 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4364 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4364 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4365 ('b', 'branch', [], _('a specific branch you would like to pull'),
4365 ('b', 'branch', [], _('a specific branch you would like to pull'),
4366 _('BRANCH')),
4366 _('BRANCH')),
4367 ] + remoteopts,
4367 ] + remoteopts,
4368 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
4368 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'),
4369 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4369 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4370 helpbasic=True)
4370 helpbasic=True)
4371 def pull(ui, repo, source="default", **opts):
4371 def pull(ui, repo, source="default", **opts):
4372 """pull changes from the specified source
4372 """pull changes from the specified source
4373
4373
4374 Pull changes from a remote repository to a local one.
4374 Pull changes from a remote repository to a local one.
4375
4375
4376 This finds all changes from the repository at the specified path
4376 This finds all changes from the repository at the specified path
4377 or URL and adds them to a local repository (the current one unless
4377 or URL and adds them to a local repository (the current one unless
4378 -R is specified). By default, this does not update the copy of the
4378 -R is specified). By default, this does not update the copy of the
4379 project in the working directory.
4379 project in the working directory.
4380
4380
4381 When cloning from servers that support it, Mercurial may fetch
4381 When cloning from servers that support it, Mercurial may fetch
4382 pre-generated data. When this is done, hooks operating on incoming
4382 pre-generated data. When this is done, hooks operating on incoming
4383 changesets and changegroups may fire more than once, once for each
4383 changesets and changegroups may fire more than once, once for each
4384 pre-generated bundle and as well as for any additional remaining
4384 pre-generated bundle and as well as for any additional remaining
4385 data. See :hg:`help -e clonebundles` for more.
4385 data. See :hg:`help -e clonebundles` for more.
4386
4386
4387 Use :hg:`incoming` if you want to see what would have been added
4387 Use :hg:`incoming` if you want to see what would have been added
4388 by a pull at the time you issued this command. If you then decide
4388 by a pull at the time you issued this command. If you then decide
4389 to add those changes to the repository, you should use :hg:`pull
4389 to add those changes to the repository, you should use :hg:`pull
4390 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4390 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4391
4391
4392 If SOURCE is omitted, the 'default' path will be used.
4392 If SOURCE is omitted, the 'default' path will be used.
4393 See :hg:`help urls` for more information.
4393 See :hg:`help urls` for more information.
4394
4394
4395 Specifying bookmark as ``.`` is equivalent to specifying the active
4395 Specifying bookmark as ``.`` is equivalent to specifying the active
4396 bookmark's name.
4396 bookmark's name.
4397
4397
4398 Returns 0 on success, 1 if an update had unresolved files.
4398 Returns 0 on success, 1 if an update had unresolved files.
4399 """
4399 """
4400
4400
4401 opts = pycompat.byteskwargs(opts)
4401 opts = pycompat.byteskwargs(opts)
4402 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
4402 if ui.configbool('commands', 'update.requiredest') and opts.get('update'):
4403 msg = _('update destination required by configuration')
4403 msg = _('update destination required by configuration')
4404 hint = _('use hg pull followed by hg update DEST')
4404 hint = _('use hg pull followed by hg update DEST')
4405 raise error.Abort(msg, hint=hint)
4405 raise error.Abort(msg, hint=hint)
4406
4406
4407 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4407 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4408 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4408 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4409 other = hg.peer(repo, opts, source)
4409 other = hg.peer(repo, opts, source)
4410 try:
4410 try:
4411 revs, checkout = hg.addbranchrevs(repo, other, branches,
4411 revs, checkout = hg.addbranchrevs(repo, other, branches,
4412 opts.get('rev'))
4412 opts.get('rev'))
4413
4413
4414
4414
4415 pullopargs = {}
4415 pullopargs = {}
4416 if opts.get('bookmark'):
4416 if opts.get('bookmark'):
4417 if not revs:
4417 if not revs:
4418 revs = []
4418 revs = []
4419 # The list of bookmark used here is not the one used to actually
4419 # The list of bookmark used here is not the one used to actually
4420 # update the bookmark name. This can result in the revision pulled
4420 # update the bookmark name. This can result in the revision pulled
4421 # not ending up with the name of the bookmark because of a race
4421 # not ending up with the name of the bookmark because of a race
4422 # condition on the server. (See issue 4689 for details)
4422 # condition on the server. (See issue 4689 for details)
4423 remotebookmarks = other.listkeys('bookmarks')
4423 remotebookmarks = other.listkeys('bookmarks')
4424 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
4424 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
4425 pullopargs['remotebookmarks'] = remotebookmarks
4425 pullopargs['remotebookmarks'] = remotebookmarks
4426 for b in opts['bookmark']:
4426 for b in opts['bookmark']:
4427 b = repo._bookmarks.expandname(b)
4427 b = repo._bookmarks.expandname(b)
4428 if b not in remotebookmarks:
4428 if b not in remotebookmarks:
4429 raise error.Abort(_('remote bookmark %s not found!') % b)
4429 raise error.Abort(_('remote bookmark %s not found!') % b)
4430 revs.append(hex(remotebookmarks[b]))
4430 revs.append(hex(remotebookmarks[b]))
4431
4431
4432 if revs:
4432 if revs:
4433 try:
4433 try:
4434 # When 'rev' is a bookmark name, we cannot guarantee that it
4434 # When 'rev' is a bookmark name, we cannot guarantee that it
4435 # will be updated with that name because of a race condition
4435 # will be updated with that name because of a race condition
4436 # server side. (See issue 4689 for details)
4436 # server side. (See issue 4689 for details)
4437 oldrevs = revs
4437 oldrevs = revs
4438 revs = [] # actually, nodes
4438 revs = [] # actually, nodes
4439 for r in oldrevs:
4439 for r in oldrevs:
4440 with other.commandexecutor() as e:
4440 with other.commandexecutor() as e:
4441 node = e.callcommand('lookup', {'key': r}).result()
4441 node = e.callcommand('lookup', {'key': r}).result()
4442
4442
4443 revs.append(node)
4443 revs.append(node)
4444 if r == checkout:
4444 if r == checkout:
4445 checkout = node
4445 checkout = node
4446 except error.CapabilityError:
4446 except error.CapabilityError:
4447 err = _("other repository doesn't support revision lookup, "
4447 err = _("other repository doesn't support revision lookup, "
4448 "so a rev cannot be specified.")
4448 "so a rev cannot be specified.")
4449 raise error.Abort(err)
4449 raise error.Abort(err)
4450
4450
4451 wlock = util.nullcontextmanager()
4451 wlock = util.nullcontextmanager()
4452 if opts.get('update'):
4452 if opts.get('update'):
4453 wlock = repo.wlock()
4453 wlock = repo.wlock()
4454 with wlock:
4454 with wlock:
4455 pullopargs.update(opts.get('opargs', {}))
4455 pullopargs.update(opts.get('opargs', {}))
4456 modheads = exchange.pull(repo, other, heads=revs,
4456 modheads = exchange.pull(repo, other, heads=revs,
4457 force=opts.get('force'),
4457 force=opts.get('force'),
4458 bookmarks=opts.get('bookmark', ()),
4458 bookmarks=opts.get('bookmark', ()),
4459 opargs=pullopargs).cgresult
4459 opargs=pullopargs).cgresult
4460
4460
4461 # brev is a name, which might be a bookmark to be activated at
4461 # brev is a name, which might be a bookmark to be activated at
4462 # the end of the update. In other words, it is an explicit
4462 # the end of the update. In other words, it is an explicit
4463 # destination of the update
4463 # destination of the update
4464 brev = None
4464 brev = None
4465
4465
4466 if checkout:
4466 if checkout:
4467 checkout = repo.changelog.rev(checkout)
4467 checkout = repo.changelog.rev(checkout)
4468
4468
4469 # order below depends on implementation of
4469 # order below depends on implementation of
4470 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4470 # hg.addbranchrevs(). opts['bookmark'] is ignored,
4471 # because 'checkout' is determined without it.
4471 # because 'checkout' is determined without it.
4472 if opts.get('rev'):
4472 if opts.get('rev'):
4473 brev = opts['rev'][0]
4473 brev = opts['rev'][0]
4474 elif opts.get('branch'):
4474 elif opts.get('branch'):
4475 brev = opts['branch'][0]
4475 brev = opts['branch'][0]
4476 else:
4476 else:
4477 brev = branches[0]
4477 brev = branches[0]
4478 repo._subtoppath = source
4478 repo._subtoppath = source
4479 try:
4479 try:
4480 ret = postincoming(ui, repo, modheads, opts.get('update'),
4480 ret = postincoming(ui, repo, modheads, opts.get('update'),
4481 checkout, brev)
4481 checkout, brev)
4482
4482
4483 finally:
4483 finally:
4484 del repo._subtoppath
4484 del repo._subtoppath
4485
4485
4486 finally:
4486 finally:
4487 other.close()
4487 other.close()
4488 return ret
4488 return ret
4489
4489
4490 @command('push',
4490 @command('push',
4491 [('f', 'force', None, _('force push')),
4491 [('f', 'force', None, _('force push')),
4492 ('r', 'rev', [],
4492 ('r', 'rev', [],
4493 _('a changeset intended to be included in the destination'),
4493 _('a changeset intended to be included in the destination'),
4494 _('REV')),
4494 _('REV')),
4495 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4495 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4496 ('b', 'branch', [],
4496 ('b', 'branch', [],
4497 _('a specific branch you would like to push'), _('BRANCH')),
4497 _('a specific branch you would like to push'), _('BRANCH')),
4498 ('', 'new-branch', False, _('allow pushing a new branch')),
4498 ('', 'new-branch', False, _('allow pushing a new branch')),
4499 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4499 ('', 'pushvars', [], _('variables that can be sent to server (ADVANCED)')),
4500 ('', 'publish', False, _('push the changeset as public (EXPERIMENTAL)')),
4500 ] + remoteopts,
4501 ] + remoteopts,
4501 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
4502 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
4502 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4503 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4503 helpbasic=True)
4504 helpbasic=True)
4504 def push(ui, repo, dest=None, **opts):
4505 def push(ui, repo, dest=None, **opts):
4505 """push changes to the specified destination
4506 """push changes to the specified destination
4506
4507
4507 Push changesets from the local repository to the specified
4508 Push changesets from the local repository to the specified
4508 destination.
4509 destination.
4509
4510
4510 This operation is symmetrical to pull: it is identical to a pull
4511 This operation is symmetrical to pull: it is identical to a pull
4511 in the destination repository from the current one.
4512 in the destination repository from the current one.
4512
4513
4513 By default, push will not allow creation of new heads at the
4514 By default, push will not allow creation of new heads at the
4514 destination, since multiple heads would make it unclear which head
4515 destination, since multiple heads would make it unclear which head
4515 to use. In this situation, it is recommended to pull and merge
4516 to use. In this situation, it is recommended to pull and merge
4516 before pushing.
4517 before pushing.
4517
4518
4518 Use --new-branch if you want to allow push to create a new named
4519 Use --new-branch if you want to allow push to create a new named
4519 branch that is not present at the destination. This allows you to
4520 branch that is not present at the destination. This allows you to
4520 only create a new branch without forcing other changes.
4521 only create a new branch without forcing other changes.
4521
4522
4522 .. note::
4523 .. note::
4523
4524
4524 Extra care should be taken with the -f/--force option,
4525 Extra care should be taken with the -f/--force option,
4525 which will push all new heads on all branches, an action which will
4526 which will push all new heads on all branches, an action which will
4526 almost always cause confusion for collaborators.
4527 almost always cause confusion for collaborators.
4527
4528
4528 If -r/--rev is used, the specified revision and all its ancestors
4529 If -r/--rev is used, the specified revision and all its ancestors
4529 will be pushed to the remote repository.
4530 will be pushed to the remote repository.
4530
4531
4531 If -B/--bookmark is used, the specified bookmarked revision, its
4532 If -B/--bookmark is used, the specified bookmarked revision, its
4532 ancestors, and the bookmark will be pushed to the remote
4533 ancestors, and the bookmark will be pushed to the remote
4533 repository. Specifying ``.`` is equivalent to specifying the active
4534 repository. Specifying ``.`` is equivalent to specifying the active
4534 bookmark's name.
4535 bookmark's name.
4535
4536
4536 Please see :hg:`help urls` for important details about ``ssh://``
4537 Please see :hg:`help urls` for important details about ``ssh://``
4537 URLs. If DESTINATION is omitted, a default path will be used.
4538 URLs. If DESTINATION is omitted, a default path will be used.
4538
4539
4539 .. container:: verbose
4540 .. container:: verbose
4540
4541
4541 The --pushvars option sends strings to the server that become
4542 The --pushvars option sends strings to the server that become
4542 environment variables prepended with ``HG_USERVAR_``. For example,
4543 environment variables prepended with ``HG_USERVAR_``. For example,
4543 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4544 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
4544 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4545 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
4545
4546
4546 pushvars can provide for user-overridable hooks as well as set debug
4547 pushvars can provide for user-overridable hooks as well as set debug
4547 levels. One example is having a hook that blocks commits containing
4548 levels. One example is having a hook that blocks commits containing
4548 conflict markers, but enables the user to override the hook if the file
4549 conflict markers, but enables the user to override the hook if the file
4549 is using conflict markers for testing purposes or the file format has
4550 is using conflict markers for testing purposes or the file format has
4550 strings that look like conflict markers.
4551 strings that look like conflict markers.
4551
4552
4552 By default, servers will ignore `--pushvars`. To enable it add the
4553 By default, servers will ignore `--pushvars`. To enable it add the
4553 following to your configuration file::
4554 following to your configuration file::
4554
4555
4555 [push]
4556 [push]
4556 pushvars.server = true
4557 pushvars.server = true
4557
4558
4558 Returns 0 if push was successful, 1 if nothing to push.
4559 Returns 0 if push was successful, 1 if nothing to push.
4559 """
4560 """
4560
4561
4561 opts = pycompat.byteskwargs(opts)
4562 opts = pycompat.byteskwargs(opts)
4562 if opts.get('bookmark'):
4563 if opts.get('bookmark'):
4563 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4564 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
4564 for b in opts['bookmark']:
4565 for b in opts['bookmark']:
4565 # translate -B options to -r so changesets get pushed
4566 # translate -B options to -r so changesets get pushed
4566 b = repo._bookmarks.expandname(b)
4567 b = repo._bookmarks.expandname(b)
4567 if b in repo._bookmarks:
4568 if b in repo._bookmarks:
4568 opts.setdefault('rev', []).append(b)
4569 opts.setdefault('rev', []).append(b)
4569 else:
4570 else:
4570 # if we try to push a deleted bookmark, translate it to null
4571 # if we try to push a deleted bookmark, translate it to null
4571 # this lets simultaneous -r, -b options continue working
4572 # this lets simultaneous -r, -b options continue working
4572 opts.setdefault('rev', []).append("null")
4573 opts.setdefault('rev', []).append("null")
4573
4574
4574 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4575 path = ui.paths.getpath(dest, default=('default-push', 'default'))
4575 if not path:
4576 if not path:
4576 raise error.Abort(_('default repository not configured!'),
4577 raise error.Abort(_('default repository not configured!'),
4577 hint=_("see 'hg help config.paths'"))
4578 hint=_("see 'hg help config.paths'"))
4578 dest = path.pushloc or path.loc
4579 dest = path.pushloc or path.loc
4579 branches = (path.branch, opts.get('branch') or [])
4580 branches = (path.branch, opts.get('branch') or [])
4580 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4581 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4581 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4582 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4582 other = hg.peer(repo, opts, dest)
4583 other = hg.peer(repo, opts, dest)
4583
4584
4584 if revs:
4585 if revs:
4585 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
4586 revs = [repo[r].node() for r in scmutil.revrange(repo, revs)]
4586 if not revs:
4587 if not revs:
4587 raise error.Abort(_("specified revisions evaluate to an empty set"),
4588 raise error.Abort(_("specified revisions evaluate to an empty set"),
4588 hint=_("use different revision arguments"))
4589 hint=_("use different revision arguments"))
4589 elif path.pushrev:
4590 elif path.pushrev:
4590 # It doesn't make any sense to specify ancestor revisions. So limit
4591 # It doesn't make any sense to specify ancestor revisions. So limit
4591 # to DAG heads to make discovery simpler.
4592 # to DAG heads to make discovery simpler.
4592 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4593 expr = revsetlang.formatspec('heads(%r)', path.pushrev)
4593 revs = scmutil.revrange(repo, [expr])
4594 revs = scmutil.revrange(repo, [expr])
4594 revs = [repo[rev].node() for rev in revs]
4595 revs = [repo[rev].node() for rev in revs]
4595 if not revs:
4596 if not revs:
4596 raise error.Abort(_('default push revset for path evaluates to an '
4597 raise error.Abort(_('default push revset for path evaluates to an '
4597 'empty set'))
4598 'empty set'))
4598
4599
4599 repo._subtoppath = dest
4600 repo._subtoppath = dest
4600 try:
4601 try:
4601 # push subrepos depth-first for coherent ordering
4602 # push subrepos depth-first for coherent ordering
4602 c = repo['.']
4603 c = repo['.']
4603 subs = c.substate # only repos that are committed
4604 subs = c.substate # only repos that are committed
4604 for s in sorted(subs):
4605 for s in sorted(subs):
4605 result = c.sub(s).push(opts)
4606 result = c.sub(s).push(opts)
4606 if result == 0:
4607 if result == 0:
4607 return not result
4608 return not result
4608 finally:
4609 finally:
4609 del repo._subtoppath
4610 del repo._subtoppath
4610
4611
4611 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4612 opargs = dict(opts.get('opargs', {})) # copy opargs since we may mutate it
4612 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4613 opargs.setdefault('pushvars', []).extend(opts.get('pushvars', []))
4613
4614
4614 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4615 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
4615 newbranch=opts.get('new_branch'),
4616 newbranch=opts.get('new_branch'),
4616 bookmarks=opts.get('bookmark', ()),
4617 bookmarks=opts.get('bookmark', ()),
4618 publish=opts.get('publish'),
4617 opargs=opargs)
4619 opargs=opargs)
4618
4620
4619 result = not pushop.cgresult
4621 result = not pushop.cgresult
4620
4622
4621 if pushop.bkresult is not None:
4623 if pushop.bkresult is not None:
4622 if pushop.bkresult == 2:
4624 if pushop.bkresult == 2:
4623 result = 2
4625 result = 2
4624 elif not result and pushop.bkresult:
4626 elif not result and pushop.bkresult:
4625 result = 2
4627 result = 2
4626
4628
4627 return result
4629 return result
4628
4630
4629 @command('recover', [], helpcategory=command.CATEGORY_MAINTENANCE)
4631 @command('recover', [], helpcategory=command.CATEGORY_MAINTENANCE)
4630 def recover(ui, repo):
4632 def recover(ui, repo):
4631 """roll back an interrupted transaction
4633 """roll back an interrupted transaction
4632
4634
4633 Recover from an interrupted commit or pull.
4635 Recover from an interrupted commit or pull.
4634
4636
4635 This command tries to fix the repository status after an
4637 This command tries to fix the repository status after an
4636 interrupted operation. It should only be necessary when Mercurial
4638 interrupted operation. It should only be necessary when Mercurial
4637 suggests it.
4639 suggests it.
4638
4640
4639 Returns 0 if successful, 1 if nothing to recover or verify fails.
4641 Returns 0 if successful, 1 if nothing to recover or verify fails.
4640 """
4642 """
4641 if repo.recover():
4643 if repo.recover():
4642 return hg.verify(repo)
4644 return hg.verify(repo)
4643 return 1
4645 return 1
4644
4646
4645 @command('remove|rm',
4647 @command('remove|rm',
4646 [('A', 'after', None, _('record delete for missing files')),
4648 [('A', 'after', None, _('record delete for missing files')),
4647 ('f', 'force', None,
4649 ('f', 'force', None,
4648 _('forget added files, delete modified files')),
4650 _('forget added files, delete modified files')),
4649 ] + subrepoopts + walkopts + dryrunopts,
4651 ] + subrepoopts + walkopts + dryrunopts,
4650 _('[OPTION]... FILE...'),
4652 _('[OPTION]... FILE...'),
4651 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4653 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4652 helpbasic=True, inferrepo=True)
4654 helpbasic=True, inferrepo=True)
4653 def remove(ui, repo, *pats, **opts):
4655 def remove(ui, repo, *pats, **opts):
4654 """remove the specified files on the next commit
4656 """remove the specified files on the next commit
4655
4657
4656 Schedule the indicated files for removal from the current branch.
4658 Schedule the indicated files for removal from the current branch.
4657
4659
4658 This command schedules the files to be removed at the next commit.
4660 This command schedules the files to be removed at the next commit.
4659 To undo a remove before that, see :hg:`revert`. To undo added
4661 To undo a remove before that, see :hg:`revert`. To undo added
4660 files, see :hg:`forget`.
4662 files, see :hg:`forget`.
4661
4663
4662 .. container:: verbose
4664 .. container:: verbose
4663
4665
4664 -A/--after can be used to remove only files that have already
4666 -A/--after can be used to remove only files that have already
4665 been deleted, -f/--force can be used to force deletion, and -Af
4667 been deleted, -f/--force can be used to force deletion, and -Af
4666 can be used to remove files from the next revision without
4668 can be used to remove files from the next revision without
4667 deleting them from the working directory.
4669 deleting them from the working directory.
4668
4670
4669 The following table details the behavior of remove for different
4671 The following table details the behavior of remove for different
4670 file states (columns) and option combinations (rows). The file
4672 file states (columns) and option combinations (rows). The file
4671 states are Added [A], Clean [C], Modified [M] and Missing [!]
4673 states are Added [A], Clean [C], Modified [M] and Missing [!]
4672 (as reported by :hg:`status`). The actions are Warn, Remove
4674 (as reported by :hg:`status`). The actions are Warn, Remove
4673 (from branch) and Delete (from disk):
4675 (from branch) and Delete (from disk):
4674
4676
4675 ========= == == == ==
4677 ========= == == == ==
4676 opt/state A C M !
4678 opt/state A C M !
4677 ========= == == == ==
4679 ========= == == == ==
4678 none W RD W R
4680 none W RD W R
4679 -f R RD RD R
4681 -f R RD RD R
4680 -A W W W R
4682 -A W W W R
4681 -Af R R R R
4683 -Af R R R R
4682 ========= == == == ==
4684 ========= == == == ==
4683
4685
4684 .. note::
4686 .. note::
4685
4687
4686 :hg:`remove` never deletes files in Added [A] state from the
4688 :hg:`remove` never deletes files in Added [A] state from the
4687 working directory, not even if ``--force`` is specified.
4689 working directory, not even if ``--force`` is specified.
4688
4690
4689 Returns 0 on success, 1 if any warnings encountered.
4691 Returns 0 on success, 1 if any warnings encountered.
4690 """
4692 """
4691
4693
4692 opts = pycompat.byteskwargs(opts)
4694 opts = pycompat.byteskwargs(opts)
4693 after, force = opts.get('after'), opts.get('force')
4695 after, force = opts.get('after'), opts.get('force')
4694 dryrun = opts.get('dry_run')
4696 dryrun = opts.get('dry_run')
4695 if not pats and not after:
4697 if not pats and not after:
4696 raise error.Abort(_('no files specified'))
4698 raise error.Abort(_('no files specified'))
4697
4699
4698 m = scmutil.match(repo[None], pats, opts)
4700 m = scmutil.match(repo[None], pats, opts)
4699 subrepos = opts.get('subrepos')
4701 subrepos = opts.get('subrepos')
4700 return cmdutil.remove(ui, repo, m, "", after, force, subrepos,
4702 return cmdutil.remove(ui, repo, m, "", after, force, subrepos,
4701 dryrun=dryrun)
4703 dryrun=dryrun)
4702
4704
4703 @command('rename|move|mv',
4705 @command('rename|move|mv',
4704 [('A', 'after', None, _('record a rename that has already occurred')),
4706 [('A', 'after', None, _('record a rename that has already occurred')),
4705 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4707 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4706 ] + walkopts + dryrunopts,
4708 ] + walkopts + dryrunopts,
4707 _('[OPTION]... SOURCE... DEST'),
4709 _('[OPTION]... SOURCE... DEST'),
4708 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
4710 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
4709 def rename(ui, repo, *pats, **opts):
4711 def rename(ui, repo, *pats, **opts):
4710 """rename files; equivalent of copy + remove
4712 """rename files; equivalent of copy + remove
4711
4713
4712 Mark dest as copies of sources; mark sources for deletion. If dest
4714 Mark dest as copies of sources; mark sources for deletion. If dest
4713 is a directory, copies are put in that directory. If dest is a
4715 is a directory, copies are put in that directory. If dest is a
4714 file, there can only be one source.
4716 file, there can only be one source.
4715
4717
4716 By default, this command copies the contents of files as they
4718 By default, this command copies the contents of files as they
4717 exist in the working directory. If invoked with -A/--after, the
4719 exist in the working directory. If invoked with -A/--after, the
4718 operation is recorded, but no copying is performed.
4720 operation is recorded, but no copying is performed.
4719
4721
4720 This command takes effect at the next commit. To undo a rename
4722 This command takes effect at the next commit. To undo a rename
4721 before that, see :hg:`revert`.
4723 before that, see :hg:`revert`.
4722
4724
4723 Returns 0 on success, 1 if errors are encountered.
4725 Returns 0 on success, 1 if errors are encountered.
4724 """
4726 """
4725 opts = pycompat.byteskwargs(opts)
4727 opts = pycompat.byteskwargs(opts)
4726 with repo.wlock(False):
4728 with repo.wlock(False):
4727 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4729 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4728
4730
4729 @command('resolve',
4731 @command('resolve',
4730 [('a', 'all', None, _('select all unresolved files')),
4732 [('a', 'all', None, _('select all unresolved files')),
4731 ('l', 'list', None, _('list state of files needing merge')),
4733 ('l', 'list', None, _('list state of files needing merge')),
4732 ('m', 'mark', None, _('mark files as resolved')),
4734 ('m', 'mark', None, _('mark files as resolved')),
4733 ('u', 'unmark', None, _('mark files as unresolved')),
4735 ('u', 'unmark', None, _('mark files as unresolved')),
4734 ('n', 'no-status', None, _('hide status prefix')),
4736 ('n', 'no-status', None, _('hide status prefix')),
4735 ('', 're-merge', None, _('re-merge files'))]
4737 ('', 're-merge', None, _('re-merge files'))]
4736 + mergetoolopts + walkopts + formatteropts,
4738 + mergetoolopts + walkopts + formatteropts,
4737 _('[OPTION]... [FILE]...'),
4739 _('[OPTION]... [FILE]...'),
4738 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4740 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4739 inferrepo=True)
4741 inferrepo=True)
4740 def resolve(ui, repo, *pats, **opts):
4742 def resolve(ui, repo, *pats, **opts):
4741 """redo merges or set/view the merge status of files
4743 """redo merges or set/view the merge status of files
4742
4744
4743 Merges with unresolved conflicts are often the result of
4745 Merges with unresolved conflicts are often the result of
4744 non-interactive merging using the ``internal:merge`` configuration
4746 non-interactive merging using the ``internal:merge`` configuration
4745 setting, or a command-line merge tool like ``diff3``. The resolve
4747 setting, or a command-line merge tool like ``diff3``. The resolve
4746 command is used to manage the files involved in a merge, after
4748 command is used to manage the files involved in a merge, after
4747 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4749 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4748 working directory must have two parents). See :hg:`help
4750 working directory must have two parents). See :hg:`help
4749 merge-tools` for information on configuring merge tools.
4751 merge-tools` for information on configuring merge tools.
4750
4752
4751 The resolve command can be used in the following ways:
4753 The resolve command can be used in the following ways:
4752
4754
4753 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
4755 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
4754 the specified files, discarding any previous merge attempts. Re-merging
4756 the specified files, discarding any previous merge attempts. Re-merging
4755 is not performed for files already marked as resolved. Use ``--all/-a``
4757 is not performed for files already marked as resolved. Use ``--all/-a``
4756 to select all unresolved files. ``--tool`` can be used to specify
4758 to select all unresolved files. ``--tool`` can be used to specify
4757 the merge tool used for the given files. It overrides the HGMERGE
4759 the merge tool used for the given files. It overrides the HGMERGE
4758 environment variable and your configuration files. Previous file
4760 environment variable and your configuration files. Previous file
4759 contents are saved with a ``.orig`` suffix.
4761 contents are saved with a ``.orig`` suffix.
4760
4762
4761 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4763 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4762 (e.g. after having manually fixed-up the files). The default is
4764 (e.g. after having manually fixed-up the files). The default is
4763 to mark all unresolved files.
4765 to mark all unresolved files.
4764
4766
4765 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4767 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4766 default is to mark all resolved files.
4768 default is to mark all resolved files.
4767
4769
4768 - :hg:`resolve -l`: list files which had or still have conflicts.
4770 - :hg:`resolve -l`: list files which had or still have conflicts.
4769 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4771 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4770 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4772 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
4771 the list. See :hg:`help filesets` for details.
4773 the list. See :hg:`help filesets` for details.
4772
4774
4773 .. note::
4775 .. note::
4774
4776
4775 Mercurial will not let you commit files with unresolved merge
4777 Mercurial will not let you commit files with unresolved merge
4776 conflicts. You must use :hg:`resolve -m ...` before you can
4778 conflicts. You must use :hg:`resolve -m ...` before you can
4777 commit after a conflicting merge.
4779 commit after a conflicting merge.
4778
4780
4779 .. container:: verbose
4781 .. container:: verbose
4780
4782
4781 Template:
4783 Template:
4782
4784
4783 The following keywords are supported in addition to the common template
4785 The following keywords are supported in addition to the common template
4784 keywords and functions. See also :hg:`help templates`.
4786 keywords and functions. See also :hg:`help templates`.
4785
4787
4786 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
4788 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
4787 :path: String. Repository-absolute path of the file.
4789 :path: String. Repository-absolute path of the file.
4788
4790
4789 Returns 0 on success, 1 if any files fail a resolve attempt.
4791 Returns 0 on success, 1 if any files fail a resolve attempt.
4790 """
4792 """
4791
4793
4792 opts = pycompat.byteskwargs(opts)
4794 opts = pycompat.byteskwargs(opts)
4793 confirm = ui.configbool('commands', 'resolve.confirm')
4795 confirm = ui.configbool('commands', 'resolve.confirm')
4794 flaglist = 'all mark unmark list no_status re_merge'.split()
4796 flaglist = 'all mark unmark list no_status re_merge'.split()
4795 all, mark, unmark, show, nostatus, remerge = \
4797 all, mark, unmark, show, nostatus, remerge = \
4796 [opts.get(o) for o in flaglist]
4798 [opts.get(o) for o in flaglist]
4797
4799
4798 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
4800 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
4799 if actioncount > 1:
4801 if actioncount > 1:
4800 raise error.Abort(_("too many actions specified"))
4802 raise error.Abort(_("too many actions specified"))
4801 elif (actioncount == 0
4803 elif (actioncount == 0
4802 and ui.configbool('commands', 'resolve.explicit-re-merge')):
4804 and ui.configbool('commands', 'resolve.explicit-re-merge')):
4803 hint = _('use --mark, --unmark, --list or --re-merge')
4805 hint = _('use --mark, --unmark, --list or --re-merge')
4804 raise error.Abort(_('no action specified'), hint=hint)
4806 raise error.Abort(_('no action specified'), hint=hint)
4805 if pats and all:
4807 if pats and all:
4806 raise error.Abort(_("can't specify --all and patterns"))
4808 raise error.Abort(_("can't specify --all and patterns"))
4807 if not (all or pats or show or mark or unmark):
4809 if not (all or pats or show or mark or unmark):
4808 raise error.Abort(_('no files or directories specified'),
4810 raise error.Abort(_('no files or directories specified'),
4809 hint=('use --all to re-merge all unresolved files'))
4811 hint=('use --all to re-merge all unresolved files'))
4810
4812
4811 if confirm:
4813 if confirm:
4812 if all:
4814 if all:
4813 if ui.promptchoice(_(b're-merge all unresolved files (yn)?'
4815 if ui.promptchoice(_(b're-merge all unresolved files (yn)?'
4814 b'$$ &Yes $$ &No')):
4816 b'$$ &Yes $$ &No')):
4815 raise error.Abort(_('user quit'))
4817 raise error.Abort(_('user quit'))
4816 if mark and not pats:
4818 if mark and not pats:
4817 if ui.promptchoice(_(b'mark all unresolved files as resolved (yn)?'
4819 if ui.promptchoice(_(b'mark all unresolved files as resolved (yn)?'
4818 b'$$ &Yes $$ &No')):
4820 b'$$ &Yes $$ &No')):
4819 raise error.Abort(_('user quit'))
4821 raise error.Abort(_('user quit'))
4820 if unmark and not pats:
4822 if unmark and not pats:
4821 if ui.promptchoice(_(b'mark all resolved files as unresolved (yn)?'
4823 if ui.promptchoice(_(b'mark all resolved files as unresolved (yn)?'
4822 b'$$ &Yes $$ &No')):
4824 b'$$ &Yes $$ &No')):
4823 raise error.Abort(_('user quit'))
4825 raise error.Abort(_('user quit'))
4824
4826
4825 if show:
4827 if show:
4826 ui.pager('resolve')
4828 ui.pager('resolve')
4827 fm = ui.formatter('resolve', opts)
4829 fm = ui.formatter('resolve', opts)
4828 ms = mergemod.mergestate.read(repo)
4830 ms = mergemod.mergestate.read(repo)
4829 wctx = repo[None]
4831 wctx = repo[None]
4830 m = scmutil.match(wctx, pats, opts)
4832 m = scmutil.match(wctx, pats, opts)
4831
4833
4832 # Labels and keys based on merge state. Unresolved path conflicts show
4834 # Labels and keys based on merge state. Unresolved path conflicts show
4833 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4835 # as 'P'. Resolved path conflicts show as 'R', the same as normal
4834 # resolved conflicts.
4836 # resolved conflicts.
4835 mergestateinfo = {
4837 mergestateinfo = {
4836 mergemod.MERGE_RECORD_UNRESOLVED: ('resolve.unresolved', 'U'),
4838 mergemod.MERGE_RECORD_UNRESOLVED: ('resolve.unresolved', 'U'),
4837 mergemod.MERGE_RECORD_RESOLVED: ('resolve.resolved', 'R'),
4839 mergemod.MERGE_RECORD_RESOLVED: ('resolve.resolved', 'R'),
4838 mergemod.MERGE_RECORD_UNRESOLVED_PATH: ('resolve.unresolved', 'P'),
4840 mergemod.MERGE_RECORD_UNRESOLVED_PATH: ('resolve.unresolved', 'P'),
4839 mergemod.MERGE_RECORD_RESOLVED_PATH: ('resolve.resolved', 'R'),
4841 mergemod.MERGE_RECORD_RESOLVED_PATH: ('resolve.resolved', 'R'),
4840 mergemod.MERGE_RECORD_DRIVER_RESOLVED: ('resolve.driverresolved',
4842 mergemod.MERGE_RECORD_DRIVER_RESOLVED: ('resolve.driverresolved',
4841 'D'),
4843 'D'),
4842 }
4844 }
4843
4845
4844 for f in ms:
4846 for f in ms:
4845 if not m(f):
4847 if not m(f):
4846 continue
4848 continue
4847
4849
4848 label, key = mergestateinfo[ms[f]]
4850 label, key = mergestateinfo[ms[f]]
4849 fm.startitem()
4851 fm.startitem()
4850 fm.context(ctx=wctx)
4852 fm.context(ctx=wctx)
4851 fm.condwrite(not nostatus, 'mergestatus', '%s ', key, label=label)
4853 fm.condwrite(not nostatus, 'mergestatus', '%s ', key, label=label)
4852 fm.write('path', '%s\n', f, label=label)
4854 fm.write('path', '%s\n', f, label=label)
4853 fm.end()
4855 fm.end()
4854 return 0
4856 return 0
4855
4857
4856 with repo.wlock():
4858 with repo.wlock():
4857 ms = mergemod.mergestate.read(repo)
4859 ms = mergemod.mergestate.read(repo)
4858
4860
4859 if not (ms.active() or repo.dirstate.p2() != nullid):
4861 if not (ms.active() or repo.dirstate.p2() != nullid):
4860 raise error.Abort(
4862 raise error.Abort(
4861 _('resolve command not applicable when not merging'))
4863 _('resolve command not applicable when not merging'))
4862
4864
4863 wctx = repo[None]
4865 wctx = repo[None]
4864
4866
4865 if (ms.mergedriver
4867 if (ms.mergedriver
4866 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED):
4868 and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED):
4867 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4869 proceed = mergemod.driverpreprocess(repo, ms, wctx)
4868 ms.commit()
4870 ms.commit()
4869 # allow mark and unmark to go through
4871 # allow mark and unmark to go through
4870 if not mark and not unmark and not proceed:
4872 if not mark and not unmark and not proceed:
4871 return 1
4873 return 1
4872
4874
4873 m = scmutil.match(wctx, pats, opts)
4875 m = scmutil.match(wctx, pats, opts)
4874 ret = 0
4876 ret = 0
4875 didwork = False
4877 didwork = False
4876 runconclude = False
4878 runconclude = False
4877
4879
4878 tocomplete = []
4880 tocomplete = []
4879 hasconflictmarkers = []
4881 hasconflictmarkers = []
4880 if mark:
4882 if mark:
4881 markcheck = ui.config('commands', 'resolve.mark-check')
4883 markcheck = ui.config('commands', 'resolve.mark-check')
4882 if markcheck not in ['warn', 'abort']:
4884 if markcheck not in ['warn', 'abort']:
4883 # Treat all invalid / unrecognized values as 'none'.
4885 # Treat all invalid / unrecognized values as 'none'.
4884 markcheck = False
4886 markcheck = False
4885 for f in ms:
4887 for f in ms:
4886 if not m(f):
4888 if not m(f):
4887 continue
4889 continue
4888
4890
4889 didwork = True
4891 didwork = True
4890
4892
4891 # don't let driver-resolved files be marked, and run the conclude
4893 # don't let driver-resolved files be marked, and run the conclude
4892 # step if asked to resolve
4894 # step if asked to resolve
4893 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
4895 if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
4894 exact = m.exact(f)
4896 exact = m.exact(f)
4895 if mark:
4897 if mark:
4896 if exact:
4898 if exact:
4897 ui.warn(_('not marking %s as it is driver-resolved\n')
4899 ui.warn(_('not marking %s as it is driver-resolved\n')
4898 % f)
4900 % f)
4899 elif unmark:
4901 elif unmark:
4900 if exact:
4902 if exact:
4901 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4903 ui.warn(_('not unmarking %s as it is driver-resolved\n')
4902 % f)
4904 % f)
4903 else:
4905 else:
4904 runconclude = True
4906 runconclude = True
4905 continue
4907 continue
4906
4908
4907 # path conflicts must be resolved manually
4909 # path conflicts must be resolved manually
4908 if ms[f] in (mergemod.MERGE_RECORD_UNRESOLVED_PATH,
4910 if ms[f] in (mergemod.MERGE_RECORD_UNRESOLVED_PATH,
4909 mergemod.MERGE_RECORD_RESOLVED_PATH):
4911 mergemod.MERGE_RECORD_RESOLVED_PATH):
4910 if mark:
4912 if mark:
4911 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
4913 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
4912 elif unmark:
4914 elif unmark:
4913 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
4915 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
4914 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
4916 elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
4915 ui.warn(_('%s: path conflict must be resolved manually\n')
4917 ui.warn(_('%s: path conflict must be resolved manually\n')
4916 % f)
4918 % f)
4917 continue
4919 continue
4918
4920
4919 if mark:
4921 if mark:
4920 if markcheck:
4922 if markcheck:
4921 with repo.wvfs(f) as fobj:
4923 with repo.wvfs(f) as fobj:
4922 fdata = fobj.read()
4924 fdata = fobj.read()
4923 if filemerge.hasconflictmarkers(fdata) and \
4925 if filemerge.hasconflictmarkers(fdata) and \
4924 ms[f] != mergemod.MERGE_RECORD_RESOLVED:
4926 ms[f] != mergemod.MERGE_RECORD_RESOLVED:
4925 hasconflictmarkers.append(f)
4927 hasconflictmarkers.append(f)
4926 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
4928 ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
4927 elif unmark:
4929 elif unmark:
4928 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
4930 ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
4929 else:
4931 else:
4930 # backup pre-resolve (merge uses .orig for its own purposes)
4932 # backup pre-resolve (merge uses .orig for its own purposes)
4931 a = repo.wjoin(f)
4933 a = repo.wjoin(f)
4932 try:
4934 try:
4933 util.copyfile(a, a + ".resolve")
4935 util.copyfile(a, a + ".resolve")
4934 except (IOError, OSError) as inst:
4936 except (IOError, OSError) as inst:
4935 if inst.errno != errno.ENOENT:
4937 if inst.errno != errno.ENOENT:
4936 raise
4938 raise
4937
4939
4938 try:
4940 try:
4939 # preresolve file
4941 # preresolve file
4940 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4942 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4941 with ui.configoverride(overrides, 'resolve'):
4943 with ui.configoverride(overrides, 'resolve'):
4942 complete, r = ms.preresolve(f, wctx)
4944 complete, r = ms.preresolve(f, wctx)
4943 if not complete:
4945 if not complete:
4944 tocomplete.append(f)
4946 tocomplete.append(f)
4945 elif r:
4947 elif r:
4946 ret = 1
4948 ret = 1
4947 finally:
4949 finally:
4948 ms.commit()
4950 ms.commit()
4949
4951
4950 # replace filemerge's .orig file with our resolve file, but only
4952 # replace filemerge's .orig file with our resolve file, but only
4951 # for merges that are complete
4953 # for merges that are complete
4952 if complete:
4954 if complete:
4953 try:
4955 try:
4954 util.rename(a + ".resolve",
4956 util.rename(a + ".resolve",
4955 scmutil.origpath(ui, repo, a))
4957 scmutil.origpath(ui, repo, a))
4956 except OSError as inst:
4958 except OSError as inst:
4957 if inst.errno != errno.ENOENT:
4959 if inst.errno != errno.ENOENT:
4958 raise
4960 raise
4959
4961
4960 if hasconflictmarkers:
4962 if hasconflictmarkers:
4961 ui.warn(_('warning: the following files still have conflict '
4963 ui.warn(_('warning: the following files still have conflict '
4962 'markers:\n ') + '\n '.join(hasconflictmarkers) + '\n')
4964 'markers:\n ') + '\n '.join(hasconflictmarkers) + '\n')
4963 if markcheck == 'abort' and not all and not pats:
4965 if markcheck == 'abort' and not all and not pats:
4964 raise error.Abort(_('conflict markers detected'),
4966 raise error.Abort(_('conflict markers detected'),
4965 hint=_('use --all to mark anyway'))
4967 hint=_('use --all to mark anyway'))
4966
4968
4967 for f in tocomplete:
4969 for f in tocomplete:
4968 try:
4970 try:
4969 # resolve file
4971 # resolve file
4970 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4972 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
4971 with ui.configoverride(overrides, 'resolve'):
4973 with ui.configoverride(overrides, 'resolve'):
4972 r = ms.resolve(f, wctx)
4974 r = ms.resolve(f, wctx)
4973 if r:
4975 if r:
4974 ret = 1
4976 ret = 1
4975 finally:
4977 finally:
4976 ms.commit()
4978 ms.commit()
4977
4979
4978 # replace filemerge's .orig file with our resolve file
4980 # replace filemerge's .orig file with our resolve file
4979 a = repo.wjoin(f)
4981 a = repo.wjoin(f)
4980 try:
4982 try:
4981 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4983 util.rename(a + ".resolve", scmutil.origpath(ui, repo, a))
4982 except OSError as inst:
4984 except OSError as inst:
4983 if inst.errno != errno.ENOENT:
4985 if inst.errno != errno.ENOENT:
4984 raise
4986 raise
4985
4987
4986 ms.commit()
4988 ms.commit()
4987 ms.recordactions()
4989 ms.recordactions()
4988
4990
4989 if not didwork and pats:
4991 if not didwork and pats:
4990 hint = None
4992 hint = None
4991 if not any([p for p in pats if p.find(':') >= 0]):
4993 if not any([p for p in pats if p.find(':') >= 0]):
4992 pats = ['path:%s' % p for p in pats]
4994 pats = ['path:%s' % p for p in pats]
4993 m = scmutil.match(wctx, pats, opts)
4995 m = scmutil.match(wctx, pats, opts)
4994 for f in ms:
4996 for f in ms:
4995 if not m(f):
4997 if not m(f):
4996 continue
4998 continue
4997 def flag(o):
4999 def flag(o):
4998 if o == 're_merge':
5000 if o == 're_merge':
4999 return '--re-merge '
5001 return '--re-merge '
5000 return '-%s ' % o[0:1]
5002 return '-%s ' % o[0:1]
5001 flags = ''.join([flag(o) for o in flaglist if opts.get(o)])
5003 flags = ''.join([flag(o) for o in flaglist if opts.get(o)])
5002 hint = _("(try: hg resolve %s%s)\n") % (
5004 hint = _("(try: hg resolve %s%s)\n") % (
5003 flags,
5005 flags,
5004 ' '.join(pats))
5006 ' '.join(pats))
5005 break
5007 break
5006 ui.warn(_("arguments do not match paths that need resolving\n"))
5008 ui.warn(_("arguments do not match paths that need resolving\n"))
5007 if hint:
5009 if hint:
5008 ui.warn(hint)
5010 ui.warn(hint)
5009 elif ms.mergedriver and ms.mdstate() != 's':
5011 elif ms.mergedriver and ms.mdstate() != 's':
5010 # run conclude step when either a driver-resolved file is requested
5012 # run conclude step when either a driver-resolved file is requested
5011 # or there are no driver-resolved files
5013 # or there are no driver-resolved files
5012 # we can't use 'ret' to determine whether any files are unresolved
5014 # we can't use 'ret' to determine whether any files are unresolved
5013 # because we might not have tried to resolve some
5015 # because we might not have tried to resolve some
5014 if ((runconclude or not list(ms.driverresolved()))
5016 if ((runconclude or not list(ms.driverresolved()))
5015 and not list(ms.unresolved())):
5017 and not list(ms.unresolved())):
5016 proceed = mergemod.driverconclude(repo, ms, wctx)
5018 proceed = mergemod.driverconclude(repo, ms, wctx)
5017 ms.commit()
5019 ms.commit()
5018 if not proceed:
5020 if not proceed:
5019 return 1
5021 return 1
5020
5022
5021 # Nudge users into finishing an unfinished operation
5023 # Nudge users into finishing an unfinished operation
5022 unresolvedf = list(ms.unresolved())
5024 unresolvedf = list(ms.unresolved())
5023 driverresolvedf = list(ms.driverresolved())
5025 driverresolvedf = list(ms.driverresolved())
5024 if not unresolvedf and not driverresolvedf:
5026 if not unresolvedf and not driverresolvedf:
5025 ui.status(_('(no more unresolved files)\n'))
5027 ui.status(_('(no more unresolved files)\n'))
5026 cmdutil.checkafterresolved(repo)
5028 cmdutil.checkafterresolved(repo)
5027 elif not unresolvedf:
5029 elif not unresolvedf:
5028 ui.status(_('(no more unresolved files -- '
5030 ui.status(_('(no more unresolved files -- '
5029 'run "hg resolve --all" to conclude)\n'))
5031 'run "hg resolve --all" to conclude)\n'))
5030
5032
5031 return ret
5033 return ret
5032
5034
5033 @command('revert',
5035 @command('revert',
5034 [('a', 'all', None, _('revert all changes when no arguments given')),
5036 [('a', 'all', None, _('revert all changes when no arguments given')),
5035 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5037 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5036 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5038 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5037 ('C', 'no-backup', None, _('do not save backup copies of files')),
5039 ('C', 'no-backup', None, _('do not save backup copies of files')),
5038 ('i', 'interactive', None, _('interactively select the changes')),
5040 ('i', 'interactive', None, _('interactively select the changes')),
5039 ] + walkopts + dryrunopts,
5041 ] + walkopts + dryrunopts,
5040 _('[OPTION]... [-r REV] [NAME]...'),
5042 _('[OPTION]... [-r REV] [NAME]...'),
5041 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5043 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5042 def revert(ui, repo, *pats, **opts):
5044 def revert(ui, repo, *pats, **opts):
5043 """restore files to their checkout state
5045 """restore files to their checkout state
5044
5046
5045 .. note::
5047 .. note::
5046
5048
5047 To check out earlier revisions, you should use :hg:`update REV`.
5049 To check out earlier revisions, you should use :hg:`update REV`.
5048 To cancel an uncommitted merge (and lose your changes),
5050 To cancel an uncommitted merge (and lose your changes),
5049 use :hg:`merge --abort`.
5051 use :hg:`merge --abort`.
5050
5052
5051 With no revision specified, revert the specified files or directories
5053 With no revision specified, revert the specified files or directories
5052 to the contents they had in the parent of the working directory.
5054 to the contents they had in the parent of the working directory.
5053 This restores the contents of files to an unmodified
5055 This restores the contents of files to an unmodified
5054 state and unschedules adds, removes, copies, and renames. If the
5056 state and unschedules adds, removes, copies, and renames. If the
5055 working directory has two parents, you must explicitly specify a
5057 working directory has two parents, you must explicitly specify a
5056 revision.
5058 revision.
5057
5059
5058 Using the -r/--rev or -d/--date options, revert the given files or
5060 Using the -r/--rev or -d/--date options, revert the given files or
5059 directories to their states as of a specific revision. Because
5061 directories to their states as of a specific revision. Because
5060 revert does not change the working directory parents, this will
5062 revert does not change the working directory parents, this will
5061 cause these files to appear modified. This can be helpful to "back
5063 cause these files to appear modified. This can be helpful to "back
5062 out" some or all of an earlier change. See :hg:`backout` for a
5064 out" some or all of an earlier change. See :hg:`backout` for a
5063 related method.
5065 related method.
5064
5066
5065 Modified files are saved with a .orig suffix before reverting.
5067 Modified files are saved with a .orig suffix before reverting.
5066 To disable these backups, use --no-backup. It is possible to store
5068 To disable these backups, use --no-backup. It is possible to store
5067 the backup files in a custom directory relative to the root of the
5069 the backup files in a custom directory relative to the root of the
5068 repository by setting the ``ui.origbackuppath`` configuration
5070 repository by setting the ``ui.origbackuppath`` configuration
5069 option.
5071 option.
5070
5072
5071 See :hg:`help dates` for a list of formats valid for -d/--date.
5073 See :hg:`help dates` for a list of formats valid for -d/--date.
5072
5074
5073 See :hg:`help backout` for a way to reverse the effect of an
5075 See :hg:`help backout` for a way to reverse the effect of an
5074 earlier changeset.
5076 earlier changeset.
5075
5077
5076 Returns 0 on success.
5078 Returns 0 on success.
5077 """
5079 """
5078
5080
5079 opts = pycompat.byteskwargs(opts)
5081 opts = pycompat.byteskwargs(opts)
5080 if opts.get("date"):
5082 if opts.get("date"):
5081 if opts.get("rev"):
5083 if opts.get("rev"):
5082 raise error.Abort(_("you can't specify a revision and a date"))
5084 raise error.Abort(_("you can't specify a revision and a date"))
5083 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5085 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5084
5086
5085 parent, p2 = repo.dirstate.parents()
5087 parent, p2 = repo.dirstate.parents()
5086 if not opts.get('rev') and p2 != nullid:
5088 if not opts.get('rev') and p2 != nullid:
5087 # revert after merge is a trap for new users (issue2915)
5089 # revert after merge is a trap for new users (issue2915)
5088 raise error.Abort(_('uncommitted merge with no revision specified'),
5090 raise error.Abort(_('uncommitted merge with no revision specified'),
5089 hint=_("use 'hg update' or see 'hg help revert'"))
5091 hint=_("use 'hg update' or see 'hg help revert'"))
5090
5092
5091 rev = opts.get('rev')
5093 rev = opts.get('rev')
5092 if rev:
5094 if rev:
5093 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
5095 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
5094 ctx = scmutil.revsingle(repo, rev)
5096 ctx = scmutil.revsingle(repo, rev)
5095
5097
5096 if (not (pats or opts.get('include') or opts.get('exclude') or
5098 if (not (pats or opts.get('include') or opts.get('exclude') or
5097 opts.get('all') or opts.get('interactive'))):
5099 opts.get('all') or opts.get('interactive'))):
5098 msg = _("no files or directories specified")
5100 msg = _("no files or directories specified")
5099 if p2 != nullid:
5101 if p2 != nullid:
5100 hint = _("uncommitted merge, use --all to discard all changes,"
5102 hint = _("uncommitted merge, use --all to discard all changes,"
5101 " or 'hg update -C .' to abort the merge")
5103 " or 'hg update -C .' to abort the merge")
5102 raise error.Abort(msg, hint=hint)
5104 raise error.Abort(msg, hint=hint)
5103 dirty = any(repo.status())
5105 dirty = any(repo.status())
5104 node = ctx.node()
5106 node = ctx.node()
5105 if node != parent:
5107 if node != parent:
5106 if dirty:
5108 if dirty:
5107 hint = _("uncommitted changes, use --all to discard all"
5109 hint = _("uncommitted changes, use --all to discard all"
5108 " changes, or 'hg update %d' to update") % ctx.rev()
5110 " changes, or 'hg update %d' to update") % ctx.rev()
5109 else:
5111 else:
5110 hint = _("use --all to revert all files,"
5112 hint = _("use --all to revert all files,"
5111 " or 'hg update %d' to update") % ctx.rev()
5113 " or 'hg update %d' to update") % ctx.rev()
5112 elif dirty:
5114 elif dirty:
5113 hint = _("uncommitted changes, use --all to discard all changes")
5115 hint = _("uncommitted changes, use --all to discard all changes")
5114 else:
5116 else:
5115 hint = _("use --all to revert all files")
5117 hint = _("use --all to revert all files")
5116 raise error.Abort(msg, hint=hint)
5118 raise error.Abort(msg, hint=hint)
5117
5119
5118 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats,
5120 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats,
5119 **pycompat.strkwargs(opts))
5121 **pycompat.strkwargs(opts))
5120
5122
5121 @command(
5123 @command(
5122 'rollback',
5124 'rollback',
5123 dryrunopts + [('f', 'force', False, _('ignore safety measures'))],
5125 dryrunopts + [('f', 'force', False, _('ignore safety measures'))],
5124 helpcategory=command.CATEGORY_MAINTENANCE)
5126 helpcategory=command.CATEGORY_MAINTENANCE)
5125 def rollback(ui, repo, **opts):
5127 def rollback(ui, repo, **opts):
5126 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5128 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5127
5129
5128 Please use :hg:`commit --amend` instead of rollback to correct
5130 Please use :hg:`commit --amend` instead of rollback to correct
5129 mistakes in the last commit.
5131 mistakes in the last commit.
5130
5132
5131 This command should be used with care. There is only one level of
5133 This command should be used with care. There is only one level of
5132 rollback, and there is no way to undo a rollback. It will also
5134 rollback, and there is no way to undo a rollback. It will also
5133 restore the dirstate at the time of the last transaction, losing
5135 restore the dirstate at the time of the last transaction, losing
5134 any dirstate changes since that time. This command does not alter
5136 any dirstate changes since that time. This command does not alter
5135 the working directory.
5137 the working directory.
5136
5138
5137 Transactions are used to encapsulate the effects of all commands
5139 Transactions are used to encapsulate the effects of all commands
5138 that create new changesets or propagate existing changesets into a
5140 that create new changesets or propagate existing changesets into a
5139 repository.
5141 repository.
5140
5142
5141 .. container:: verbose
5143 .. container:: verbose
5142
5144
5143 For example, the following commands are transactional, and their
5145 For example, the following commands are transactional, and their
5144 effects can be rolled back:
5146 effects can be rolled back:
5145
5147
5146 - commit
5148 - commit
5147 - import
5149 - import
5148 - pull
5150 - pull
5149 - push (with this repository as the destination)
5151 - push (with this repository as the destination)
5150 - unbundle
5152 - unbundle
5151
5153
5152 To avoid permanent data loss, rollback will refuse to rollback a
5154 To avoid permanent data loss, rollback will refuse to rollback a
5153 commit transaction if it isn't checked out. Use --force to
5155 commit transaction if it isn't checked out. Use --force to
5154 override this protection.
5156 override this protection.
5155
5157
5156 The rollback command can be entirely disabled by setting the
5158 The rollback command can be entirely disabled by setting the
5157 ``ui.rollback`` configuration setting to false. If you're here
5159 ``ui.rollback`` configuration setting to false. If you're here
5158 because you want to use rollback and it's disabled, you can
5160 because you want to use rollback and it's disabled, you can
5159 re-enable the command by setting ``ui.rollback`` to true.
5161 re-enable the command by setting ``ui.rollback`` to true.
5160
5162
5161 This command is not intended for use on public repositories. Once
5163 This command is not intended for use on public repositories. Once
5162 changes are visible for pull by other users, rolling a transaction
5164 changes are visible for pull by other users, rolling a transaction
5163 back locally is ineffective (someone else may already have pulled
5165 back locally is ineffective (someone else may already have pulled
5164 the changes). Furthermore, a race is possible with readers of the
5166 the changes). Furthermore, a race is possible with readers of the
5165 repository; for example an in-progress pull from the repository
5167 repository; for example an in-progress pull from the repository
5166 may fail if a rollback is performed.
5168 may fail if a rollback is performed.
5167
5169
5168 Returns 0 on success, 1 if no rollback data is available.
5170 Returns 0 on success, 1 if no rollback data is available.
5169 """
5171 """
5170 if not ui.configbool('ui', 'rollback'):
5172 if not ui.configbool('ui', 'rollback'):
5171 raise error.Abort(_('rollback is disabled because it is unsafe'),
5173 raise error.Abort(_('rollback is disabled because it is unsafe'),
5172 hint=('see `hg help -v rollback` for information'))
5174 hint=('see `hg help -v rollback` for information'))
5173 return repo.rollback(dryrun=opts.get(r'dry_run'),
5175 return repo.rollback(dryrun=opts.get(r'dry_run'),
5174 force=opts.get(r'force'))
5176 force=opts.get(r'force'))
5175
5177
5176 @command(
5178 @command(
5177 'root', [], intents={INTENT_READONLY},
5179 'root', [], intents={INTENT_READONLY},
5178 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5180 helpcategory=command.CATEGORY_WORKING_DIRECTORY)
5179 def root(ui, repo):
5181 def root(ui, repo):
5180 """print the root (top) of the current working directory
5182 """print the root (top) of the current working directory
5181
5183
5182 Print the root directory of the current repository.
5184 Print the root directory of the current repository.
5183
5185
5184 Returns 0 on success.
5186 Returns 0 on success.
5185 """
5187 """
5186 ui.write(repo.root + "\n")
5188 ui.write(repo.root + "\n")
5187
5189
5188 @command('serve',
5190 @command('serve',
5189 [('A', 'accesslog', '', _('name of access log file to write to'),
5191 [('A', 'accesslog', '', _('name of access log file to write to'),
5190 _('FILE')),
5192 _('FILE')),
5191 ('d', 'daemon', None, _('run server in background')),
5193 ('d', 'daemon', None, _('run server in background')),
5192 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
5194 ('', 'daemon-postexec', [], _('used internally by daemon mode')),
5193 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5195 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5194 # use string type, then we can check if something was passed
5196 # use string type, then we can check if something was passed
5195 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5197 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5196 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5198 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5197 _('ADDR')),
5199 _('ADDR')),
5198 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5200 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5199 _('PREFIX')),
5201 _('PREFIX')),
5200 ('n', 'name', '',
5202 ('n', 'name', '',
5201 _('name to show in web pages (default: working directory)'), _('NAME')),
5203 _('name to show in web pages (default: working directory)'), _('NAME')),
5202 ('', 'web-conf', '',
5204 ('', 'web-conf', '',
5203 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
5205 _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')),
5204 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5206 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5205 _('FILE')),
5207 _('FILE')),
5206 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5208 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5207 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
5209 ('', 'stdio', None, _('for remote clients (ADVANCED)')),
5208 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
5210 ('', 'cmdserver', '', _('for remote clients (ADVANCED)'), _('MODE')),
5209 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5211 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5210 ('', 'style', '', _('template style to use'), _('STYLE')),
5212 ('', 'style', '', _('template style to use'), _('STYLE')),
5211 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5213 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5212 ('', 'certificate', '', _('SSL certificate file'), _('FILE')),
5214 ('', 'certificate', '', _('SSL certificate file'), _('FILE')),
5213 ('', 'print-url', None, _('start and print only the URL'))]
5215 ('', 'print-url', None, _('start and print only the URL'))]
5214 + subrepoopts,
5216 + subrepoopts,
5215 _('[OPTION]...'),
5217 _('[OPTION]...'),
5216 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5218 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5217 helpbasic=True, optionalrepo=True)
5219 helpbasic=True, optionalrepo=True)
5218 def serve(ui, repo, **opts):
5220 def serve(ui, repo, **opts):
5219 """start stand-alone webserver
5221 """start stand-alone webserver
5220
5222
5221 Start a local HTTP repository browser and pull server. You can use
5223 Start a local HTTP repository browser and pull server. You can use
5222 this for ad-hoc sharing and browsing of repositories. It is
5224 this for ad-hoc sharing and browsing of repositories. It is
5223 recommended to use a real web server to serve a repository for
5225 recommended to use a real web server to serve a repository for
5224 longer periods of time.
5226 longer periods of time.
5225
5227
5226 Please note that the server does not implement access control.
5228 Please note that the server does not implement access control.
5227 This means that, by default, anybody can read from the server and
5229 This means that, by default, anybody can read from the server and
5228 nobody can write to it by default. Set the ``web.allow-push``
5230 nobody can write to it by default. Set the ``web.allow-push``
5229 option to ``*`` to allow everybody to push to the server. You
5231 option to ``*`` to allow everybody to push to the server. You
5230 should use a real web server if you need to authenticate users.
5232 should use a real web server if you need to authenticate users.
5231
5233
5232 By default, the server logs accesses to stdout and errors to
5234 By default, the server logs accesses to stdout and errors to
5233 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5235 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5234 files.
5236 files.
5235
5237
5236 To have the server choose a free port number to listen on, specify
5238 To have the server choose a free port number to listen on, specify
5237 a port number of 0; in this case, the server will print the port
5239 a port number of 0; in this case, the server will print the port
5238 number it uses.
5240 number it uses.
5239
5241
5240 Returns 0 on success.
5242 Returns 0 on success.
5241 """
5243 """
5242
5244
5243 opts = pycompat.byteskwargs(opts)
5245 opts = pycompat.byteskwargs(opts)
5244 if opts["stdio"] and opts["cmdserver"]:
5246 if opts["stdio"] and opts["cmdserver"]:
5245 raise error.Abort(_("cannot use --stdio with --cmdserver"))
5247 raise error.Abort(_("cannot use --stdio with --cmdserver"))
5246 if opts["print_url"] and ui.verbose:
5248 if opts["print_url"] and ui.verbose:
5247 raise error.Abort(_("cannot use --print-url with --verbose"))
5249 raise error.Abort(_("cannot use --print-url with --verbose"))
5248
5250
5249 if opts["stdio"]:
5251 if opts["stdio"]:
5250 if repo is None:
5252 if repo is None:
5251 raise error.RepoError(_("there is no Mercurial repository here"
5253 raise error.RepoError(_("there is no Mercurial repository here"
5252 " (.hg not found)"))
5254 " (.hg not found)"))
5253 s = wireprotoserver.sshserver(ui, repo)
5255 s = wireprotoserver.sshserver(ui, repo)
5254 s.serve_forever()
5256 s.serve_forever()
5255
5257
5256 service = server.createservice(ui, repo, opts)
5258 service = server.createservice(ui, repo, opts)
5257 return server.runservice(opts, initfn=service.init, runfn=service.run)
5259 return server.runservice(opts, initfn=service.init, runfn=service.run)
5258
5260
5259 _NOTTERSE = 'nothing'
5261 _NOTTERSE = 'nothing'
5260
5262
5261 @command('status|st',
5263 @command('status|st',
5262 [('A', 'all', None, _('show status of all files')),
5264 [('A', 'all', None, _('show status of all files')),
5263 ('m', 'modified', None, _('show only modified files')),
5265 ('m', 'modified', None, _('show only modified files')),
5264 ('a', 'added', None, _('show only added files')),
5266 ('a', 'added', None, _('show only added files')),
5265 ('r', 'removed', None, _('show only removed files')),
5267 ('r', 'removed', None, _('show only removed files')),
5266 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5268 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5267 ('c', 'clean', None, _('show only files without changes')),
5269 ('c', 'clean', None, _('show only files without changes')),
5268 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5270 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5269 ('i', 'ignored', None, _('show only ignored files')),
5271 ('i', 'ignored', None, _('show only ignored files')),
5270 ('n', 'no-status', None, _('hide status prefix')),
5272 ('n', 'no-status', None, _('hide status prefix')),
5271 ('t', 'terse', _NOTTERSE, _('show the terse output (EXPERIMENTAL)')),
5273 ('t', 'terse', _NOTTERSE, _('show the terse output (EXPERIMENTAL)')),
5272 ('C', 'copies', None, _('show source of copied files')),
5274 ('C', 'copies', None, _('show source of copied files')),
5273 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5275 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5274 ('', 'rev', [], _('show difference from revision'), _('REV')),
5276 ('', 'rev', [], _('show difference from revision'), _('REV')),
5275 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5277 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5276 ] + walkopts + subrepoopts + formatteropts,
5278 ] + walkopts + subrepoopts + formatteropts,
5277 _('[OPTION]... [FILE]...'),
5279 _('[OPTION]... [FILE]...'),
5278 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5280 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5279 helpbasic=True, inferrepo=True,
5281 helpbasic=True, inferrepo=True,
5280 intents={INTENT_READONLY})
5282 intents={INTENT_READONLY})
5281 def status(ui, repo, *pats, **opts):
5283 def status(ui, repo, *pats, **opts):
5282 """show changed files in the working directory
5284 """show changed files in the working directory
5283
5285
5284 Show status of files in the repository. If names are given, only
5286 Show status of files in the repository. If names are given, only
5285 files that match are shown. Files that are clean or ignored or
5287 files that match are shown. Files that are clean or ignored or
5286 the source of a copy/move operation, are not listed unless
5288 the source of a copy/move operation, are not listed unless
5287 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5289 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5288 Unless options described with "show only ..." are given, the
5290 Unless options described with "show only ..." are given, the
5289 options -mardu are used.
5291 options -mardu are used.
5290
5292
5291 Option -q/--quiet hides untracked (unknown and ignored) files
5293 Option -q/--quiet hides untracked (unknown and ignored) files
5292 unless explicitly requested with -u/--unknown or -i/--ignored.
5294 unless explicitly requested with -u/--unknown or -i/--ignored.
5293
5295
5294 .. note::
5296 .. note::
5295
5297
5296 :hg:`status` may appear to disagree with diff if permissions have
5298 :hg:`status` may appear to disagree with diff if permissions have
5297 changed or a merge has occurred. The standard diff format does
5299 changed or a merge has occurred. The standard diff format does
5298 not report permission changes and diff only reports changes
5300 not report permission changes and diff only reports changes
5299 relative to one merge parent.
5301 relative to one merge parent.
5300
5302
5301 If one revision is given, it is used as the base revision.
5303 If one revision is given, it is used as the base revision.
5302 If two revisions are given, the differences between them are
5304 If two revisions are given, the differences between them are
5303 shown. The --change option can also be used as a shortcut to list
5305 shown. The --change option can also be used as a shortcut to list
5304 the changed files of a revision from its first parent.
5306 the changed files of a revision from its first parent.
5305
5307
5306 The codes used to show the status of files are::
5308 The codes used to show the status of files are::
5307
5309
5308 M = modified
5310 M = modified
5309 A = added
5311 A = added
5310 R = removed
5312 R = removed
5311 C = clean
5313 C = clean
5312 ! = missing (deleted by non-hg command, but still tracked)
5314 ! = missing (deleted by non-hg command, but still tracked)
5313 ? = not tracked
5315 ? = not tracked
5314 I = ignored
5316 I = ignored
5315 = origin of the previous file (with --copies)
5317 = origin of the previous file (with --copies)
5316
5318
5317 .. container:: verbose
5319 .. container:: verbose
5318
5320
5319 The -t/--terse option abbreviates the output by showing only the directory
5321 The -t/--terse option abbreviates the output by showing only the directory
5320 name if all the files in it share the same status. The option takes an
5322 name if all the files in it share the same status. The option takes an
5321 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
5323 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
5322 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
5324 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
5323 for 'ignored' and 'c' for clean.
5325 for 'ignored' and 'c' for clean.
5324
5326
5325 It abbreviates only those statuses which are passed. Note that clean and
5327 It abbreviates only those statuses which are passed. Note that clean and
5326 ignored files are not displayed with '--terse ic' unless the -c/--clean
5328 ignored files are not displayed with '--terse ic' unless the -c/--clean
5327 and -i/--ignored options are also used.
5329 and -i/--ignored options are also used.
5328
5330
5329 The -v/--verbose option shows information when the repository is in an
5331 The -v/--verbose option shows information when the repository is in an
5330 unfinished merge, shelve, rebase state etc. You can have this behavior
5332 unfinished merge, shelve, rebase state etc. You can have this behavior
5331 turned on by default by enabling the ``commands.status.verbose`` option.
5333 turned on by default by enabling the ``commands.status.verbose`` option.
5332
5334
5333 You can skip displaying some of these states by setting
5335 You can skip displaying some of these states by setting
5334 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
5336 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
5335 'histedit', 'merge', 'rebase', or 'unshelve'.
5337 'histedit', 'merge', 'rebase', or 'unshelve'.
5336
5338
5337 Template:
5339 Template:
5338
5340
5339 The following keywords are supported in addition to the common template
5341 The following keywords are supported in addition to the common template
5340 keywords and functions. See also :hg:`help templates`.
5342 keywords and functions. See also :hg:`help templates`.
5341
5343
5342 :path: String. Repository-absolute path of the file.
5344 :path: String. Repository-absolute path of the file.
5343 :source: String. Repository-absolute path of the file originated from.
5345 :source: String. Repository-absolute path of the file originated from.
5344 Available if ``--copies`` is specified.
5346 Available if ``--copies`` is specified.
5345 :status: String. Character denoting file's status.
5347 :status: String. Character denoting file's status.
5346
5348
5347 Examples:
5349 Examples:
5348
5350
5349 - show changes in the working directory relative to a
5351 - show changes in the working directory relative to a
5350 changeset::
5352 changeset::
5351
5353
5352 hg status --rev 9353
5354 hg status --rev 9353
5353
5355
5354 - show changes in the working directory relative to the
5356 - show changes in the working directory relative to the
5355 current directory (see :hg:`help patterns` for more information)::
5357 current directory (see :hg:`help patterns` for more information)::
5356
5358
5357 hg status re:
5359 hg status re:
5358
5360
5359 - show all changes including copies in an existing changeset::
5361 - show all changes including copies in an existing changeset::
5360
5362
5361 hg status --copies --change 9353
5363 hg status --copies --change 9353
5362
5364
5363 - get a NUL separated list of added files, suitable for xargs::
5365 - get a NUL separated list of added files, suitable for xargs::
5364
5366
5365 hg status -an0
5367 hg status -an0
5366
5368
5367 - show more information about the repository status, abbreviating
5369 - show more information about the repository status, abbreviating
5368 added, removed, modified, deleted, and untracked paths::
5370 added, removed, modified, deleted, and untracked paths::
5369
5371
5370 hg status -v -t mardu
5372 hg status -v -t mardu
5371
5373
5372 Returns 0 on success.
5374 Returns 0 on success.
5373
5375
5374 """
5376 """
5375
5377
5376 opts = pycompat.byteskwargs(opts)
5378 opts = pycompat.byteskwargs(opts)
5377 revs = opts.get('rev')
5379 revs = opts.get('rev')
5378 change = opts.get('change')
5380 change = opts.get('change')
5379 terse = opts.get('terse')
5381 terse = opts.get('terse')
5380 if terse is _NOTTERSE:
5382 if terse is _NOTTERSE:
5381 if revs:
5383 if revs:
5382 terse = ''
5384 terse = ''
5383 else:
5385 else:
5384 terse = ui.config('commands', 'status.terse')
5386 terse = ui.config('commands', 'status.terse')
5385
5387
5386 if revs and change:
5388 if revs and change:
5387 msg = _('cannot specify --rev and --change at the same time')
5389 msg = _('cannot specify --rev and --change at the same time')
5388 raise error.Abort(msg)
5390 raise error.Abort(msg)
5389 elif revs and terse:
5391 elif revs and terse:
5390 msg = _('cannot use --terse with --rev')
5392 msg = _('cannot use --terse with --rev')
5391 raise error.Abort(msg)
5393 raise error.Abort(msg)
5392 elif change:
5394 elif change:
5393 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
5395 repo = scmutil.unhidehashlikerevs(repo, [change], 'nowarn')
5394 ctx2 = scmutil.revsingle(repo, change, None)
5396 ctx2 = scmutil.revsingle(repo, change, None)
5395 ctx1 = ctx2.p1()
5397 ctx1 = ctx2.p1()
5396 else:
5398 else:
5397 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
5399 repo = scmutil.unhidehashlikerevs(repo, revs, 'nowarn')
5398 ctx1, ctx2 = scmutil.revpair(repo, revs)
5400 ctx1, ctx2 = scmutil.revpair(repo, revs)
5399
5401
5400 if pats or ui.configbool('commands', 'status.relative'):
5402 if pats or ui.configbool('commands', 'status.relative'):
5401 cwd = repo.getcwd()
5403 cwd = repo.getcwd()
5402 else:
5404 else:
5403 cwd = ''
5405 cwd = ''
5404
5406
5405 if opts.get('print0'):
5407 if opts.get('print0'):
5406 end = '\0'
5408 end = '\0'
5407 else:
5409 else:
5408 end = '\n'
5410 end = '\n'
5409 copy = {}
5411 copy = {}
5410 states = 'modified added removed deleted unknown ignored clean'.split()
5412 states = 'modified added removed deleted unknown ignored clean'.split()
5411 show = [k for k in states if opts.get(k)]
5413 show = [k for k in states if opts.get(k)]
5412 if opts.get('all'):
5414 if opts.get('all'):
5413 show += ui.quiet and (states[:4] + ['clean']) or states
5415 show += ui.quiet and (states[:4] + ['clean']) or states
5414
5416
5415 if not show:
5417 if not show:
5416 if ui.quiet:
5418 if ui.quiet:
5417 show = states[:4]
5419 show = states[:4]
5418 else:
5420 else:
5419 show = states[:5]
5421 show = states[:5]
5420
5422
5421 m = scmutil.match(ctx2, pats, opts)
5423 m = scmutil.match(ctx2, pats, opts)
5422 if terse:
5424 if terse:
5423 # we need to compute clean and unknown to terse
5425 # we need to compute clean and unknown to terse
5424 stat = repo.status(ctx1.node(), ctx2.node(), m,
5426 stat = repo.status(ctx1.node(), ctx2.node(), m,
5425 'ignored' in show or 'i' in terse,
5427 'ignored' in show or 'i' in terse,
5426 clean=True, unknown=True,
5428 clean=True, unknown=True,
5427 listsubrepos=opts.get('subrepos'))
5429 listsubrepos=opts.get('subrepos'))
5428
5430
5429 stat = cmdutil.tersedir(stat, terse)
5431 stat = cmdutil.tersedir(stat, terse)
5430 else:
5432 else:
5431 stat = repo.status(ctx1.node(), ctx2.node(), m,
5433 stat = repo.status(ctx1.node(), ctx2.node(), m,
5432 'ignored' in show, 'clean' in show,
5434 'ignored' in show, 'clean' in show,
5433 'unknown' in show, opts.get('subrepos'))
5435 'unknown' in show, opts.get('subrepos'))
5434
5436
5435 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
5437 changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), stat)
5436
5438
5437 if (opts.get('all') or opts.get('copies')
5439 if (opts.get('all') or opts.get('copies')
5438 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5440 or ui.configbool('ui', 'statuscopies')) and not opts.get('no_status'):
5439 copy = copies.pathcopies(ctx1, ctx2, m)
5441 copy = copies.pathcopies(ctx1, ctx2, m)
5440
5442
5441 ui.pager('status')
5443 ui.pager('status')
5442 fm = ui.formatter('status', opts)
5444 fm = ui.formatter('status', opts)
5443 fmt = '%s' + end
5445 fmt = '%s' + end
5444 showchar = not opts.get('no_status')
5446 showchar = not opts.get('no_status')
5445
5447
5446 for state, char, files in changestates:
5448 for state, char, files in changestates:
5447 if state in show:
5449 if state in show:
5448 label = 'status.' + state
5450 label = 'status.' + state
5449 for f in files:
5451 for f in files:
5450 fm.startitem()
5452 fm.startitem()
5451 fm.context(ctx=ctx2)
5453 fm.context(ctx=ctx2)
5452 fm.data(path=f)
5454 fm.data(path=f)
5453 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5455 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5454 fm.plain(fmt % repo.pathto(f, cwd), label=label)
5456 fm.plain(fmt % repo.pathto(f, cwd), label=label)
5455 if f in copy:
5457 if f in copy:
5456 fm.data(source=copy[f])
5458 fm.data(source=copy[f])
5457 fm.plain((' %s' + end) % repo.pathto(copy[f], cwd),
5459 fm.plain((' %s' + end) % repo.pathto(copy[f], cwd),
5458 label='status.copied')
5460 label='status.copied')
5459
5461
5460 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
5462 if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
5461 and not ui.plain()):
5463 and not ui.plain()):
5462 cmdutil.morestatus(repo, fm)
5464 cmdutil.morestatus(repo, fm)
5463 fm.end()
5465 fm.end()
5464
5466
5465 @command('summary|sum',
5467 @command('summary|sum',
5466 [('', 'remote', None, _('check for push and pull'))],
5468 [('', 'remote', None, _('check for push and pull'))],
5467 '[--remote]',
5469 '[--remote]',
5468 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5470 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5469 helpbasic=True,
5471 helpbasic=True,
5470 intents={INTENT_READONLY})
5472 intents={INTENT_READONLY})
5471 def summary(ui, repo, **opts):
5473 def summary(ui, repo, **opts):
5472 """summarize working directory state
5474 """summarize working directory state
5473
5475
5474 This generates a brief summary of the working directory state,
5476 This generates a brief summary of the working directory state,
5475 including parents, branch, commit status, phase and available updates.
5477 including parents, branch, commit status, phase and available updates.
5476
5478
5477 With the --remote option, this will check the default paths for
5479 With the --remote option, this will check the default paths for
5478 incoming and outgoing changes. This can be time-consuming.
5480 incoming and outgoing changes. This can be time-consuming.
5479
5481
5480 Returns 0 on success.
5482 Returns 0 on success.
5481 """
5483 """
5482
5484
5483 opts = pycompat.byteskwargs(opts)
5485 opts = pycompat.byteskwargs(opts)
5484 ui.pager('summary')
5486 ui.pager('summary')
5485 ctx = repo[None]
5487 ctx = repo[None]
5486 parents = ctx.parents()
5488 parents = ctx.parents()
5487 pnode = parents[0].node()
5489 pnode = parents[0].node()
5488 marks = []
5490 marks = []
5489
5491
5490 ms = None
5492 ms = None
5491 try:
5493 try:
5492 ms = mergemod.mergestate.read(repo)
5494 ms = mergemod.mergestate.read(repo)
5493 except error.UnsupportedMergeRecords as e:
5495 except error.UnsupportedMergeRecords as e:
5494 s = ' '.join(e.recordtypes)
5496 s = ' '.join(e.recordtypes)
5495 ui.warn(
5497 ui.warn(
5496 _('warning: merge state has unsupported record types: %s\n') % s)
5498 _('warning: merge state has unsupported record types: %s\n') % s)
5497 unresolved = []
5499 unresolved = []
5498 else:
5500 else:
5499 unresolved = list(ms.unresolved())
5501 unresolved = list(ms.unresolved())
5500
5502
5501 for p in parents:
5503 for p in parents:
5502 # label with log.changeset (instead of log.parent) since this
5504 # label with log.changeset (instead of log.parent) since this
5503 # shows a working directory parent *changeset*:
5505 # shows a working directory parent *changeset*:
5504 # i18n: column positioning for "hg summary"
5506 # i18n: column positioning for "hg summary"
5505 ui.write(_('parent: %d:%s ') % (p.rev(), p),
5507 ui.write(_('parent: %d:%s ') % (p.rev(), p),
5506 label=logcmdutil.changesetlabels(p))
5508 label=logcmdutil.changesetlabels(p))
5507 ui.write(' '.join(p.tags()), label='log.tag')
5509 ui.write(' '.join(p.tags()), label='log.tag')
5508 if p.bookmarks():
5510 if p.bookmarks():
5509 marks.extend(p.bookmarks())
5511 marks.extend(p.bookmarks())
5510 if p.rev() == -1:
5512 if p.rev() == -1:
5511 if not len(repo):
5513 if not len(repo):
5512 ui.write(_(' (empty repository)'))
5514 ui.write(_(' (empty repository)'))
5513 else:
5515 else:
5514 ui.write(_(' (no revision checked out)'))
5516 ui.write(_(' (no revision checked out)'))
5515 if p.obsolete():
5517 if p.obsolete():
5516 ui.write(_(' (obsolete)'))
5518 ui.write(_(' (obsolete)'))
5517 if p.isunstable():
5519 if p.isunstable():
5518 instabilities = (ui.label(instability, 'trouble.%s' % instability)
5520 instabilities = (ui.label(instability, 'trouble.%s' % instability)
5519 for instability in p.instabilities())
5521 for instability in p.instabilities())
5520 ui.write(' ('
5522 ui.write(' ('
5521 + ', '.join(instabilities)
5523 + ', '.join(instabilities)
5522 + ')')
5524 + ')')
5523 ui.write('\n')
5525 ui.write('\n')
5524 if p.description():
5526 if p.description():
5525 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5527 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5526 label='log.summary')
5528 label='log.summary')
5527
5529
5528 branch = ctx.branch()
5530 branch = ctx.branch()
5529 bheads = repo.branchheads(branch)
5531 bheads = repo.branchheads(branch)
5530 # i18n: column positioning for "hg summary"
5532 # i18n: column positioning for "hg summary"
5531 m = _('branch: %s\n') % branch
5533 m = _('branch: %s\n') % branch
5532 if branch != 'default':
5534 if branch != 'default':
5533 ui.write(m, label='log.branch')
5535 ui.write(m, label='log.branch')
5534 else:
5536 else:
5535 ui.status(m, label='log.branch')
5537 ui.status(m, label='log.branch')
5536
5538
5537 if marks:
5539 if marks:
5538 active = repo._activebookmark
5540 active = repo._activebookmark
5539 # i18n: column positioning for "hg summary"
5541 # i18n: column positioning for "hg summary"
5540 ui.write(_('bookmarks:'), label='log.bookmark')
5542 ui.write(_('bookmarks:'), label='log.bookmark')
5541 if active is not None:
5543 if active is not None:
5542 if active in marks:
5544 if active in marks:
5543 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
5545 ui.write(' *' + active, label=bookmarks.activebookmarklabel)
5544 marks.remove(active)
5546 marks.remove(active)
5545 else:
5547 else:
5546 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
5548 ui.write(' [%s]' % active, label=bookmarks.activebookmarklabel)
5547 for m in marks:
5549 for m in marks:
5548 ui.write(' ' + m, label='log.bookmark')
5550 ui.write(' ' + m, label='log.bookmark')
5549 ui.write('\n', label='log.bookmark')
5551 ui.write('\n', label='log.bookmark')
5550
5552
5551 status = repo.status(unknown=True)
5553 status = repo.status(unknown=True)
5552
5554
5553 c = repo.dirstate.copies()
5555 c = repo.dirstate.copies()
5554 copied, renamed = [], []
5556 copied, renamed = [], []
5555 for d, s in c.iteritems():
5557 for d, s in c.iteritems():
5556 if s in status.removed:
5558 if s in status.removed:
5557 status.removed.remove(s)
5559 status.removed.remove(s)
5558 renamed.append(d)
5560 renamed.append(d)
5559 else:
5561 else:
5560 copied.append(d)
5562 copied.append(d)
5561 if d in status.added:
5563 if d in status.added:
5562 status.added.remove(d)
5564 status.added.remove(d)
5563
5565
5564 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5566 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5565
5567
5566 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5568 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5567 (ui.label(_('%d added'), 'status.added'), status.added),
5569 (ui.label(_('%d added'), 'status.added'), status.added),
5568 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5570 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5569 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5571 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5570 (ui.label(_('%d copied'), 'status.copied'), copied),
5572 (ui.label(_('%d copied'), 'status.copied'), copied),
5571 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5573 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5572 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5574 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5573 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5575 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5574 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5576 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5575 t = []
5577 t = []
5576 for l, s in labels:
5578 for l, s in labels:
5577 if s:
5579 if s:
5578 t.append(l % len(s))
5580 t.append(l % len(s))
5579
5581
5580 t = ', '.join(t)
5582 t = ', '.join(t)
5581 cleanworkdir = False
5583 cleanworkdir = False
5582
5584
5583 if repo.vfs.exists('graftstate'):
5585 if repo.vfs.exists('graftstate'):
5584 t += _(' (graft in progress)')
5586 t += _(' (graft in progress)')
5585 if repo.vfs.exists('updatestate'):
5587 if repo.vfs.exists('updatestate'):
5586 t += _(' (interrupted update)')
5588 t += _(' (interrupted update)')
5587 elif len(parents) > 1:
5589 elif len(parents) > 1:
5588 t += _(' (merge)')
5590 t += _(' (merge)')
5589 elif branch != parents[0].branch():
5591 elif branch != parents[0].branch():
5590 t += _(' (new branch)')
5592 t += _(' (new branch)')
5591 elif (parents[0].closesbranch() and
5593 elif (parents[0].closesbranch() and
5592 pnode in repo.branchheads(branch, closed=True)):
5594 pnode in repo.branchheads(branch, closed=True)):
5593 t += _(' (head closed)')
5595 t += _(' (head closed)')
5594 elif not (status.modified or status.added or status.removed or renamed or
5596 elif not (status.modified or status.added or status.removed or renamed or
5595 copied or subs):
5597 copied or subs):
5596 t += _(' (clean)')
5598 t += _(' (clean)')
5597 cleanworkdir = True
5599 cleanworkdir = True
5598 elif pnode not in bheads:
5600 elif pnode not in bheads:
5599 t += _(' (new branch head)')
5601 t += _(' (new branch head)')
5600
5602
5601 if parents:
5603 if parents:
5602 pendingphase = max(p.phase() for p in parents)
5604 pendingphase = max(p.phase() for p in parents)
5603 else:
5605 else:
5604 pendingphase = phases.public
5606 pendingphase = phases.public
5605
5607
5606 if pendingphase > phases.newcommitphase(ui):
5608 if pendingphase > phases.newcommitphase(ui):
5607 t += ' (%s)' % phases.phasenames[pendingphase]
5609 t += ' (%s)' % phases.phasenames[pendingphase]
5608
5610
5609 if cleanworkdir:
5611 if cleanworkdir:
5610 # i18n: column positioning for "hg summary"
5612 # i18n: column positioning for "hg summary"
5611 ui.status(_('commit: %s\n') % t.strip())
5613 ui.status(_('commit: %s\n') % t.strip())
5612 else:
5614 else:
5613 # i18n: column positioning for "hg summary"
5615 # i18n: column positioning for "hg summary"
5614 ui.write(_('commit: %s\n') % t.strip())
5616 ui.write(_('commit: %s\n') % t.strip())
5615
5617
5616 # all ancestors of branch heads - all ancestors of parent = new csets
5618 # all ancestors of branch heads - all ancestors of parent = new csets
5617 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5619 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5618 bheads))
5620 bheads))
5619
5621
5620 if new == 0:
5622 if new == 0:
5621 # i18n: column positioning for "hg summary"
5623 # i18n: column positioning for "hg summary"
5622 ui.status(_('update: (current)\n'))
5624 ui.status(_('update: (current)\n'))
5623 elif pnode not in bheads:
5625 elif pnode not in bheads:
5624 # i18n: column positioning for "hg summary"
5626 # i18n: column positioning for "hg summary"
5625 ui.write(_('update: %d new changesets (update)\n') % new)
5627 ui.write(_('update: %d new changesets (update)\n') % new)
5626 else:
5628 else:
5627 # i18n: column positioning for "hg summary"
5629 # i18n: column positioning for "hg summary"
5628 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5630 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5629 (new, len(bheads)))
5631 (new, len(bheads)))
5630
5632
5631 t = []
5633 t = []
5632 draft = len(repo.revs('draft()'))
5634 draft = len(repo.revs('draft()'))
5633 if draft:
5635 if draft:
5634 t.append(_('%d draft') % draft)
5636 t.append(_('%d draft') % draft)
5635 secret = len(repo.revs('secret()'))
5637 secret = len(repo.revs('secret()'))
5636 if secret:
5638 if secret:
5637 t.append(_('%d secret') % secret)
5639 t.append(_('%d secret') % secret)
5638
5640
5639 if draft or secret:
5641 if draft or secret:
5640 ui.status(_('phases: %s\n') % ', '.join(t))
5642 ui.status(_('phases: %s\n') % ', '.join(t))
5641
5643
5642 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5644 if obsolete.isenabled(repo, obsolete.createmarkersopt):
5643 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5645 for trouble in ("orphan", "contentdivergent", "phasedivergent"):
5644 numtrouble = len(repo.revs(trouble + "()"))
5646 numtrouble = len(repo.revs(trouble + "()"))
5645 # We write all the possibilities to ease translation
5647 # We write all the possibilities to ease translation
5646 troublemsg = {
5648 troublemsg = {
5647 "orphan": _("orphan: %d changesets"),
5649 "orphan": _("orphan: %d changesets"),
5648 "contentdivergent": _("content-divergent: %d changesets"),
5650 "contentdivergent": _("content-divergent: %d changesets"),
5649 "phasedivergent": _("phase-divergent: %d changesets"),
5651 "phasedivergent": _("phase-divergent: %d changesets"),
5650 }
5652 }
5651 if numtrouble > 0:
5653 if numtrouble > 0:
5652 ui.status(troublemsg[trouble] % numtrouble + "\n")
5654 ui.status(troublemsg[trouble] % numtrouble + "\n")
5653
5655
5654 cmdutil.summaryhooks(ui, repo)
5656 cmdutil.summaryhooks(ui, repo)
5655
5657
5656 if opts.get('remote'):
5658 if opts.get('remote'):
5657 needsincoming, needsoutgoing = True, True
5659 needsincoming, needsoutgoing = True, True
5658 else:
5660 else:
5659 needsincoming, needsoutgoing = False, False
5661 needsincoming, needsoutgoing = False, False
5660 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5662 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5661 if i:
5663 if i:
5662 needsincoming = True
5664 needsincoming = True
5663 if o:
5665 if o:
5664 needsoutgoing = True
5666 needsoutgoing = True
5665 if not needsincoming and not needsoutgoing:
5667 if not needsincoming and not needsoutgoing:
5666 return
5668 return
5667
5669
5668 def getincoming():
5670 def getincoming():
5669 source, branches = hg.parseurl(ui.expandpath('default'))
5671 source, branches = hg.parseurl(ui.expandpath('default'))
5670 sbranch = branches[0]
5672 sbranch = branches[0]
5671 try:
5673 try:
5672 other = hg.peer(repo, {}, source)
5674 other = hg.peer(repo, {}, source)
5673 except error.RepoError:
5675 except error.RepoError:
5674 if opts.get('remote'):
5676 if opts.get('remote'):
5675 raise
5677 raise
5676 return source, sbranch, None, None, None
5678 return source, sbranch, None, None, None
5677 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5679 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5678 if revs:
5680 if revs:
5679 revs = [other.lookup(rev) for rev in revs]
5681 revs = [other.lookup(rev) for rev in revs]
5680 ui.debug('comparing with %s\n' % util.hidepassword(source))
5682 ui.debug('comparing with %s\n' % util.hidepassword(source))
5681 repo.ui.pushbuffer()
5683 repo.ui.pushbuffer()
5682 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5684 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5683 repo.ui.popbuffer()
5685 repo.ui.popbuffer()
5684 return source, sbranch, other, commoninc, commoninc[1]
5686 return source, sbranch, other, commoninc, commoninc[1]
5685
5687
5686 if needsincoming:
5688 if needsincoming:
5687 source, sbranch, sother, commoninc, incoming = getincoming()
5689 source, sbranch, sother, commoninc, incoming = getincoming()
5688 else:
5690 else:
5689 source = sbranch = sother = commoninc = incoming = None
5691 source = sbranch = sother = commoninc = incoming = None
5690
5692
5691 def getoutgoing():
5693 def getoutgoing():
5692 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5694 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5693 dbranch = branches[0]
5695 dbranch = branches[0]
5694 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5696 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5695 if source != dest:
5697 if source != dest:
5696 try:
5698 try:
5697 dother = hg.peer(repo, {}, dest)
5699 dother = hg.peer(repo, {}, dest)
5698 except error.RepoError:
5700 except error.RepoError:
5699 if opts.get('remote'):
5701 if opts.get('remote'):
5700 raise
5702 raise
5701 return dest, dbranch, None, None
5703 return dest, dbranch, None, None
5702 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5704 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5703 elif sother is None:
5705 elif sother is None:
5704 # there is no explicit destination peer, but source one is invalid
5706 # there is no explicit destination peer, but source one is invalid
5705 return dest, dbranch, None, None
5707 return dest, dbranch, None, None
5706 else:
5708 else:
5707 dother = sother
5709 dother = sother
5708 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5710 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5709 common = None
5711 common = None
5710 else:
5712 else:
5711 common = commoninc
5713 common = commoninc
5712 if revs:
5714 if revs:
5713 revs = [repo.lookup(rev) for rev in revs]
5715 revs = [repo.lookup(rev) for rev in revs]
5714 repo.ui.pushbuffer()
5716 repo.ui.pushbuffer()
5715 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5717 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5716 commoninc=common)
5718 commoninc=common)
5717 repo.ui.popbuffer()
5719 repo.ui.popbuffer()
5718 return dest, dbranch, dother, outgoing
5720 return dest, dbranch, dother, outgoing
5719
5721
5720 if needsoutgoing:
5722 if needsoutgoing:
5721 dest, dbranch, dother, outgoing = getoutgoing()
5723 dest, dbranch, dother, outgoing = getoutgoing()
5722 else:
5724 else:
5723 dest = dbranch = dother = outgoing = None
5725 dest = dbranch = dother = outgoing = None
5724
5726
5725 if opts.get('remote'):
5727 if opts.get('remote'):
5726 t = []
5728 t = []
5727 if incoming:
5729 if incoming:
5728 t.append(_('1 or more incoming'))
5730 t.append(_('1 or more incoming'))
5729 o = outgoing.missing
5731 o = outgoing.missing
5730 if o:
5732 if o:
5731 t.append(_('%d outgoing') % len(o))
5733 t.append(_('%d outgoing') % len(o))
5732 other = dother or sother
5734 other = dother or sother
5733 if 'bookmarks' in other.listkeys('namespaces'):
5735 if 'bookmarks' in other.listkeys('namespaces'):
5734 counts = bookmarks.summary(repo, other)
5736 counts = bookmarks.summary(repo, other)
5735 if counts[0] > 0:
5737 if counts[0] > 0:
5736 t.append(_('%d incoming bookmarks') % counts[0])
5738 t.append(_('%d incoming bookmarks') % counts[0])
5737 if counts[1] > 0:
5739 if counts[1] > 0:
5738 t.append(_('%d outgoing bookmarks') % counts[1])
5740 t.append(_('%d outgoing bookmarks') % counts[1])
5739
5741
5740 if t:
5742 if t:
5741 # i18n: column positioning for "hg summary"
5743 # i18n: column positioning for "hg summary"
5742 ui.write(_('remote: %s\n') % (', '.join(t)))
5744 ui.write(_('remote: %s\n') % (', '.join(t)))
5743 else:
5745 else:
5744 # i18n: column positioning for "hg summary"
5746 # i18n: column positioning for "hg summary"
5745 ui.status(_('remote: (synced)\n'))
5747 ui.status(_('remote: (synced)\n'))
5746
5748
5747 cmdutil.summaryremotehooks(ui, repo, opts,
5749 cmdutil.summaryremotehooks(ui, repo, opts,
5748 ((source, sbranch, sother, commoninc),
5750 ((source, sbranch, sother, commoninc),
5749 (dest, dbranch, dother, outgoing)))
5751 (dest, dbranch, dother, outgoing)))
5750
5752
5751 @command('tag',
5753 @command('tag',
5752 [('f', 'force', None, _('force tag')),
5754 [('f', 'force', None, _('force tag')),
5753 ('l', 'local', None, _('make the tag local')),
5755 ('l', 'local', None, _('make the tag local')),
5754 ('r', 'rev', '', _('revision to tag'), _('REV')),
5756 ('r', 'rev', '', _('revision to tag'), _('REV')),
5755 ('', 'remove', None, _('remove a tag')),
5757 ('', 'remove', None, _('remove a tag')),
5756 # -l/--local is already there, commitopts cannot be used
5758 # -l/--local is already there, commitopts cannot be used
5757 ('e', 'edit', None, _('invoke editor on commit messages')),
5759 ('e', 'edit', None, _('invoke editor on commit messages')),
5758 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5760 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5759 ] + commitopts2,
5761 ] + commitopts2,
5760 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
5762 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
5761 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
5763 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION)
5762 def tag(ui, repo, name1, *names, **opts):
5764 def tag(ui, repo, name1, *names, **opts):
5763 """add one or more tags for the current or given revision
5765 """add one or more tags for the current or given revision
5764
5766
5765 Name a particular revision using <name>.
5767 Name a particular revision using <name>.
5766
5768
5767 Tags are used to name particular revisions of the repository and are
5769 Tags are used to name particular revisions of the repository and are
5768 very useful to compare different revisions, to go back to significant
5770 very useful to compare different revisions, to go back to significant
5769 earlier versions or to mark branch points as releases, etc. Changing
5771 earlier versions or to mark branch points as releases, etc. Changing
5770 an existing tag is normally disallowed; use -f/--force to override.
5772 an existing tag is normally disallowed; use -f/--force to override.
5771
5773
5772 If no revision is given, the parent of the working directory is
5774 If no revision is given, the parent of the working directory is
5773 used.
5775 used.
5774
5776
5775 To facilitate version control, distribution, and merging of tags,
5777 To facilitate version control, distribution, and merging of tags,
5776 they are stored as a file named ".hgtags" which is managed similarly
5778 they are stored as a file named ".hgtags" which is managed similarly
5777 to other project files and can be hand-edited if necessary. This
5779 to other project files and can be hand-edited if necessary. This
5778 also means that tagging creates a new commit. The file
5780 also means that tagging creates a new commit. The file
5779 ".hg/localtags" is used for local tags (not shared among
5781 ".hg/localtags" is used for local tags (not shared among
5780 repositories).
5782 repositories).
5781
5783
5782 Tag commits are usually made at the head of a branch. If the parent
5784 Tag commits are usually made at the head of a branch. If the parent
5783 of the working directory is not a branch head, :hg:`tag` aborts; use
5785 of the working directory is not a branch head, :hg:`tag` aborts; use
5784 -f/--force to force the tag commit to be based on a non-head
5786 -f/--force to force the tag commit to be based on a non-head
5785 changeset.
5787 changeset.
5786
5788
5787 See :hg:`help dates` for a list of formats valid for -d/--date.
5789 See :hg:`help dates` for a list of formats valid for -d/--date.
5788
5790
5789 Since tag names have priority over branch names during revision
5791 Since tag names have priority over branch names during revision
5790 lookup, using an existing branch name as a tag name is discouraged.
5792 lookup, using an existing branch name as a tag name is discouraged.
5791
5793
5792 Returns 0 on success.
5794 Returns 0 on success.
5793 """
5795 """
5794 opts = pycompat.byteskwargs(opts)
5796 opts = pycompat.byteskwargs(opts)
5795 with repo.wlock(), repo.lock():
5797 with repo.wlock(), repo.lock():
5796 rev_ = "."
5798 rev_ = "."
5797 names = [t.strip() for t in (name1,) + names]
5799 names = [t.strip() for t in (name1,) + names]
5798 if len(names) != len(set(names)):
5800 if len(names) != len(set(names)):
5799 raise error.Abort(_('tag names must be unique'))
5801 raise error.Abort(_('tag names must be unique'))
5800 for n in names:
5802 for n in names:
5801 scmutil.checknewlabel(repo, n, 'tag')
5803 scmutil.checknewlabel(repo, n, 'tag')
5802 if not n:
5804 if not n:
5803 raise error.Abort(_('tag names cannot consist entirely of '
5805 raise error.Abort(_('tag names cannot consist entirely of '
5804 'whitespace'))
5806 'whitespace'))
5805 if opts.get('rev') and opts.get('remove'):
5807 if opts.get('rev') and opts.get('remove'):
5806 raise error.Abort(_("--rev and --remove are incompatible"))
5808 raise error.Abort(_("--rev and --remove are incompatible"))
5807 if opts.get('rev'):
5809 if opts.get('rev'):
5808 rev_ = opts['rev']
5810 rev_ = opts['rev']
5809 message = opts.get('message')
5811 message = opts.get('message')
5810 if opts.get('remove'):
5812 if opts.get('remove'):
5811 if opts.get('local'):
5813 if opts.get('local'):
5812 expectedtype = 'local'
5814 expectedtype = 'local'
5813 else:
5815 else:
5814 expectedtype = 'global'
5816 expectedtype = 'global'
5815
5817
5816 for n in names:
5818 for n in names:
5817 if not repo.tagtype(n):
5819 if not repo.tagtype(n):
5818 raise error.Abort(_("tag '%s' does not exist") % n)
5820 raise error.Abort(_("tag '%s' does not exist") % n)
5819 if repo.tagtype(n) != expectedtype:
5821 if repo.tagtype(n) != expectedtype:
5820 if expectedtype == 'global':
5822 if expectedtype == 'global':
5821 raise error.Abort(_("tag '%s' is not a global tag") % n)
5823 raise error.Abort(_("tag '%s' is not a global tag") % n)
5822 else:
5824 else:
5823 raise error.Abort(_("tag '%s' is not a local tag") % n)
5825 raise error.Abort(_("tag '%s' is not a local tag") % n)
5824 rev_ = 'null'
5826 rev_ = 'null'
5825 if not message:
5827 if not message:
5826 # we don't translate commit messages
5828 # we don't translate commit messages
5827 message = 'Removed tag %s' % ', '.join(names)
5829 message = 'Removed tag %s' % ', '.join(names)
5828 elif not opts.get('force'):
5830 elif not opts.get('force'):
5829 for n in names:
5831 for n in names:
5830 if n in repo.tags():
5832 if n in repo.tags():
5831 raise error.Abort(_("tag '%s' already exists "
5833 raise error.Abort(_("tag '%s' already exists "
5832 "(use -f to force)") % n)
5834 "(use -f to force)") % n)
5833 if not opts.get('local'):
5835 if not opts.get('local'):
5834 p1, p2 = repo.dirstate.parents()
5836 p1, p2 = repo.dirstate.parents()
5835 if p2 != nullid:
5837 if p2 != nullid:
5836 raise error.Abort(_('uncommitted merge'))
5838 raise error.Abort(_('uncommitted merge'))
5837 bheads = repo.branchheads()
5839 bheads = repo.branchheads()
5838 if not opts.get('force') and bheads and p1 not in bheads:
5840 if not opts.get('force') and bheads and p1 not in bheads:
5839 raise error.Abort(_('working directory is not at a branch head '
5841 raise error.Abort(_('working directory is not at a branch head '
5840 '(use -f to force)'))
5842 '(use -f to force)'))
5841 node = scmutil.revsingle(repo, rev_).node()
5843 node = scmutil.revsingle(repo, rev_).node()
5842
5844
5843 if not message:
5845 if not message:
5844 # we don't translate commit messages
5846 # we don't translate commit messages
5845 message = ('Added tag %s for changeset %s' %
5847 message = ('Added tag %s for changeset %s' %
5846 (', '.join(names), short(node)))
5848 (', '.join(names), short(node)))
5847
5849
5848 date = opts.get('date')
5850 date = opts.get('date')
5849 if date:
5851 if date:
5850 date = dateutil.parsedate(date)
5852 date = dateutil.parsedate(date)
5851
5853
5852 if opts.get('remove'):
5854 if opts.get('remove'):
5853 editform = 'tag.remove'
5855 editform = 'tag.remove'
5854 else:
5856 else:
5855 editform = 'tag.add'
5857 editform = 'tag.add'
5856 editor = cmdutil.getcommiteditor(editform=editform,
5858 editor = cmdutil.getcommiteditor(editform=editform,
5857 **pycompat.strkwargs(opts))
5859 **pycompat.strkwargs(opts))
5858
5860
5859 # don't allow tagging the null rev
5861 # don't allow tagging the null rev
5860 if (not opts.get('remove') and
5862 if (not opts.get('remove') and
5861 scmutil.revsingle(repo, rev_).rev() == nullrev):
5863 scmutil.revsingle(repo, rev_).rev() == nullrev):
5862 raise error.Abort(_("cannot tag null revision"))
5864 raise error.Abort(_("cannot tag null revision"))
5863
5865
5864 tagsmod.tag(repo, names, node, message, opts.get('local'),
5866 tagsmod.tag(repo, names, node, message, opts.get('local'),
5865 opts.get('user'), date, editor=editor)
5867 opts.get('user'), date, editor=editor)
5866
5868
5867 @command(
5869 @command(
5868 'tags', formatteropts, '',
5870 'tags', formatteropts, '',
5869 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5871 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5870 intents={INTENT_READONLY})
5872 intents={INTENT_READONLY})
5871 def tags(ui, repo, **opts):
5873 def tags(ui, repo, **opts):
5872 """list repository tags
5874 """list repository tags
5873
5875
5874 This lists both regular and local tags. When the -v/--verbose
5876 This lists both regular and local tags. When the -v/--verbose
5875 switch is used, a third column "local" is printed for local tags.
5877 switch is used, a third column "local" is printed for local tags.
5876 When the -q/--quiet switch is used, only the tag name is printed.
5878 When the -q/--quiet switch is used, only the tag name is printed.
5877
5879
5878 .. container:: verbose
5880 .. container:: verbose
5879
5881
5880 Template:
5882 Template:
5881
5883
5882 The following keywords are supported in addition to the common template
5884 The following keywords are supported in addition to the common template
5883 keywords and functions such as ``{tag}``. See also
5885 keywords and functions such as ``{tag}``. See also
5884 :hg:`help templates`.
5886 :hg:`help templates`.
5885
5887
5886 :type: String. ``local`` for local tags.
5888 :type: String. ``local`` for local tags.
5887
5889
5888 Returns 0 on success.
5890 Returns 0 on success.
5889 """
5891 """
5890
5892
5891 opts = pycompat.byteskwargs(opts)
5893 opts = pycompat.byteskwargs(opts)
5892 ui.pager('tags')
5894 ui.pager('tags')
5893 fm = ui.formatter('tags', opts)
5895 fm = ui.formatter('tags', opts)
5894 hexfunc = fm.hexfunc
5896 hexfunc = fm.hexfunc
5895 tagtype = ""
5897 tagtype = ""
5896
5898
5897 for t, n in reversed(repo.tagslist()):
5899 for t, n in reversed(repo.tagslist()):
5898 hn = hexfunc(n)
5900 hn = hexfunc(n)
5899 label = 'tags.normal'
5901 label = 'tags.normal'
5900 tagtype = ''
5902 tagtype = ''
5901 if repo.tagtype(t) == 'local':
5903 if repo.tagtype(t) == 'local':
5902 label = 'tags.local'
5904 label = 'tags.local'
5903 tagtype = 'local'
5905 tagtype = 'local'
5904
5906
5905 fm.startitem()
5907 fm.startitem()
5906 fm.context(repo=repo)
5908 fm.context(repo=repo)
5907 fm.write('tag', '%s', t, label=label)
5909 fm.write('tag', '%s', t, label=label)
5908 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5910 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5909 fm.condwrite(not ui.quiet, 'rev node', fmt,
5911 fm.condwrite(not ui.quiet, 'rev node', fmt,
5910 repo.changelog.rev(n), hn, label=label)
5912 repo.changelog.rev(n), hn, label=label)
5911 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5913 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5912 tagtype, label=label)
5914 tagtype, label=label)
5913 fm.plain('\n')
5915 fm.plain('\n')
5914 fm.end()
5916 fm.end()
5915
5917
5916 @command('tip',
5918 @command('tip',
5917 [('p', 'patch', None, _('show patch')),
5919 [('p', 'patch', None, _('show patch')),
5918 ('g', 'git', None, _('use git extended diff format')),
5920 ('g', 'git', None, _('use git extended diff format')),
5919 ] + templateopts,
5921 ] + templateopts,
5920 _('[-p] [-g]'),
5922 _('[-p] [-g]'),
5921 helpcategory=command.CATEGORY_CHANGE_NAVIGATION)
5923 helpcategory=command.CATEGORY_CHANGE_NAVIGATION)
5922 def tip(ui, repo, **opts):
5924 def tip(ui, repo, **opts):
5923 """show the tip revision (DEPRECATED)
5925 """show the tip revision (DEPRECATED)
5924
5926
5925 The tip revision (usually just called the tip) is the changeset
5927 The tip revision (usually just called the tip) is the changeset
5926 most recently added to the repository (and therefore the most
5928 most recently added to the repository (and therefore the most
5927 recently changed head).
5929 recently changed head).
5928
5930
5929 If you have just made a commit, that commit will be the tip. If
5931 If you have just made a commit, that commit will be the tip. If
5930 you have just pulled changes from another repository, the tip of
5932 you have just pulled changes from another repository, the tip of
5931 that repository becomes the current tip. The "tip" tag is special
5933 that repository becomes the current tip. The "tip" tag is special
5932 and cannot be renamed or assigned to a different changeset.
5934 and cannot be renamed or assigned to a different changeset.
5933
5935
5934 This command is deprecated, please use :hg:`heads` instead.
5936 This command is deprecated, please use :hg:`heads` instead.
5935
5937
5936 Returns 0 on success.
5938 Returns 0 on success.
5937 """
5939 """
5938 opts = pycompat.byteskwargs(opts)
5940 opts = pycompat.byteskwargs(opts)
5939 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5941 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5940 displayer.show(repo['tip'])
5942 displayer.show(repo['tip'])
5941 displayer.close()
5943 displayer.close()
5942
5944
5943 @command('unbundle',
5945 @command('unbundle',
5944 [('u', 'update', None,
5946 [('u', 'update', None,
5945 _('update to new branch head if changesets were unbundled'))],
5947 _('update to new branch head if changesets were unbundled'))],
5946 _('[-u] FILE...'),
5948 _('[-u] FILE...'),
5947 helpcategory=command.CATEGORY_IMPORT_EXPORT)
5949 helpcategory=command.CATEGORY_IMPORT_EXPORT)
5948 def unbundle(ui, repo, fname1, *fnames, **opts):
5950 def unbundle(ui, repo, fname1, *fnames, **opts):
5949 """apply one or more bundle files
5951 """apply one or more bundle files
5950
5952
5951 Apply one or more bundle files generated by :hg:`bundle`.
5953 Apply one or more bundle files generated by :hg:`bundle`.
5952
5954
5953 Returns 0 on success, 1 if an update has unresolved files.
5955 Returns 0 on success, 1 if an update has unresolved files.
5954 """
5956 """
5955 fnames = (fname1,) + fnames
5957 fnames = (fname1,) + fnames
5956
5958
5957 with repo.lock():
5959 with repo.lock():
5958 for fname in fnames:
5960 for fname in fnames:
5959 f = hg.openpath(ui, fname)
5961 f = hg.openpath(ui, fname)
5960 gen = exchange.readbundle(ui, f, fname)
5962 gen = exchange.readbundle(ui, f, fname)
5961 if isinstance(gen, streamclone.streamcloneapplier):
5963 if isinstance(gen, streamclone.streamcloneapplier):
5962 raise error.Abort(
5964 raise error.Abort(
5963 _('packed bundles cannot be applied with '
5965 _('packed bundles cannot be applied with '
5964 '"hg unbundle"'),
5966 '"hg unbundle"'),
5965 hint=_('use "hg debugapplystreamclonebundle"'))
5967 hint=_('use "hg debugapplystreamclonebundle"'))
5966 url = 'bundle:' + fname
5968 url = 'bundle:' + fname
5967 try:
5969 try:
5968 txnname = 'unbundle'
5970 txnname = 'unbundle'
5969 if not isinstance(gen, bundle2.unbundle20):
5971 if not isinstance(gen, bundle2.unbundle20):
5970 txnname = 'unbundle\n%s' % util.hidepassword(url)
5972 txnname = 'unbundle\n%s' % util.hidepassword(url)
5971 with repo.transaction(txnname) as tr:
5973 with repo.transaction(txnname) as tr:
5972 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5974 op = bundle2.applybundle(repo, gen, tr, source='unbundle',
5973 url=url)
5975 url=url)
5974 except error.BundleUnknownFeatureError as exc:
5976 except error.BundleUnknownFeatureError as exc:
5975 raise error.Abort(
5977 raise error.Abort(
5976 _('%s: unknown bundle feature, %s') % (fname, exc),
5978 _('%s: unknown bundle feature, %s') % (fname, exc),
5977 hint=_("see https://mercurial-scm.org/"
5979 hint=_("see https://mercurial-scm.org/"
5978 "wiki/BundleFeature for more "
5980 "wiki/BundleFeature for more "
5979 "information"))
5981 "information"))
5980 modheads = bundle2.combinechangegroupresults(op)
5982 modheads = bundle2.combinechangegroupresults(op)
5981
5983
5982 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5984 return postincoming(ui, repo, modheads, opts.get(r'update'), None, None)
5983
5985
5984 @command('update|up|checkout|co',
5986 @command('update|up|checkout|co',
5985 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5987 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5986 ('c', 'check', None, _('require clean working directory')),
5988 ('c', 'check', None, _('require clean working directory')),
5987 ('m', 'merge', None, _('merge uncommitted changes')),
5989 ('m', 'merge', None, _('merge uncommitted changes')),
5988 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5990 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5989 ('r', 'rev', '', _('revision'), _('REV'))
5991 ('r', 'rev', '', _('revision'), _('REV'))
5990 ] + mergetoolopts,
5992 ] + mergetoolopts,
5991 _('[-C|-c|-m] [-d DATE] [[-r] REV]'),
5993 _('[-C|-c|-m] [-d DATE] [[-r] REV]'),
5992 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5994 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5993 helpbasic=True)
5995 helpbasic=True)
5994 def update(ui, repo, node=None, **opts):
5996 def update(ui, repo, node=None, **opts):
5995 """update working directory (or switch revisions)
5997 """update working directory (or switch revisions)
5996
5998
5997 Update the repository's working directory to the specified
5999 Update the repository's working directory to the specified
5998 changeset. If no changeset is specified, update to the tip of the
6000 changeset. If no changeset is specified, update to the tip of the
5999 current named branch and move the active bookmark (see :hg:`help
6001 current named branch and move the active bookmark (see :hg:`help
6000 bookmarks`).
6002 bookmarks`).
6001
6003
6002 Update sets the working directory's parent revision to the specified
6004 Update sets the working directory's parent revision to the specified
6003 changeset (see :hg:`help parents`).
6005 changeset (see :hg:`help parents`).
6004
6006
6005 If the changeset is not a descendant or ancestor of the working
6007 If the changeset is not a descendant or ancestor of the working
6006 directory's parent and there are uncommitted changes, the update is
6008 directory's parent and there are uncommitted changes, the update is
6007 aborted. With the -c/--check option, the working directory is checked
6009 aborted. With the -c/--check option, the working directory is checked
6008 for uncommitted changes; if none are found, the working directory is
6010 for uncommitted changes; if none are found, the working directory is
6009 updated to the specified changeset.
6011 updated to the specified changeset.
6010
6012
6011 .. container:: verbose
6013 .. container:: verbose
6012
6014
6013 The -C/--clean, -c/--check, and -m/--merge options control what
6015 The -C/--clean, -c/--check, and -m/--merge options control what
6014 happens if the working directory contains uncommitted changes.
6016 happens if the working directory contains uncommitted changes.
6015 At most of one of them can be specified.
6017 At most of one of them can be specified.
6016
6018
6017 1. If no option is specified, and if
6019 1. If no option is specified, and if
6018 the requested changeset is an ancestor or descendant of
6020 the requested changeset is an ancestor or descendant of
6019 the working directory's parent, the uncommitted changes
6021 the working directory's parent, the uncommitted changes
6020 are merged into the requested changeset and the merged
6022 are merged into the requested changeset and the merged
6021 result is left uncommitted. If the requested changeset is
6023 result is left uncommitted. If the requested changeset is
6022 not an ancestor or descendant (that is, it is on another
6024 not an ancestor or descendant (that is, it is on another
6023 branch), the update is aborted and the uncommitted changes
6025 branch), the update is aborted and the uncommitted changes
6024 are preserved.
6026 are preserved.
6025
6027
6026 2. With the -m/--merge option, the update is allowed even if the
6028 2. With the -m/--merge option, the update is allowed even if the
6027 requested changeset is not an ancestor or descendant of
6029 requested changeset is not an ancestor or descendant of
6028 the working directory's parent.
6030 the working directory's parent.
6029
6031
6030 3. With the -c/--check option, the update is aborted and the
6032 3. With the -c/--check option, the update is aborted and the
6031 uncommitted changes are preserved.
6033 uncommitted changes are preserved.
6032
6034
6033 4. With the -C/--clean option, uncommitted changes are discarded and
6035 4. With the -C/--clean option, uncommitted changes are discarded and
6034 the working directory is updated to the requested changeset.
6036 the working directory is updated to the requested changeset.
6035
6037
6036 To cancel an uncommitted merge (and lose your changes), use
6038 To cancel an uncommitted merge (and lose your changes), use
6037 :hg:`merge --abort`.
6039 :hg:`merge --abort`.
6038
6040
6039 Use null as the changeset to remove the working directory (like
6041 Use null as the changeset to remove the working directory (like
6040 :hg:`clone -U`).
6042 :hg:`clone -U`).
6041
6043
6042 If you want to revert just one file to an older revision, use
6044 If you want to revert just one file to an older revision, use
6043 :hg:`revert [-r REV] NAME`.
6045 :hg:`revert [-r REV] NAME`.
6044
6046
6045 See :hg:`help dates` for a list of formats valid for -d/--date.
6047 See :hg:`help dates` for a list of formats valid for -d/--date.
6046
6048
6047 Returns 0 on success, 1 if there are unresolved files.
6049 Returns 0 on success, 1 if there are unresolved files.
6048 """
6050 """
6049 rev = opts.get(r'rev')
6051 rev = opts.get(r'rev')
6050 date = opts.get(r'date')
6052 date = opts.get(r'date')
6051 clean = opts.get(r'clean')
6053 clean = opts.get(r'clean')
6052 check = opts.get(r'check')
6054 check = opts.get(r'check')
6053 merge = opts.get(r'merge')
6055 merge = opts.get(r'merge')
6054 if rev and node:
6056 if rev and node:
6055 raise error.Abort(_("please specify just one revision"))
6057 raise error.Abort(_("please specify just one revision"))
6056
6058
6057 if ui.configbool('commands', 'update.requiredest'):
6059 if ui.configbool('commands', 'update.requiredest'):
6058 if not node and not rev and not date:
6060 if not node and not rev and not date:
6059 raise error.Abort(_('you must specify a destination'),
6061 raise error.Abort(_('you must specify a destination'),
6060 hint=_('for example: hg update ".::"'))
6062 hint=_('for example: hg update ".::"'))
6061
6063
6062 if rev is None or rev == '':
6064 if rev is None or rev == '':
6063 rev = node
6065 rev = node
6064
6066
6065 if date and rev is not None:
6067 if date and rev is not None:
6066 raise error.Abort(_("you can't specify a revision and a date"))
6068 raise error.Abort(_("you can't specify a revision and a date"))
6067
6069
6068 if len([x for x in (clean, check, merge) if x]) > 1:
6070 if len([x for x in (clean, check, merge) if x]) > 1:
6069 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
6071 raise error.Abort(_("can only specify one of -C/--clean, -c/--check, "
6070 "or -m/--merge"))
6072 "or -m/--merge"))
6071
6073
6072 updatecheck = None
6074 updatecheck = None
6073 if check:
6075 if check:
6074 updatecheck = 'abort'
6076 updatecheck = 'abort'
6075 elif merge:
6077 elif merge:
6076 updatecheck = 'none'
6078 updatecheck = 'none'
6077
6079
6078 with repo.wlock():
6080 with repo.wlock():
6079 cmdutil.clearunfinished(repo)
6081 cmdutil.clearunfinished(repo)
6080
6082
6081 if date:
6083 if date:
6082 rev = cmdutil.finddate(ui, repo, date)
6084 rev = cmdutil.finddate(ui, repo, date)
6083
6085
6084 # if we defined a bookmark, we have to remember the original name
6086 # if we defined a bookmark, we have to remember the original name
6085 brev = rev
6087 brev = rev
6086 if rev:
6088 if rev:
6087 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
6089 repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
6088 ctx = scmutil.revsingle(repo, rev, rev)
6090 ctx = scmutil.revsingle(repo, rev, rev)
6089 rev = ctx.rev()
6091 rev = ctx.rev()
6090 hidden = ctx.hidden()
6092 hidden = ctx.hidden()
6091 overrides = {('ui', 'forcemerge'): opts.get(r'tool', '')}
6093 overrides = {('ui', 'forcemerge'): opts.get(r'tool', '')}
6092 with ui.configoverride(overrides, 'update'):
6094 with ui.configoverride(overrides, 'update'):
6093 ret = hg.updatetotally(ui, repo, rev, brev, clean=clean,
6095 ret = hg.updatetotally(ui, repo, rev, brev, clean=clean,
6094 updatecheck=updatecheck)
6096 updatecheck=updatecheck)
6095 if hidden:
6097 if hidden:
6096 ctxstr = ctx.hex()[:12]
6098 ctxstr = ctx.hex()[:12]
6097 ui.warn(_("updated to hidden changeset %s\n") % ctxstr)
6099 ui.warn(_("updated to hidden changeset %s\n") % ctxstr)
6098
6100
6099 if ctx.obsolete():
6101 if ctx.obsolete():
6100 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
6102 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
6101 ui.warn("(%s)\n" % obsfatemsg)
6103 ui.warn("(%s)\n" % obsfatemsg)
6102 return ret
6104 return ret
6103
6105
6104 @command('verify', [], helpcategory=command.CATEGORY_MAINTENANCE)
6106 @command('verify', [], helpcategory=command.CATEGORY_MAINTENANCE)
6105 def verify(ui, repo):
6107 def verify(ui, repo):
6106 """verify the integrity of the repository
6108 """verify the integrity of the repository
6107
6109
6108 Verify the integrity of the current repository.
6110 Verify the integrity of the current repository.
6109
6111
6110 This will perform an extensive check of the repository's
6112 This will perform an extensive check of the repository's
6111 integrity, validating the hashes and checksums of each entry in
6113 integrity, validating the hashes and checksums of each entry in
6112 the changelog, manifest, and tracked files, as well as the
6114 the changelog, manifest, and tracked files, as well as the
6113 integrity of their crosslinks and indices.
6115 integrity of their crosslinks and indices.
6114
6116
6115 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
6117 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
6116 for more information about recovery from corruption of the
6118 for more information about recovery from corruption of the
6117 repository.
6119 repository.
6118
6120
6119 Returns 0 on success, 1 if errors are encountered.
6121 Returns 0 on success, 1 if errors are encountered.
6120 """
6122 """
6121 return hg.verify(repo)
6123 return hg.verify(repo)
6122
6124
6123 @command(
6125 @command(
6124 'version', [] + formatteropts, helpcategory=command.CATEGORY_HELP,
6126 'version', [] + formatteropts, helpcategory=command.CATEGORY_HELP,
6125 norepo=True, intents={INTENT_READONLY})
6127 norepo=True, intents={INTENT_READONLY})
6126 def version_(ui, **opts):
6128 def version_(ui, **opts):
6127 """output version and copyright information
6129 """output version and copyright information
6128
6130
6129 .. container:: verbose
6131 .. container:: verbose
6130
6132
6131 Template:
6133 Template:
6132
6134
6133 The following keywords are supported. See also :hg:`help templates`.
6135 The following keywords are supported. See also :hg:`help templates`.
6134
6136
6135 :extensions: List of extensions.
6137 :extensions: List of extensions.
6136 :ver: String. Version number.
6138 :ver: String. Version number.
6137
6139
6138 And each entry of ``{extensions}`` provides the following sub-keywords
6140 And each entry of ``{extensions}`` provides the following sub-keywords
6139 in addition to ``{ver}``.
6141 in addition to ``{ver}``.
6140
6142
6141 :bundled: Boolean. True if included in the release.
6143 :bundled: Boolean. True if included in the release.
6142 :name: String. Extension name.
6144 :name: String. Extension name.
6143 """
6145 """
6144 opts = pycompat.byteskwargs(opts)
6146 opts = pycompat.byteskwargs(opts)
6145 if ui.verbose:
6147 if ui.verbose:
6146 ui.pager('version')
6148 ui.pager('version')
6147 fm = ui.formatter("version", opts)
6149 fm = ui.formatter("version", opts)
6148 fm.startitem()
6150 fm.startitem()
6149 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
6151 fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"),
6150 util.version())
6152 util.version())
6151 license = _(
6153 license = _(
6152 "(see https://mercurial-scm.org for more information)\n"
6154 "(see https://mercurial-scm.org for more information)\n"
6153 "\nCopyright (C) 2005-2018 Matt Mackall and others\n"
6155 "\nCopyright (C) 2005-2018 Matt Mackall and others\n"
6154 "This is free software; see the source for copying conditions. "
6156 "This is free software; see the source for copying conditions. "
6155 "There is NO\nwarranty; "
6157 "There is NO\nwarranty; "
6156 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6158 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6157 )
6159 )
6158 if not ui.quiet:
6160 if not ui.quiet:
6159 fm.plain(license)
6161 fm.plain(license)
6160
6162
6161 if ui.verbose:
6163 if ui.verbose:
6162 fm.plain(_("\nEnabled extensions:\n\n"))
6164 fm.plain(_("\nEnabled extensions:\n\n"))
6163 # format names and versions into columns
6165 # format names and versions into columns
6164 names = []
6166 names = []
6165 vers = []
6167 vers = []
6166 isinternals = []
6168 isinternals = []
6167 for name, module in extensions.extensions():
6169 for name, module in extensions.extensions():
6168 names.append(name)
6170 names.append(name)
6169 vers.append(extensions.moduleversion(module) or None)
6171 vers.append(extensions.moduleversion(module) or None)
6170 isinternals.append(extensions.ismoduleinternal(module))
6172 isinternals.append(extensions.ismoduleinternal(module))
6171 fn = fm.nested("extensions", tmpl='{name}\n')
6173 fn = fm.nested("extensions", tmpl='{name}\n')
6172 if names:
6174 if names:
6173 namefmt = " %%-%ds " % max(len(n) for n in names)
6175 namefmt = " %%-%ds " % max(len(n) for n in names)
6174 places = [_("external"), _("internal")]
6176 places = [_("external"), _("internal")]
6175 for n, v, p in zip(names, vers, isinternals):
6177 for n, v, p in zip(names, vers, isinternals):
6176 fn.startitem()
6178 fn.startitem()
6177 fn.condwrite(ui.verbose, "name", namefmt, n)
6179 fn.condwrite(ui.verbose, "name", namefmt, n)
6178 if ui.verbose:
6180 if ui.verbose:
6179 fn.plain("%s " % places[p])
6181 fn.plain("%s " % places[p])
6180 fn.data(bundled=p)
6182 fn.data(bundled=p)
6181 fn.condwrite(ui.verbose and v, "ver", "%s", v)
6183 fn.condwrite(ui.verbose and v, "ver", "%s", v)
6182 if ui.verbose:
6184 if ui.verbose:
6183 fn.plain("\n")
6185 fn.plain("\n")
6184 fn.end()
6186 fn.end()
6185 fm.end()
6187 fm.end()
6186
6188
6187 def loadcmdtable(ui, name, cmdtable):
6189 def loadcmdtable(ui, name, cmdtable):
6188 """Load command functions from specified cmdtable
6190 """Load command functions from specified cmdtable
6189 """
6191 """
6190 cmdtable = cmdtable.copy()
6192 cmdtable = cmdtable.copy()
6191 for cmd in list(cmdtable):
6193 for cmd in list(cmdtable):
6192 if not cmd.startswith('^'):
6194 if not cmd.startswith('^'):
6193 continue
6195 continue
6194 ui.deprecwarn("old-style command registration '%s' in extension '%s'"
6196 ui.deprecwarn("old-style command registration '%s' in extension '%s'"
6195 % (cmd, name), '4.8')
6197 % (cmd, name), '4.8')
6196 entry = cmdtable.pop(cmd)
6198 entry = cmdtable.pop(cmd)
6197 entry[0].helpbasic = True
6199 entry[0].helpbasic = True
6198 cmdtable[cmd[1:]] = entry
6200 cmdtable[cmd[1:]] = entry
6199
6201
6200 overrides = [cmd for cmd in cmdtable if cmd in table]
6202 overrides = [cmd for cmd in cmdtable if cmd in table]
6201 if overrides:
6203 if overrides:
6202 ui.warn(_("extension '%s' overrides commands: %s\n")
6204 ui.warn(_("extension '%s' overrides commands: %s\n")
6203 % (name, " ".join(overrides)))
6205 % (name, " ".join(overrides)))
6204 table.update(cmdtable)
6206 table.update(cmdtable)
@@ -1,2665 +1,2670 b''
1 # exchange.py - utility to exchange data between repos.
1 # exchange.py - utility to exchange data between repos.
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 collections
10 import collections
11 import hashlib
11 import hashlib
12
12
13 from .i18n import _
13 from .i18n import _
14 from .node import (
14 from .node import (
15 bin,
15 bin,
16 hex,
16 hex,
17 nullid,
17 nullid,
18 nullrev,
18 nullrev,
19 )
19 )
20 from .thirdparty import (
20 from .thirdparty import (
21 attr,
21 attr,
22 )
22 )
23 from . import (
23 from . import (
24 bookmarks as bookmod,
24 bookmarks as bookmod,
25 bundle2,
25 bundle2,
26 changegroup,
26 changegroup,
27 discovery,
27 discovery,
28 error,
28 error,
29 exchangev2,
29 exchangev2,
30 lock as lockmod,
30 lock as lockmod,
31 logexchange,
31 logexchange,
32 narrowspec,
32 narrowspec,
33 obsolete,
33 obsolete,
34 phases,
34 phases,
35 pushkey,
35 pushkey,
36 pycompat,
36 pycompat,
37 repository,
37 repository,
38 scmutil,
38 scmutil,
39 sslutil,
39 sslutil,
40 streamclone,
40 streamclone,
41 url as urlmod,
41 url as urlmod,
42 util,
42 util,
43 wireprototypes,
43 wireprototypes,
44 )
44 )
45 from .utils import (
45 from .utils import (
46 stringutil,
46 stringutil,
47 )
47 )
48
48
49 urlerr = util.urlerr
49 urlerr = util.urlerr
50 urlreq = util.urlreq
50 urlreq = util.urlreq
51
51
52 _NARROWACL_SECTION = 'narrowhgacl'
52 _NARROWACL_SECTION = 'narrowhgacl'
53
53
54 # Maps bundle version human names to changegroup versions.
54 # Maps bundle version human names to changegroup versions.
55 _bundlespeccgversions = {'v1': '01',
55 _bundlespeccgversions = {'v1': '01',
56 'v2': '02',
56 'v2': '02',
57 'packed1': 's1',
57 'packed1': 's1',
58 'bundle2': '02', #legacy
58 'bundle2': '02', #legacy
59 }
59 }
60
60
61 # Maps bundle version with content opts to choose which part to bundle
61 # Maps bundle version with content opts to choose which part to bundle
62 _bundlespeccontentopts = {
62 _bundlespeccontentopts = {
63 'v1': {
63 'v1': {
64 'changegroup': True,
64 'changegroup': True,
65 'cg.version': '01',
65 'cg.version': '01',
66 'obsolescence': False,
66 'obsolescence': False,
67 'phases': False,
67 'phases': False,
68 'tagsfnodescache': False,
68 'tagsfnodescache': False,
69 'revbranchcache': False
69 'revbranchcache': False
70 },
70 },
71 'v2': {
71 'v2': {
72 'changegroup': True,
72 'changegroup': True,
73 'cg.version': '02',
73 'cg.version': '02',
74 'obsolescence': False,
74 'obsolescence': False,
75 'phases': False,
75 'phases': False,
76 'tagsfnodescache': True,
76 'tagsfnodescache': True,
77 'revbranchcache': True
77 'revbranchcache': True
78 },
78 },
79 'packed1' : {
79 'packed1' : {
80 'cg.version': 's1'
80 'cg.version': 's1'
81 }
81 }
82 }
82 }
83 _bundlespeccontentopts['bundle2'] = _bundlespeccontentopts['v2']
83 _bundlespeccontentopts['bundle2'] = _bundlespeccontentopts['v2']
84
84
85 _bundlespecvariants = {"streamv2": {"changegroup": False, "streamv2": True,
85 _bundlespecvariants = {"streamv2": {"changegroup": False, "streamv2": True,
86 "tagsfnodescache": False,
86 "tagsfnodescache": False,
87 "revbranchcache": False}}
87 "revbranchcache": False}}
88
88
89 # Compression engines allowed in version 1. THIS SHOULD NEVER CHANGE.
89 # Compression engines allowed in version 1. THIS SHOULD NEVER CHANGE.
90 _bundlespecv1compengines = {'gzip', 'bzip2', 'none'}
90 _bundlespecv1compengines = {'gzip', 'bzip2', 'none'}
91
91
92 @attr.s
92 @attr.s
93 class bundlespec(object):
93 class bundlespec(object):
94 compression = attr.ib()
94 compression = attr.ib()
95 wirecompression = attr.ib()
95 wirecompression = attr.ib()
96 version = attr.ib()
96 version = attr.ib()
97 wireversion = attr.ib()
97 wireversion = attr.ib()
98 params = attr.ib()
98 params = attr.ib()
99 contentopts = attr.ib()
99 contentopts = attr.ib()
100
100
101 def parsebundlespec(repo, spec, strict=True):
101 def parsebundlespec(repo, spec, strict=True):
102 """Parse a bundle string specification into parts.
102 """Parse a bundle string specification into parts.
103
103
104 Bundle specifications denote a well-defined bundle/exchange format.
104 Bundle specifications denote a well-defined bundle/exchange format.
105 The content of a given specification should not change over time in
105 The content of a given specification should not change over time in
106 order to ensure that bundles produced by a newer version of Mercurial are
106 order to ensure that bundles produced by a newer version of Mercurial are
107 readable from an older version.
107 readable from an older version.
108
108
109 The string currently has the form:
109 The string currently has the form:
110
110
111 <compression>-<type>[;<parameter0>[;<parameter1>]]
111 <compression>-<type>[;<parameter0>[;<parameter1>]]
112
112
113 Where <compression> is one of the supported compression formats
113 Where <compression> is one of the supported compression formats
114 and <type> is (currently) a version string. A ";" can follow the type and
114 and <type> is (currently) a version string. A ";" can follow the type and
115 all text afterwards is interpreted as URI encoded, ";" delimited key=value
115 all text afterwards is interpreted as URI encoded, ";" delimited key=value
116 pairs.
116 pairs.
117
117
118 If ``strict`` is True (the default) <compression> is required. Otherwise,
118 If ``strict`` is True (the default) <compression> is required. Otherwise,
119 it is optional.
119 it is optional.
120
120
121 Returns a bundlespec object of (compression, version, parameters).
121 Returns a bundlespec object of (compression, version, parameters).
122 Compression will be ``None`` if not in strict mode and a compression isn't
122 Compression will be ``None`` if not in strict mode and a compression isn't
123 defined.
123 defined.
124
124
125 An ``InvalidBundleSpecification`` is raised when the specification is
125 An ``InvalidBundleSpecification`` is raised when the specification is
126 not syntactically well formed.
126 not syntactically well formed.
127
127
128 An ``UnsupportedBundleSpecification`` is raised when the compression or
128 An ``UnsupportedBundleSpecification`` is raised when the compression or
129 bundle type/version is not recognized.
129 bundle type/version is not recognized.
130
130
131 Note: this function will likely eventually return a more complex data
131 Note: this function will likely eventually return a more complex data
132 structure, including bundle2 part information.
132 structure, including bundle2 part information.
133 """
133 """
134 def parseparams(s):
134 def parseparams(s):
135 if ';' not in s:
135 if ';' not in s:
136 return s, {}
136 return s, {}
137
137
138 params = {}
138 params = {}
139 version, paramstr = s.split(';', 1)
139 version, paramstr = s.split(';', 1)
140
140
141 for p in paramstr.split(';'):
141 for p in paramstr.split(';'):
142 if '=' not in p:
142 if '=' not in p:
143 raise error.InvalidBundleSpecification(
143 raise error.InvalidBundleSpecification(
144 _('invalid bundle specification: '
144 _('invalid bundle specification: '
145 'missing "=" in parameter: %s') % p)
145 'missing "=" in parameter: %s') % p)
146
146
147 key, value = p.split('=', 1)
147 key, value = p.split('=', 1)
148 key = urlreq.unquote(key)
148 key = urlreq.unquote(key)
149 value = urlreq.unquote(value)
149 value = urlreq.unquote(value)
150 params[key] = value
150 params[key] = value
151
151
152 return version, params
152 return version, params
153
153
154
154
155 if strict and '-' not in spec:
155 if strict and '-' not in spec:
156 raise error.InvalidBundleSpecification(
156 raise error.InvalidBundleSpecification(
157 _('invalid bundle specification; '
157 _('invalid bundle specification; '
158 'must be prefixed with compression: %s') % spec)
158 'must be prefixed with compression: %s') % spec)
159
159
160 if '-' in spec:
160 if '-' in spec:
161 compression, version = spec.split('-', 1)
161 compression, version = spec.split('-', 1)
162
162
163 if compression not in util.compengines.supportedbundlenames:
163 if compression not in util.compengines.supportedbundlenames:
164 raise error.UnsupportedBundleSpecification(
164 raise error.UnsupportedBundleSpecification(
165 _('%s compression is not supported') % compression)
165 _('%s compression is not supported') % compression)
166
166
167 version, params = parseparams(version)
167 version, params = parseparams(version)
168
168
169 if version not in _bundlespeccgversions:
169 if version not in _bundlespeccgversions:
170 raise error.UnsupportedBundleSpecification(
170 raise error.UnsupportedBundleSpecification(
171 _('%s is not a recognized bundle version') % version)
171 _('%s is not a recognized bundle version') % version)
172 else:
172 else:
173 # Value could be just the compression or just the version, in which
173 # Value could be just the compression or just the version, in which
174 # case some defaults are assumed (but only when not in strict mode).
174 # case some defaults are assumed (but only when not in strict mode).
175 assert not strict
175 assert not strict
176
176
177 spec, params = parseparams(spec)
177 spec, params = parseparams(spec)
178
178
179 if spec in util.compengines.supportedbundlenames:
179 if spec in util.compengines.supportedbundlenames:
180 compression = spec
180 compression = spec
181 version = 'v1'
181 version = 'v1'
182 # Generaldelta repos require v2.
182 # Generaldelta repos require v2.
183 if 'generaldelta' in repo.requirements:
183 if 'generaldelta' in repo.requirements:
184 version = 'v2'
184 version = 'v2'
185 # Modern compression engines require v2.
185 # Modern compression engines require v2.
186 if compression not in _bundlespecv1compengines:
186 if compression not in _bundlespecv1compengines:
187 version = 'v2'
187 version = 'v2'
188 elif spec in _bundlespeccgversions:
188 elif spec in _bundlespeccgversions:
189 if spec == 'packed1':
189 if spec == 'packed1':
190 compression = 'none'
190 compression = 'none'
191 else:
191 else:
192 compression = 'bzip2'
192 compression = 'bzip2'
193 version = spec
193 version = spec
194 else:
194 else:
195 raise error.UnsupportedBundleSpecification(
195 raise error.UnsupportedBundleSpecification(
196 _('%s is not a recognized bundle specification') % spec)
196 _('%s is not a recognized bundle specification') % spec)
197
197
198 # Bundle version 1 only supports a known set of compression engines.
198 # Bundle version 1 only supports a known set of compression engines.
199 if version == 'v1' and compression not in _bundlespecv1compengines:
199 if version == 'v1' and compression not in _bundlespecv1compengines:
200 raise error.UnsupportedBundleSpecification(
200 raise error.UnsupportedBundleSpecification(
201 _('compression engine %s is not supported on v1 bundles') %
201 _('compression engine %s is not supported on v1 bundles') %
202 compression)
202 compression)
203
203
204 # The specification for packed1 can optionally declare the data formats
204 # The specification for packed1 can optionally declare the data formats
205 # required to apply it. If we see this metadata, compare against what the
205 # required to apply it. If we see this metadata, compare against what the
206 # repo supports and error if the bundle isn't compatible.
206 # repo supports and error if the bundle isn't compatible.
207 if version == 'packed1' and 'requirements' in params:
207 if version == 'packed1' and 'requirements' in params:
208 requirements = set(params['requirements'].split(','))
208 requirements = set(params['requirements'].split(','))
209 missingreqs = requirements - repo.supportedformats
209 missingreqs = requirements - repo.supportedformats
210 if missingreqs:
210 if missingreqs:
211 raise error.UnsupportedBundleSpecification(
211 raise error.UnsupportedBundleSpecification(
212 _('missing support for repository features: %s') %
212 _('missing support for repository features: %s') %
213 ', '.join(sorted(missingreqs)))
213 ', '.join(sorted(missingreqs)))
214
214
215 # Compute contentopts based on the version
215 # Compute contentopts based on the version
216 contentopts = _bundlespeccontentopts.get(version, {}).copy()
216 contentopts = _bundlespeccontentopts.get(version, {}).copy()
217
217
218 # Process the variants
218 # Process the variants
219 if "stream" in params and params["stream"] == "v2":
219 if "stream" in params and params["stream"] == "v2":
220 variant = _bundlespecvariants["streamv2"]
220 variant = _bundlespecvariants["streamv2"]
221 contentopts.update(variant)
221 contentopts.update(variant)
222
222
223 engine = util.compengines.forbundlename(compression)
223 engine = util.compengines.forbundlename(compression)
224 compression, wirecompression = engine.bundletype()
224 compression, wirecompression = engine.bundletype()
225 wireversion = _bundlespeccgversions[version]
225 wireversion = _bundlespeccgversions[version]
226
226
227 return bundlespec(compression, wirecompression, version, wireversion,
227 return bundlespec(compression, wirecompression, version, wireversion,
228 params, contentopts)
228 params, contentopts)
229
229
230 def readbundle(ui, fh, fname, vfs=None):
230 def readbundle(ui, fh, fname, vfs=None):
231 header = changegroup.readexactly(fh, 4)
231 header = changegroup.readexactly(fh, 4)
232
232
233 alg = None
233 alg = None
234 if not fname:
234 if not fname:
235 fname = "stream"
235 fname = "stream"
236 if not header.startswith('HG') and header.startswith('\0'):
236 if not header.startswith('HG') and header.startswith('\0'):
237 fh = changegroup.headerlessfixup(fh, header)
237 fh = changegroup.headerlessfixup(fh, header)
238 header = "HG10"
238 header = "HG10"
239 alg = 'UN'
239 alg = 'UN'
240 elif vfs:
240 elif vfs:
241 fname = vfs.join(fname)
241 fname = vfs.join(fname)
242
242
243 magic, version = header[0:2], header[2:4]
243 magic, version = header[0:2], header[2:4]
244
244
245 if magic != 'HG':
245 if magic != 'HG':
246 raise error.Abort(_('%s: not a Mercurial bundle') % fname)
246 raise error.Abort(_('%s: not a Mercurial bundle') % fname)
247 if version == '10':
247 if version == '10':
248 if alg is None:
248 if alg is None:
249 alg = changegroup.readexactly(fh, 2)
249 alg = changegroup.readexactly(fh, 2)
250 return changegroup.cg1unpacker(fh, alg)
250 return changegroup.cg1unpacker(fh, alg)
251 elif version.startswith('2'):
251 elif version.startswith('2'):
252 return bundle2.getunbundler(ui, fh, magicstring=magic + version)
252 return bundle2.getunbundler(ui, fh, magicstring=magic + version)
253 elif version == 'S1':
253 elif version == 'S1':
254 return streamclone.streamcloneapplier(fh)
254 return streamclone.streamcloneapplier(fh)
255 else:
255 else:
256 raise error.Abort(_('%s: unknown bundle version %s') % (fname, version))
256 raise error.Abort(_('%s: unknown bundle version %s') % (fname, version))
257
257
258 def getbundlespec(ui, fh):
258 def getbundlespec(ui, fh):
259 """Infer the bundlespec from a bundle file handle.
259 """Infer the bundlespec from a bundle file handle.
260
260
261 The input file handle is seeked and the original seek position is not
261 The input file handle is seeked and the original seek position is not
262 restored.
262 restored.
263 """
263 """
264 def speccompression(alg):
264 def speccompression(alg):
265 try:
265 try:
266 return util.compengines.forbundletype(alg).bundletype()[0]
266 return util.compengines.forbundletype(alg).bundletype()[0]
267 except KeyError:
267 except KeyError:
268 return None
268 return None
269
269
270 b = readbundle(ui, fh, None)
270 b = readbundle(ui, fh, None)
271 if isinstance(b, changegroup.cg1unpacker):
271 if isinstance(b, changegroup.cg1unpacker):
272 alg = b._type
272 alg = b._type
273 if alg == '_truncatedBZ':
273 if alg == '_truncatedBZ':
274 alg = 'BZ'
274 alg = 'BZ'
275 comp = speccompression(alg)
275 comp = speccompression(alg)
276 if not comp:
276 if not comp:
277 raise error.Abort(_('unknown compression algorithm: %s') % alg)
277 raise error.Abort(_('unknown compression algorithm: %s') % alg)
278 return '%s-v1' % comp
278 return '%s-v1' % comp
279 elif isinstance(b, bundle2.unbundle20):
279 elif isinstance(b, bundle2.unbundle20):
280 if 'Compression' in b.params:
280 if 'Compression' in b.params:
281 comp = speccompression(b.params['Compression'])
281 comp = speccompression(b.params['Compression'])
282 if not comp:
282 if not comp:
283 raise error.Abort(_('unknown compression algorithm: %s') % comp)
283 raise error.Abort(_('unknown compression algorithm: %s') % comp)
284 else:
284 else:
285 comp = 'none'
285 comp = 'none'
286
286
287 version = None
287 version = None
288 for part in b.iterparts():
288 for part in b.iterparts():
289 if part.type == 'changegroup':
289 if part.type == 'changegroup':
290 version = part.params['version']
290 version = part.params['version']
291 if version in ('01', '02'):
291 if version in ('01', '02'):
292 version = 'v2'
292 version = 'v2'
293 else:
293 else:
294 raise error.Abort(_('changegroup version %s does not have '
294 raise error.Abort(_('changegroup version %s does not have '
295 'a known bundlespec') % version,
295 'a known bundlespec') % version,
296 hint=_('try upgrading your Mercurial '
296 hint=_('try upgrading your Mercurial '
297 'client'))
297 'client'))
298 elif part.type == 'stream2' and version is None:
298 elif part.type == 'stream2' and version is None:
299 # A stream2 part requires to be part of a v2 bundle
299 # A stream2 part requires to be part of a v2 bundle
300 version = "v2"
300 version = "v2"
301 requirements = urlreq.unquote(part.params['requirements'])
301 requirements = urlreq.unquote(part.params['requirements'])
302 splitted = requirements.split()
302 splitted = requirements.split()
303 params = bundle2._formatrequirementsparams(splitted)
303 params = bundle2._formatrequirementsparams(splitted)
304 return 'none-v2;stream=v2;%s' % params
304 return 'none-v2;stream=v2;%s' % params
305
305
306 if not version:
306 if not version:
307 raise error.Abort(_('could not identify changegroup version in '
307 raise error.Abort(_('could not identify changegroup version in '
308 'bundle'))
308 'bundle'))
309
309
310 return '%s-%s' % (comp, version)
310 return '%s-%s' % (comp, version)
311 elif isinstance(b, streamclone.streamcloneapplier):
311 elif isinstance(b, streamclone.streamcloneapplier):
312 requirements = streamclone.readbundle1header(fh)[2]
312 requirements = streamclone.readbundle1header(fh)[2]
313 formatted = bundle2._formatrequirementsparams(requirements)
313 formatted = bundle2._formatrequirementsparams(requirements)
314 return 'none-packed1;%s' % formatted
314 return 'none-packed1;%s' % formatted
315 else:
315 else:
316 raise error.Abort(_('unknown bundle type: %s') % b)
316 raise error.Abort(_('unknown bundle type: %s') % b)
317
317
318 def _computeoutgoing(repo, heads, common):
318 def _computeoutgoing(repo, heads, common):
319 """Computes which revs are outgoing given a set of common
319 """Computes which revs are outgoing given a set of common
320 and a set of heads.
320 and a set of heads.
321
321
322 This is a separate function so extensions can have access to
322 This is a separate function so extensions can have access to
323 the logic.
323 the logic.
324
324
325 Returns a discovery.outgoing object.
325 Returns a discovery.outgoing object.
326 """
326 """
327 cl = repo.changelog
327 cl = repo.changelog
328 if common:
328 if common:
329 hasnode = cl.hasnode
329 hasnode = cl.hasnode
330 common = [n for n in common if hasnode(n)]
330 common = [n for n in common if hasnode(n)]
331 else:
331 else:
332 common = [nullid]
332 common = [nullid]
333 if not heads:
333 if not heads:
334 heads = cl.heads()
334 heads = cl.heads()
335 return discovery.outgoing(repo, common, heads)
335 return discovery.outgoing(repo, common, heads)
336
336
337 def _forcebundle1(op):
337 def _forcebundle1(op):
338 """return true if a pull/push must use bundle1
338 """return true if a pull/push must use bundle1
339
339
340 This function is used to allow testing of the older bundle version"""
340 This function is used to allow testing of the older bundle version"""
341 ui = op.repo.ui
341 ui = op.repo.ui
342 # The goal is this config is to allow developer to choose the bundle
342 # The goal is this config is to allow developer to choose the bundle
343 # version used during exchanged. This is especially handy during test.
343 # version used during exchanged. This is especially handy during test.
344 # Value is a list of bundle version to be picked from, highest version
344 # Value is a list of bundle version to be picked from, highest version
345 # should be used.
345 # should be used.
346 #
346 #
347 # developer config: devel.legacy.exchange
347 # developer config: devel.legacy.exchange
348 exchange = ui.configlist('devel', 'legacy.exchange')
348 exchange = ui.configlist('devel', 'legacy.exchange')
349 forcebundle1 = 'bundle2' not in exchange and 'bundle1' in exchange
349 forcebundle1 = 'bundle2' not in exchange and 'bundle1' in exchange
350 return forcebundle1 or not op.remote.capable('bundle2')
350 return forcebundle1 or not op.remote.capable('bundle2')
351
351
352 class pushoperation(object):
352 class pushoperation(object):
353 """A object that represent a single push operation
353 """A object that represent a single push operation
354
354
355 Its purpose is to carry push related state and very common operations.
355 Its purpose is to carry push related state and very common operations.
356
356
357 A new pushoperation should be created at the beginning of each push and
357 A new pushoperation should be created at the beginning of each push and
358 discarded afterward.
358 discarded afterward.
359 """
359 """
360
360
361 def __init__(self, repo, remote, force=False, revs=None, newbranch=False,
361 def __init__(self, repo, remote, force=False, revs=None, newbranch=False,
362 bookmarks=(), pushvars=None):
362 bookmarks=(), publish=False, pushvars=None):
363 # repo we push from
363 # repo we push from
364 self.repo = repo
364 self.repo = repo
365 self.ui = repo.ui
365 self.ui = repo.ui
366 # repo we push to
366 # repo we push to
367 self.remote = remote
367 self.remote = remote
368 # force option provided
368 # force option provided
369 self.force = force
369 self.force = force
370 # revs to be pushed (None is "all")
370 # revs to be pushed (None is "all")
371 self.revs = revs
371 self.revs = revs
372 # bookmark explicitly pushed
372 # bookmark explicitly pushed
373 self.bookmarks = bookmarks
373 self.bookmarks = bookmarks
374 # allow push of new branch
374 # allow push of new branch
375 self.newbranch = newbranch
375 self.newbranch = newbranch
376 # step already performed
376 # step already performed
377 # (used to check what steps have been already performed through bundle2)
377 # (used to check what steps have been already performed through bundle2)
378 self.stepsdone = set()
378 self.stepsdone = set()
379 # Integer version of the changegroup push result
379 # Integer version of the changegroup push result
380 # - None means nothing to push
380 # - None means nothing to push
381 # - 0 means HTTP error
381 # - 0 means HTTP error
382 # - 1 means we pushed and remote head count is unchanged *or*
382 # - 1 means we pushed and remote head count is unchanged *or*
383 # we have outgoing changesets but refused to push
383 # we have outgoing changesets but refused to push
384 # - other values as described by addchangegroup()
384 # - other values as described by addchangegroup()
385 self.cgresult = None
385 self.cgresult = None
386 # Boolean value for the bookmark push
386 # Boolean value for the bookmark push
387 self.bkresult = None
387 self.bkresult = None
388 # discover.outgoing object (contains common and outgoing data)
388 # discover.outgoing object (contains common and outgoing data)
389 self.outgoing = None
389 self.outgoing = None
390 # all remote topological heads before the push
390 # all remote topological heads before the push
391 self.remoteheads = None
391 self.remoteheads = None
392 # Details of the remote branch pre and post push
392 # Details of the remote branch pre and post push
393 #
393 #
394 # mapping: {'branch': ([remoteheads],
394 # mapping: {'branch': ([remoteheads],
395 # [newheads],
395 # [newheads],
396 # [unsyncedheads],
396 # [unsyncedheads],
397 # [discardedheads])}
397 # [discardedheads])}
398 # - branch: the branch name
398 # - branch: the branch name
399 # - remoteheads: the list of remote heads known locally
399 # - remoteheads: the list of remote heads known locally
400 # None if the branch is new
400 # None if the branch is new
401 # - newheads: the new remote heads (known locally) with outgoing pushed
401 # - newheads: the new remote heads (known locally) with outgoing pushed
402 # - unsyncedheads: the list of remote heads unknown locally.
402 # - unsyncedheads: the list of remote heads unknown locally.
403 # - discardedheads: the list of remote heads made obsolete by the push
403 # - discardedheads: the list of remote heads made obsolete by the push
404 self.pushbranchmap = None
404 self.pushbranchmap = None
405 # testable as a boolean indicating if any nodes are missing locally.
405 # testable as a boolean indicating if any nodes are missing locally.
406 self.incoming = None
406 self.incoming = None
407 # summary of the remote phase situation
407 # summary of the remote phase situation
408 self.remotephases = None
408 self.remotephases = None
409 # phases changes that must be pushed along side the changesets
409 # phases changes that must be pushed along side the changesets
410 self.outdatedphases = None
410 self.outdatedphases = None
411 # phases changes that must be pushed if changeset push fails
411 # phases changes that must be pushed if changeset push fails
412 self.fallbackoutdatedphases = None
412 self.fallbackoutdatedphases = None
413 # outgoing obsmarkers
413 # outgoing obsmarkers
414 self.outobsmarkers = set()
414 self.outobsmarkers = set()
415 # outgoing bookmarks
415 # outgoing bookmarks
416 self.outbookmarks = []
416 self.outbookmarks = []
417 # transaction manager
417 # transaction manager
418 self.trmanager = None
418 self.trmanager = None
419 # map { pushkey partid -> callback handling failure}
419 # map { pushkey partid -> callback handling failure}
420 # used to handle exception from mandatory pushkey part failure
420 # used to handle exception from mandatory pushkey part failure
421 self.pkfailcb = {}
421 self.pkfailcb = {}
422 # an iterable of pushvars or None
422 # an iterable of pushvars or None
423 self.pushvars = pushvars
423 self.pushvars = pushvars
424 # publish pushed changesets
425 self.publish = publish
424
426
425 @util.propertycache
427 @util.propertycache
426 def futureheads(self):
428 def futureheads(self):
427 """future remote heads if the changeset push succeeds"""
429 """future remote heads if the changeset push succeeds"""
428 return self.outgoing.missingheads
430 return self.outgoing.missingheads
429
431
430 @util.propertycache
432 @util.propertycache
431 def fallbackheads(self):
433 def fallbackheads(self):
432 """future remote heads if the changeset push fails"""
434 """future remote heads if the changeset push fails"""
433 if self.revs is None:
435 if self.revs is None:
434 # not target to push, all common are relevant
436 # not target to push, all common are relevant
435 return self.outgoing.commonheads
437 return self.outgoing.commonheads
436 unfi = self.repo.unfiltered()
438 unfi = self.repo.unfiltered()
437 # I want cheads = heads(::missingheads and ::commonheads)
439 # I want cheads = heads(::missingheads and ::commonheads)
438 # (missingheads is revs with secret changeset filtered out)
440 # (missingheads is revs with secret changeset filtered out)
439 #
441 #
440 # This can be expressed as:
442 # This can be expressed as:
441 # cheads = ( (missingheads and ::commonheads)
443 # cheads = ( (missingheads and ::commonheads)
442 # + (commonheads and ::missingheads))"
444 # + (commonheads and ::missingheads))"
443 # )
445 # )
444 #
446 #
445 # while trying to push we already computed the following:
447 # while trying to push we already computed the following:
446 # common = (::commonheads)
448 # common = (::commonheads)
447 # missing = ((commonheads::missingheads) - commonheads)
449 # missing = ((commonheads::missingheads) - commonheads)
448 #
450 #
449 # We can pick:
451 # We can pick:
450 # * missingheads part of common (::commonheads)
452 # * missingheads part of common (::commonheads)
451 common = self.outgoing.common
453 common = self.outgoing.common
452 nm = self.repo.changelog.nodemap
454 nm = self.repo.changelog.nodemap
453 cheads = [node for node in self.revs if nm[node] in common]
455 cheads = [node for node in self.revs if nm[node] in common]
454 # and
456 # and
455 # * commonheads parents on missing
457 # * commonheads parents on missing
456 revset = unfi.set('%ln and parents(roots(%ln))',
458 revset = unfi.set('%ln and parents(roots(%ln))',
457 self.outgoing.commonheads,
459 self.outgoing.commonheads,
458 self.outgoing.missing)
460 self.outgoing.missing)
459 cheads.extend(c.node() for c in revset)
461 cheads.extend(c.node() for c in revset)
460 return cheads
462 return cheads
461
463
462 @property
464 @property
463 def commonheads(self):
465 def commonheads(self):
464 """set of all common heads after changeset bundle push"""
466 """set of all common heads after changeset bundle push"""
465 if self.cgresult:
467 if self.cgresult:
466 return self.futureheads
468 return self.futureheads
467 else:
469 else:
468 return self.fallbackheads
470 return self.fallbackheads
469
471
470 # mapping of message used when pushing bookmark
472 # mapping of message used when pushing bookmark
471 bookmsgmap = {'update': (_("updating bookmark %s\n"),
473 bookmsgmap = {'update': (_("updating bookmark %s\n"),
472 _('updating bookmark %s failed!\n')),
474 _('updating bookmark %s failed!\n')),
473 'export': (_("exporting bookmark %s\n"),
475 'export': (_("exporting bookmark %s\n"),
474 _('exporting bookmark %s failed!\n')),
476 _('exporting bookmark %s failed!\n')),
475 'delete': (_("deleting remote bookmark %s\n"),
477 'delete': (_("deleting remote bookmark %s\n"),
476 _('deleting remote bookmark %s failed!\n')),
478 _('deleting remote bookmark %s failed!\n')),
477 }
479 }
478
480
479
481
480 def push(repo, remote, force=False, revs=None, newbranch=False, bookmarks=(),
482 def push(repo, remote, force=False, revs=None, newbranch=False, bookmarks=(),
481 opargs=None):
483 publish=False, opargs=None):
482 '''Push outgoing changesets (limited by revs) from a local
484 '''Push outgoing changesets (limited by revs) from a local
483 repository to remote. Return an integer:
485 repository to remote. Return an integer:
484 - None means nothing to push
486 - None means nothing to push
485 - 0 means HTTP error
487 - 0 means HTTP error
486 - 1 means we pushed and remote head count is unchanged *or*
488 - 1 means we pushed and remote head count is unchanged *or*
487 we have outgoing changesets but refused to push
489 we have outgoing changesets but refused to push
488 - other values as described by addchangegroup()
490 - other values as described by addchangegroup()
489 '''
491 '''
490 if opargs is None:
492 if opargs is None:
491 opargs = {}
493 opargs = {}
492 pushop = pushoperation(repo, remote, force, revs, newbranch, bookmarks,
494 pushop = pushoperation(repo, remote, force, revs, newbranch, bookmarks,
493 **pycompat.strkwargs(opargs))
495 publish, **pycompat.strkwargs(opargs))
494 if pushop.remote.local():
496 if pushop.remote.local():
495 missing = (set(pushop.repo.requirements)
497 missing = (set(pushop.repo.requirements)
496 - pushop.remote.local().supported)
498 - pushop.remote.local().supported)
497 if missing:
499 if missing:
498 msg = _("required features are not"
500 msg = _("required features are not"
499 " supported in the destination:"
501 " supported in the destination:"
500 " %s") % (', '.join(sorted(missing)))
502 " %s") % (', '.join(sorted(missing)))
501 raise error.Abort(msg)
503 raise error.Abort(msg)
502
504
503 if not pushop.remote.canpush():
505 if not pushop.remote.canpush():
504 raise error.Abort(_("destination does not support push"))
506 raise error.Abort(_("destination does not support push"))
505
507
506 if not pushop.remote.capable('unbundle'):
508 if not pushop.remote.capable('unbundle'):
507 raise error.Abort(_('cannot push: destination does not support the '
509 raise error.Abort(_('cannot push: destination does not support the '
508 'unbundle wire protocol command'))
510 'unbundle wire protocol command'))
509
511
510 # get lock as we might write phase data
512 # get lock as we might write phase data
511 wlock = lock = None
513 wlock = lock = None
512 try:
514 try:
513 # bundle2 push may receive a reply bundle touching bookmarks or other
515 # bundle2 push may receive a reply bundle touching bookmarks or other
514 # things requiring the wlock. Take it now to ensure proper ordering.
516 # things requiring the wlock. Take it now to ensure proper ordering.
515 maypushback = pushop.ui.configbool('experimental', 'bundle2.pushback')
517 maypushback = pushop.ui.configbool('experimental', 'bundle2.pushback')
516 if (not _forcebundle1(pushop)) and maypushback:
518 if (not _forcebundle1(pushop)) and maypushback:
517 wlock = pushop.repo.wlock()
519 wlock = pushop.repo.wlock()
518 lock = pushop.repo.lock()
520 lock = pushop.repo.lock()
519 pushop.trmanager = transactionmanager(pushop.repo,
521 pushop.trmanager = transactionmanager(pushop.repo,
520 'push-response',
522 'push-response',
521 pushop.remote.url())
523 pushop.remote.url())
522 except error.LockUnavailable as err:
524 except error.LockUnavailable as err:
523 # source repo cannot be locked.
525 # source repo cannot be locked.
524 # We do not abort the push, but just disable the local phase
526 # We do not abort the push, but just disable the local phase
525 # synchronisation.
527 # synchronisation.
526 msg = ('cannot lock source repository: %s\n'
528 msg = ('cannot lock source repository: %s\n'
527 % stringutil.forcebytestr(err))
529 % stringutil.forcebytestr(err))
528 pushop.ui.debug(msg)
530 pushop.ui.debug(msg)
529
531
530 with wlock or util.nullcontextmanager(), \
532 with wlock or util.nullcontextmanager(), \
531 lock or util.nullcontextmanager(), \
533 lock or util.nullcontextmanager(), \
532 pushop.trmanager or util.nullcontextmanager():
534 pushop.trmanager or util.nullcontextmanager():
533 pushop.repo.checkpush(pushop)
535 pushop.repo.checkpush(pushop)
534 _pushdiscovery(pushop)
536 _pushdiscovery(pushop)
535 if not _forcebundle1(pushop):
537 if not _forcebundle1(pushop):
536 _pushbundle2(pushop)
538 _pushbundle2(pushop)
537 _pushchangeset(pushop)
539 _pushchangeset(pushop)
538 _pushsyncphase(pushop)
540 _pushsyncphase(pushop)
539 _pushobsolete(pushop)
541 _pushobsolete(pushop)
540 _pushbookmark(pushop)
542 _pushbookmark(pushop)
541
543
542 if repo.ui.configbool('experimental', 'remotenames'):
544 if repo.ui.configbool('experimental', 'remotenames'):
543 logexchange.pullremotenames(repo, remote)
545 logexchange.pullremotenames(repo, remote)
544
546
545 return pushop
547 return pushop
546
548
547 # list of steps to perform discovery before push
549 # list of steps to perform discovery before push
548 pushdiscoveryorder = []
550 pushdiscoveryorder = []
549
551
550 # Mapping between step name and function
552 # Mapping between step name and function
551 #
553 #
552 # This exists to help extensions wrap steps if necessary
554 # This exists to help extensions wrap steps if necessary
553 pushdiscoverymapping = {}
555 pushdiscoverymapping = {}
554
556
555 def pushdiscovery(stepname):
557 def pushdiscovery(stepname):
556 """decorator for function performing discovery before push
558 """decorator for function performing discovery before push
557
559
558 The function is added to the step -> function mapping and appended to the
560 The function is added to the step -> function mapping and appended to the
559 list of steps. Beware that decorated function will be added in order (this
561 list of steps. Beware that decorated function will be added in order (this
560 may matter).
562 may matter).
561
563
562 You can only use this decorator for a new step, if you want to wrap a step
564 You can only use this decorator for a new step, if you want to wrap a step
563 from an extension, change the pushdiscovery dictionary directly."""
565 from an extension, change the pushdiscovery dictionary directly."""
564 def dec(func):
566 def dec(func):
565 assert stepname not in pushdiscoverymapping
567 assert stepname not in pushdiscoverymapping
566 pushdiscoverymapping[stepname] = func
568 pushdiscoverymapping[stepname] = func
567 pushdiscoveryorder.append(stepname)
569 pushdiscoveryorder.append(stepname)
568 return func
570 return func
569 return dec
571 return dec
570
572
571 def _pushdiscovery(pushop):
573 def _pushdiscovery(pushop):
572 """Run all discovery steps"""
574 """Run all discovery steps"""
573 for stepname in pushdiscoveryorder:
575 for stepname in pushdiscoveryorder:
574 step = pushdiscoverymapping[stepname]
576 step = pushdiscoverymapping[stepname]
575 step(pushop)
577 step(pushop)
576
578
577 @pushdiscovery('changeset')
579 @pushdiscovery('changeset')
578 def _pushdiscoverychangeset(pushop):
580 def _pushdiscoverychangeset(pushop):
579 """discover the changeset that need to be pushed"""
581 """discover the changeset that need to be pushed"""
580 fci = discovery.findcommonincoming
582 fci = discovery.findcommonincoming
581 if pushop.revs:
583 if pushop.revs:
582 commoninc = fci(pushop.repo, pushop.remote, force=pushop.force,
584 commoninc = fci(pushop.repo, pushop.remote, force=pushop.force,
583 ancestorsof=pushop.revs)
585 ancestorsof=pushop.revs)
584 else:
586 else:
585 commoninc = fci(pushop.repo, pushop.remote, force=pushop.force)
587 commoninc = fci(pushop.repo, pushop.remote, force=pushop.force)
586 common, inc, remoteheads = commoninc
588 common, inc, remoteheads = commoninc
587 fco = discovery.findcommonoutgoing
589 fco = discovery.findcommonoutgoing
588 outgoing = fco(pushop.repo, pushop.remote, onlyheads=pushop.revs,
590 outgoing = fco(pushop.repo, pushop.remote, onlyheads=pushop.revs,
589 commoninc=commoninc, force=pushop.force)
591 commoninc=commoninc, force=pushop.force)
590 pushop.outgoing = outgoing
592 pushop.outgoing = outgoing
591 pushop.remoteheads = remoteheads
593 pushop.remoteheads = remoteheads
592 pushop.incoming = inc
594 pushop.incoming = inc
593
595
594 @pushdiscovery('phase')
596 @pushdiscovery('phase')
595 def _pushdiscoveryphase(pushop):
597 def _pushdiscoveryphase(pushop):
596 """discover the phase that needs to be pushed
598 """discover the phase that needs to be pushed
597
599
598 (computed for both success and failure case for changesets push)"""
600 (computed for both success and failure case for changesets push)"""
599 outgoing = pushop.outgoing
601 outgoing = pushop.outgoing
600 unfi = pushop.repo.unfiltered()
602 unfi = pushop.repo.unfiltered()
601 remotephases = listkeys(pushop.remote, 'phases')
603 remotephases = listkeys(pushop.remote, 'phases')
602
604
603 if (pushop.ui.configbool('ui', '_usedassubrepo')
605 if (pushop.ui.configbool('ui', '_usedassubrepo')
604 and remotephases # server supports phases
606 and remotephases # server supports phases
605 and not pushop.outgoing.missing # no changesets to be pushed
607 and not pushop.outgoing.missing # no changesets to be pushed
606 and remotephases.get('publishing', False)):
608 and remotephases.get('publishing', False)):
607 # When:
609 # When:
608 # - this is a subrepo push
610 # - this is a subrepo push
609 # - and remote support phase
611 # - and remote support phase
610 # - and no changeset are to be pushed
612 # - and no changeset are to be pushed
611 # - and remote is publishing
613 # - and remote is publishing
612 # We may be in issue 3781 case!
614 # We may be in issue 3781 case!
613 # We drop the possible phase synchronisation done by
615 # We drop the possible phase synchronisation done by
614 # courtesy to publish changesets possibly locally draft
616 # courtesy to publish changesets possibly locally draft
615 # on the remote.
617 # on the remote.
616 pushop.outdatedphases = []
618 pushop.outdatedphases = []
617 pushop.fallbackoutdatedphases = []
619 pushop.fallbackoutdatedphases = []
618 return
620 return
619
621
620 pushop.remotephases = phases.remotephasessummary(pushop.repo,
622 pushop.remotephases = phases.remotephasessummary(pushop.repo,
621 pushop.fallbackheads,
623 pushop.fallbackheads,
622 remotephases)
624 remotephases)
623 droots = pushop.remotephases.draftroots
625 droots = pushop.remotephases.draftroots
624
626
625 extracond = ''
627 extracond = ''
626 if not pushop.remotephases.publishing:
628 if not pushop.remotephases.publishing:
627 extracond = ' and public()'
629 extracond = ' and public()'
628 revset = 'heads((%%ln::%%ln) %s)' % extracond
630 revset = 'heads((%%ln::%%ln) %s)' % extracond
629 # Get the list of all revs draft on remote by public here.
631 # Get the list of all revs draft on remote by public here.
630 # XXX Beware that revset break if droots is not strictly
632 # XXX Beware that revset break if droots is not strictly
631 # XXX root we may want to ensure it is but it is costly
633 # XXX root we may want to ensure it is but it is costly
632 fallback = list(unfi.set(revset, droots, pushop.fallbackheads))
634 fallback = list(unfi.set(revset, droots, pushop.fallbackheads))
633 if not outgoing.missing:
635 if not pushop.remotephases.publishing and pushop.publish:
636 future = list(unfi.set('%ln and (not public() or %ln::)',
637 pushop.futureheads, droots))
638 elif not outgoing.missing:
634 future = fallback
639 future = fallback
635 else:
640 else:
636 # adds changeset we are going to push as draft
641 # adds changeset we are going to push as draft
637 #
642 #
638 # should not be necessary for publishing server, but because of an
643 # should not be necessary for publishing server, but because of an
639 # issue fixed in xxxxx we have to do it anyway.
644 # issue fixed in xxxxx we have to do it anyway.
640 fdroots = list(unfi.set('roots(%ln + %ln::)',
645 fdroots = list(unfi.set('roots(%ln + %ln::)',
641 outgoing.missing, droots))
646 outgoing.missing, droots))
642 fdroots = [f.node() for f in fdroots]
647 fdroots = [f.node() for f in fdroots]
643 future = list(unfi.set(revset, fdroots, pushop.futureheads))
648 future = list(unfi.set(revset, fdroots, pushop.futureheads))
644 pushop.outdatedphases = future
649 pushop.outdatedphases = future
645 pushop.fallbackoutdatedphases = fallback
650 pushop.fallbackoutdatedphases = fallback
646
651
647 @pushdiscovery('obsmarker')
652 @pushdiscovery('obsmarker')
648 def _pushdiscoveryobsmarkers(pushop):
653 def _pushdiscoveryobsmarkers(pushop):
649 if not obsolete.isenabled(pushop.repo, obsolete.exchangeopt):
654 if not obsolete.isenabled(pushop.repo, obsolete.exchangeopt):
650 return
655 return
651
656
652 if not pushop.repo.obsstore:
657 if not pushop.repo.obsstore:
653 return
658 return
654
659
655 if 'obsolete' not in listkeys(pushop.remote, 'namespaces'):
660 if 'obsolete' not in listkeys(pushop.remote, 'namespaces'):
656 return
661 return
657
662
658 repo = pushop.repo
663 repo = pushop.repo
659 # very naive computation, that can be quite expensive on big repo.
664 # very naive computation, that can be quite expensive on big repo.
660 # However: evolution is currently slow on them anyway.
665 # However: evolution is currently slow on them anyway.
661 nodes = (c.node() for c in repo.set('::%ln', pushop.futureheads))
666 nodes = (c.node() for c in repo.set('::%ln', pushop.futureheads))
662 pushop.outobsmarkers = pushop.repo.obsstore.relevantmarkers(nodes)
667 pushop.outobsmarkers = pushop.repo.obsstore.relevantmarkers(nodes)
663
668
664 @pushdiscovery('bookmarks')
669 @pushdiscovery('bookmarks')
665 def _pushdiscoverybookmarks(pushop):
670 def _pushdiscoverybookmarks(pushop):
666 ui = pushop.ui
671 ui = pushop.ui
667 repo = pushop.repo.unfiltered()
672 repo = pushop.repo.unfiltered()
668 remote = pushop.remote
673 remote = pushop.remote
669 ui.debug("checking for updated bookmarks\n")
674 ui.debug("checking for updated bookmarks\n")
670 ancestors = ()
675 ancestors = ()
671 if pushop.revs:
676 if pushop.revs:
672 revnums = pycompat.maplist(repo.changelog.rev, pushop.revs)
677 revnums = pycompat.maplist(repo.changelog.rev, pushop.revs)
673 ancestors = repo.changelog.ancestors(revnums, inclusive=True)
678 ancestors = repo.changelog.ancestors(revnums, inclusive=True)
674
679
675 remotebookmark = listkeys(remote, 'bookmarks')
680 remotebookmark = listkeys(remote, 'bookmarks')
676
681
677 explicit = set([repo._bookmarks.expandname(bookmark)
682 explicit = set([repo._bookmarks.expandname(bookmark)
678 for bookmark in pushop.bookmarks])
683 for bookmark in pushop.bookmarks])
679
684
680 remotebookmark = bookmod.unhexlifybookmarks(remotebookmark)
685 remotebookmark = bookmod.unhexlifybookmarks(remotebookmark)
681 comp = bookmod.comparebookmarks(repo, repo._bookmarks, remotebookmark)
686 comp = bookmod.comparebookmarks(repo, repo._bookmarks, remotebookmark)
682
687
683 def safehex(x):
688 def safehex(x):
684 if x is None:
689 if x is None:
685 return x
690 return x
686 return hex(x)
691 return hex(x)
687
692
688 def hexifycompbookmarks(bookmarks):
693 def hexifycompbookmarks(bookmarks):
689 return [(b, safehex(scid), safehex(dcid))
694 return [(b, safehex(scid), safehex(dcid))
690 for (b, scid, dcid) in bookmarks]
695 for (b, scid, dcid) in bookmarks]
691
696
692 comp = [hexifycompbookmarks(marks) for marks in comp]
697 comp = [hexifycompbookmarks(marks) for marks in comp]
693 return _processcompared(pushop, ancestors, explicit, remotebookmark, comp)
698 return _processcompared(pushop, ancestors, explicit, remotebookmark, comp)
694
699
695 def _processcompared(pushop, pushed, explicit, remotebms, comp):
700 def _processcompared(pushop, pushed, explicit, remotebms, comp):
696 """take decision on bookmark to pull from the remote bookmark
701 """take decision on bookmark to pull from the remote bookmark
697
702
698 Exist to help extensions who want to alter this behavior.
703 Exist to help extensions who want to alter this behavior.
699 """
704 """
700 addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = comp
705 addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = comp
701
706
702 repo = pushop.repo
707 repo = pushop.repo
703
708
704 for b, scid, dcid in advsrc:
709 for b, scid, dcid in advsrc:
705 if b in explicit:
710 if b in explicit:
706 explicit.remove(b)
711 explicit.remove(b)
707 if not pushed or repo[scid].rev() in pushed:
712 if not pushed or repo[scid].rev() in pushed:
708 pushop.outbookmarks.append((b, dcid, scid))
713 pushop.outbookmarks.append((b, dcid, scid))
709 # search added bookmark
714 # search added bookmark
710 for b, scid, dcid in addsrc:
715 for b, scid, dcid in addsrc:
711 if b in explicit:
716 if b in explicit:
712 explicit.remove(b)
717 explicit.remove(b)
713 pushop.outbookmarks.append((b, '', scid))
718 pushop.outbookmarks.append((b, '', scid))
714 # search for overwritten bookmark
719 # search for overwritten bookmark
715 for b, scid, dcid in list(advdst) + list(diverge) + list(differ):
720 for b, scid, dcid in list(advdst) + list(diverge) + list(differ):
716 if b in explicit:
721 if b in explicit:
717 explicit.remove(b)
722 explicit.remove(b)
718 pushop.outbookmarks.append((b, dcid, scid))
723 pushop.outbookmarks.append((b, dcid, scid))
719 # search for bookmark to delete
724 # search for bookmark to delete
720 for b, scid, dcid in adddst:
725 for b, scid, dcid in adddst:
721 if b in explicit:
726 if b in explicit:
722 explicit.remove(b)
727 explicit.remove(b)
723 # treat as "deleted locally"
728 # treat as "deleted locally"
724 pushop.outbookmarks.append((b, dcid, ''))
729 pushop.outbookmarks.append((b, dcid, ''))
725 # identical bookmarks shouldn't get reported
730 # identical bookmarks shouldn't get reported
726 for b, scid, dcid in same:
731 for b, scid, dcid in same:
727 if b in explicit:
732 if b in explicit:
728 explicit.remove(b)
733 explicit.remove(b)
729
734
730 if explicit:
735 if explicit:
731 explicit = sorted(explicit)
736 explicit = sorted(explicit)
732 # we should probably list all of them
737 # we should probably list all of them
733 pushop.ui.warn(_('bookmark %s does not exist on the local '
738 pushop.ui.warn(_('bookmark %s does not exist on the local '
734 'or remote repository!\n') % explicit[0])
739 'or remote repository!\n') % explicit[0])
735 pushop.bkresult = 2
740 pushop.bkresult = 2
736
741
737 pushop.outbookmarks.sort()
742 pushop.outbookmarks.sort()
738
743
739 def _pushcheckoutgoing(pushop):
744 def _pushcheckoutgoing(pushop):
740 outgoing = pushop.outgoing
745 outgoing = pushop.outgoing
741 unfi = pushop.repo.unfiltered()
746 unfi = pushop.repo.unfiltered()
742 if not outgoing.missing:
747 if not outgoing.missing:
743 # nothing to push
748 # nothing to push
744 scmutil.nochangesfound(unfi.ui, unfi, outgoing.excluded)
749 scmutil.nochangesfound(unfi.ui, unfi, outgoing.excluded)
745 return False
750 return False
746 # something to push
751 # something to push
747 if not pushop.force:
752 if not pushop.force:
748 # if repo.obsstore == False --> no obsolete
753 # if repo.obsstore == False --> no obsolete
749 # then, save the iteration
754 # then, save the iteration
750 if unfi.obsstore:
755 if unfi.obsstore:
751 # this message are here for 80 char limit reason
756 # this message are here for 80 char limit reason
752 mso = _("push includes obsolete changeset: %s!")
757 mso = _("push includes obsolete changeset: %s!")
753 mspd = _("push includes phase-divergent changeset: %s!")
758 mspd = _("push includes phase-divergent changeset: %s!")
754 mscd = _("push includes content-divergent changeset: %s!")
759 mscd = _("push includes content-divergent changeset: %s!")
755 mst = {"orphan": _("push includes orphan changeset: %s!"),
760 mst = {"orphan": _("push includes orphan changeset: %s!"),
756 "phase-divergent": mspd,
761 "phase-divergent": mspd,
757 "content-divergent": mscd}
762 "content-divergent": mscd}
758 # If we are to push if there is at least one
763 # If we are to push if there is at least one
759 # obsolete or unstable changeset in missing, at
764 # obsolete or unstable changeset in missing, at
760 # least one of the missinghead will be obsolete or
765 # least one of the missinghead will be obsolete or
761 # unstable. So checking heads only is ok
766 # unstable. So checking heads only is ok
762 for node in outgoing.missingheads:
767 for node in outgoing.missingheads:
763 ctx = unfi[node]
768 ctx = unfi[node]
764 if ctx.obsolete():
769 if ctx.obsolete():
765 raise error.Abort(mso % ctx)
770 raise error.Abort(mso % ctx)
766 elif ctx.isunstable():
771 elif ctx.isunstable():
767 # TODO print more than one instability in the abort
772 # TODO print more than one instability in the abort
768 # message
773 # message
769 raise error.Abort(mst[ctx.instabilities()[0]] % ctx)
774 raise error.Abort(mst[ctx.instabilities()[0]] % ctx)
770
775
771 discovery.checkheads(pushop)
776 discovery.checkheads(pushop)
772 return True
777 return True
773
778
774 # List of names of steps to perform for an outgoing bundle2, order matters.
779 # List of names of steps to perform for an outgoing bundle2, order matters.
775 b2partsgenorder = []
780 b2partsgenorder = []
776
781
777 # Mapping between step name and function
782 # Mapping between step name and function
778 #
783 #
779 # This exists to help extensions wrap steps if necessary
784 # This exists to help extensions wrap steps if necessary
780 b2partsgenmapping = {}
785 b2partsgenmapping = {}
781
786
782 def b2partsgenerator(stepname, idx=None):
787 def b2partsgenerator(stepname, idx=None):
783 """decorator for function generating bundle2 part
788 """decorator for function generating bundle2 part
784
789
785 The function is added to the step -> function mapping and appended to the
790 The function is added to the step -> function mapping and appended to the
786 list of steps. Beware that decorated functions will be added in order
791 list of steps. Beware that decorated functions will be added in order
787 (this may matter).
792 (this may matter).
788
793
789 You can only use this decorator for new steps, if you want to wrap a step
794 You can only use this decorator for new steps, if you want to wrap a step
790 from an extension, attack the b2partsgenmapping dictionary directly."""
795 from an extension, attack the b2partsgenmapping dictionary directly."""
791 def dec(func):
796 def dec(func):
792 assert stepname not in b2partsgenmapping
797 assert stepname not in b2partsgenmapping
793 b2partsgenmapping[stepname] = func
798 b2partsgenmapping[stepname] = func
794 if idx is None:
799 if idx is None:
795 b2partsgenorder.append(stepname)
800 b2partsgenorder.append(stepname)
796 else:
801 else:
797 b2partsgenorder.insert(idx, stepname)
802 b2partsgenorder.insert(idx, stepname)
798 return func
803 return func
799 return dec
804 return dec
800
805
801 def _pushb2ctxcheckheads(pushop, bundler):
806 def _pushb2ctxcheckheads(pushop, bundler):
802 """Generate race condition checking parts
807 """Generate race condition checking parts
803
808
804 Exists as an independent function to aid extensions
809 Exists as an independent function to aid extensions
805 """
810 """
806 # * 'force' do not check for push race,
811 # * 'force' do not check for push race,
807 # * if we don't push anything, there are nothing to check.
812 # * if we don't push anything, there are nothing to check.
808 if not pushop.force and pushop.outgoing.missingheads:
813 if not pushop.force and pushop.outgoing.missingheads:
809 allowunrelated = 'related' in bundler.capabilities.get('checkheads', ())
814 allowunrelated = 'related' in bundler.capabilities.get('checkheads', ())
810 emptyremote = pushop.pushbranchmap is None
815 emptyremote = pushop.pushbranchmap is None
811 if not allowunrelated or emptyremote:
816 if not allowunrelated or emptyremote:
812 bundler.newpart('check:heads', data=iter(pushop.remoteheads))
817 bundler.newpart('check:heads', data=iter(pushop.remoteheads))
813 else:
818 else:
814 affected = set()
819 affected = set()
815 for branch, heads in pushop.pushbranchmap.iteritems():
820 for branch, heads in pushop.pushbranchmap.iteritems():
816 remoteheads, newheads, unsyncedheads, discardedheads = heads
821 remoteheads, newheads, unsyncedheads, discardedheads = heads
817 if remoteheads is not None:
822 if remoteheads is not None:
818 remote = set(remoteheads)
823 remote = set(remoteheads)
819 affected |= set(discardedheads) & remote
824 affected |= set(discardedheads) & remote
820 affected |= remote - set(newheads)
825 affected |= remote - set(newheads)
821 if affected:
826 if affected:
822 data = iter(sorted(affected))
827 data = iter(sorted(affected))
823 bundler.newpart('check:updated-heads', data=data)
828 bundler.newpart('check:updated-heads', data=data)
824
829
825 def _pushing(pushop):
830 def _pushing(pushop):
826 """return True if we are pushing anything"""
831 """return True if we are pushing anything"""
827 return bool(pushop.outgoing.missing
832 return bool(pushop.outgoing.missing
828 or pushop.outdatedphases
833 or pushop.outdatedphases
829 or pushop.outobsmarkers
834 or pushop.outobsmarkers
830 or pushop.outbookmarks)
835 or pushop.outbookmarks)
831
836
832 @b2partsgenerator('check-bookmarks')
837 @b2partsgenerator('check-bookmarks')
833 def _pushb2checkbookmarks(pushop, bundler):
838 def _pushb2checkbookmarks(pushop, bundler):
834 """insert bookmark move checking"""
839 """insert bookmark move checking"""
835 if not _pushing(pushop) or pushop.force:
840 if not _pushing(pushop) or pushop.force:
836 return
841 return
837 b2caps = bundle2.bundle2caps(pushop.remote)
842 b2caps = bundle2.bundle2caps(pushop.remote)
838 hasbookmarkcheck = 'bookmarks' in b2caps
843 hasbookmarkcheck = 'bookmarks' in b2caps
839 if not (pushop.outbookmarks and hasbookmarkcheck):
844 if not (pushop.outbookmarks and hasbookmarkcheck):
840 return
845 return
841 data = []
846 data = []
842 for book, old, new in pushop.outbookmarks:
847 for book, old, new in pushop.outbookmarks:
843 old = bin(old)
848 old = bin(old)
844 data.append((book, old))
849 data.append((book, old))
845 checkdata = bookmod.binaryencode(data)
850 checkdata = bookmod.binaryencode(data)
846 bundler.newpart('check:bookmarks', data=checkdata)
851 bundler.newpart('check:bookmarks', data=checkdata)
847
852
848 @b2partsgenerator('check-phases')
853 @b2partsgenerator('check-phases')
849 def _pushb2checkphases(pushop, bundler):
854 def _pushb2checkphases(pushop, bundler):
850 """insert phase move checking"""
855 """insert phase move checking"""
851 if not _pushing(pushop) or pushop.force:
856 if not _pushing(pushop) or pushop.force:
852 return
857 return
853 b2caps = bundle2.bundle2caps(pushop.remote)
858 b2caps = bundle2.bundle2caps(pushop.remote)
854 hasphaseheads = 'heads' in b2caps.get('phases', ())
859 hasphaseheads = 'heads' in b2caps.get('phases', ())
855 if pushop.remotephases is not None and hasphaseheads:
860 if pushop.remotephases is not None and hasphaseheads:
856 # check that the remote phase has not changed
861 # check that the remote phase has not changed
857 checks = [[] for p in phases.allphases]
862 checks = [[] for p in phases.allphases]
858 checks[phases.public].extend(pushop.remotephases.publicheads)
863 checks[phases.public].extend(pushop.remotephases.publicheads)
859 checks[phases.draft].extend(pushop.remotephases.draftroots)
864 checks[phases.draft].extend(pushop.remotephases.draftroots)
860 if any(checks):
865 if any(checks):
861 for nodes in checks:
866 for nodes in checks:
862 nodes.sort()
867 nodes.sort()
863 checkdata = phases.binaryencode(checks)
868 checkdata = phases.binaryencode(checks)
864 bundler.newpart('check:phases', data=checkdata)
869 bundler.newpart('check:phases', data=checkdata)
865
870
866 @b2partsgenerator('changeset')
871 @b2partsgenerator('changeset')
867 def _pushb2ctx(pushop, bundler):
872 def _pushb2ctx(pushop, bundler):
868 """handle changegroup push through bundle2
873 """handle changegroup push through bundle2
869
874
870 addchangegroup result is stored in the ``pushop.cgresult`` attribute.
875 addchangegroup result is stored in the ``pushop.cgresult`` attribute.
871 """
876 """
872 if 'changesets' in pushop.stepsdone:
877 if 'changesets' in pushop.stepsdone:
873 return
878 return
874 pushop.stepsdone.add('changesets')
879 pushop.stepsdone.add('changesets')
875 # Send known heads to the server for race detection.
880 # Send known heads to the server for race detection.
876 if not _pushcheckoutgoing(pushop):
881 if not _pushcheckoutgoing(pushop):
877 return
882 return
878 pushop.repo.prepushoutgoinghooks(pushop)
883 pushop.repo.prepushoutgoinghooks(pushop)
879
884
880 _pushb2ctxcheckheads(pushop, bundler)
885 _pushb2ctxcheckheads(pushop, bundler)
881
886
882 b2caps = bundle2.bundle2caps(pushop.remote)
887 b2caps = bundle2.bundle2caps(pushop.remote)
883 version = '01'
888 version = '01'
884 cgversions = b2caps.get('changegroup')
889 cgversions = b2caps.get('changegroup')
885 if cgversions: # 3.1 and 3.2 ship with an empty value
890 if cgversions: # 3.1 and 3.2 ship with an empty value
886 cgversions = [v for v in cgversions
891 cgversions = [v for v in cgversions
887 if v in changegroup.supportedoutgoingversions(
892 if v in changegroup.supportedoutgoingversions(
888 pushop.repo)]
893 pushop.repo)]
889 if not cgversions:
894 if not cgversions:
890 raise ValueError(_('no common changegroup version'))
895 raise ValueError(_('no common changegroup version'))
891 version = max(cgversions)
896 version = max(cgversions)
892 cgstream = changegroup.makestream(pushop.repo, pushop.outgoing, version,
897 cgstream = changegroup.makestream(pushop.repo, pushop.outgoing, version,
893 'push')
898 'push')
894 cgpart = bundler.newpart('changegroup', data=cgstream)
899 cgpart = bundler.newpart('changegroup', data=cgstream)
895 if cgversions:
900 if cgversions:
896 cgpart.addparam('version', version)
901 cgpart.addparam('version', version)
897 if 'treemanifest' in pushop.repo.requirements:
902 if 'treemanifest' in pushop.repo.requirements:
898 cgpart.addparam('treemanifest', '1')
903 cgpart.addparam('treemanifest', '1')
899 def handlereply(op):
904 def handlereply(op):
900 """extract addchangegroup returns from server reply"""
905 """extract addchangegroup returns from server reply"""
901 cgreplies = op.records.getreplies(cgpart.id)
906 cgreplies = op.records.getreplies(cgpart.id)
902 assert len(cgreplies['changegroup']) == 1
907 assert len(cgreplies['changegroup']) == 1
903 pushop.cgresult = cgreplies['changegroup'][0]['return']
908 pushop.cgresult = cgreplies['changegroup'][0]['return']
904 return handlereply
909 return handlereply
905
910
906 @b2partsgenerator('phase')
911 @b2partsgenerator('phase')
907 def _pushb2phases(pushop, bundler):
912 def _pushb2phases(pushop, bundler):
908 """handle phase push through bundle2"""
913 """handle phase push through bundle2"""
909 if 'phases' in pushop.stepsdone:
914 if 'phases' in pushop.stepsdone:
910 return
915 return
911 b2caps = bundle2.bundle2caps(pushop.remote)
916 b2caps = bundle2.bundle2caps(pushop.remote)
912 ui = pushop.repo.ui
917 ui = pushop.repo.ui
913
918
914 legacyphase = 'phases' in ui.configlist('devel', 'legacy.exchange')
919 legacyphase = 'phases' in ui.configlist('devel', 'legacy.exchange')
915 haspushkey = 'pushkey' in b2caps
920 haspushkey = 'pushkey' in b2caps
916 hasphaseheads = 'heads' in b2caps.get('phases', ())
921 hasphaseheads = 'heads' in b2caps.get('phases', ())
917
922
918 if hasphaseheads and not legacyphase:
923 if hasphaseheads and not legacyphase:
919 return _pushb2phaseheads(pushop, bundler)
924 return _pushb2phaseheads(pushop, bundler)
920 elif haspushkey:
925 elif haspushkey:
921 return _pushb2phasespushkey(pushop, bundler)
926 return _pushb2phasespushkey(pushop, bundler)
922
927
923 def _pushb2phaseheads(pushop, bundler):
928 def _pushb2phaseheads(pushop, bundler):
924 """push phase information through a bundle2 - binary part"""
929 """push phase information through a bundle2 - binary part"""
925 pushop.stepsdone.add('phases')
930 pushop.stepsdone.add('phases')
926 if pushop.outdatedphases:
931 if pushop.outdatedphases:
927 updates = [[] for p in phases.allphases]
932 updates = [[] for p in phases.allphases]
928 updates[0].extend(h.node() for h in pushop.outdatedphases)
933 updates[0].extend(h.node() for h in pushop.outdatedphases)
929 phasedata = phases.binaryencode(updates)
934 phasedata = phases.binaryencode(updates)
930 bundler.newpart('phase-heads', data=phasedata)
935 bundler.newpart('phase-heads', data=phasedata)
931
936
932 def _pushb2phasespushkey(pushop, bundler):
937 def _pushb2phasespushkey(pushop, bundler):
933 """push phase information through a bundle2 - pushkey part"""
938 """push phase information through a bundle2 - pushkey part"""
934 pushop.stepsdone.add('phases')
939 pushop.stepsdone.add('phases')
935 part2node = []
940 part2node = []
936
941
937 def handlefailure(pushop, exc):
942 def handlefailure(pushop, exc):
938 targetid = int(exc.partid)
943 targetid = int(exc.partid)
939 for partid, node in part2node:
944 for partid, node in part2node:
940 if partid == targetid:
945 if partid == targetid:
941 raise error.Abort(_('updating %s to public failed') % node)
946 raise error.Abort(_('updating %s to public failed') % node)
942
947
943 enc = pushkey.encode
948 enc = pushkey.encode
944 for newremotehead in pushop.outdatedphases:
949 for newremotehead in pushop.outdatedphases:
945 part = bundler.newpart('pushkey')
950 part = bundler.newpart('pushkey')
946 part.addparam('namespace', enc('phases'))
951 part.addparam('namespace', enc('phases'))
947 part.addparam('key', enc(newremotehead.hex()))
952 part.addparam('key', enc(newremotehead.hex()))
948 part.addparam('old', enc('%d' % phases.draft))
953 part.addparam('old', enc('%d' % phases.draft))
949 part.addparam('new', enc('%d' % phases.public))
954 part.addparam('new', enc('%d' % phases.public))
950 part2node.append((part.id, newremotehead))
955 part2node.append((part.id, newremotehead))
951 pushop.pkfailcb[part.id] = handlefailure
956 pushop.pkfailcb[part.id] = handlefailure
952
957
953 def handlereply(op):
958 def handlereply(op):
954 for partid, node in part2node:
959 for partid, node in part2node:
955 partrep = op.records.getreplies(partid)
960 partrep = op.records.getreplies(partid)
956 results = partrep['pushkey']
961 results = partrep['pushkey']
957 assert len(results) <= 1
962 assert len(results) <= 1
958 msg = None
963 msg = None
959 if not results:
964 if not results:
960 msg = _('server ignored update of %s to public!\n') % node
965 msg = _('server ignored update of %s to public!\n') % node
961 elif not int(results[0]['return']):
966 elif not int(results[0]['return']):
962 msg = _('updating %s to public failed!\n') % node
967 msg = _('updating %s to public failed!\n') % node
963 if msg is not None:
968 if msg is not None:
964 pushop.ui.warn(msg)
969 pushop.ui.warn(msg)
965 return handlereply
970 return handlereply
966
971
967 @b2partsgenerator('obsmarkers')
972 @b2partsgenerator('obsmarkers')
968 def _pushb2obsmarkers(pushop, bundler):
973 def _pushb2obsmarkers(pushop, bundler):
969 if 'obsmarkers' in pushop.stepsdone:
974 if 'obsmarkers' in pushop.stepsdone:
970 return
975 return
971 remoteversions = bundle2.obsmarkersversion(bundler.capabilities)
976 remoteversions = bundle2.obsmarkersversion(bundler.capabilities)
972 if obsolete.commonversion(remoteversions) is None:
977 if obsolete.commonversion(remoteversions) is None:
973 return
978 return
974 pushop.stepsdone.add('obsmarkers')
979 pushop.stepsdone.add('obsmarkers')
975 if pushop.outobsmarkers:
980 if pushop.outobsmarkers:
976 markers = sorted(pushop.outobsmarkers)
981 markers = sorted(pushop.outobsmarkers)
977 bundle2.buildobsmarkerspart(bundler, markers)
982 bundle2.buildobsmarkerspart(bundler, markers)
978
983
979 @b2partsgenerator('bookmarks')
984 @b2partsgenerator('bookmarks')
980 def _pushb2bookmarks(pushop, bundler):
985 def _pushb2bookmarks(pushop, bundler):
981 """handle bookmark push through bundle2"""
986 """handle bookmark push through bundle2"""
982 if 'bookmarks' in pushop.stepsdone:
987 if 'bookmarks' in pushop.stepsdone:
983 return
988 return
984 b2caps = bundle2.bundle2caps(pushop.remote)
989 b2caps = bundle2.bundle2caps(pushop.remote)
985
990
986 legacy = pushop.repo.ui.configlist('devel', 'legacy.exchange')
991 legacy = pushop.repo.ui.configlist('devel', 'legacy.exchange')
987 legacybooks = 'bookmarks' in legacy
992 legacybooks = 'bookmarks' in legacy
988
993
989 if not legacybooks and 'bookmarks' in b2caps:
994 if not legacybooks and 'bookmarks' in b2caps:
990 return _pushb2bookmarkspart(pushop, bundler)
995 return _pushb2bookmarkspart(pushop, bundler)
991 elif 'pushkey' in b2caps:
996 elif 'pushkey' in b2caps:
992 return _pushb2bookmarkspushkey(pushop, bundler)
997 return _pushb2bookmarkspushkey(pushop, bundler)
993
998
994 def _bmaction(old, new):
999 def _bmaction(old, new):
995 """small utility for bookmark pushing"""
1000 """small utility for bookmark pushing"""
996 if not old:
1001 if not old:
997 return 'export'
1002 return 'export'
998 elif not new:
1003 elif not new:
999 return 'delete'
1004 return 'delete'
1000 return 'update'
1005 return 'update'
1001
1006
1002 def _pushb2bookmarkspart(pushop, bundler):
1007 def _pushb2bookmarkspart(pushop, bundler):
1003 pushop.stepsdone.add('bookmarks')
1008 pushop.stepsdone.add('bookmarks')
1004 if not pushop.outbookmarks:
1009 if not pushop.outbookmarks:
1005 return
1010 return
1006
1011
1007 allactions = []
1012 allactions = []
1008 data = []
1013 data = []
1009 for book, old, new in pushop.outbookmarks:
1014 for book, old, new in pushop.outbookmarks:
1010 new = bin(new)
1015 new = bin(new)
1011 data.append((book, new))
1016 data.append((book, new))
1012 allactions.append((book, _bmaction(old, new)))
1017 allactions.append((book, _bmaction(old, new)))
1013 checkdata = bookmod.binaryencode(data)
1018 checkdata = bookmod.binaryencode(data)
1014 bundler.newpart('bookmarks', data=checkdata)
1019 bundler.newpart('bookmarks', data=checkdata)
1015
1020
1016 def handlereply(op):
1021 def handlereply(op):
1017 ui = pushop.ui
1022 ui = pushop.ui
1018 # if success
1023 # if success
1019 for book, action in allactions:
1024 for book, action in allactions:
1020 ui.status(bookmsgmap[action][0] % book)
1025 ui.status(bookmsgmap[action][0] % book)
1021
1026
1022 return handlereply
1027 return handlereply
1023
1028
1024 def _pushb2bookmarkspushkey(pushop, bundler):
1029 def _pushb2bookmarkspushkey(pushop, bundler):
1025 pushop.stepsdone.add('bookmarks')
1030 pushop.stepsdone.add('bookmarks')
1026 part2book = []
1031 part2book = []
1027 enc = pushkey.encode
1032 enc = pushkey.encode
1028
1033
1029 def handlefailure(pushop, exc):
1034 def handlefailure(pushop, exc):
1030 targetid = int(exc.partid)
1035 targetid = int(exc.partid)
1031 for partid, book, action in part2book:
1036 for partid, book, action in part2book:
1032 if partid == targetid:
1037 if partid == targetid:
1033 raise error.Abort(bookmsgmap[action][1].rstrip() % book)
1038 raise error.Abort(bookmsgmap[action][1].rstrip() % book)
1034 # we should not be called for part we did not generated
1039 # we should not be called for part we did not generated
1035 assert False
1040 assert False
1036
1041
1037 for book, old, new in pushop.outbookmarks:
1042 for book, old, new in pushop.outbookmarks:
1038 part = bundler.newpart('pushkey')
1043 part = bundler.newpart('pushkey')
1039 part.addparam('namespace', enc('bookmarks'))
1044 part.addparam('namespace', enc('bookmarks'))
1040 part.addparam('key', enc(book))
1045 part.addparam('key', enc(book))
1041 part.addparam('old', enc(old))
1046 part.addparam('old', enc(old))
1042 part.addparam('new', enc(new))
1047 part.addparam('new', enc(new))
1043 action = 'update'
1048 action = 'update'
1044 if not old:
1049 if not old:
1045 action = 'export'
1050 action = 'export'
1046 elif not new:
1051 elif not new:
1047 action = 'delete'
1052 action = 'delete'
1048 part2book.append((part.id, book, action))
1053 part2book.append((part.id, book, action))
1049 pushop.pkfailcb[part.id] = handlefailure
1054 pushop.pkfailcb[part.id] = handlefailure
1050
1055
1051 def handlereply(op):
1056 def handlereply(op):
1052 ui = pushop.ui
1057 ui = pushop.ui
1053 for partid, book, action in part2book:
1058 for partid, book, action in part2book:
1054 partrep = op.records.getreplies(partid)
1059 partrep = op.records.getreplies(partid)
1055 results = partrep['pushkey']
1060 results = partrep['pushkey']
1056 assert len(results) <= 1
1061 assert len(results) <= 1
1057 if not results:
1062 if not results:
1058 pushop.ui.warn(_('server ignored bookmark %s update\n') % book)
1063 pushop.ui.warn(_('server ignored bookmark %s update\n') % book)
1059 else:
1064 else:
1060 ret = int(results[0]['return'])
1065 ret = int(results[0]['return'])
1061 if ret:
1066 if ret:
1062 ui.status(bookmsgmap[action][0] % book)
1067 ui.status(bookmsgmap[action][0] % book)
1063 else:
1068 else:
1064 ui.warn(bookmsgmap[action][1] % book)
1069 ui.warn(bookmsgmap[action][1] % book)
1065 if pushop.bkresult is not None:
1070 if pushop.bkresult is not None:
1066 pushop.bkresult = 1
1071 pushop.bkresult = 1
1067 return handlereply
1072 return handlereply
1068
1073
1069 @b2partsgenerator('pushvars', idx=0)
1074 @b2partsgenerator('pushvars', idx=0)
1070 def _getbundlesendvars(pushop, bundler):
1075 def _getbundlesendvars(pushop, bundler):
1071 '''send shellvars via bundle2'''
1076 '''send shellvars via bundle2'''
1072 pushvars = pushop.pushvars
1077 pushvars = pushop.pushvars
1073 if pushvars:
1078 if pushvars:
1074 shellvars = {}
1079 shellvars = {}
1075 for raw in pushvars:
1080 for raw in pushvars:
1076 if '=' not in raw:
1081 if '=' not in raw:
1077 msg = ("unable to parse variable '%s', should follow "
1082 msg = ("unable to parse variable '%s', should follow "
1078 "'KEY=VALUE' or 'KEY=' format")
1083 "'KEY=VALUE' or 'KEY=' format")
1079 raise error.Abort(msg % raw)
1084 raise error.Abort(msg % raw)
1080 k, v = raw.split('=', 1)
1085 k, v = raw.split('=', 1)
1081 shellvars[k] = v
1086 shellvars[k] = v
1082
1087
1083 part = bundler.newpart('pushvars')
1088 part = bundler.newpart('pushvars')
1084
1089
1085 for key, value in shellvars.iteritems():
1090 for key, value in shellvars.iteritems():
1086 part.addparam(key, value, mandatory=False)
1091 part.addparam(key, value, mandatory=False)
1087
1092
1088 def _pushbundle2(pushop):
1093 def _pushbundle2(pushop):
1089 """push data to the remote using bundle2
1094 """push data to the remote using bundle2
1090
1095
1091 The only currently supported type of data is changegroup but this will
1096 The only currently supported type of data is changegroup but this will
1092 evolve in the future."""
1097 evolve in the future."""
1093 bundler = bundle2.bundle20(pushop.ui, bundle2.bundle2caps(pushop.remote))
1098 bundler = bundle2.bundle20(pushop.ui, bundle2.bundle2caps(pushop.remote))
1094 pushback = (pushop.trmanager
1099 pushback = (pushop.trmanager
1095 and pushop.ui.configbool('experimental', 'bundle2.pushback'))
1100 and pushop.ui.configbool('experimental', 'bundle2.pushback'))
1096
1101
1097 # create reply capability
1102 # create reply capability
1098 capsblob = bundle2.encodecaps(bundle2.getrepocaps(pushop.repo,
1103 capsblob = bundle2.encodecaps(bundle2.getrepocaps(pushop.repo,
1099 allowpushback=pushback,
1104 allowpushback=pushback,
1100 role='client'))
1105 role='client'))
1101 bundler.newpart('replycaps', data=capsblob)
1106 bundler.newpart('replycaps', data=capsblob)
1102 replyhandlers = []
1107 replyhandlers = []
1103 for partgenname in b2partsgenorder:
1108 for partgenname in b2partsgenorder:
1104 partgen = b2partsgenmapping[partgenname]
1109 partgen = b2partsgenmapping[partgenname]
1105 ret = partgen(pushop, bundler)
1110 ret = partgen(pushop, bundler)
1106 if callable(ret):
1111 if callable(ret):
1107 replyhandlers.append(ret)
1112 replyhandlers.append(ret)
1108 # do not push if nothing to push
1113 # do not push if nothing to push
1109 if bundler.nbparts <= 1:
1114 if bundler.nbparts <= 1:
1110 return
1115 return
1111 stream = util.chunkbuffer(bundler.getchunks())
1116 stream = util.chunkbuffer(bundler.getchunks())
1112 try:
1117 try:
1113 try:
1118 try:
1114 with pushop.remote.commandexecutor() as e:
1119 with pushop.remote.commandexecutor() as e:
1115 reply = e.callcommand('unbundle', {
1120 reply = e.callcommand('unbundle', {
1116 'bundle': stream,
1121 'bundle': stream,
1117 'heads': ['force'],
1122 'heads': ['force'],
1118 'url': pushop.remote.url(),
1123 'url': pushop.remote.url(),
1119 }).result()
1124 }).result()
1120 except error.BundleValueError as exc:
1125 except error.BundleValueError as exc:
1121 raise error.Abort(_('missing support for %s') % exc)
1126 raise error.Abort(_('missing support for %s') % exc)
1122 try:
1127 try:
1123 trgetter = None
1128 trgetter = None
1124 if pushback:
1129 if pushback:
1125 trgetter = pushop.trmanager.transaction
1130 trgetter = pushop.trmanager.transaction
1126 op = bundle2.processbundle(pushop.repo, reply, trgetter)
1131 op = bundle2.processbundle(pushop.repo, reply, trgetter)
1127 except error.BundleValueError as exc:
1132 except error.BundleValueError as exc:
1128 raise error.Abort(_('missing support for %s') % exc)
1133 raise error.Abort(_('missing support for %s') % exc)
1129 except bundle2.AbortFromPart as exc:
1134 except bundle2.AbortFromPart as exc:
1130 pushop.ui.status(_('remote: %s\n') % exc)
1135 pushop.ui.status(_('remote: %s\n') % exc)
1131 if exc.hint is not None:
1136 if exc.hint is not None:
1132 pushop.ui.status(_('remote: %s\n') % ('(%s)' % exc.hint))
1137 pushop.ui.status(_('remote: %s\n') % ('(%s)' % exc.hint))
1133 raise error.Abort(_('push failed on remote'))
1138 raise error.Abort(_('push failed on remote'))
1134 except error.PushkeyFailed as exc:
1139 except error.PushkeyFailed as exc:
1135 partid = int(exc.partid)
1140 partid = int(exc.partid)
1136 if partid not in pushop.pkfailcb:
1141 if partid not in pushop.pkfailcb:
1137 raise
1142 raise
1138 pushop.pkfailcb[partid](pushop, exc)
1143 pushop.pkfailcb[partid](pushop, exc)
1139 for rephand in replyhandlers:
1144 for rephand in replyhandlers:
1140 rephand(op)
1145 rephand(op)
1141
1146
1142 def _pushchangeset(pushop):
1147 def _pushchangeset(pushop):
1143 """Make the actual push of changeset bundle to remote repo"""
1148 """Make the actual push of changeset bundle to remote repo"""
1144 if 'changesets' in pushop.stepsdone:
1149 if 'changesets' in pushop.stepsdone:
1145 return
1150 return
1146 pushop.stepsdone.add('changesets')
1151 pushop.stepsdone.add('changesets')
1147 if not _pushcheckoutgoing(pushop):
1152 if not _pushcheckoutgoing(pushop):
1148 return
1153 return
1149
1154
1150 # Should have verified this in push().
1155 # Should have verified this in push().
1151 assert pushop.remote.capable('unbundle')
1156 assert pushop.remote.capable('unbundle')
1152
1157
1153 pushop.repo.prepushoutgoinghooks(pushop)
1158 pushop.repo.prepushoutgoinghooks(pushop)
1154 outgoing = pushop.outgoing
1159 outgoing = pushop.outgoing
1155 # TODO: get bundlecaps from remote
1160 # TODO: get bundlecaps from remote
1156 bundlecaps = None
1161 bundlecaps = None
1157 # create a changegroup from local
1162 # create a changegroup from local
1158 if pushop.revs is None and not (outgoing.excluded
1163 if pushop.revs is None and not (outgoing.excluded
1159 or pushop.repo.changelog.filteredrevs):
1164 or pushop.repo.changelog.filteredrevs):
1160 # push everything,
1165 # push everything,
1161 # use the fast path, no race possible on push
1166 # use the fast path, no race possible on push
1162 cg = changegroup.makechangegroup(pushop.repo, outgoing, '01', 'push',
1167 cg = changegroup.makechangegroup(pushop.repo, outgoing, '01', 'push',
1163 fastpath=True, bundlecaps=bundlecaps)
1168 fastpath=True, bundlecaps=bundlecaps)
1164 else:
1169 else:
1165 cg = changegroup.makechangegroup(pushop.repo, outgoing, '01',
1170 cg = changegroup.makechangegroup(pushop.repo, outgoing, '01',
1166 'push', bundlecaps=bundlecaps)
1171 'push', bundlecaps=bundlecaps)
1167
1172
1168 # apply changegroup to remote
1173 # apply changegroup to remote
1169 # local repo finds heads on server, finds out what
1174 # local repo finds heads on server, finds out what
1170 # revs it must push. once revs transferred, if server
1175 # revs it must push. once revs transferred, if server
1171 # finds it has different heads (someone else won
1176 # finds it has different heads (someone else won
1172 # commit/push race), server aborts.
1177 # commit/push race), server aborts.
1173 if pushop.force:
1178 if pushop.force:
1174 remoteheads = ['force']
1179 remoteheads = ['force']
1175 else:
1180 else:
1176 remoteheads = pushop.remoteheads
1181 remoteheads = pushop.remoteheads
1177 # ssh: return remote's addchangegroup()
1182 # ssh: return remote's addchangegroup()
1178 # http: return remote's addchangegroup() or 0 for error
1183 # http: return remote's addchangegroup() or 0 for error
1179 pushop.cgresult = pushop.remote.unbundle(cg, remoteheads,
1184 pushop.cgresult = pushop.remote.unbundle(cg, remoteheads,
1180 pushop.repo.url())
1185 pushop.repo.url())
1181
1186
1182 def _pushsyncphase(pushop):
1187 def _pushsyncphase(pushop):
1183 """synchronise phase information locally and remotely"""
1188 """synchronise phase information locally and remotely"""
1184 cheads = pushop.commonheads
1189 cheads = pushop.commonheads
1185 # even when we don't push, exchanging phase data is useful
1190 # even when we don't push, exchanging phase data is useful
1186 remotephases = listkeys(pushop.remote, 'phases')
1191 remotephases = listkeys(pushop.remote, 'phases')
1187 if (pushop.ui.configbool('ui', '_usedassubrepo')
1192 if (pushop.ui.configbool('ui', '_usedassubrepo')
1188 and remotephases # server supports phases
1193 and remotephases # server supports phases
1189 and pushop.cgresult is None # nothing was pushed
1194 and pushop.cgresult is None # nothing was pushed
1190 and remotephases.get('publishing', False)):
1195 and remotephases.get('publishing', False)):
1191 # When:
1196 # When:
1192 # - this is a subrepo push
1197 # - this is a subrepo push
1193 # - and remote support phase
1198 # - and remote support phase
1194 # - and no changeset was pushed
1199 # - and no changeset was pushed
1195 # - and remote is publishing
1200 # - and remote is publishing
1196 # We may be in issue 3871 case!
1201 # We may be in issue 3871 case!
1197 # We drop the possible phase synchronisation done by
1202 # We drop the possible phase synchronisation done by
1198 # courtesy to publish changesets possibly locally draft
1203 # courtesy to publish changesets possibly locally draft
1199 # on the remote.
1204 # on the remote.
1200 remotephases = {'publishing': 'True'}
1205 remotephases = {'publishing': 'True'}
1201 if not remotephases: # old server or public only reply from non-publishing
1206 if not remotephases: # old server or public only reply from non-publishing
1202 _localphasemove(pushop, cheads)
1207 _localphasemove(pushop, cheads)
1203 # don't push any phase data as there is nothing to push
1208 # don't push any phase data as there is nothing to push
1204 else:
1209 else:
1205 ana = phases.analyzeremotephases(pushop.repo, cheads,
1210 ana = phases.analyzeremotephases(pushop.repo, cheads,
1206 remotephases)
1211 remotephases)
1207 pheads, droots = ana
1212 pheads, droots = ana
1208 ### Apply remote phase on local
1213 ### Apply remote phase on local
1209 if remotephases.get('publishing', False):
1214 if remotephases.get('publishing', False):
1210 _localphasemove(pushop, cheads)
1215 _localphasemove(pushop, cheads)
1211 else: # publish = False
1216 else: # publish = False
1212 _localphasemove(pushop, pheads)
1217 _localphasemove(pushop, pheads)
1213 _localphasemove(pushop, cheads, phases.draft)
1218 _localphasemove(pushop, cheads, phases.draft)
1214 ### Apply local phase on remote
1219 ### Apply local phase on remote
1215
1220
1216 if pushop.cgresult:
1221 if pushop.cgresult:
1217 if 'phases' in pushop.stepsdone:
1222 if 'phases' in pushop.stepsdone:
1218 # phases already pushed though bundle2
1223 # phases already pushed though bundle2
1219 return
1224 return
1220 outdated = pushop.outdatedphases
1225 outdated = pushop.outdatedphases
1221 else:
1226 else:
1222 outdated = pushop.fallbackoutdatedphases
1227 outdated = pushop.fallbackoutdatedphases
1223
1228
1224 pushop.stepsdone.add('phases')
1229 pushop.stepsdone.add('phases')
1225
1230
1226 # filter heads already turned public by the push
1231 # filter heads already turned public by the push
1227 outdated = [c for c in outdated if c.node() not in pheads]
1232 outdated = [c for c in outdated if c.node() not in pheads]
1228 # fallback to independent pushkey command
1233 # fallback to independent pushkey command
1229 for newremotehead in outdated:
1234 for newremotehead in outdated:
1230 with pushop.remote.commandexecutor() as e:
1235 with pushop.remote.commandexecutor() as e:
1231 r = e.callcommand('pushkey', {
1236 r = e.callcommand('pushkey', {
1232 'namespace': 'phases',
1237 'namespace': 'phases',
1233 'key': newremotehead.hex(),
1238 'key': newremotehead.hex(),
1234 'old': '%d' % phases.draft,
1239 'old': '%d' % phases.draft,
1235 'new': '%d' % phases.public
1240 'new': '%d' % phases.public
1236 }).result()
1241 }).result()
1237
1242
1238 if not r:
1243 if not r:
1239 pushop.ui.warn(_('updating %s to public failed!\n')
1244 pushop.ui.warn(_('updating %s to public failed!\n')
1240 % newremotehead)
1245 % newremotehead)
1241
1246
1242 def _localphasemove(pushop, nodes, phase=phases.public):
1247 def _localphasemove(pushop, nodes, phase=phases.public):
1243 """move <nodes> to <phase> in the local source repo"""
1248 """move <nodes> to <phase> in the local source repo"""
1244 if pushop.trmanager:
1249 if pushop.trmanager:
1245 phases.advanceboundary(pushop.repo,
1250 phases.advanceboundary(pushop.repo,
1246 pushop.trmanager.transaction(),
1251 pushop.trmanager.transaction(),
1247 phase,
1252 phase,
1248 nodes)
1253 nodes)
1249 else:
1254 else:
1250 # repo is not locked, do not change any phases!
1255 # repo is not locked, do not change any phases!
1251 # Informs the user that phases should have been moved when
1256 # Informs the user that phases should have been moved when
1252 # applicable.
1257 # applicable.
1253 actualmoves = [n for n in nodes if phase < pushop.repo[n].phase()]
1258 actualmoves = [n for n in nodes if phase < pushop.repo[n].phase()]
1254 phasestr = phases.phasenames[phase]
1259 phasestr = phases.phasenames[phase]
1255 if actualmoves:
1260 if actualmoves:
1256 pushop.ui.status(_('cannot lock source repo, skipping '
1261 pushop.ui.status(_('cannot lock source repo, skipping '
1257 'local %s phase update\n') % phasestr)
1262 'local %s phase update\n') % phasestr)
1258
1263
1259 def _pushobsolete(pushop):
1264 def _pushobsolete(pushop):
1260 """utility function to push obsolete markers to a remote"""
1265 """utility function to push obsolete markers to a remote"""
1261 if 'obsmarkers' in pushop.stepsdone:
1266 if 'obsmarkers' in pushop.stepsdone:
1262 return
1267 return
1263 repo = pushop.repo
1268 repo = pushop.repo
1264 remote = pushop.remote
1269 remote = pushop.remote
1265 pushop.stepsdone.add('obsmarkers')
1270 pushop.stepsdone.add('obsmarkers')
1266 if pushop.outobsmarkers:
1271 if pushop.outobsmarkers:
1267 pushop.ui.debug('try to push obsolete markers to remote\n')
1272 pushop.ui.debug('try to push obsolete markers to remote\n')
1268 rslts = []
1273 rslts = []
1269 remotedata = obsolete._pushkeyescape(sorted(pushop.outobsmarkers))
1274 remotedata = obsolete._pushkeyescape(sorted(pushop.outobsmarkers))
1270 for key in sorted(remotedata, reverse=True):
1275 for key in sorted(remotedata, reverse=True):
1271 # reverse sort to ensure we end with dump0
1276 # reverse sort to ensure we end with dump0
1272 data = remotedata[key]
1277 data = remotedata[key]
1273 rslts.append(remote.pushkey('obsolete', key, '', data))
1278 rslts.append(remote.pushkey('obsolete', key, '', data))
1274 if [r for r in rslts if not r]:
1279 if [r for r in rslts if not r]:
1275 msg = _('failed to push some obsolete markers!\n')
1280 msg = _('failed to push some obsolete markers!\n')
1276 repo.ui.warn(msg)
1281 repo.ui.warn(msg)
1277
1282
1278 def _pushbookmark(pushop):
1283 def _pushbookmark(pushop):
1279 """Update bookmark position on remote"""
1284 """Update bookmark position on remote"""
1280 if pushop.cgresult == 0 or 'bookmarks' in pushop.stepsdone:
1285 if pushop.cgresult == 0 or 'bookmarks' in pushop.stepsdone:
1281 return
1286 return
1282 pushop.stepsdone.add('bookmarks')
1287 pushop.stepsdone.add('bookmarks')
1283 ui = pushop.ui
1288 ui = pushop.ui
1284 remote = pushop.remote
1289 remote = pushop.remote
1285
1290
1286 for b, old, new in pushop.outbookmarks:
1291 for b, old, new in pushop.outbookmarks:
1287 action = 'update'
1292 action = 'update'
1288 if not old:
1293 if not old:
1289 action = 'export'
1294 action = 'export'
1290 elif not new:
1295 elif not new:
1291 action = 'delete'
1296 action = 'delete'
1292
1297
1293 with remote.commandexecutor() as e:
1298 with remote.commandexecutor() as e:
1294 r = e.callcommand('pushkey', {
1299 r = e.callcommand('pushkey', {
1295 'namespace': 'bookmarks',
1300 'namespace': 'bookmarks',
1296 'key': b,
1301 'key': b,
1297 'old': old,
1302 'old': old,
1298 'new': new,
1303 'new': new,
1299 }).result()
1304 }).result()
1300
1305
1301 if r:
1306 if r:
1302 ui.status(bookmsgmap[action][0] % b)
1307 ui.status(bookmsgmap[action][0] % b)
1303 else:
1308 else:
1304 ui.warn(bookmsgmap[action][1] % b)
1309 ui.warn(bookmsgmap[action][1] % b)
1305 # discovery can have set the value form invalid entry
1310 # discovery can have set the value form invalid entry
1306 if pushop.bkresult is not None:
1311 if pushop.bkresult is not None:
1307 pushop.bkresult = 1
1312 pushop.bkresult = 1
1308
1313
1309 class pulloperation(object):
1314 class pulloperation(object):
1310 """A object that represent a single pull operation
1315 """A object that represent a single pull operation
1311
1316
1312 It purpose is to carry pull related state and very common operation.
1317 It purpose is to carry pull related state and very common operation.
1313
1318
1314 A new should be created at the beginning of each pull and discarded
1319 A new should be created at the beginning of each pull and discarded
1315 afterward.
1320 afterward.
1316 """
1321 """
1317
1322
1318 def __init__(self, repo, remote, heads=None, force=False, bookmarks=(),
1323 def __init__(self, repo, remote, heads=None, force=False, bookmarks=(),
1319 remotebookmarks=None, streamclonerequested=None,
1324 remotebookmarks=None, streamclonerequested=None,
1320 includepats=None, excludepats=None, depth=None):
1325 includepats=None, excludepats=None, depth=None):
1321 # repo we pull into
1326 # repo we pull into
1322 self.repo = repo
1327 self.repo = repo
1323 # repo we pull from
1328 # repo we pull from
1324 self.remote = remote
1329 self.remote = remote
1325 # revision we try to pull (None is "all")
1330 # revision we try to pull (None is "all")
1326 self.heads = heads
1331 self.heads = heads
1327 # bookmark pulled explicitly
1332 # bookmark pulled explicitly
1328 self.explicitbookmarks = [repo._bookmarks.expandname(bookmark)
1333 self.explicitbookmarks = [repo._bookmarks.expandname(bookmark)
1329 for bookmark in bookmarks]
1334 for bookmark in bookmarks]
1330 # do we force pull?
1335 # do we force pull?
1331 self.force = force
1336 self.force = force
1332 # whether a streaming clone was requested
1337 # whether a streaming clone was requested
1333 self.streamclonerequested = streamclonerequested
1338 self.streamclonerequested = streamclonerequested
1334 # transaction manager
1339 # transaction manager
1335 self.trmanager = None
1340 self.trmanager = None
1336 # set of common changeset between local and remote before pull
1341 # set of common changeset between local and remote before pull
1337 self.common = None
1342 self.common = None
1338 # set of pulled head
1343 # set of pulled head
1339 self.rheads = None
1344 self.rheads = None
1340 # list of missing changeset to fetch remotely
1345 # list of missing changeset to fetch remotely
1341 self.fetch = None
1346 self.fetch = None
1342 # remote bookmarks data
1347 # remote bookmarks data
1343 self.remotebookmarks = remotebookmarks
1348 self.remotebookmarks = remotebookmarks
1344 # result of changegroup pulling (used as return code by pull)
1349 # result of changegroup pulling (used as return code by pull)
1345 self.cgresult = None
1350 self.cgresult = None
1346 # list of step already done
1351 # list of step already done
1347 self.stepsdone = set()
1352 self.stepsdone = set()
1348 # Whether we attempted a clone from pre-generated bundles.
1353 # Whether we attempted a clone from pre-generated bundles.
1349 self.clonebundleattempted = False
1354 self.clonebundleattempted = False
1350 # Set of file patterns to include.
1355 # Set of file patterns to include.
1351 self.includepats = includepats
1356 self.includepats = includepats
1352 # Set of file patterns to exclude.
1357 # Set of file patterns to exclude.
1353 self.excludepats = excludepats
1358 self.excludepats = excludepats
1354 # Number of ancestor changesets to pull from each pulled head.
1359 # Number of ancestor changesets to pull from each pulled head.
1355 self.depth = depth
1360 self.depth = depth
1356
1361
1357 @util.propertycache
1362 @util.propertycache
1358 def pulledsubset(self):
1363 def pulledsubset(self):
1359 """heads of the set of changeset target by the pull"""
1364 """heads of the set of changeset target by the pull"""
1360 # compute target subset
1365 # compute target subset
1361 if self.heads is None:
1366 if self.heads is None:
1362 # We pulled every thing possible
1367 # We pulled every thing possible
1363 # sync on everything common
1368 # sync on everything common
1364 c = set(self.common)
1369 c = set(self.common)
1365 ret = list(self.common)
1370 ret = list(self.common)
1366 for n in self.rheads:
1371 for n in self.rheads:
1367 if n not in c:
1372 if n not in c:
1368 ret.append(n)
1373 ret.append(n)
1369 return ret
1374 return ret
1370 else:
1375 else:
1371 # We pulled a specific subset
1376 # We pulled a specific subset
1372 # sync on this subset
1377 # sync on this subset
1373 return self.heads
1378 return self.heads
1374
1379
1375 @util.propertycache
1380 @util.propertycache
1376 def canusebundle2(self):
1381 def canusebundle2(self):
1377 return not _forcebundle1(self)
1382 return not _forcebundle1(self)
1378
1383
1379 @util.propertycache
1384 @util.propertycache
1380 def remotebundle2caps(self):
1385 def remotebundle2caps(self):
1381 return bundle2.bundle2caps(self.remote)
1386 return bundle2.bundle2caps(self.remote)
1382
1387
1383 def gettransaction(self):
1388 def gettransaction(self):
1384 # deprecated; talk to trmanager directly
1389 # deprecated; talk to trmanager directly
1385 return self.trmanager.transaction()
1390 return self.trmanager.transaction()
1386
1391
1387 class transactionmanager(util.transactional):
1392 class transactionmanager(util.transactional):
1388 """An object to manage the life cycle of a transaction
1393 """An object to manage the life cycle of a transaction
1389
1394
1390 It creates the transaction on demand and calls the appropriate hooks when
1395 It creates the transaction on demand and calls the appropriate hooks when
1391 closing the transaction."""
1396 closing the transaction."""
1392 def __init__(self, repo, source, url):
1397 def __init__(self, repo, source, url):
1393 self.repo = repo
1398 self.repo = repo
1394 self.source = source
1399 self.source = source
1395 self.url = url
1400 self.url = url
1396 self._tr = None
1401 self._tr = None
1397
1402
1398 def transaction(self):
1403 def transaction(self):
1399 """Return an open transaction object, constructing if necessary"""
1404 """Return an open transaction object, constructing if necessary"""
1400 if not self._tr:
1405 if not self._tr:
1401 trname = '%s\n%s' % (self.source, util.hidepassword(self.url))
1406 trname = '%s\n%s' % (self.source, util.hidepassword(self.url))
1402 self._tr = self.repo.transaction(trname)
1407 self._tr = self.repo.transaction(trname)
1403 self._tr.hookargs['source'] = self.source
1408 self._tr.hookargs['source'] = self.source
1404 self._tr.hookargs['url'] = self.url
1409 self._tr.hookargs['url'] = self.url
1405 return self._tr
1410 return self._tr
1406
1411
1407 def close(self):
1412 def close(self):
1408 """close transaction if created"""
1413 """close transaction if created"""
1409 if self._tr is not None:
1414 if self._tr is not None:
1410 self._tr.close()
1415 self._tr.close()
1411
1416
1412 def release(self):
1417 def release(self):
1413 """release transaction if created"""
1418 """release transaction if created"""
1414 if self._tr is not None:
1419 if self._tr is not None:
1415 self._tr.release()
1420 self._tr.release()
1416
1421
1417 def listkeys(remote, namespace):
1422 def listkeys(remote, namespace):
1418 with remote.commandexecutor() as e:
1423 with remote.commandexecutor() as e:
1419 return e.callcommand('listkeys', {'namespace': namespace}).result()
1424 return e.callcommand('listkeys', {'namespace': namespace}).result()
1420
1425
1421 def _fullpullbundle2(repo, pullop):
1426 def _fullpullbundle2(repo, pullop):
1422 # The server may send a partial reply, i.e. when inlining
1427 # The server may send a partial reply, i.e. when inlining
1423 # pre-computed bundles. In that case, update the common
1428 # pre-computed bundles. In that case, update the common
1424 # set based on the results and pull another bundle.
1429 # set based on the results and pull another bundle.
1425 #
1430 #
1426 # There are two indicators that the process is finished:
1431 # There are two indicators that the process is finished:
1427 # - no changeset has been added, or
1432 # - no changeset has been added, or
1428 # - all remote heads are known locally.
1433 # - all remote heads are known locally.
1429 # The head check must use the unfiltered view as obsoletion
1434 # The head check must use the unfiltered view as obsoletion
1430 # markers can hide heads.
1435 # markers can hide heads.
1431 unfi = repo.unfiltered()
1436 unfi = repo.unfiltered()
1432 unficl = unfi.changelog
1437 unficl = unfi.changelog
1433 def headsofdiff(h1, h2):
1438 def headsofdiff(h1, h2):
1434 """Returns heads(h1 % h2)"""
1439 """Returns heads(h1 % h2)"""
1435 res = unfi.set('heads(%ln %% %ln)', h1, h2)
1440 res = unfi.set('heads(%ln %% %ln)', h1, h2)
1436 return set(ctx.node() for ctx in res)
1441 return set(ctx.node() for ctx in res)
1437 def headsofunion(h1, h2):
1442 def headsofunion(h1, h2):
1438 """Returns heads((h1 + h2) - null)"""
1443 """Returns heads((h1 + h2) - null)"""
1439 res = unfi.set('heads((%ln + %ln - null))', h1, h2)
1444 res = unfi.set('heads((%ln + %ln - null))', h1, h2)
1440 return set(ctx.node() for ctx in res)
1445 return set(ctx.node() for ctx in res)
1441 while True:
1446 while True:
1442 old_heads = unficl.heads()
1447 old_heads = unficl.heads()
1443 clstart = len(unficl)
1448 clstart = len(unficl)
1444 _pullbundle2(pullop)
1449 _pullbundle2(pullop)
1445 if repository.NARROW_REQUIREMENT in repo.requirements:
1450 if repository.NARROW_REQUIREMENT in repo.requirements:
1446 # XXX narrow clones filter the heads on the server side during
1451 # XXX narrow clones filter the heads on the server side during
1447 # XXX getbundle and result in partial replies as well.
1452 # XXX getbundle and result in partial replies as well.
1448 # XXX Disable pull bundles in this case as band aid to avoid
1453 # XXX Disable pull bundles in this case as band aid to avoid
1449 # XXX extra round trips.
1454 # XXX extra round trips.
1450 break
1455 break
1451 if clstart == len(unficl):
1456 if clstart == len(unficl):
1452 break
1457 break
1453 if all(unficl.hasnode(n) for n in pullop.rheads):
1458 if all(unficl.hasnode(n) for n in pullop.rheads):
1454 break
1459 break
1455 new_heads = headsofdiff(unficl.heads(), old_heads)
1460 new_heads = headsofdiff(unficl.heads(), old_heads)
1456 pullop.common = headsofunion(new_heads, pullop.common)
1461 pullop.common = headsofunion(new_heads, pullop.common)
1457 pullop.rheads = set(pullop.rheads) - pullop.common
1462 pullop.rheads = set(pullop.rheads) - pullop.common
1458
1463
1459 def pull(repo, remote, heads=None, force=False, bookmarks=(), opargs=None,
1464 def pull(repo, remote, heads=None, force=False, bookmarks=(), opargs=None,
1460 streamclonerequested=None, includepats=None, excludepats=None,
1465 streamclonerequested=None, includepats=None, excludepats=None,
1461 depth=None):
1466 depth=None):
1462 """Fetch repository data from a remote.
1467 """Fetch repository data from a remote.
1463
1468
1464 This is the main function used to retrieve data from a remote repository.
1469 This is the main function used to retrieve data from a remote repository.
1465
1470
1466 ``repo`` is the local repository to clone into.
1471 ``repo`` is the local repository to clone into.
1467 ``remote`` is a peer instance.
1472 ``remote`` is a peer instance.
1468 ``heads`` is an iterable of revisions we want to pull. ``None`` (the
1473 ``heads`` is an iterable of revisions we want to pull. ``None`` (the
1469 default) means to pull everything from the remote.
1474 default) means to pull everything from the remote.
1470 ``bookmarks`` is an iterable of bookmarks requesting to be pulled. By
1475 ``bookmarks`` is an iterable of bookmarks requesting to be pulled. By
1471 default, all remote bookmarks are pulled.
1476 default, all remote bookmarks are pulled.
1472 ``opargs`` are additional keyword arguments to pass to ``pulloperation``
1477 ``opargs`` are additional keyword arguments to pass to ``pulloperation``
1473 initialization.
1478 initialization.
1474 ``streamclonerequested`` is a boolean indicating whether a "streaming
1479 ``streamclonerequested`` is a boolean indicating whether a "streaming
1475 clone" is requested. A "streaming clone" is essentially a raw file copy
1480 clone" is requested. A "streaming clone" is essentially a raw file copy
1476 of revlogs from the server. This only works when the local repository is
1481 of revlogs from the server. This only works when the local repository is
1477 empty. The default value of ``None`` means to respect the server
1482 empty. The default value of ``None`` means to respect the server
1478 configuration for preferring stream clones.
1483 configuration for preferring stream clones.
1479 ``includepats`` and ``excludepats`` define explicit file patterns to
1484 ``includepats`` and ``excludepats`` define explicit file patterns to
1480 include and exclude in storage, respectively. If not defined, narrow
1485 include and exclude in storage, respectively. If not defined, narrow
1481 patterns from the repo instance are used, if available.
1486 patterns from the repo instance are used, if available.
1482 ``depth`` is an integer indicating the DAG depth of history we're
1487 ``depth`` is an integer indicating the DAG depth of history we're
1483 interested in. If defined, for each revision specified in ``heads``, we
1488 interested in. If defined, for each revision specified in ``heads``, we
1484 will fetch up to this many of its ancestors and data associated with them.
1489 will fetch up to this many of its ancestors and data associated with them.
1485
1490
1486 Returns the ``pulloperation`` created for this pull.
1491 Returns the ``pulloperation`` created for this pull.
1487 """
1492 """
1488 if opargs is None:
1493 if opargs is None:
1489 opargs = {}
1494 opargs = {}
1490
1495
1491 # We allow the narrow patterns to be passed in explicitly to provide more
1496 # We allow the narrow patterns to be passed in explicitly to provide more
1492 # flexibility for API consumers.
1497 # flexibility for API consumers.
1493 if includepats or excludepats:
1498 if includepats or excludepats:
1494 includepats = includepats or set()
1499 includepats = includepats or set()
1495 excludepats = excludepats or set()
1500 excludepats = excludepats or set()
1496 else:
1501 else:
1497 includepats, excludepats = repo.narrowpats
1502 includepats, excludepats = repo.narrowpats
1498
1503
1499 narrowspec.validatepatterns(includepats)
1504 narrowspec.validatepatterns(includepats)
1500 narrowspec.validatepatterns(excludepats)
1505 narrowspec.validatepatterns(excludepats)
1501
1506
1502 pullop = pulloperation(repo, remote, heads, force, bookmarks=bookmarks,
1507 pullop = pulloperation(repo, remote, heads, force, bookmarks=bookmarks,
1503 streamclonerequested=streamclonerequested,
1508 streamclonerequested=streamclonerequested,
1504 includepats=includepats, excludepats=excludepats,
1509 includepats=includepats, excludepats=excludepats,
1505 depth=depth,
1510 depth=depth,
1506 **pycompat.strkwargs(opargs))
1511 **pycompat.strkwargs(opargs))
1507
1512
1508 peerlocal = pullop.remote.local()
1513 peerlocal = pullop.remote.local()
1509 if peerlocal:
1514 if peerlocal:
1510 missing = set(peerlocal.requirements) - pullop.repo.supported
1515 missing = set(peerlocal.requirements) - pullop.repo.supported
1511 if missing:
1516 if missing:
1512 msg = _("required features are not"
1517 msg = _("required features are not"
1513 " supported in the destination:"
1518 " supported in the destination:"
1514 " %s") % (', '.join(sorted(missing)))
1519 " %s") % (', '.join(sorted(missing)))
1515 raise error.Abort(msg)
1520 raise error.Abort(msg)
1516
1521
1517 pullop.trmanager = transactionmanager(repo, 'pull', remote.url())
1522 pullop.trmanager = transactionmanager(repo, 'pull', remote.url())
1518 with repo.wlock(), repo.lock(), pullop.trmanager:
1523 with repo.wlock(), repo.lock(), pullop.trmanager:
1519 # Use the modern wire protocol, if available.
1524 # Use the modern wire protocol, if available.
1520 if remote.capable('command-changesetdata'):
1525 if remote.capable('command-changesetdata'):
1521 exchangev2.pull(pullop)
1526 exchangev2.pull(pullop)
1522 else:
1527 else:
1523 # This should ideally be in _pullbundle2(). However, it needs to run
1528 # This should ideally be in _pullbundle2(). However, it needs to run
1524 # before discovery to avoid extra work.
1529 # before discovery to avoid extra work.
1525 _maybeapplyclonebundle(pullop)
1530 _maybeapplyclonebundle(pullop)
1526 streamclone.maybeperformlegacystreamclone(pullop)
1531 streamclone.maybeperformlegacystreamclone(pullop)
1527 _pulldiscovery(pullop)
1532 _pulldiscovery(pullop)
1528 if pullop.canusebundle2:
1533 if pullop.canusebundle2:
1529 _fullpullbundle2(repo, pullop)
1534 _fullpullbundle2(repo, pullop)
1530 _pullchangeset(pullop)
1535 _pullchangeset(pullop)
1531 _pullphase(pullop)
1536 _pullphase(pullop)
1532 _pullbookmarks(pullop)
1537 _pullbookmarks(pullop)
1533 _pullobsolete(pullop)
1538 _pullobsolete(pullop)
1534
1539
1535 # storing remotenames
1540 # storing remotenames
1536 if repo.ui.configbool('experimental', 'remotenames'):
1541 if repo.ui.configbool('experimental', 'remotenames'):
1537 logexchange.pullremotenames(repo, remote)
1542 logexchange.pullremotenames(repo, remote)
1538
1543
1539 return pullop
1544 return pullop
1540
1545
1541 # list of steps to perform discovery before pull
1546 # list of steps to perform discovery before pull
1542 pulldiscoveryorder = []
1547 pulldiscoveryorder = []
1543
1548
1544 # Mapping between step name and function
1549 # Mapping between step name and function
1545 #
1550 #
1546 # This exists to help extensions wrap steps if necessary
1551 # This exists to help extensions wrap steps if necessary
1547 pulldiscoverymapping = {}
1552 pulldiscoverymapping = {}
1548
1553
1549 def pulldiscovery(stepname):
1554 def pulldiscovery(stepname):
1550 """decorator for function performing discovery before pull
1555 """decorator for function performing discovery before pull
1551
1556
1552 The function is added to the step -> function mapping and appended to the
1557 The function is added to the step -> function mapping and appended to the
1553 list of steps. Beware that decorated function will be added in order (this
1558 list of steps. Beware that decorated function will be added in order (this
1554 may matter).
1559 may matter).
1555
1560
1556 You can only use this decorator for a new step, if you want to wrap a step
1561 You can only use this decorator for a new step, if you want to wrap a step
1557 from an extension, change the pulldiscovery dictionary directly."""
1562 from an extension, change the pulldiscovery dictionary directly."""
1558 def dec(func):
1563 def dec(func):
1559 assert stepname not in pulldiscoverymapping
1564 assert stepname not in pulldiscoverymapping
1560 pulldiscoverymapping[stepname] = func
1565 pulldiscoverymapping[stepname] = func
1561 pulldiscoveryorder.append(stepname)
1566 pulldiscoveryorder.append(stepname)
1562 return func
1567 return func
1563 return dec
1568 return dec
1564
1569
1565 def _pulldiscovery(pullop):
1570 def _pulldiscovery(pullop):
1566 """Run all discovery steps"""
1571 """Run all discovery steps"""
1567 for stepname in pulldiscoveryorder:
1572 for stepname in pulldiscoveryorder:
1568 step = pulldiscoverymapping[stepname]
1573 step = pulldiscoverymapping[stepname]
1569 step(pullop)
1574 step(pullop)
1570
1575
1571 @pulldiscovery('b1:bookmarks')
1576 @pulldiscovery('b1:bookmarks')
1572 def _pullbookmarkbundle1(pullop):
1577 def _pullbookmarkbundle1(pullop):
1573 """fetch bookmark data in bundle1 case
1578 """fetch bookmark data in bundle1 case
1574
1579
1575 If not using bundle2, we have to fetch bookmarks before changeset
1580 If not using bundle2, we have to fetch bookmarks before changeset
1576 discovery to reduce the chance and impact of race conditions."""
1581 discovery to reduce the chance and impact of race conditions."""
1577 if pullop.remotebookmarks is not None:
1582 if pullop.remotebookmarks is not None:
1578 return
1583 return
1579 if pullop.canusebundle2 and 'listkeys' in pullop.remotebundle2caps:
1584 if pullop.canusebundle2 and 'listkeys' in pullop.remotebundle2caps:
1580 # all known bundle2 servers now support listkeys, but lets be nice with
1585 # all known bundle2 servers now support listkeys, but lets be nice with
1581 # new implementation.
1586 # new implementation.
1582 return
1587 return
1583 books = listkeys(pullop.remote, 'bookmarks')
1588 books = listkeys(pullop.remote, 'bookmarks')
1584 pullop.remotebookmarks = bookmod.unhexlifybookmarks(books)
1589 pullop.remotebookmarks = bookmod.unhexlifybookmarks(books)
1585
1590
1586
1591
1587 @pulldiscovery('changegroup')
1592 @pulldiscovery('changegroup')
1588 def _pulldiscoverychangegroup(pullop):
1593 def _pulldiscoverychangegroup(pullop):
1589 """discovery phase for the pull
1594 """discovery phase for the pull
1590
1595
1591 Current handle changeset discovery only, will change handle all discovery
1596 Current handle changeset discovery only, will change handle all discovery
1592 at some point."""
1597 at some point."""
1593 tmp = discovery.findcommonincoming(pullop.repo,
1598 tmp = discovery.findcommonincoming(pullop.repo,
1594 pullop.remote,
1599 pullop.remote,
1595 heads=pullop.heads,
1600 heads=pullop.heads,
1596 force=pullop.force)
1601 force=pullop.force)
1597 common, fetch, rheads = tmp
1602 common, fetch, rheads = tmp
1598 nm = pullop.repo.unfiltered().changelog.nodemap
1603 nm = pullop.repo.unfiltered().changelog.nodemap
1599 if fetch and rheads:
1604 if fetch and rheads:
1600 # If a remote heads is filtered locally, put in back in common.
1605 # If a remote heads is filtered locally, put in back in common.
1601 #
1606 #
1602 # This is a hackish solution to catch most of "common but locally
1607 # This is a hackish solution to catch most of "common but locally
1603 # hidden situation". We do not performs discovery on unfiltered
1608 # hidden situation". We do not performs discovery on unfiltered
1604 # repository because it end up doing a pathological amount of round
1609 # repository because it end up doing a pathological amount of round
1605 # trip for w huge amount of changeset we do not care about.
1610 # trip for w huge amount of changeset we do not care about.
1606 #
1611 #
1607 # If a set of such "common but filtered" changeset exist on the server
1612 # If a set of such "common but filtered" changeset exist on the server
1608 # but are not including a remote heads, we'll not be able to detect it,
1613 # but are not including a remote heads, we'll not be able to detect it,
1609 scommon = set(common)
1614 scommon = set(common)
1610 for n in rheads:
1615 for n in rheads:
1611 if n in nm:
1616 if n in nm:
1612 if n not in scommon:
1617 if n not in scommon:
1613 common.append(n)
1618 common.append(n)
1614 if set(rheads).issubset(set(common)):
1619 if set(rheads).issubset(set(common)):
1615 fetch = []
1620 fetch = []
1616 pullop.common = common
1621 pullop.common = common
1617 pullop.fetch = fetch
1622 pullop.fetch = fetch
1618 pullop.rheads = rheads
1623 pullop.rheads = rheads
1619
1624
1620 def _pullbundle2(pullop):
1625 def _pullbundle2(pullop):
1621 """pull data using bundle2
1626 """pull data using bundle2
1622
1627
1623 For now, the only supported data are changegroup."""
1628 For now, the only supported data are changegroup."""
1624 kwargs = {'bundlecaps': caps20to10(pullop.repo, role='client')}
1629 kwargs = {'bundlecaps': caps20to10(pullop.repo, role='client')}
1625
1630
1626 # make ui easier to access
1631 # make ui easier to access
1627 ui = pullop.repo.ui
1632 ui = pullop.repo.ui
1628
1633
1629 # At the moment we don't do stream clones over bundle2. If that is
1634 # At the moment we don't do stream clones over bundle2. If that is
1630 # implemented then here's where the check for that will go.
1635 # implemented then here's where the check for that will go.
1631 streaming = streamclone.canperformstreamclone(pullop, bundle2=True)[0]
1636 streaming = streamclone.canperformstreamclone(pullop, bundle2=True)[0]
1632
1637
1633 # declare pull perimeters
1638 # declare pull perimeters
1634 kwargs['common'] = pullop.common
1639 kwargs['common'] = pullop.common
1635 kwargs['heads'] = pullop.heads or pullop.rheads
1640 kwargs['heads'] = pullop.heads or pullop.rheads
1636
1641
1637 # check server supports narrow and then adding includepats and excludepats
1642 # check server supports narrow and then adding includepats and excludepats
1638 servernarrow = pullop.remote.capable(wireprototypes.NARROWCAP)
1643 servernarrow = pullop.remote.capable(wireprototypes.NARROWCAP)
1639 if servernarrow and pullop.includepats:
1644 if servernarrow and pullop.includepats:
1640 kwargs['includepats'] = pullop.includepats
1645 kwargs['includepats'] = pullop.includepats
1641 if servernarrow and pullop.excludepats:
1646 if servernarrow and pullop.excludepats:
1642 kwargs['excludepats'] = pullop.excludepats
1647 kwargs['excludepats'] = pullop.excludepats
1643
1648
1644 if streaming:
1649 if streaming:
1645 kwargs['cg'] = False
1650 kwargs['cg'] = False
1646 kwargs['stream'] = True
1651 kwargs['stream'] = True
1647 pullop.stepsdone.add('changegroup')
1652 pullop.stepsdone.add('changegroup')
1648 pullop.stepsdone.add('phases')
1653 pullop.stepsdone.add('phases')
1649
1654
1650 else:
1655 else:
1651 # pulling changegroup
1656 # pulling changegroup
1652 pullop.stepsdone.add('changegroup')
1657 pullop.stepsdone.add('changegroup')
1653
1658
1654 kwargs['cg'] = pullop.fetch
1659 kwargs['cg'] = pullop.fetch
1655
1660
1656 legacyphase = 'phases' in ui.configlist('devel', 'legacy.exchange')
1661 legacyphase = 'phases' in ui.configlist('devel', 'legacy.exchange')
1657 hasbinaryphase = 'heads' in pullop.remotebundle2caps.get('phases', ())
1662 hasbinaryphase = 'heads' in pullop.remotebundle2caps.get('phases', ())
1658 if (not legacyphase and hasbinaryphase):
1663 if (not legacyphase and hasbinaryphase):
1659 kwargs['phases'] = True
1664 kwargs['phases'] = True
1660 pullop.stepsdone.add('phases')
1665 pullop.stepsdone.add('phases')
1661
1666
1662 if 'listkeys' in pullop.remotebundle2caps:
1667 if 'listkeys' in pullop.remotebundle2caps:
1663 if 'phases' not in pullop.stepsdone:
1668 if 'phases' not in pullop.stepsdone:
1664 kwargs['listkeys'] = ['phases']
1669 kwargs['listkeys'] = ['phases']
1665
1670
1666 bookmarksrequested = False
1671 bookmarksrequested = False
1667 legacybookmark = 'bookmarks' in ui.configlist('devel', 'legacy.exchange')
1672 legacybookmark = 'bookmarks' in ui.configlist('devel', 'legacy.exchange')
1668 hasbinarybook = 'bookmarks' in pullop.remotebundle2caps
1673 hasbinarybook = 'bookmarks' in pullop.remotebundle2caps
1669
1674
1670 if pullop.remotebookmarks is not None:
1675 if pullop.remotebookmarks is not None:
1671 pullop.stepsdone.add('request-bookmarks')
1676 pullop.stepsdone.add('request-bookmarks')
1672
1677
1673 if ('request-bookmarks' not in pullop.stepsdone
1678 if ('request-bookmarks' not in pullop.stepsdone
1674 and pullop.remotebookmarks is None
1679 and pullop.remotebookmarks is None
1675 and not legacybookmark and hasbinarybook):
1680 and not legacybookmark and hasbinarybook):
1676 kwargs['bookmarks'] = True
1681 kwargs['bookmarks'] = True
1677 bookmarksrequested = True
1682 bookmarksrequested = True
1678
1683
1679 if 'listkeys' in pullop.remotebundle2caps:
1684 if 'listkeys' in pullop.remotebundle2caps:
1680 if 'request-bookmarks' not in pullop.stepsdone:
1685 if 'request-bookmarks' not in pullop.stepsdone:
1681 # make sure to always includes bookmark data when migrating
1686 # make sure to always includes bookmark data when migrating
1682 # `hg incoming --bundle` to using this function.
1687 # `hg incoming --bundle` to using this function.
1683 pullop.stepsdone.add('request-bookmarks')
1688 pullop.stepsdone.add('request-bookmarks')
1684 kwargs.setdefault('listkeys', []).append('bookmarks')
1689 kwargs.setdefault('listkeys', []).append('bookmarks')
1685
1690
1686 # If this is a full pull / clone and the server supports the clone bundles
1691 # If this is a full pull / clone and the server supports the clone bundles
1687 # feature, tell the server whether we attempted a clone bundle. The
1692 # feature, tell the server whether we attempted a clone bundle. The
1688 # presence of this flag indicates the client supports clone bundles. This
1693 # presence of this flag indicates the client supports clone bundles. This
1689 # will enable the server to treat clients that support clone bundles
1694 # will enable the server to treat clients that support clone bundles
1690 # differently from those that don't.
1695 # differently from those that don't.
1691 if (pullop.remote.capable('clonebundles')
1696 if (pullop.remote.capable('clonebundles')
1692 and pullop.heads is None and list(pullop.common) == [nullid]):
1697 and pullop.heads is None and list(pullop.common) == [nullid]):
1693 kwargs['cbattempted'] = pullop.clonebundleattempted
1698 kwargs['cbattempted'] = pullop.clonebundleattempted
1694
1699
1695 if streaming:
1700 if streaming:
1696 pullop.repo.ui.status(_('streaming all changes\n'))
1701 pullop.repo.ui.status(_('streaming all changes\n'))
1697 elif not pullop.fetch:
1702 elif not pullop.fetch:
1698 pullop.repo.ui.status(_("no changes found\n"))
1703 pullop.repo.ui.status(_("no changes found\n"))
1699 pullop.cgresult = 0
1704 pullop.cgresult = 0
1700 else:
1705 else:
1701 if pullop.heads is None and list(pullop.common) == [nullid]:
1706 if pullop.heads is None and list(pullop.common) == [nullid]:
1702 pullop.repo.ui.status(_("requesting all changes\n"))
1707 pullop.repo.ui.status(_("requesting all changes\n"))
1703 if obsolete.isenabled(pullop.repo, obsolete.exchangeopt):
1708 if obsolete.isenabled(pullop.repo, obsolete.exchangeopt):
1704 remoteversions = bundle2.obsmarkersversion(pullop.remotebundle2caps)
1709 remoteversions = bundle2.obsmarkersversion(pullop.remotebundle2caps)
1705 if obsolete.commonversion(remoteversions) is not None:
1710 if obsolete.commonversion(remoteversions) is not None:
1706 kwargs['obsmarkers'] = True
1711 kwargs['obsmarkers'] = True
1707 pullop.stepsdone.add('obsmarkers')
1712 pullop.stepsdone.add('obsmarkers')
1708 _pullbundle2extraprepare(pullop, kwargs)
1713 _pullbundle2extraprepare(pullop, kwargs)
1709
1714
1710 with pullop.remote.commandexecutor() as e:
1715 with pullop.remote.commandexecutor() as e:
1711 args = dict(kwargs)
1716 args = dict(kwargs)
1712 args['source'] = 'pull'
1717 args['source'] = 'pull'
1713 bundle = e.callcommand('getbundle', args).result()
1718 bundle = e.callcommand('getbundle', args).result()
1714
1719
1715 try:
1720 try:
1716 op = bundle2.bundleoperation(pullop.repo, pullop.gettransaction,
1721 op = bundle2.bundleoperation(pullop.repo, pullop.gettransaction,
1717 source='pull')
1722 source='pull')
1718 op.modes['bookmarks'] = 'records'
1723 op.modes['bookmarks'] = 'records'
1719 bundle2.processbundle(pullop.repo, bundle, op=op)
1724 bundle2.processbundle(pullop.repo, bundle, op=op)
1720 except bundle2.AbortFromPart as exc:
1725 except bundle2.AbortFromPart as exc:
1721 pullop.repo.ui.status(_('remote: abort: %s\n') % exc)
1726 pullop.repo.ui.status(_('remote: abort: %s\n') % exc)
1722 raise error.Abort(_('pull failed on remote'), hint=exc.hint)
1727 raise error.Abort(_('pull failed on remote'), hint=exc.hint)
1723 except error.BundleValueError as exc:
1728 except error.BundleValueError as exc:
1724 raise error.Abort(_('missing support for %s') % exc)
1729 raise error.Abort(_('missing support for %s') % exc)
1725
1730
1726 if pullop.fetch:
1731 if pullop.fetch:
1727 pullop.cgresult = bundle2.combinechangegroupresults(op)
1732 pullop.cgresult = bundle2.combinechangegroupresults(op)
1728
1733
1729 # processing phases change
1734 # processing phases change
1730 for namespace, value in op.records['listkeys']:
1735 for namespace, value in op.records['listkeys']:
1731 if namespace == 'phases':
1736 if namespace == 'phases':
1732 _pullapplyphases(pullop, value)
1737 _pullapplyphases(pullop, value)
1733
1738
1734 # processing bookmark update
1739 # processing bookmark update
1735 if bookmarksrequested:
1740 if bookmarksrequested:
1736 books = {}
1741 books = {}
1737 for record in op.records['bookmarks']:
1742 for record in op.records['bookmarks']:
1738 books[record['bookmark']] = record["node"]
1743 books[record['bookmark']] = record["node"]
1739 pullop.remotebookmarks = books
1744 pullop.remotebookmarks = books
1740 else:
1745 else:
1741 for namespace, value in op.records['listkeys']:
1746 for namespace, value in op.records['listkeys']:
1742 if namespace == 'bookmarks':
1747 if namespace == 'bookmarks':
1743 pullop.remotebookmarks = bookmod.unhexlifybookmarks(value)
1748 pullop.remotebookmarks = bookmod.unhexlifybookmarks(value)
1744
1749
1745 # bookmark data were either already there or pulled in the bundle
1750 # bookmark data were either already there or pulled in the bundle
1746 if pullop.remotebookmarks is not None:
1751 if pullop.remotebookmarks is not None:
1747 _pullbookmarks(pullop)
1752 _pullbookmarks(pullop)
1748
1753
1749 def _pullbundle2extraprepare(pullop, kwargs):
1754 def _pullbundle2extraprepare(pullop, kwargs):
1750 """hook function so that extensions can extend the getbundle call"""
1755 """hook function so that extensions can extend the getbundle call"""
1751
1756
1752 def _pullchangeset(pullop):
1757 def _pullchangeset(pullop):
1753 """pull changeset from unbundle into the local repo"""
1758 """pull changeset from unbundle into the local repo"""
1754 # We delay the open of the transaction as late as possible so we
1759 # We delay the open of the transaction as late as possible so we
1755 # don't open transaction for nothing or you break future useful
1760 # don't open transaction for nothing or you break future useful
1756 # rollback call
1761 # rollback call
1757 if 'changegroup' in pullop.stepsdone:
1762 if 'changegroup' in pullop.stepsdone:
1758 return
1763 return
1759 pullop.stepsdone.add('changegroup')
1764 pullop.stepsdone.add('changegroup')
1760 if not pullop.fetch:
1765 if not pullop.fetch:
1761 pullop.repo.ui.status(_("no changes found\n"))
1766 pullop.repo.ui.status(_("no changes found\n"))
1762 pullop.cgresult = 0
1767 pullop.cgresult = 0
1763 return
1768 return
1764 tr = pullop.gettransaction()
1769 tr = pullop.gettransaction()
1765 if pullop.heads is None and list(pullop.common) == [nullid]:
1770 if pullop.heads is None and list(pullop.common) == [nullid]:
1766 pullop.repo.ui.status(_("requesting all changes\n"))
1771 pullop.repo.ui.status(_("requesting all changes\n"))
1767 elif pullop.heads is None and pullop.remote.capable('changegroupsubset'):
1772 elif pullop.heads is None and pullop.remote.capable('changegroupsubset'):
1768 # issue1320, avoid a race if remote changed after discovery
1773 # issue1320, avoid a race if remote changed after discovery
1769 pullop.heads = pullop.rheads
1774 pullop.heads = pullop.rheads
1770
1775
1771 if pullop.remote.capable('getbundle'):
1776 if pullop.remote.capable('getbundle'):
1772 # TODO: get bundlecaps from remote
1777 # TODO: get bundlecaps from remote
1773 cg = pullop.remote.getbundle('pull', common=pullop.common,
1778 cg = pullop.remote.getbundle('pull', common=pullop.common,
1774 heads=pullop.heads or pullop.rheads)
1779 heads=pullop.heads or pullop.rheads)
1775 elif pullop.heads is None:
1780 elif pullop.heads is None:
1776 with pullop.remote.commandexecutor() as e:
1781 with pullop.remote.commandexecutor() as e:
1777 cg = e.callcommand('changegroup', {
1782 cg = e.callcommand('changegroup', {
1778 'nodes': pullop.fetch,
1783 'nodes': pullop.fetch,
1779 'source': 'pull',
1784 'source': 'pull',
1780 }).result()
1785 }).result()
1781
1786
1782 elif not pullop.remote.capable('changegroupsubset'):
1787 elif not pullop.remote.capable('changegroupsubset'):
1783 raise error.Abort(_("partial pull cannot be done because "
1788 raise error.Abort(_("partial pull cannot be done because "
1784 "other repository doesn't support "
1789 "other repository doesn't support "
1785 "changegroupsubset."))
1790 "changegroupsubset."))
1786 else:
1791 else:
1787 with pullop.remote.commandexecutor() as e:
1792 with pullop.remote.commandexecutor() as e:
1788 cg = e.callcommand('changegroupsubset', {
1793 cg = e.callcommand('changegroupsubset', {
1789 'bases': pullop.fetch,
1794 'bases': pullop.fetch,
1790 'heads': pullop.heads,
1795 'heads': pullop.heads,
1791 'source': 'pull',
1796 'source': 'pull',
1792 }).result()
1797 }).result()
1793
1798
1794 bundleop = bundle2.applybundle(pullop.repo, cg, tr, 'pull',
1799 bundleop = bundle2.applybundle(pullop.repo, cg, tr, 'pull',
1795 pullop.remote.url())
1800 pullop.remote.url())
1796 pullop.cgresult = bundle2.combinechangegroupresults(bundleop)
1801 pullop.cgresult = bundle2.combinechangegroupresults(bundleop)
1797
1802
1798 def _pullphase(pullop):
1803 def _pullphase(pullop):
1799 # Get remote phases data from remote
1804 # Get remote phases data from remote
1800 if 'phases' in pullop.stepsdone:
1805 if 'phases' in pullop.stepsdone:
1801 return
1806 return
1802 remotephases = listkeys(pullop.remote, 'phases')
1807 remotephases = listkeys(pullop.remote, 'phases')
1803 _pullapplyphases(pullop, remotephases)
1808 _pullapplyphases(pullop, remotephases)
1804
1809
1805 def _pullapplyphases(pullop, remotephases):
1810 def _pullapplyphases(pullop, remotephases):
1806 """apply phase movement from observed remote state"""
1811 """apply phase movement from observed remote state"""
1807 if 'phases' in pullop.stepsdone:
1812 if 'phases' in pullop.stepsdone:
1808 return
1813 return
1809 pullop.stepsdone.add('phases')
1814 pullop.stepsdone.add('phases')
1810 publishing = bool(remotephases.get('publishing', False))
1815 publishing = bool(remotephases.get('publishing', False))
1811 if remotephases and not publishing:
1816 if remotephases and not publishing:
1812 # remote is new and non-publishing
1817 # remote is new and non-publishing
1813 pheads, _dr = phases.analyzeremotephases(pullop.repo,
1818 pheads, _dr = phases.analyzeremotephases(pullop.repo,
1814 pullop.pulledsubset,
1819 pullop.pulledsubset,
1815 remotephases)
1820 remotephases)
1816 dheads = pullop.pulledsubset
1821 dheads = pullop.pulledsubset
1817 else:
1822 else:
1818 # Remote is old or publishing all common changesets
1823 # Remote is old or publishing all common changesets
1819 # should be seen as public
1824 # should be seen as public
1820 pheads = pullop.pulledsubset
1825 pheads = pullop.pulledsubset
1821 dheads = []
1826 dheads = []
1822 unfi = pullop.repo.unfiltered()
1827 unfi = pullop.repo.unfiltered()
1823 phase = unfi._phasecache.phase
1828 phase = unfi._phasecache.phase
1824 rev = unfi.changelog.nodemap.get
1829 rev = unfi.changelog.nodemap.get
1825 public = phases.public
1830 public = phases.public
1826 draft = phases.draft
1831 draft = phases.draft
1827
1832
1828 # exclude changesets already public locally and update the others
1833 # exclude changesets already public locally and update the others
1829 pheads = [pn for pn in pheads if phase(unfi, rev(pn)) > public]
1834 pheads = [pn for pn in pheads if phase(unfi, rev(pn)) > public]
1830 if pheads:
1835 if pheads:
1831 tr = pullop.gettransaction()
1836 tr = pullop.gettransaction()
1832 phases.advanceboundary(pullop.repo, tr, public, pheads)
1837 phases.advanceboundary(pullop.repo, tr, public, pheads)
1833
1838
1834 # exclude changesets already draft locally and update the others
1839 # exclude changesets already draft locally and update the others
1835 dheads = [pn for pn in dheads if phase(unfi, rev(pn)) > draft]
1840 dheads = [pn for pn in dheads if phase(unfi, rev(pn)) > draft]
1836 if dheads:
1841 if dheads:
1837 tr = pullop.gettransaction()
1842 tr = pullop.gettransaction()
1838 phases.advanceboundary(pullop.repo, tr, draft, dheads)
1843 phases.advanceboundary(pullop.repo, tr, draft, dheads)
1839
1844
1840 def _pullbookmarks(pullop):
1845 def _pullbookmarks(pullop):
1841 """process the remote bookmark information to update the local one"""
1846 """process the remote bookmark information to update the local one"""
1842 if 'bookmarks' in pullop.stepsdone:
1847 if 'bookmarks' in pullop.stepsdone:
1843 return
1848 return
1844 pullop.stepsdone.add('bookmarks')
1849 pullop.stepsdone.add('bookmarks')
1845 repo = pullop.repo
1850 repo = pullop.repo
1846 remotebookmarks = pullop.remotebookmarks
1851 remotebookmarks = pullop.remotebookmarks
1847 bookmod.updatefromremote(repo.ui, repo, remotebookmarks,
1852 bookmod.updatefromremote(repo.ui, repo, remotebookmarks,
1848 pullop.remote.url(),
1853 pullop.remote.url(),
1849 pullop.gettransaction,
1854 pullop.gettransaction,
1850 explicit=pullop.explicitbookmarks)
1855 explicit=pullop.explicitbookmarks)
1851
1856
1852 def _pullobsolete(pullop):
1857 def _pullobsolete(pullop):
1853 """utility function to pull obsolete markers from a remote
1858 """utility function to pull obsolete markers from a remote
1854
1859
1855 The `gettransaction` is function that return the pull transaction, creating
1860 The `gettransaction` is function that return the pull transaction, creating
1856 one if necessary. We return the transaction to inform the calling code that
1861 one if necessary. We return the transaction to inform the calling code that
1857 a new transaction have been created (when applicable).
1862 a new transaction have been created (when applicable).
1858
1863
1859 Exists mostly to allow overriding for experimentation purpose"""
1864 Exists mostly to allow overriding for experimentation purpose"""
1860 if 'obsmarkers' in pullop.stepsdone:
1865 if 'obsmarkers' in pullop.stepsdone:
1861 return
1866 return
1862 pullop.stepsdone.add('obsmarkers')
1867 pullop.stepsdone.add('obsmarkers')
1863 tr = None
1868 tr = None
1864 if obsolete.isenabled(pullop.repo, obsolete.exchangeopt):
1869 if obsolete.isenabled(pullop.repo, obsolete.exchangeopt):
1865 pullop.repo.ui.debug('fetching remote obsolete markers\n')
1870 pullop.repo.ui.debug('fetching remote obsolete markers\n')
1866 remoteobs = listkeys(pullop.remote, 'obsolete')
1871 remoteobs = listkeys(pullop.remote, 'obsolete')
1867 if 'dump0' in remoteobs:
1872 if 'dump0' in remoteobs:
1868 tr = pullop.gettransaction()
1873 tr = pullop.gettransaction()
1869 markers = []
1874 markers = []
1870 for key in sorted(remoteobs, reverse=True):
1875 for key in sorted(remoteobs, reverse=True):
1871 if key.startswith('dump'):
1876 if key.startswith('dump'):
1872 data = util.b85decode(remoteobs[key])
1877 data = util.b85decode(remoteobs[key])
1873 version, newmarks = obsolete._readmarkers(data)
1878 version, newmarks = obsolete._readmarkers(data)
1874 markers += newmarks
1879 markers += newmarks
1875 if markers:
1880 if markers:
1876 pullop.repo.obsstore.add(tr, markers)
1881 pullop.repo.obsstore.add(tr, markers)
1877 pullop.repo.invalidatevolatilesets()
1882 pullop.repo.invalidatevolatilesets()
1878 return tr
1883 return tr
1879
1884
1880 def applynarrowacl(repo, kwargs):
1885 def applynarrowacl(repo, kwargs):
1881 """Apply narrow fetch access control.
1886 """Apply narrow fetch access control.
1882
1887
1883 This massages the named arguments for getbundle wire protocol commands
1888 This massages the named arguments for getbundle wire protocol commands
1884 so requested data is filtered through access control rules.
1889 so requested data is filtered through access control rules.
1885 """
1890 """
1886 ui = repo.ui
1891 ui = repo.ui
1887 # TODO this assumes existence of HTTP and is a layering violation.
1892 # TODO this assumes existence of HTTP and is a layering violation.
1888 username = ui.shortuser(ui.environ.get('REMOTE_USER') or ui.username())
1893 username = ui.shortuser(ui.environ.get('REMOTE_USER') or ui.username())
1889 user_includes = ui.configlist(
1894 user_includes = ui.configlist(
1890 _NARROWACL_SECTION, username + '.includes',
1895 _NARROWACL_SECTION, username + '.includes',
1891 ui.configlist(_NARROWACL_SECTION, 'default.includes'))
1896 ui.configlist(_NARROWACL_SECTION, 'default.includes'))
1892 user_excludes = ui.configlist(
1897 user_excludes = ui.configlist(
1893 _NARROWACL_SECTION, username + '.excludes',
1898 _NARROWACL_SECTION, username + '.excludes',
1894 ui.configlist(_NARROWACL_SECTION, 'default.excludes'))
1899 ui.configlist(_NARROWACL_SECTION, 'default.excludes'))
1895 if not user_includes:
1900 if not user_includes:
1896 raise error.Abort(_("{} configuration for user {} is empty")
1901 raise error.Abort(_("{} configuration for user {} is empty")
1897 .format(_NARROWACL_SECTION, username))
1902 .format(_NARROWACL_SECTION, username))
1898
1903
1899 user_includes = [
1904 user_includes = [
1900 'path:.' if p == '*' else 'path:' + p for p in user_includes]
1905 'path:.' if p == '*' else 'path:' + p for p in user_includes]
1901 user_excludes = [
1906 user_excludes = [
1902 'path:.' if p == '*' else 'path:' + p for p in user_excludes]
1907 'path:.' if p == '*' else 'path:' + p for p in user_excludes]
1903
1908
1904 req_includes = set(kwargs.get(r'includepats', []))
1909 req_includes = set(kwargs.get(r'includepats', []))
1905 req_excludes = set(kwargs.get(r'excludepats', []))
1910 req_excludes = set(kwargs.get(r'excludepats', []))
1906
1911
1907 req_includes, req_excludes, invalid_includes = narrowspec.restrictpatterns(
1912 req_includes, req_excludes, invalid_includes = narrowspec.restrictpatterns(
1908 req_includes, req_excludes, user_includes, user_excludes)
1913 req_includes, req_excludes, user_includes, user_excludes)
1909
1914
1910 if invalid_includes:
1915 if invalid_includes:
1911 raise error.Abort(
1916 raise error.Abort(
1912 _("The following includes are not accessible for {}: {}")
1917 _("The following includes are not accessible for {}: {}")
1913 .format(username, invalid_includes))
1918 .format(username, invalid_includes))
1914
1919
1915 new_args = {}
1920 new_args = {}
1916 new_args.update(kwargs)
1921 new_args.update(kwargs)
1917 new_args[r'narrow'] = True
1922 new_args[r'narrow'] = True
1918 new_args[r'narrow_acl'] = True
1923 new_args[r'narrow_acl'] = True
1919 new_args[r'includepats'] = req_includes
1924 new_args[r'includepats'] = req_includes
1920 if req_excludes:
1925 if req_excludes:
1921 new_args[r'excludepats'] = req_excludes
1926 new_args[r'excludepats'] = req_excludes
1922
1927
1923 return new_args
1928 return new_args
1924
1929
1925 def _computeellipsis(repo, common, heads, known, match, depth=None):
1930 def _computeellipsis(repo, common, heads, known, match, depth=None):
1926 """Compute the shape of a narrowed DAG.
1931 """Compute the shape of a narrowed DAG.
1927
1932
1928 Args:
1933 Args:
1929 repo: The repository we're transferring.
1934 repo: The repository we're transferring.
1930 common: The roots of the DAG range we're transferring.
1935 common: The roots of the DAG range we're transferring.
1931 May be just [nullid], which means all ancestors of heads.
1936 May be just [nullid], which means all ancestors of heads.
1932 heads: The heads of the DAG range we're transferring.
1937 heads: The heads of the DAG range we're transferring.
1933 match: The narrowmatcher that allows us to identify relevant changes.
1938 match: The narrowmatcher that allows us to identify relevant changes.
1934 depth: If not None, only consider nodes to be full nodes if they are at
1939 depth: If not None, only consider nodes to be full nodes if they are at
1935 most depth changesets away from one of heads.
1940 most depth changesets away from one of heads.
1936
1941
1937 Returns:
1942 Returns:
1938 A tuple of (visitnodes, relevant_nodes, ellipsisroots) where:
1943 A tuple of (visitnodes, relevant_nodes, ellipsisroots) where:
1939
1944
1940 visitnodes: The list of nodes (either full or ellipsis) which
1945 visitnodes: The list of nodes (either full or ellipsis) which
1941 need to be sent to the client.
1946 need to be sent to the client.
1942 relevant_nodes: The set of changelog nodes which change a file inside
1947 relevant_nodes: The set of changelog nodes which change a file inside
1943 the narrowspec. The client needs these as non-ellipsis nodes.
1948 the narrowspec. The client needs these as non-ellipsis nodes.
1944 ellipsisroots: A dict of {rev: parents} that is used in
1949 ellipsisroots: A dict of {rev: parents} that is used in
1945 narrowchangegroup to produce ellipsis nodes with the
1950 narrowchangegroup to produce ellipsis nodes with the
1946 correct parents.
1951 correct parents.
1947 """
1952 """
1948 cl = repo.changelog
1953 cl = repo.changelog
1949 mfl = repo.manifestlog
1954 mfl = repo.manifestlog
1950
1955
1951 clrev = cl.rev
1956 clrev = cl.rev
1952
1957
1953 commonrevs = {clrev(n) for n in common} | {nullrev}
1958 commonrevs = {clrev(n) for n in common} | {nullrev}
1954 headsrevs = {clrev(n) for n in heads}
1959 headsrevs = {clrev(n) for n in heads}
1955
1960
1956 if depth:
1961 if depth:
1957 revdepth = {h: 0 for h in headsrevs}
1962 revdepth = {h: 0 for h in headsrevs}
1958
1963
1959 ellipsisheads = collections.defaultdict(set)
1964 ellipsisheads = collections.defaultdict(set)
1960 ellipsisroots = collections.defaultdict(set)
1965 ellipsisroots = collections.defaultdict(set)
1961
1966
1962 def addroot(head, curchange):
1967 def addroot(head, curchange):
1963 """Add a root to an ellipsis head, splitting heads with 3 roots."""
1968 """Add a root to an ellipsis head, splitting heads with 3 roots."""
1964 ellipsisroots[head].add(curchange)
1969 ellipsisroots[head].add(curchange)
1965 # Recursively split ellipsis heads with 3 roots by finding the
1970 # Recursively split ellipsis heads with 3 roots by finding the
1966 # roots' youngest common descendant which is an elided merge commit.
1971 # roots' youngest common descendant which is an elided merge commit.
1967 # That descendant takes 2 of the 3 roots as its own, and becomes a
1972 # That descendant takes 2 of the 3 roots as its own, and becomes a
1968 # root of the head.
1973 # root of the head.
1969 while len(ellipsisroots[head]) > 2:
1974 while len(ellipsisroots[head]) > 2:
1970 child, roots = splithead(head)
1975 child, roots = splithead(head)
1971 splitroots(head, child, roots)
1976 splitroots(head, child, roots)
1972 head = child # Recurse in case we just added a 3rd root
1977 head = child # Recurse in case we just added a 3rd root
1973
1978
1974 def splitroots(head, child, roots):
1979 def splitroots(head, child, roots):
1975 ellipsisroots[head].difference_update(roots)
1980 ellipsisroots[head].difference_update(roots)
1976 ellipsisroots[head].add(child)
1981 ellipsisroots[head].add(child)
1977 ellipsisroots[child].update(roots)
1982 ellipsisroots[child].update(roots)
1978 ellipsisroots[child].discard(child)
1983 ellipsisroots[child].discard(child)
1979
1984
1980 def splithead(head):
1985 def splithead(head):
1981 r1, r2, r3 = sorted(ellipsisroots[head])
1986 r1, r2, r3 = sorted(ellipsisroots[head])
1982 for nr1, nr2 in ((r2, r3), (r1, r3), (r1, r2)):
1987 for nr1, nr2 in ((r2, r3), (r1, r3), (r1, r2)):
1983 mid = repo.revs('sort(merge() & %d::%d & %d::%d, -rev)',
1988 mid = repo.revs('sort(merge() & %d::%d & %d::%d, -rev)',
1984 nr1, head, nr2, head)
1989 nr1, head, nr2, head)
1985 for j in mid:
1990 for j in mid:
1986 if j == nr2:
1991 if j == nr2:
1987 return nr2, (nr1, nr2)
1992 return nr2, (nr1, nr2)
1988 if j not in ellipsisroots or len(ellipsisroots[j]) < 2:
1993 if j not in ellipsisroots or len(ellipsisroots[j]) < 2:
1989 return j, (nr1, nr2)
1994 return j, (nr1, nr2)
1990 raise error.Abort(_('Failed to split up ellipsis node! head: %d, '
1995 raise error.Abort(_('Failed to split up ellipsis node! head: %d, '
1991 'roots: %d %d %d') % (head, r1, r2, r3))
1996 'roots: %d %d %d') % (head, r1, r2, r3))
1992
1997
1993 missing = list(cl.findmissingrevs(common=commonrevs, heads=headsrevs))
1998 missing = list(cl.findmissingrevs(common=commonrevs, heads=headsrevs))
1994 visit = reversed(missing)
1999 visit = reversed(missing)
1995 relevant_nodes = set()
2000 relevant_nodes = set()
1996 visitnodes = [cl.node(m) for m in missing]
2001 visitnodes = [cl.node(m) for m in missing]
1997 required = set(headsrevs) | known
2002 required = set(headsrevs) | known
1998 for rev in visit:
2003 for rev in visit:
1999 clrev = cl.changelogrevision(rev)
2004 clrev = cl.changelogrevision(rev)
2000 ps = [prev for prev in cl.parentrevs(rev) if prev != nullrev]
2005 ps = [prev for prev in cl.parentrevs(rev) if prev != nullrev]
2001 if depth is not None:
2006 if depth is not None:
2002 curdepth = revdepth[rev]
2007 curdepth = revdepth[rev]
2003 for p in ps:
2008 for p in ps:
2004 revdepth[p] = min(curdepth + 1, revdepth.get(p, depth + 1))
2009 revdepth[p] = min(curdepth + 1, revdepth.get(p, depth + 1))
2005 needed = False
2010 needed = False
2006 shallow_enough = depth is None or revdepth[rev] <= depth
2011 shallow_enough = depth is None or revdepth[rev] <= depth
2007 if shallow_enough:
2012 if shallow_enough:
2008 curmf = mfl[clrev.manifest].read()
2013 curmf = mfl[clrev.manifest].read()
2009 if ps:
2014 if ps:
2010 # We choose to not trust the changed files list in
2015 # We choose to not trust the changed files list in
2011 # changesets because it's not always correct. TODO: could
2016 # changesets because it's not always correct. TODO: could
2012 # we trust it for the non-merge case?
2017 # we trust it for the non-merge case?
2013 p1mf = mfl[cl.changelogrevision(ps[0]).manifest].read()
2018 p1mf = mfl[cl.changelogrevision(ps[0]).manifest].read()
2014 needed = bool(curmf.diff(p1mf, match))
2019 needed = bool(curmf.diff(p1mf, match))
2015 if not needed and len(ps) > 1:
2020 if not needed and len(ps) > 1:
2016 # For merge changes, the list of changed files is not
2021 # For merge changes, the list of changed files is not
2017 # helpful, since we need to emit the merge if a file
2022 # helpful, since we need to emit the merge if a file
2018 # in the narrow spec has changed on either side of the
2023 # in the narrow spec has changed on either side of the
2019 # merge. As a result, we do a manifest diff to check.
2024 # merge. As a result, we do a manifest diff to check.
2020 p2mf = mfl[cl.changelogrevision(ps[1]).manifest].read()
2025 p2mf = mfl[cl.changelogrevision(ps[1]).manifest].read()
2021 needed = bool(curmf.diff(p2mf, match))
2026 needed = bool(curmf.diff(p2mf, match))
2022 else:
2027 else:
2023 # For a root node, we need to include the node if any
2028 # For a root node, we need to include the node if any
2024 # files in the node match the narrowspec.
2029 # files in the node match the narrowspec.
2025 needed = any(curmf.walk(match))
2030 needed = any(curmf.walk(match))
2026
2031
2027 if needed:
2032 if needed:
2028 for head in ellipsisheads[rev]:
2033 for head in ellipsisheads[rev]:
2029 addroot(head, rev)
2034 addroot(head, rev)
2030 for p in ps:
2035 for p in ps:
2031 required.add(p)
2036 required.add(p)
2032 relevant_nodes.add(cl.node(rev))
2037 relevant_nodes.add(cl.node(rev))
2033 else:
2038 else:
2034 if not ps:
2039 if not ps:
2035 ps = [nullrev]
2040 ps = [nullrev]
2036 if rev in required:
2041 if rev in required:
2037 for head in ellipsisheads[rev]:
2042 for head in ellipsisheads[rev]:
2038 addroot(head, rev)
2043 addroot(head, rev)
2039 for p in ps:
2044 for p in ps:
2040 ellipsisheads[p].add(rev)
2045 ellipsisheads[p].add(rev)
2041 else:
2046 else:
2042 for p in ps:
2047 for p in ps:
2043 ellipsisheads[p] |= ellipsisheads[rev]
2048 ellipsisheads[p] |= ellipsisheads[rev]
2044
2049
2045 # add common changesets as roots of their reachable ellipsis heads
2050 # add common changesets as roots of their reachable ellipsis heads
2046 for c in commonrevs:
2051 for c in commonrevs:
2047 for head in ellipsisheads[c]:
2052 for head in ellipsisheads[c]:
2048 addroot(head, c)
2053 addroot(head, c)
2049 return visitnodes, relevant_nodes, ellipsisroots
2054 return visitnodes, relevant_nodes, ellipsisroots
2050
2055
2051 def caps20to10(repo, role):
2056 def caps20to10(repo, role):
2052 """return a set with appropriate options to use bundle20 during getbundle"""
2057 """return a set with appropriate options to use bundle20 during getbundle"""
2053 caps = {'HG20'}
2058 caps = {'HG20'}
2054 capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo, role=role))
2059 capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo, role=role))
2055 caps.add('bundle2=' + urlreq.quote(capsblob))
2060 caps.add('bundle2=' + urlreq.quote(capsblob))
2056 return caps
2061 return caps
2057
2062
2058 # List of names of steps to perform for a bundle2 for getbundle, order matters.
2063 # List of names of steps to perform for a bundle2 for getbundle, order matters.
2059 getbundle2partsorder = []
2064 getbundle2partsorder = []
2060
2065
2061 # Mapping between step name and function
2066 # Mapping between step name and function
2062 #
2067 #
2063 # This exists to help extensions wrap steps if necessary
2068 # This exists to help extensions wrap steps if necessary
2064 getbundle2partsmapping = {}
2069 getbundle2partsmapping = {}
2065
2070
2066 def getbundle2partsgenerator(stepname, idx=None):
2071 def getbundle2partsgenerator(stepname, idx=None):
2067 """decorator for function generating bundle2 part for getbundle
2072 """decorator for function generating bundle2 part for getbundle
2068
2073
2069 The function is added to the step -> function mapping and appended to the
2074 The function is added to the step -> function mapping and appended to the
2070 list of steps. Beware that decorated functions will be added in order
2075 list of steps. Beware that decorated functions will be added in order
2071 (this may matter).
2076 (this may matter).
2072
2077
2073 You can only use this decorator for new steps, if you want to wrap a step
2078 You can only use this decorator for new steps, if you want to wrap a step
2074 from an extension, attack the getbundle2partsmapping dictionary directly."""
2079 from an extension, attack the getbundle2partsmapping dictionary directly."""
2075 def dec(func):
2080 def dec(func):
2076 assert stepname not in getbundle2partsmapping
2081 assert stepname not in getbundle2partsmapping
2077 getbundle2partsmapping[stepname] = func
2082 getbundle2partsmapping[stepname] = func
2078 if idx is None:
2083 if idx is None:
2079 getbundle2partsorder.append(stepname)
2084 getbundle2partsorder.append(stepname)
2080 else:
2085 else:
2081 getbundle2partsorder.insert(idx, stepname)
2086 getbundle2partsorder.insert(idx, stepname)
2082 return func
2087 return func
2083 return dec
2088 return dec
2084
2089
2085 def bundle2requested(bundlecaps):
2090 def bundle2requested(bundlecaps):
2086 if bundlecaps is not None:
2091 if bundlecaps is not None:
2087 return any(cap.startswith('HG2') for cap in bundlecaps)
2092 return any(cap.startswith('HG2') for cap in bundlecaps)
2088 return False
2093 return False
2089
2094
2090 def getbundlechunks(repo, source, heads=None, common=None, bundlecaps=None,
2095 def getbundlechunks(repo, source, heads=None, common=None, bundlecaps=None,
2091 **kwargs):
2096 **kwargs):
2092 """Return chunks constituting a bundle's raw data.
2097 """Return chunks constituting a bundle's raw data.
2093
2098
2094 Could be a bundle HG10 or a bundle HG20 depending on bundlecaps
2099 Could be a bundle HG10 or a bundle HG20 depending on bundlecaps
2095 passed.
2100 passed.
2096
2101
2097 Returns a 2-tuple of a dict with metadata about the generated bundle
2102 Returns a 2-tuple of a dict with metadata about the generated bundle
2098 and an iterator over raw chunks (of varying sizes).
2103 and an iterator over raw chunks (of varying sizes).
2099 """
2104 """
2100 kwargs = pycompat.byteskwargs(kwargs)
2105 kwargs = pycompat.byteskwargs(kwargs)
2101 info = {}
2106 info = {}
2102 usebundle2 = bundle2requested(bundlecaps)
2107 usebundle2 = bundle2requested(bundlecaps)
2103 # bundle10 case
2108 # bundle10 case
2104 if not usebundle2:
2109 if not usebundle2:
2105 if bundlecaps and not kwargs.get('cg', True):
2110 if bundlecaps and not kwargs.get('cg', True):
2106 raise ValueError(_('request for bundle10 must include changegroup'))
2111 raise ValueError(_('request for bundle10 must include changegroup'))
2107
2112
2108 if kwargs:
2113 if kwargs:
2109 raise ValueError(_('unsupported getbundle arguments: %s')
2114 raise ValueError(_('unsupported getbundle arguments: %s')
2110 % ', '.join(sorted(kwargs.keys())))
2115 % ', '.join(sorted(kwargs.keys())))
2111 outgoing = _computeoutgoing(repo, heads, common)
2116 outgoing = _computeoutgoing(repo, heads, common)
2112 info['bundleversion'] = 1
2117 info['bundleversion'] = 1
2113 return info, changegroup.makestream(repo, outgoing, '01', source,
2118 return info, changegroup.makestream(repo, outgoing, '01', source,
2114 bundlecaps=bundlecaps)
2119 bundlecaps=bundlecaps)
2115
2120
2116 # bundle20 case
2121 # bundle20 case
2117 info['bundleversion'] = 2
2122 info['bundleversion'] = 2
2118 b2caps = {}
2123 b2caps = {}
2119 for bcaps in bundlecaps:
2124 for bcaps in bundlecaps:
2120 if bcaps.startswith('bundle2='):
2125 if bcaps.startswith('bundle2='):
2121 blob = urlreq.unquote(bcaps[len('bundle2='):])
2126 blob = urlreq.unquote(bcaps[len('bundle2='):])
2122 b2caps.update(bundle2.decodecaps(blob))
2127 b2caps.update(bundle2.decodecaps(blob))
2123 bundler = bundle2.bundle20(repo.ui, b2caps)
2128 bundler = bundle2.bundle20(repo.ui, b2caps)
2124
2129
2125 kwargs['heads'] = heads
2130 kwargs['heads'] = heads
2126 kwargs['common'] = common
2131 kwargs['common'] = common
2127
2132
2128 for name in getbundle2partsorder:
2133 for name in getbundle2partsorder:
2129 func = getbundle2partsmapping[name]
2134 func = getbundle2partsmapping[name]
2130 func(bundler, repo, source, bundlecaps=bundlecaps, b2caps=b2caps,
2135 func(bundler, repo, source, bundlecaps=bundlecaps, b2caps=b2caps,
2131 **pycompat.strkwargs(kwargs))
2136 **pycompat.strkwargs(kwargs))
2132
2137
2133 info['prefercompressed'] = bundler.prefercompressed
2138 info['prefercompressed'] = bundler.prefercompressed
2134
2139
2135 return info, bundler.getchunks()
2140 return info, bundler.getchunks()
2136
2141
2137 @getbundle2partsgenerator('stream2')
2142 @getbundle2partsgenerator('stream2')
2138 def _getbundlestream2(bundler, repo, *args, **kwargs):
2143 def _getbundlestream2(bundler, repo, *args, **kwargs):
2139 return bundle2.addpartbundlestream2(bundler, repo, **kwargs)
2144 return bundle2.addpartbundlestream2(bundler, repo, **kwargs)
2140
2145
2141 @getbundle2partsgenerator('changegroup')
2146 @getbundle2partsgenerator('changegroup')
2142 def _getbundlechangegrouppart(bundler, repo, source, bundlecaps=None,
2147 def _getbundlechangegrouppart(bundler, repo, source, bundlecaps=None,
2143 b2caps=None, heads=None, common=None, **kwargs):
2148 b2caps=None, heads=None, common=None, **kwargs):
2144 """add a changegroup part to the requested bundle"""
2149 """add a changegroup part to the requested bundle"""
2145 if not kwargs.get(r'cg', True):
2150 if not kwargs.get(r'cg', True):
2146 return
2151 return
2147
2152
2148 version = '01'
2153 version = '01'
2149 cgversions = b2caps.get('changegroup')
2154 cgversions = b2caps.get('changegroup')
2150 if cgversions: # 3.1 and 3.2 ship with an empty value
2155 if cgversions: # 3.1 and 3.2 ship with an empty value
2151 cgversions = [v for v in cgversions
2156 cgversions = [v for v in cgversions
2152 if v in changegroup.supportedoutgoingversions(repo)]
2157 if v in changegroup.supportedoutgoingversions(repo)]
2153 if not cgversions:
2158 if not cgversions:
2154 raise ValueError(_('no common changegroup version'))
2159 raise ValueError(_('no common changegroup version'))
2155 version = max(cgversions)
2160 version = max(cgversions)
2156
2161
2157 outgoing = _computeoutgoing(repo, heads, common)
2162 outgoing = _computeoutgoing(repo, heads, common)
2158 if not outgoing.missing:
2163 if not outgoing.missing:
2159 return
2164 return
2160
2165
2161 if kwargs.get(r'narrow', False):
2166 if kwargs.get(r'narrow', False):
2162 include = sorted(filter(bool, kwargs.get(r'includepats', [])))
2167 include = sorted(filter(bool, kwargs.get(r'includepats', [])))
2163 exclude = sorted(filter(bool, kwargs.get(r'excludepats', [])))
2168 exclude = sorted(filter(bool, kwargs.get(r'excludepats', [])))
2164 matcher = narrowspec.match(repo.root, include=include, exclude=exclude)
2169 matcher = narrowspec.match(repo.root, include=include, exclude=exclude)
2165 else:
2170 else:
2166 matcher = None
2171 matcher = None
2167
2172
2168 cgstream = changegroup.makestream(repo, outgoing, version, source,
2173 cgstream = changegroup.makestream(repo, outgoing, version, source,
2169 bundlecaps=bundlecaps, matcher=matcher)
2174 bundlecaps=bundlecaps, matcher=matcher)
2170
2175
2171 part = bundler.newpart('changegroup', data=cgstream)
2176 part = bundler.newpart('changegroup', data=cgstream)
2172 if cgversions:
2177 if cgversions:
2173 part.addparam('version', version)
2178 part.addparam('version', version)
2174
2179
2175 part.addparam('nbchanges', '%d' % len(outgoing.missing),
2180 part.addparam('nbchanges', '%d' % len(outgoing.missing),
2176 mandatory=False)
2181 mandatory=False)
2177
2182
2178 if 'treemanifest' in repo.requirements:
2183 if 'treemanifest' in repo.requirements:
2179 part.addparam('treemanifest', '1')
2184 part.addparam('treemanifest', '1')
2180
2185
2181 if (kwargs.get(r'narrow', False) and kwargs.get(r'narrow_acl', False)
2186 if (kwargs.get(r'narrow', False) and kwargs.get(r'narrow_acl', False)
2182 and (include or exclude)):
2187 and (include or exclude)):
2183 narrowspecpart = bundler.newpart('narrow:spec')
2188 narrowspecpart = bundler.newpart('narrow:spec')
2184 if include:
2189 if include:
2185 narrowspecpart.addparam(
2190 narrowspecpart.addparam(
2186 'include', '\n'.join(include), mandatory=True)
2191 'include', '\n'.join(include), mandatory=True)
2187 if exclude:
2192 if exclude:
2188 narrowspecpart.addparam(
2193 narrowspecpart.addparam(
2189 'exclude', '\n'.join(exclude), mandatory=True)
2194 'exclude', '\n'.join(exclude), mandatory=True)
2190
2195
2191 @getbundle2partsgenerator('bookmarks')
2196 @getbundle2partsgenerator('bookmarks')
2192 def _getbundlebookmarkpart(bundler, repo, source, bundlecaps=None,
2197 def _getbundlebookmarkpart(bundler, repo, source, bundlecaps=None,
2193 b2caps=None, **kwargs):
2198 b2caps=None, **kwargs):
2194 """add a bookmark part to the requested bundle"""
2199 """add a bookmark part to the requested bundle"""
2195 if not kwargs.get(r'bookmarks', False):
2200 if not kwargs.get(r'bookmarks', False):
2196 return
2201 return
2197 if 'bookmarks' not in b2caps:
2202 if 'bookmarks' not in b2caps:
2198 raise ValueError(_('no common bookmarks exchange method'))
2203 raise ValueError(_('no common bookmarks exchange method'))
2199 books = bookmod.listbinbookmarks(repo)
2204 books = bookmod.listbinbookmarks(repo)
2200 data = bookmod.binaryencode(books)
2205 data = bookmod.binaryencode(books)
2201 if data:
2206 if data:
2202 bundler.newpart('bookmarks', data=data)
2207 bundler.newpart('bookmarks', data=data)
2203
2208
2204 @getbundle2partsgenerator('listkeys')
2209 @getbundle2partsgenerator('listkeys')
2205 def _getbundlelistkeysparts(bundler, repo, source, bundlecaps=None,
2210 def _getbundlelistkeysparts(bundler, repo, source, bundlecaps=None,
2206 b2caps=None, **kwargs):
2211 b2caps=None, **kwargs):
2207 """add parts containing listkeys namespaces to the requested bundle"""
2212 """add parts containing listkeys namespaces to the requested bundle"""
2208 listkeys = kwargs.get(r'listkeys', ())
2213 listkeys = kwargs.get(r'listkeys', ())
2209 for namespace in listkeys:
2214 for namespace in listkeys:
2210 part = bundler.newpart('listkeys')
2215 part = bundler.newpart('listkeys')
2211 part.addparam('namespace', namespace)
2216 part.addparam('namespace', namespace)
2212 keys = repo.listkeys(namespace).items()
2217 keys = repo.listkeys(namespace).items()
2213 part.data = pushkey.encodekeys(keys)
2218 part.data = pushkey.encodekeys(keys)
2214
2219
2215 @getbundle2partsgenerator('obsmarkers')
2220 @getbundle2partsgenerator('obsmarkers')
2216 def _getbundleobsmarkerpart(bundler, repo, source, bundlecaps=None,
2221 def _getbundleobsmarkerpart(bundler, repo, source, bundlecaps=None,
2217 b2caps=None, heads=None, **kwargs):
2222 b2caps=None, heads=None, **kwargs):
2218 """add an obsolescence markers part to the requested bundle"""
2223 """add an obsolescence markers part to the requested bundle"""
2219 if kwargs.get(r'obsmarkers', False):
2224 if kwargs.get(r'obsmarkers', False):
2220 if heads is None:
2225 if heads is None:
2221 heads = repo.heads()
2226 heads = repo.heads()
2222 subset = [c.node() for c in repo.set('::%ln', heads)]
2227 subset = [c.node() for c in repo.set('::%ln', heads)]
2223 markers = repo.obsstore.relevantmarkers(subset)
2228 markers = repo.obsstore.relevantmarkers(subset)
2224 markers = sorted(markers)
2229 markers = sorted(markers)
2225 bundle2.buildobsmarkerspart(bundler, markers)
2230 bundle2.buildobsmarkerspart(bundler, markers)
2226
2231
2227 @getbundle2partsgenerator('phases')
2232 @getbundle2partsgenerator('phases')
2228 def _getbundlephasespart(bundler, repo, source, bundlecaps=None,
2233 def _getbundlephasespart(bundler, repo, source, bundlecaps=None,
2229 b2caps=None, heads=None, **kwargs):
2234 b2caps=None, heads=None, **kwargs):
2230 """add phase heads part to the requested bundle"""
2235 """add phase heads part to the requested bundle"""
2231 if kwargs.get(r'phases', False):
2236 if kwargs.get(r'phases', False):
2232 if not 'heads' in b2caps.get('phases'):
2237 if not 'heads' in b2caps.get('phases'):
2233 raise ValueError(_('no common phases exchange method'))
2238 raise ValueError(_('no common phases exchange method'))
2234 if heads is None:
2239 if heads is None:
2235 heads = repo.heads()
2240 heads = repo.heads()
2236
2241
2237 headsbyphase = collections.defaultdict(set)
2242 headsbyphase = collections.defaultdict(set)
2238 if repo.publishing():
2243 if repo.publishing():
2239 headsbyphase[phases.public] = heads
2244 headsbyphase[phases.public] = heads
2240 else:
2245 else:
2241 # find the appropriate heads to move
2246 # find the appropriate heads to move
2242
2247
2243 phase = repo._phasecache.phase
2248 phase = repo._phasecache.phase
2244 node = repo.changelog.node
2249 node = repo.changelog.node
2245 rev = repo.changelog.rev
2250 rev = repo.changelog.rev
2246 for h in heads:
2251 for h in heads:
2247 headsbyphase[phase(repo, rev(h))].add(h)
2252 headsbyphase[phase(repo, rev(h))].add(h)
2248 seenphases = list(headsbyphase.keys())
2253 seenphases = list(headsbyphase.keys())
2249
2254
2250 # We do not handle anything but public and draft phase for now)
2255 # We do not handle anything but public and draft phase for now)
2251 if seenphases:
2256 if seenphases:
2252 assert max(seenphases) <= phases.draft
2257 assert max(seenphases) <= phases.draft
2253
2258
2254 # if client is pulling non-public changesets, we need to find
2259 # if client is pulling non-public changesets, we need to find
2255 # intermediate public heads.
2260 # intermediate public heads.
2256 draftheads = headsbyphase.get(phases.draft, set())
2261 draftheads = headsbyphase.get(phases.draft, set())
2257 if draftheads:
2262 if draftheads:
2258 publicheads = headsbyphase.get(phases.public, set())
2263 publicheads = headsbyphase.get(phases.public, set())
2259
2264
2260 revset = 'heads(only(%ln, %ln) and public())'
2265 revset = 'heads(only(%ln, %ln) and public())'
2261 extraheads = repo.revs(revset, draftheads, publicheads)
2266 extraheads = repo.revs(revset, draftheads, publicheads)
2262 for r in extraheads:
2267 for r in extraheads:
2263 headsbyphase[phases.public].add(node(r))
2268 headsbyphase[phases.public].add(node(r))
2264
2269
2265 # transform data in a format used by the encoding function
2270 # transform data in a format used by the encoding function
2266 phasemapping = []
2271 phasemapping = []
2267 for phase in phases.allphases:
2272 for phase in phases.allphases:
2268 phasemapping.append(sorted(headsbyphase[phase]))
2273 phasemapping.append(sorted(headsbyphase[phase]))
2269
2274
2270 # generate the actual part
2275 # generate the actual part
2271 phasedata = phases.binaryencode(phasemapping)
2276 phasedata = phases.binaryencode(phasemapping)
2272 bundler.newpart('phase-heads', data=phasedata)
2277 bundler.newpart('phase-heads', data=phasedata)
2273
2278
2274 @getbundle2partsgenerator('hgtagsfnodes')
2279 @getbundle2partsgenerator('hgtagsfnodes')
2275 def _getbundletagsfnodes(bundler, repo, source, bundlecaps=None,
2280 def _getbundletagsfnodes(bundler, repo, source, bundlecaps=None,
2276 b2caps=None, heads=None, common=None,
2281 b2caps=None, heads=None, common=None,
2277 **kwargs):
2282 **kwargs):
2278 """Transfer the .hgtags filenodes mapping.
2283 """Transfer the .hgtags filenodes mapping.
2279
2284
2280 Only values for heads in this bundle will be transferred.
2285 Only values for heads in this bundle will be transferred.
2281
2286
2282 The part data consists of pairs of 20 byte changeset node and .hgtags
2287 The part data consists of pairs of 20 byte changeset node and .hgtags
2283 filenodes raw values.
2288 filenodes raw values.
2284 """
2289 """
2285 # Don't send unless:
2290 # Don't send unless:
2286 # - changeset are being exchanged,
2291 # - changeset are being exchanged,
2287 # - the client supports it.
2292 # - the client supports it.
2288 if not (kwargs.get(r'cg', True) and 'hgtagsfnodes' in b2caps):
2293 if not (kwargs.get(r'cg', True) and 'hgtagsfnodes' in b2caps):
2289 return
2294 return
2290
2295
2291 outgoing = _computeoutgoing(repo, heads, common)
2296 outgoing = _computeoutgoing(repo, heads, common)
2292 bundle2.addparttagsfnodescache(repo, bundler, outgoing)
2297 bundle2.addparttagsfnodescache(repo, bundler, outgoing)
2293
2298
2294 @getbundle2partsgenerator('cache:rev-branch-cache')
2299 @getbundle2partsgenerator('cache:rev-branch-cache')
2295 def _getbundlerevbranchcache(bundler, repo, source, bundlecaps=None,
2300 def _getbundlerevbranchcache(bundler, repo, source, bundlecaps=None,
2296 b2caps=None, heads=None, common=None,
2301 b2caps=None, heads=None, common=None,
2297 **kwargs):
2302 **kwargs):
2298 """Transfer the rev-branch-cache mapping
2303 """Transfer the rev-branch-cache mapping
2299
2304
2300 The payload is a series of data related to each branch
2305 The payload is a series of data related to each branch
2301
2306
2302 1) branch name length
2307 1) branch name length
2303 2) number of open heads
2308 2) number of open heads
2304 3) number of closed heads
2309 3) number of closed heads
2305 4) open heads nodes
2310 4) open heads nodes
2306 5) closed heads nodes
2311 5) closed heads nodes
2307 """
2312 """
2308 # Don't send unless:
2313 # Don't send unless:
2309 # - changeset are being exchanged,
2314 # - changeset are being exchanged,
2310 # - the client supports it.
2315 # - the client supports it.
2311 # - narrow bundle isn't in play (not currently compatible).
2316 # - narrow bundle isn't in play (not currently compatible).
2312 if (not kwargs.get(r'cg', True)
2317 if (not kwargs.get(r'cg', True)
2313 or 'rev-branch-cache' not in b2caps
2318 or 'rev-branch-cache' not in b2caps
2314 or kwargs.get(r'narrow', False)
2319 or kwargs.get(r'narrow', False)
2315 or repo.ui.has_section(_NARROWACL_SECTION)):
2320 or repo.ui.has_section(_NARROWACL_SECTION)):
2316 return
2321 return
2317
2322
2318 outgoing = _computeoutgoing(repo, heads, common)
2323 outgoing = _computeoutgoing(repo, heads, common)
2319 bundle2.addpartrevbranchcache(repo, bundler, outgoing)
2324 bundle2.addpartrevbranchcache(repo, bundler, outgoing)
2320
2325
2321 def check_heads(repo, their_heads, context):
2326 def check_heads(repo, their_heads, context):
2322 """check if the heads of a repo have been modified
2327 """check if the heads of a repo have been modified
2323
2328
2324 Used by peer for unbundling.
2329 Used by peer for unbundling.
2325 """
2330 """
2326 heads = repo.heads()
2331 heads = repo.heads()
2327 heads_hash = hashlib.sha1(''.join(sorted(heads))).digest()
2332 heads_hash = hashlib.sha1(''.join(sorted(heads))).digest()
2328 if not (their_heads == ['force'] or their_heads == heads or
2333 if not (their_heads == ['force'] or their_heads == heads or
2329 their_heads == ['hashed', heads_hash]):
2334 their_heads == ['hashed', heads_hash]):
2330 # someone else committed/pushed/unbundled while we
2335 # someone else committed/pushed/unbundled while we
2331 # were transferring data
2336 # were transferring data
2332 raise error.PushRaced('repository changed while %s - '
2337 raise error.PushRaced('repository changed while %s - '
2333 'please try again' % context)
2338 'please try again' % context)
2334
2339
2335 def unbundle(repo, cg, heads, source, url):
2340 def unbundle(repo, cg, heads, source, url):
2336 """Apply a bundle to a repo.
2341 """Apply a bundle to a repo.
2337
2342
2338 this function makes sure the repo is locked during the application and have
2343 this function makes sure the repo is locked during the application and have
2339 mechanism to check that no push race occurred between the creation of the
2344 mechanism to check that no push race occurred between the creation of the
2340 bundle and its application.
2345 bundle and its application.
2341
2346
2342 If the push was raced as PushRaced exception is raised."""
2347 If the push was raced as PushRaced exception is raised."""
2343 r = 0
2348 r = 0
2344 # need a transaction when processing a bundle2 stream
2349 # need a transaction when processing a bundle2 stream
2345 # [wlock, lock, tr] - needs to be an array so nested functions can modify it
2350 # [wlock, lock, tr] - needs to be an array so nested functions can modify it
2346 lockandtr = [None, None, None]
2351 lockandtr = [None, None, None]
2347 recordout = None
2352 recordout = None
2348 # quick fix for output mismatch with bundle2 in 3.4
2353 # quick fix for output mismatch with bundle2 in 3.4
2349 captureoutput = repo.ui.configbool('experimental', 'bundle2-output-capture')
2354 captureoutput = repo.ui.configbool('experimental', 'bundle2-output-capture')
2350 if url.startswith('remote:http:') or url.startswith('remote:https:'):
2355 if url.startswith('remote:http:') or url.startswith('remote:https:'):
2351 captureoutput = True
2356 captureoutput = True
2352 try:
2357 try:
2353 # note: outside bundle1, 'heads' is expected to be empty and this
2358 # note: outside bundle1, 'heads' is expected to be empty and this
2354 # 'check_heads' call wil be a no-op
2359 # 'check_heads' call wil be a no-op
2355 check_heads(repo, heads, 'uploading changes')
2360 check_heads(repo, heads, 'uploading changes')
2356 # push can proceed
2361 # push can proceed
2357 if not isinstance(cg, bundle2.unbundle20):
2362 if not isinstance(cg, bundle2.unbundle20):
2358 # legacy case: bundle1 (changegroup 01)
2363 # legacy case: bundle1 (changegroup 01)
2359 txnname = "\n".join([source, util.hidepassword(url)])
2364 txnname = "\n".join([source, util.hidepassword(url)])
2360 with repo.lock(), repo.transaction(txnname) as tr:
2365 with repo.lock(), repo.transaction(txnname) as tr:
2361 op = bundle2.applybundle(repo, cg, tr, source, url)
2366 op = bundle2.applybundle(repo, cg, tr, source, url)
2362 r = bundle2.combinechangegroupresults(op)
2367 r = bundle2.combinechangegroupresults(op)
2363 else:
2368 else:
2364 r = None
2369 r = None
2365 try:
2370 try:
2366 def gettransaction():
2371 def gettransaction():
2367 if not lockandtr[2]:
2372 if not lockandtr[2]:
2368 lockandtr[0] = repo.wlock()
2373 lockandtr[0] = repo.wlock()
2369 lockandtr[1] = repo.lock()
2374 lockandtr[1] = repo.lock()
2370 lockandtr[2] = repo.transaction(source)
2375 lockandtr[2] = repo.transaction(source)
2371 lockandtr[2].hookargs['source'] = source
2376 lockandtr[2].hookargs['source'] = source
2372 lockandtr[2].hookargs['url'] = url
2377 lockandtr[2].hookargs['url'] = url
2373 lockandtr[2].hookargs['bundle2'] = '1'
2378 lockandtr[2].hookargs['bundle2'] = '1'
2374 return lockandtr[2]
2379 return lockandtr[2]
2375
2380
2376 # Do greedy locking by default until we're satisfied with lazy
2381 # Do greedy locking by default until we're satisfied with lazy
2377 # locking.
2382 # locking.
2378 if not repo.ui.configbool('experimental', 'bundle2lazylocking'):
2383 if not repo.ui.configbool('experimental', 'bundle2lazylocking'):
2379 gettransaction()
2384 gettransaction()
2380
2385
2381 op = bundle2.bundleoperation(repo, gettransaction,
2386 op = bundle2.bundleoperation(repo, gettransaction,
2382 captureoutput=captureoutput,
2387 captureoutput=captureoutput,
2383 source='push')
2388 source='push')
2384 try:
2389 try:
2385 op = bundle2.processbundle(repo, cg, op=op)
2390 op = bundle2.processbundle(repo, cg, op=op)
2386 finally:
2391 finally:
2387 r = op.reply
2392 r = op.reply
2388 if captureoutput and r is not None:
2393 if captureoutput and r is not None:
2389 repo.ui.pushbuffer(error=True, subproc=True)
2394 repo.ui.pushbuffer(error=True, subproc=True)
2390 def recordout(output):
2395 def recordout(output):
2391 r.newpart('output', data=output, mandatory=False)
2396 r.newpart('output', data=output, mandatory=False)
2392 if lockandtr[2] is not None:
2397 if lockandtr[2] is not None:
2393 lockandtr[2].close()
2398 lockandtr[2].close()
2394 except BaseException as exc:
2399 except BaseException as exc:
2395 exc.duringunbundle2 = True
2400 exc.duringunbundle2 = True
2396 if captureoutput and r is not None:
2401 if captureoutput and r is not None:
2397 parts = exc._bundle2salvagedoutput = r.salvageoutput()
2402 parts = exc._bundle2salvagedoutput = r.salvageoutput()
2398 def recordout(output):
2403 def recordout(output):
2399 part = bundle2.bundlepart('output', data=output,
2404 part = bundle2.bundlepart('output', data=output,
2400 mandatory=False)
2405 mandatory=False)
2401 parts.append(part)
2406 parts.append(part)
2402 raise
2407 raise
2403 finally:
2408 finally:
2404 lockmod.release(lockandtr[2], lockandtr[1], lockandtr[0])
2409 lockmod.release(lockandtr[2], lockandtr[1], lockandtr[0])
2405 if recordout is not None:
2410 if recordout is not None:
2406 recordout(repo.ui.popbuffer())
2411 recordout(repo.ui.popbuffer())
2407 return r
2412 return r
2408
2413
2409 def _maybeapplyclonebundle(pullop):
2414 def _maybeapplyclonebundle(pullop):
2410 """Apply a clone bundle from a remote, if possible."""
2415 """Apply a clone bundle from a remote, if possible."""
2411
2416
2412 repo = pullop.repo
2417 repo = pullop.repo
2413 remote = pullop.remote
2418 remote = pullop.remote
2414
2419
2415 if not repo.ui.configbool('ui', 'clonebundles'):
2420 if not repo.ui.configbool('ui', 'clonebundles'):
2416 return
2421 return
2417
2422
2418 # Only run if local repo is empty.
2423 # Only run if local repo is empty.
2419 if len(repo):
2424 if len(repo):
2420 return
2425 return
2421
2426
2422 if pullop.heads:
2427 if pullop.heads:
2423 return
2428 return
2424
2429
2425 if not remote.capable('clonebundles'):
2430 if not remote.capable('clonebundles'):
2426 return
2431 return
2427
2432
2428 with remote.commandexecutor() as e:
2433 with remote.commandexecutor() as e:
2429 res = e.callcommand('clonebundles', {}).result()
2434 res = e.callcommand('clonebundles', {}).result()
2430
2435
2431 # If we call the wire protocol command, that's good enough to record the
2436 # If we call the wire protocol command, that's good enough to record the
2432 # attempt.
2437 # attempt.
2433 pullop.clonebundleattempted = True
2438 pullop.clonebundleattempted = True
2434
2439
2435 entries = parseclonebundlesmanifest(repo, res)
2440 entries = parseclonebundlesmanifest(repo, res)
2436 if not entries:
2441 if not entries:
2437 repo.ui.note(_('no clone bundles available on remote; '
2442 repo.ui.note(_('no clone bundles available on remote; '
2438 'falling back to regular clone\n'))
2443 'falling back to regular clone\n'))
2439 return
2444 return
2440
2445
2441 entries = filterclonebundleentries(
2446 entries = filterclonebundleentries(
2442 repo, entries, streamclonerequested=pullop.streamclonerequested)
2447 repo, entries, streamclonerequested=pullop.streamclonerequested)
2443
2448
2444 if not entries:
2449 if not entries:
2445 # There is a thundering herd concern here. However, if a server
2450 # There is a thundering herd concern here. However, if a server
2446 # operator doesn't advertise bundles appropriate for its clients,
2451 # operator doesn't advertise bundles appropriate for its clients,
2447 # they deserve what's coming. Furthermore, from a client's
2452 # they deserve what's coming. Furthermore, from a client's
2448 # perspective, no automatic fallback would mean not being able to
2453 # perspective, no automatic fallback would mean not being able to
2449 # clone!
2454 # clone!
2450 repo.ui.warn(_('no compatible clone bundles available on server; '
2455 repo.ui.warn(_('no compatible clone bundles available on server; '
2451 'falling back to regular clone\n'))
2456 'falling back to regular clone\n'))
2452 repo.ui.warn(_('(you may want to report this to the server '
2457 repo.ui.warn(_('(you may want to report this to the server '
2453 'operator)\n'))
2458 'operator)\n'))
2454 return
2459 return
2455
2460
2456 entries = sortclonebundleentries(repo.ui, entries)
2461 entries = sortclonebundleentries(repo.ui, entries)
2457
2462
2458 url = entries[0]['URL']
2463 url = entries[0]['URL']
2459 repo.ui.status(_('applying clone bundle from %s\n') % url)
2464 repo.ui.status(_('applying clone bundle from %s\n') % url)
2460 if trypullbundlefromurl(repo.ui, repo, url):
2465 if trypullbundlefromurl(repo.ui, repo, url):
2461 repo.ui.status(_('finished applying clone bundle\n'))
2466 repo.ui.status(_('finished applying clone bundle\n'))
2462 # Bundle failed.
2467 # Bundle failed.
2463 #
2468 #
2464 # We abort by default to avoid the thundering herd of
2469 # We abort by default to avoid the thundering herd of
2465 # clients flooding a server that was expecting expensive
2470 # clients flooding a server that was expecting expensive
2466 # clone load to be offloaded.
2471 # clone load to be offloaded.
2467 elif repo.ui.configbool('ui', 'clonebundlefallback'):
2472 elif repo.ui.configbool('ui', 'clonebundlefallback'):
2468 repo.ui.warn(_('falling back to normal clone\n'))
2473 repo.ui.warn(_('falling back to normal clone\n'))
2469 else:
2474 else:
2470 raise error.Abort(_('error applying bundle'),
2475 raise error.Abort(_('error applying bundle'),
2471 hint=_('if this error persists, consider contacting '
2476 hint=_('if this error persists, consider contacting '
2472 'the server operator or disable clone '
2477 'the server operator or disable clone '
2473 'bundles via '
2478 'bundles via '
2474 '"--config ui.clonebundles=false"'))
2479 '"--config ui.clonebundles=false"'))
2475
2480
2476 def parseclonebundlesmanifest(repo, s):
2481 def parseclonebundlesmanifest(repo, s):
2477 """Parses the raw text of a clone bundles manifest.
2482 """Parses the raw text of a clone bundles manifest.
2478
2483
2479 Returns a list of dicts. The dicts have a ``URL`` key corresponding
2484 Returns a list of dicts. The dicts have a ``URL`` key corresponding
2480 to the URL and other keys are the attributes for the entry.
2485 to the URL and other keys are the attributes for the entry.
2481 """
2486 """
2482 m = []
2487 m = []
2483 for line in s.splitlines():
2488 for line in s.splitlines():
2484 fields = line.split()
2489 fields = line.split()
2485 if not fields:
2490 if not fields:
2486 continue
2491 continue
2487 attrs = {'URL': fields[0]}
2492 attrs = {'URL': fields[0]}
2488 for rawattr in fields[1:]:
2493 for rawattr in fields[1:]:
2489 key, value = rawattr.split('=', 1)
2494 key, value = rawattr.split('=', 1)
2490 key = urlreq.unquote(key)
2495 key = urlreq.unquote(key)
2491 value = urlreq.unquote(value)
2496 value = urlreq.unquote(value)
2492 attrs[key] = value
2497 attrs[key] = value
2493
2498
2494 # Parse BUNDLESPEC into components. This makes client-side
2499 # Parse BUNDLESPEC into components. This makes client-side
2495 # preferences easier to specify since you can prefer a single
2500 # preferences easier to specify since you can prefer a single
2496 # component of the BUNDLESPEC.
2501 # component of the BUNDLESPEC.
2497 if key == 'BUNDLESPEC':
2502 if key == 'BUNDLESPEC':
2498 try:
2503 try:
2499 bundlespec = parsebundlespec(repo, value)
2504 bundlespec = parsebundlespec(repo, value)
2500 attrs['COMPRESSION'] = bundlespec.compression
2505 attrs['COMPRESSION'] = bundlespec.compression
2501 attrs['VERSION'] = bundlespec.version
2506 attrs['VERSION'] = bundlespec.version
2502 except error.InvalidBundleSpecification:
2507 except error.InvalidBundleSpecification:
2503 pass
2508 pass
2504 except error.UnsupportedBundleSpecification:
2509 except error.UnsupportedBundleSpecification:
2505 pass
2510 pass
2506
2511
2507 m.append(attrs)
2512 m.append(attrs)
2508
2513
2509 return m
2514 return m
2510
2515
2511 def isstreamclonespec(bundlespec):
2516 def isstreamclonespec(bundlespec):
2512 # Stream clone v1
2517 # Stream clone v1
2513 if (bundlespec.wirecompression == 'UN' and bundlespec.wireversion == 's1'):
2518 if (bundlespec.wirecompression == 'UN' and bundlespec.wireversion == 's1'):
2514 return True
2519 return True
2515
2520
2516 # Stream clone v2
2521 # Stream clone v2
2517 if (bundlespec.wirecompression == 'UN' and \
2522 if (bundlespec.wirecompression == 'UN' and \
2518 bundlespec.wireversion == '02' and \
2523 bundlespec.wireversion == '02' and \
2519 bundlespec.contentopts.get('streamv2')):
2524 bundlespec.contentopts.get('streamv2')):
2520 return True
2525 return True
2521
2526
2522 return False
2527 return False
2523
2528
2524 def filterclonebundleentries(repo, entries, streamclonerequested=False):
2529 def filterclonebundleentries(repo, entries, streamclonerequested=False):
2525 """Remove incompatible clone bundle manifest entries.
2530 """Remove incompatible clone bundle manifest entries.
2526
2531
2527 Accepts a list of entries parsed with ``parseclonebundlesmanifest``
2532 Accepts a list of entries parsed with ``parseclonebundlesmanifest``
2528 and returns a new list consisting of only the entries that this client
2533 and returns a new list consisting of only the entries that this client
2529 should be able to apply.
2534 should be able to apply.
2530
2535
2531 There is no guarantee we'll be able to apply all returned entries because
2536 There is no guarantee we'll be able to apply all returned entries because
2532 the metadata we use to filter on may be missing or wrong.
2537 the metadata we use to filter on may be missing or wrong.
2533 """
2538 """
2534 newentries = []
2539 newentries = []
2535 for entry in entries:
2540 for entry in entries:
2536 spec = entry.get('BUNDLESPEC')
2541 spec = entry.get('BUNDLESPEC')
2537 if spec:
2542 if spec:
2538 try:
2543 try:
2539 bundlespec = parsebundlespec(repo, spec, strict=True)
2544 bundlespec = parsebundlespec(repo, spec, strict=True)
2540
2545
2541 # If a stream clone was requested, filter out non-streamclone
2546 # If a stream clone was requested, filter out non-streamclone
2542 # entries.
2547 # entries.
2543 if streamclonerequested and not isstreamclonespec(bundlespec):
2548 if streamclonerequested and not isstreamclonespec(bundlespec):
2544 repo.ui.debug('filtering %s because not a stream clone\n' %
2549 repo.ui.debug('filtering %s because not a stream clone\n' %
2545 entry['URL'])
2550 entry['URL'])
2546 continue
2551 continue
2547
2552
2548 except error.InvalidBundleSpecification as e:
2553 except error.InvalidBundleSpecification as e:
2549 repo.ui.debug(stringutil.forcebytestr(e) + '\n')
2554 repo.ui.debug(stringutil.forcebytestr(e) + '\n')
2550 continue
2555 continue
2551 except error.UnsupportedBundleSpecification as e:
2556 except error.UnsupportedBundleSpecification as e:
2552 repo.ui.debug('filtering %s because unsupported bundle '
2557 repo.ui.debug('filtering %s because unsupported bundle '
2553 'spec: %s\n' % (
2558 'spec: %s\n' % (
2554 entry['URL'], stringutil.forcebytestr(e)))
2559 entry['URL'], stringutil.forcebytestr(e)))
2555 continue
2560 continue
2556 # If we don't have a spec and requested a stream clone, we don't know
2561 # If we don't have a spec and requested a stream clone, we don't know
2557 # what the entry is so don't attempt to apply it.
2562 # what the entry is so don't attempt to apply it.
2558 elif streamclonerequested:
2563 elif streamclonerequested:
2559 repo.ui.debug('filtering %s because cannot determine if a stream '
2564 repo.ui.debug('filtering %s because cannot determine if a stream '
2560 'clone bundle\n' % entry['URL'])
2565 'clone bundle\n' % entry['URL'])
2561 continue
2566 continue
2562
2567
2563 if 'REQUIRESNI' in entry and not sslutil.hassni:
2568 if 'REQUIRESNI' in entry and not sslutil.hassni:
2564 repo.ui.debug('filtering %s because SNI not supported\n' %
2569 repo.ui.debug('filtering %s because SNI not supported\n' %
2565 entry['URL'])
2570 entry['URL'])
2566 continue
2571 continue
2567
2572
2568 newentries.append(entry)
2573 newentries.append(entry)
2569
2574
2570 return newentries
2575 return newentries
2571
2576
2572 class clonebundleentry(object):
2577 class clonebundleentry(object):
2573 """Represents an item in a clone bundles manifest.
2578 """Represents an item in a clone bundles manifest.
2574
2579
2575 This rich class is needed to support sorting since sorted() in Python 3
2580 This rich class is needed to support sorting since sorted() in Python 3
2576 doesn't support ``cmp`` and our comparison is complex enough that ``key=``
2581 doesn't support ``cmp`` and our comparison is complex enough that ``key=``
2577 won't work.
2582 won't work.
2578 """
2583 """
2579
2584
2580 def __init__(self, value, prefers):
2585 def __init__(self, value, prefers):
2581 self.value = value
2586 self.value = value
2582 self.prefers = prefers
2587 self.prefers = prefers
2583
2588
2584 def _cmp(self, other):
2589 def _cmp(self, other):
2585 for prefkey, prefvalue in self.prefers:
2590 for prefkey, prefvalue in self.prefers:
2586 avalue = self.value.get(prefkey)
2591 avalue = self.value.get(prefkey)
2587 bvalue = other.value.get(prefkey)
2592 bvalue = other.value.get(prefkey)
2588
2593
2589 # Special case for b missing attribute and a matches exactly.
2594 # Special case for b missing attribute and a matches exactly.
2590 if avalue is not None and bvalue is None and avalue == prefvalue:
2595 if avalue is not None and bvalue is None and avalue == prefvalue:
2591 return -1
2596 return -1
2592
2597
2593 # Special case for a missing attribute and b matches exactly.
2598 # Special case for a missing attribute and b matches exactly.
2594 if bvalue is not None and avalue is None and bvalue == prefvalue:
2599 if bvalue is not None and avalue is None and bvalue == prefvalue:
2595 return 1
2600 return 1
2596
2601
2597 # We can't compare unless attribute present on both.
2602 # We can't compare unless attribute present on both.
2598 if avalue is None or bvalue is None:
2603 if avalue is None or bvalue is None:
2599 continue
2604 continue
2600
2605
2601 # Same values should fall back to next attribute.
2606 # Same values should fall back to next attribute.
2602 if avalue == bvalue:
2607 if avalue == bvalue:
2603 continue
2608 continue
2604
2609
2605 # Exact matches come first.
2610 # Exact matches come first.
2606 if avalue == prefvalue:
2611 if avalue == prefvalue:
2607 return -1
2612 return -1
2608 if bvalue == prefvalue:
2613 if bvalue == prefvalue:
2609 return 1
2614 return 1
2610
2615
2611 # Fall back to next attribute.
2616 # Fall back to next attribute.
2612 continue
2617 continue
2613
2618
2614 # If we got here we couldn't sort by attributes and prefers. Fall
2619 # If we got here we couldn't sort by attributes and prefers. Fall
2615 # back to index order.
2620 # back to index order.
2616 return 0
2621 return 0
2617
2622
2618 def __lt__(self, other):
2623 def __lt__(self, other):
2619 return self._cmp(other) < 0
2624 return self._cmp(other) < 0
2620
2625
2621 def __gt__(self, other):
2626 def __gt__(self, other):
2622 return self._cmp(other) > 0
2627 return self._cmp(other) > 0
2623
2628
2624 def __eq__(self, other):
2629 def __eq__(self, other):
2625 return self._cmp(other) == 0
2630 return self._cmp(other) == 0
2626
2631
2627 def __le__(self, other):
2632 def __le__(self, other):
2628 return self._cmp(other) <= 0
2633 return self._cmp(other) <= 0
2629
2634
2630 def __ge__(self, other):
2635 def __ge__(self, other):
2631 return self._cmp(other) >= 0
2636 return self._cmp(other) >= 0
2632
2637
2633 def __ne__(self, other):
2638 def __ne__(self, other):
2634 return self._cmp(other) != 0
2639 return self._cmp(other) != 0
2635
2640
2636 def sortclonebundleentries(ui, entries):
2641 def sortclonebundleentries(ui, entries):
2637 prefers = ui.configlist('ui', 'clonebundleprefers')
2642 prefers = ui.configlist('ui', 'clonebundleprefers')
2638 if not prefers:
2643 if not prefers:
2639 return list(entries)
2644 return list(entries)
2640
2645
2641 prefers = [p.split('=', 1) for p in prefers]
2646 prefers = [p.split('=', 1) for p in prefers]
2642
2647
2643 items = sorted(clonebundleentry(v, prefers) for v in entries)
2648 items = sorted(clonebundleentry(v, prefers) for v in entries)
2644 return [i.value for i in items]
2649 return [i.value for i in items]
2645
2650
2646 def trypullbundlefromurl(ui, repo, url):
2651 def trypullbundlefromurl(ui, repo, url):
2647 """Attempt to apply a bundle from a URL."""
2652 """Attempt to apply a bundle from a URL."""
2648 with repo.lock(), repo.transaction('bundleurl') as tr:
2653 with repo.lock(), repo.transaction('bundleurl') as tr:
2649 try:
2654 try:
2650 fh = urlmod.open(ui, url)
2655 fh = urlmod.open(ui, url)
2651 cg = readbundle(ui, fh, 'stream')
2656 cg = readbundle(ui, fh, 'stream')
2652
2657
2653 if isinstance(cg, streamclone.streamcloneapplier):
2658 if isinstance(cg, streamclone.streamcloneapplier):
2654 cg.apply(repo)
2659 cg.apply(repo)
2655 else:
2660 else:
2656 bundle2.applybundle(repo, cg, tr, 'clonebundles', url)
2661 bundle2.applybundle(repo, cg, tr, 'clonebundles', url)
2657 return True
2662 return True
2658 except urlerr.httperror as e:
2663 except urlerr.httperror as e:
2659 ui.warn(_('HTTP error fetching bundle: %s\n') %
2664 ui.warn(_('HTTP error fetching bundle: %s\n') %
2660 stringutil.forcebytestr(e))
2665 stringutil.forcebytestr(e))
2661 except urlerr.urlerror as e:
2666 except urlerr.urlerror as e:
2662 ui.warn(_('error fetching bundle: %s\n') %
2667 ui.warn(_('error fetching bundle: %s\n') %
2663 stringutil.forcebytestr(e.reason))
2668 stringutil.forcebytestr(e.reason))
2664
2669
2665 return False
2670 return False
@@ -1,408 +1,408 b''
1 Show all commands except debug commands
1 Show all commands except debug commands
2 $ hg debugcomplete
2 $ hg debugcomplete
3 add
3 add
4 addremove
4 addremove
5 annotate
5 annotate
6 archive
6 archive
7 backout
7 backout
8 bisect
8 bisect
9 bookmarks
9 bookmarks
10 branch
10 branch
11 branches
11 branches
12 bundle
12 bundle
13 cat
13 cat
14 clone
14 clone
15 commit
15 commit
16 config
16 config
17 copy
17 copy
18 diff
18 diff
19 export
19 export
20 files
20 files
21 forget
21 forget
22 graft
22 graft
23 grep
23 grep
24 heads
24 heads
25 help
25 help
26 identify
26 identify
27 import
27 import
28 incoming
28 incoming
29 init
29 init
30 locate
30 locate
31 log
31 log
32 manifest
32 manifest
33 merge
33 merge
34 outgoing
34 outgoing
35 parents
35 parents
36 paths
36 paths
37 phase
37 phase
38 pull
38 pull
39 push
39 push
40 recover
40 recover
41 remove
41 remove
42 rename
42 rename
43 resolve
43 resolve
44 revert
44 revert
45 rollback
45 rollback
46 root
46 root
47 serve
47 serve
48 status
48 status
49 summary
49 summary
50 tag
50 tag
51 tags
51 tags
52 tip
52 tip
53 unbundle
53 unbundle
54 update
54 update
55 verify
55 verify
56 version
56 version
57
57
58 Show all commands that start with "a"
58 Show all commands that start with "a"
59 $ hg debugcomplete a
59 $ hg debugcomplete a
60 add
60 add
61 addremove
61 addremove
62 annotate
62 annotate
63 archive
63 archive
64
64
65 Do not show debug commands if there are other candidates
65 Do not show debug commands if there are other candidates
66 $ hg debugcomplete d
66 $ hg debugcomplete d
67 diff
67 diff
68
68
69 Show debug commands if there are no other candidates
69 Show debug commands if there are no other candidates
70 $ hg debugcomplete debug
70 $ hg debugcomplete debug
71 debugancestor
71 debugancestor
72 debugapplystreamclonebundle
72 debugapplystreamclonebundle
73 debugbuilddag
73 debugbuilddag
74 debugbundle
74 debugbundle
75 debugcapabilities
75 debugcapabilities
76 debugcheckstate
76 debugcheckstate
77 debugcolor
77 debugcolor
78 debugcommands
78 debugcommands
79 debugcomplete
79 debugcomplete
80 debugconfig
80 debugconfig
81 debugcreatestreamclonebundle
81 debugcreatestreamclonebundle
82 debugdag
82 debugdag
83 debugdata
83 debugdata
84 debugdate
84 debugdate
85 debugdeltachain
85 debugdeltachain
86 debugdirstate
86 debugdirstate
87 debugdiscovery
87 debugdiscovery
88 debugdownload
88 debugdownload
89 debugextensions
89 debugextensions
90 debugfileset
90 debugfileset
91 debugformat
91 debugformat
92 debugfsinfo
92 debugfsinfo
93 debuggetbundle
93 debuggetbundle
94 debugignore
94 debugignore
95 debugindex
95 debugindex
96 debugindexdot
96 debugindexdot
97 debugindexstats
97 debugindexstats
98 debuginstall
98 debuginstall
99 debugknown
99 debugknown
100 debuglabelcomplete
100 debuglabelcomplete
101 debuglocks
101 debuglocks
102 debugmanifestfulltextcache
102 debugmanifestfulltextcache
103 debugmergestate
103 debugmergestate
104 debugnamecomplete
104 debugnamecomplete
105 debugobsolete
105 debugobsolete
106 debugpathcomplete
106 debugpathcomplete
107 debugpeer
107 debugpeer
108 debugpickmergetool
108 debugpickmergetool
109 debugpushkey
109 debugpushkey
110 debugpvec
110 debugpvec
111 debugrebuilddirstate
111 debugrebuilddirstate
112 debugrebuildfncache
112 debugrebuildfncache
113 debugrename
113 debugrename
114 debugrevlog
114 debugrevlog
115 debugrevlogindex
115 debugrevlogindex
116 debugrevspec
116 debugrevspec
117 debugserve
117 debugserve
118 debugsetparents
118 debugsetparents
119 debugssl
119 debugssl
120 debugsub
120 debugsub
121 debugsuccessorssets
121 debugsuccessorssets
122 debugtemplate
122 debugtemplate
123 debuguigetpass
123 debuguigetpass
124 debuguiprompt
124 debuguiprompt
125 debugupdatecaches
125 debugupdatecaches
126 debugupgraderepo
126 debugupgraderepo
127 debugwalk
127 debugwalk
128 debugwhyunstable
128 debugwhyunstable
129 debugwireargs
129 debugwireargs
130 debugwireproto
130 debugwireproto
131
131
132 Do not show the alias of a debug command if there are other candidates
132 Do not show the alias of a debug command if there are other candidates
133 (this should hide rawcommit)
133 (this should hide rawcommit)
134 $ hg debugcomplete r
134 $ hg debugcomplete r
135 recover
135 recover
136 remove
136 remove
137 rename
137 rename
138 resolve
138 resolve
139 revert
139 revert
140 rollback
140 rollback
141 root
141 root
142 Show the alias of a debug command if there are no other candidates
142 Show the alias of a debug command if there are no other candidates
143 $ hg debugcomplete rawc
143 $ hg debugcomplete rawc
144
144
145
145
146 Show the global options
146 Show the global options
147 $ hg debugcomplete --options | sort
147 $ hg debugcomplete --options | sort
148 --color
148 --color
149 --config
149 --config
150 --cwd
150 --cwd
151 --debug
151 --debug
152 --debugger
152 --debugger
153 --encoding
153 --encoding
154 --encodingmode
154 --encodingmode
155 --help
155 --help
156 --hidden
156 --hidden
157 --noninteractive
157 --noninteractive
158 --pager
158 --pager
159 --profile
159 --profile
160 --quiet
160 --quiet
161 --repository
161 --repository
162 --time
162 --time
163 --traceback
163 --traceback
164 --verbose
164 --verbose
165 --version
165 --version
166 -R
166 -R
167 -h
167 -h
168 -q
168 -q
169 -v
169 -v
170 -y
170 -y
171
171
172 Show the options for the "serve" command
172 Show the options for the "serve" command
173 $ hg debugcomplete --options serve | sort
173 $ hg debugcomplete --options serve | sort
174 --accesslog
174 --accesslog
175 --address
175 --address
176 --certificate
176 --certificate
177 --cmdserver
177 --cmdserver
178 --color
178 --color
179 --config
179 --config
180 --cwd
180 --cwd
181 --daemon
181 --daemon
182 --daemon-postexec
182 --daemon-postexec
183 --debug
183 --debug
184 --debugger
184 --debugger
185 --encoding
185 --encoding
186 --encodingmode
186 --encodingmode
187 --errorlog
187 --errorlog
188 --help
188 --help
189 --hidden
189 --hidden
190 --ipv6
190 --ipv6
191 --name
191 --name
192 --noninteractive
192 --noninteractive
193 --pager
193 --pager
194 --pid-file
194 --pid-file
195 --port
195 --port
196 --prefix
196 --prefix
197 --print-url
197 --print-url
198 --profile
198 --profile
199 --quiet
199 --quiet
200 --repository
200 --repository
201 --stdio
201 --stdio
202 --style
202 --style
203 --subrepos
203 --subrepos
204 --templates
204 --templates
205 --time
205 --time
206 --traceback
206 --traceback
207 --verbose
207 --verbose
208 --version
208 --version
209 --web-conf
209 --web-conf
210 -6
210 -6
211 -A
211 -A
212 -E
212 -E
213 -R
213 -R
214 -S
214 -S
215 -a
215 -a
216 -d
216 -d
217 -h
217 -h
218 -n
218 -n
219 -p
219 -p
220 -q
220 -q
221 -t
221 -t
222 -v
222 -v
223 -y
223 -y
224
224
225 Show an error if we use --options with an ambiguous abbreviation
225 Show an error if we use --options with an ambiguous abbreviation
226 $ hg debugcomplete --options s
226 $ hg debugcomplete --options s
227 hg: command 's' is ambiguous:
227 hg: command 's' is ambiguous:
228 serve showconfig status summary
228 serve showconfig status summary
229 [255]
229 [255]
230
230
231 Show all commands + options
231 Show all commands + options
232 $ hg debugcommands
232 $ hg debugcommands
233 add: include, exclude, subrepos, dry-run
233 add: include, exclude, subrepos, dry-run
234 addremove: similarity, subrepos, include, exclude, dry-run
234 addremove: similarity, subrepos, include, exclude, dry-run
235 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, skip, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, include, exclude, template
235 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, skip, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, include, exclude, template
236 archive: no-decode, prefix, rev, type, subrepos, include, exclude
236 archive: no-decode, prefix, rev, type, subrepos, include, exclude
237 backout: merge, commit, no-commit, parent, rev, edit, tool, include, exclude, message, logfile, date, user
237 backout: merge, commit, no-commit, parent, rev, edit, tool, include, exclude, message, logfile, date, user
238 bisect: reset, good, bad, skip, extend, command, noupdate
238 bisect: reset, good, bad, skip, extend, command, noupdate
239 bookmarks: force, rev, delete, rename, inactive, list, template
239 bookmarks: force, rev, delete, rename, inactive, list, template
240 branch: force, clean, rev
240 branch: force, clean, rev
241 branches: active, closed, template
241 branches: active, closed, template
242 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
242 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
243 cat: output, rev, decode, include, exclude, template
243 cat: output, rev, decode, include, exclude, template
244 clone: noupdate, updaterev, rev, branch, pull, uncompressed, stream, ssh, remotecmd, insecure
244 clone: noupdate, updaterev, rev, branch, pull, uncompressed, stream, ssh, remotecmd, insecure
245 commit: addremove, close-branch, amend, secret, edit, interactive, include, exclude, message, logfile, date, user, subrepos
245 commit: addremove, close-branch, amend, secret, edit, interactive, include, exclude, message, logfile, date, user, subrepos
246 config: untrusted, edit, local, global, template
246 config: untrusted, edit, local, global, template
247 copy: after, force, include, exclude, dry-run
247 copy: after, force, include, exclude, dry-run
248 debugancestor:
248 debugancestor:
249 debugapplystreamclonebundle:
249 debugapplystreamclonebundle:
250 debugbuilddag: mergeable-file, overwritten-file, new-file
250 debugbuilddag: mergeable-file, overwritten-file, new-file
251 debugbundle: all, part-type, spec
251 debugbundle: all, part-type, spec
252 debugcapabilities:
252 debugcapabilities:
253 debugcheckstate:
253 debugcheckstate:
254 debugcolor: style
254 debugcolor: style
255 debugcommands:
255 debugcommands:
256 debugcomplete: options
256 debugcomplete: options
257 debugcreatestreamclonebundle:
257 debugcreatestreamclonebundle:
258 debugdag: tags, branches, dots, spaces
258 debugdag: tags, branches, dots, spaces
259 debugdata: changelog, manifest, dir
259 debugdata: changelog, manifest, dir
260 debugdate: extended
260 debugdate: extended
261 debugdeltachain: changelog, manifest, dir, template
261 debugdeltachain: changelog, manifest, dir, template
262 debugdirstate: nodates, dates, datesort
262 debugdirstate: nodates, dates, datesort
263 debugdiscovery: old, nonheads, rev, ssh, remotecmd, insecure
263 debugdiscovery: old, nonheads, rev, ssh, remotecmd, insecure
264 debugdownload: output
264 debugdownload: output
265 debugextensions: template
265 debugextensions: template
266 debugfileset: rev, all-files, show-matcher, show-stage
266 debugfileset: rev, all-files, show-matcher, show-stage
267 debugformat: template
267 debugformat: template
268 debugfsinfo:
268 debugfsinfo:
269 debuggetbundle: head, common, type
269 debuggetbundle: head, common, type
270 debugignore:
270 debugignore:
271 debugindex: changelog, manifest, dir, template
271 debugindex: changelog, manifest, dir, template
272 debugindexdot: changelog, manifest, dir
272 debugindexdot: changelog, manifest, dir
273 debugindexstats:
273 debugindexstats:
274 debuginstall: template
274 debuginstall: template
275 debugknown:
275 debugknown:
276 debuglabelcomplete:
276 debuglabelcomplete:
277 debuglocks: force-lock, force-wlock, set-lock, set-wlock
277 debuglocks: force-lock, force-wlock, set-lock, set-wlock
278 debugmanifestfulltextcache: clear, add
278 debugmanifestfulltextcache: clear, add
279 debugmergestate:
279 debugmergestate:
280 debugnamecomplete:
280 debugnamecomplete:
281 debugobsolete: flags, record-parents, rev, exclusive, index, delete, date, user, template
281 debugobsolete: flags, record-parents, rev, exclusive, index, delete, date, user, template
282 debugpathcomplete: full, normal, added, removed
282 debugpathcomplete: full, normal, added, removed
283 debugpeer:
283 debugpeer:
284 debugpickmergetool: rev, changedelete, include, exclude, tool
284 debugpickmergetool: rev, changedelete, include, exclude, tool
285 debugpushkey:
285 debugpushkey:
286 debugpvec:
286 debugpvec:
287 debugrebuilddirstate: rev, minimal
287 debugrebuilddirstate: rev, minimal
288 debugrebuildfncache:
288 debugrebuildfncache:
289 debugrename: rev
289 debugrename: rev
290 debugrevlog: changelog, manifest, dir, dump
290 debugrevlog: changelog, manifest, dir, dump
291 debugrevlogindex: changelog, manifest, dir, format
291 debugrevlogindex: changelog, manifest, dir, format
292 debugrevspec: optimize, show-revs, show-set, show-stage, no-optimized, verify-optimized
292 debugrevspec: optimize, show-revs, show-set, show-stage, no-optimized, verify-optimized
293 debugserve: sshstdio, logiofd, logiofile
293 debugserve: sshstdio, logiofd, logiofile
294 debugsetparents:
294 debugsetparents:
295 debugssl:
295 debugssl:
296 debugsub: rev
296 debugsub: rev
297 debugsuccessorssets: closest
297 debugsuccessorssets: closest
298 debugtemplate: rev, define
298 debugtemplate: rev, define
299 debuguigetpass: prompt
299 debuguigetpass: prompt
300 debuguiprompt: prompt
300 debuguiprompt: prompt
301 debugupdatecaches:
301 debugupdatecaches:
302 debugupgraderepo: optimize, run
302 debugupgraderepo: optimize, run
303 debugwalk: include, exclude
303 debugwalk: include, exclude
304 debugwhyunstable:
304 debugwhyunstable:
305 debugwireargs: three, four, five, ssh, remotecmd, insecure
305 debugwireargs: three, four, five, ssh, remotecmd, insecure
306 debugwireproto: localssh, peer, noreadstderr, nologhandshake, ssh, remotecmd, insecure
306 debugwireproto: localssh, peer, noreadstderr, nologhandshake, ssh, remotecmd, insecure
307 diff: rev, change, text, git, binary, nodates, noprefix, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, unified, stat, root, include, exclude, subrepos
307 diff: rev, change, text, git, binary, nodates, noprefix, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, unified, stat, root, include, exclude, subrepos
308 export: bookmark, output, switch-parent, rev, text, git, binary, nodates, template
308 export: bookmark, output, switch-parent, rev, text, git, binary, nodates, template
309 files: rev, print0, include, exclude, template, subrepos
309 files: rev, print0, include, exclude, template, subrepos
310 forget: interactive, include, exclude, dry-run
310 forget: interactive, include, exclude, dry-run
311 graft: rev, base, continue, stop, abort, edit, log, no-commit, force, currentdate, currentuser, date, user, tool, dry-run
311 graft: rev, base, continue, stop, abort, edit, log, no-commit, force, currentdate, currentuser, date, user, tool, dry-run
312 grep: print0, all, diff, text, follow, ignore-case, files-with-matches, line-number, rev, all-files, user, date, template, include, exclude
312 grep: print0, all, diff, text, follow, ignore-case, files-with-matches, line-number, rev, all-files, user, date, template, include, exclude
313 heads: rev, topo, active, closed, style, template
313 heads: rev, topo, active, closed, style, template
314 help: extension, command, keyword, system
314 help: extension, command, keyword, system
315 identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure, template
315 identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure, template
316 import: strip, base, edit, force, no-commit, bypass, partial, exact, prefix, import-branch, message, logfile, date, user, similarity
316 import: strip, base, edit, force, no-commit, bypass, partial, exact, prefix, import-branch, message, logfile, date, user, similarity
317 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
317 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
318 init: ssh, remotecmd, insecure
318 init: ssh, remotecmd, insecure
319 locate: rev, print0, fullpath, include, exclude
319 locate: rev, print0, fullpath, include, exclude
320 log: follow, follow-first, date, copies, keyword, rev, line-range, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude
320 log: follow, follow-first, date, copies, keyword, rev, line-range, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude
321 manifest: rev, all, template
321 manifest: rev, all, template
322 merge: force, rev, preview, abort, tool
322 merge: force, rev, preview, abort, tool
323 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
323 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
324 parents: rev, style, template
324 parents: rev, style, template
325 paths: template
325 paths: template
326 phase: public, draft, secret, force, rev
326 phase: public, draft, secret, force, rev
327 pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
327 pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
328 push: force, rev, bookmark, branch, new-branch, pushvars, ssh, remotecmd, insecure
328 push: force, rev, bookmark, branch, new-branch, pushvars, publish, ssh, remotecmd, insecure
329 recover:
329 recover:
330 remove: after, force, subrepos, include, exclude, dry-run
330 remove: after, force, subrepos, include, exclude, dry-run
331 rename: after, force, include, exclude, dry-run
331 rename: after, force, include, exclude, dry-run
332 resolve: all, list, mark, unmark, no-status, re-merge, tool, include, exclude, template
332 resolve: all, list, mark, unmark, no-status, re-merge, tool, include, exclude, template
333 revert: all, date, rev, no-backup, interactive, include, exclude, dry-run
333 revert: all, date, rev, no-backup, interactive, include, exclude, dry-run
334 rollback: dry-run, force
334 rollback: dry-run, force
335 root:
335 root:
336 serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate, print-url, subrepos
336 serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate, print-url, subrepos
337 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, terse, copies, print0, rev, change, include, exclude, subrepos, template
337 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, terse, copies, print0, rev, change, include, exclude, subrepos, template
338 summary: remote
338 summary: remote
339 tag: force, local, rev, remove, edit, message, date, user
339 tag: force, local, rev, remove, edit, message, date, user
340 tags: template
340 tags: template
341 tip: patch, git, style, template
341 tip: patch, git, style, template
342 unbundle: update
342 unbundle: update
343 update: clean, check, merge, date, rev, tool
343 update: clean, check, merge, date, rev, tool
344 verify:
344 verify:
345 version: template
345 version: template
346
346
347 $ hg init a
347 $ hg init a
348 $ cd a
348 $ cd a
349 $ echo fee > fee
349 $ echo fee > fee
350 $ hg ci -q -Amfee
350 $ hg ci -q -Amfee
351 $ hg tag fee
351 $ hg tag fee
352 $ mkdir fie
352 $ mkdir fie
353 $ echo dead > fie/dead
353 $ echo dead > fie/dead
354 $ echo live > fie/live
354 $ echo live > fie/live
355 $ hg bookmark fo
355 $ hg bookmark fo
356 $ hg branch -q fie
356 $ hg branch -q fie
357 $ hg ci -q -Amfie
357 $ hg ci -q -Amfie
358 $ echo fo > fo
358 $ echo fo > fo
359 $ hg branch -qf default
359 $ hg branch -qf default
360 $ hg ci -q -Amfo
360 $ hg ci -q -Amfo
361 $ echo Fum > Fum
361 $ echo Fum > Fum
362 $ hg ci -q -AmFum
362 $ hg ci -q -AmFum
363 $ hg bookmark Fum
363 $ hg bookmark Fum
364
364
365 Test debugpathcomplete
365 Test debugpathcomplete
366
366
367 $ hg debugpathcomplete f
367 $ hg debugpathcomplete f
368 fee
368 fee
369 fie
369 fie
370 fo
370 fo
371 $ hg debugpathcomplete -f f
371 $ hg debugpathcomplete -f f
372 fee
372 fee
373 fie/dead
373 fie/dead
374 fie/live
374 fie/live
375 fo
375 fo
376
376
377 $ hg rm Fum
377 $ hg rm Fum
378 $ hg debugpathcomplete -r F
378 $ hg debugpathcomplete -r F
379 Fum
379 Fum
380
380
381 Test debugnamecomplete
381 Test debugnamecomplete
382
382
383 $ hg debugnamecomplete
383 $ hg debugnamecomplete
384 Fum
384 Fum
385 default
385 default
386 fee
386 fee
387 fie
387 fie
388 fo
388 fo
389 tip
389 tip
390 $ hg debugnamecomplete f
390 $ hg debugnamecomplete f
391 fee
391 fee
392 fie
392 fie
393 fo
393 fo
394
394
395 Test debuglabelcomplete, a deprecated name for debugnamecomplete that is still
395 Test debuglabelcomplete, a deprecated name for debugnamecomplete that is still
396 used for completions in some shells.
396 used for completions in some shells.
397
397
398 $ hg debuglabelcomplete
398 $ hg debuglabelcomplete
399 Fum
399 Fum
400 default
400 default
401 fee
401 fee
402 fie
402 fie
403 fo
403 fo
404 tip
404 tip
405 $ hg debuglabelcomplete f
405 $ hg debuglabelcomplete f
406 fee
406 fee
407 fie
407 fie
408 fo
408 fo
@@ -1,1561 +1,1752 b''
1 $ cat >> $HGRCPATH << EOF
1 $ cat >> $HGRCPATH << EOF
2 > [extensions]
2 > [extensions]
3 > drawdag=$TESTDIR/drawdag.py
3 > drawdag=$TESTDIR/drawdag.py
4 > phasereport=$TESTDIR/testlib/ext-phase-report.py
4 > phasereport=$TESTDIR/testlib/ext-phase-report.py
5 > EOF
5 > EOF
6
6
7 $ hgph() { hg log -G --template "{rev} {phase} {desc} - {node|short}\n" $*; }
7 $ hgph() { hg log -G --template "{rev} {phase} {desc} - {node|short}\n" $*; }
8
8
9 $ mkcommit() {
9 $ mkcommit() {
10 > echo "$1" > "$1"
10 > echo "$1" > "$1"
11 > hg add "$1"
11 > hg add "$1"
12 > message="$1"
12 > message="$1"
13 > shift
13 > shift
14 > hg ci -m "$message" $*
14 > hg ci -m "$message" $*
15 > }
15 > }
16
16
17 $ hg init alpha
17 $ hg init alpha
18 $ cd alpha
18 $ cd alpha
19 $ mkcommit a-A
19 $ mkcommit a-A
20 test-debug-phase: new rev 0: x -> 1
20 test-debug-phase: new rev 0: x -> 1
21 $ mkcommit a-B
21 $ mkcommit a-B
22 test-debug-phase: new rev 1: x -> 1
22 test-debug-phase: new rev 1: x -> 1
23 $ mkcommit a-C
23 $ mkcommit a-C
24 test-debug-phase: new rev 2: x -> 1
24 test-debug-phase: new rev 2: x -> 1
25 $ mkcommit a-D
25 $ mkcommit a-D
26 test-debug-phase: new rev 3: x -> 1
26 test-debug-phase: new rev 3: x -> 1
27 $ hgph
27 $ hgph
28 @ 3 draft a-D - b555f63b6063
28 @ 3 draft a-D - b555f63b6063
29 |
29 |
30 o 2 draft a-C - 54acac6f23ab
30 o 2 draft a-C - 54acac6f23ab
31 |
31 |
32 o 1 draft a-B - 548a3d25dbf0
32 o 1 draft a-B - 548a3d25dbf0
33 |
33 |
34 o 0 draft a-A - 054250a37db4
34 o 0 draft a-A - 054250a37db4
35
35
36
36
37 $ hg init ../beta
37 $ hg init ../beta
38 $ hg push -r 1 ../beta
38 $ hg push -r 1 ../beta
39 pushing to ../beta
39 pushing to ../beta
40 searching for changes
40 searching for changes
41 adding changesets
41 adding changesets
42 adding manifests
42 adding manifests
43 adding file changes
43 adding file changes
44 added 2 changesets with 2 changes to 2 files
44 added 2 changesets with 2 changes to 2 files
45 test-debug-phase: new rev 0: x -> 0
45 test-debug-phase: new rev 0: x -> 0
46 test-debug-phase: new rev 1: x -> 0
46 test-debug-phase: new rev 1: x -> 0
47 test-debug-phase: move rev 0: 1 -> 0
47 test-debug-phase: move rev 0: 1 -> 0
48 test-debug-phase: move rev 1: 1 -> 0
48 test-debug-phase: move rev 1: 1 -> 0
49 $ hgph
49 $ hgph
50 @ 3 draft a-D - b555f63b6063
50 @ 3 draft a-D - b555f63b6063
51 |
51 |
52 o 2 draft a-C - 54acac6f23ab
52 o 2 draft a-C - 54acac6f23ab
53 |
53 |
54 o 1 public a-B - 548a3d25dbf0
54 o 1 public a-B - 548a3d25dbf0
55 |
55 |
56 o 0 public a-A - 054250a37db4
56 o 0 public a-A - 054250a37db4
57
57
58
58
59 $ cd ../beta
59 $ cd ../beta
60 $ hgph
60 $ hgph
61 o 1 public a-B - 548a3d25dbf0
61 o 1 public a-B - 548a3d25dbf0
62 |
62 |
63 o 0 public a-A - 054250a37db4
63 o 0 public a-A - 054250a37db4
64
64
65 $ hg up -q
65 $ hg up -q
66 $ mkcommit b-A
66 $ mkcommit b-A
67 test-debug-phase: new rev 2: x -> 1
67 test-debug-phase: new rev 2: x -> 1
68 $ hgph
68 $ hgph
69 @ 2 draft b-A - f54f1bb90ff3
69 @ 2 draft b-A - f54f1bb90ff3
70 |
70 |
71 o 1 public a-B - 548a3d25dbf0
71 o 1 public a-B - 548a3d25dbf0
72 |
72 |
73 o 0 public a-A - 054250a37db4
73 o 0 public a-A - 054250a37db4
74
74
75 $ hg pull ../alpha
75 $ hg pull ../alpha
76 pulling from ../alpha
76 pulling from ../alpha
77 searching for changes
77 searching for changes
78 adding changesets
78 adding changesets
79 adding manifests
79 adding manifests
80 adding file changes
80 adding file changes
81 added 2 changesets with 2 changes to 2 files (+1 heads)
81 added 2 changesets with 2 changes to 2 files (+1 heads)
82 new changesets 54acac6f23ab:b555f63b6063
82 new changesets 54acac6f23ab:b555f63b6063
83 test-debug-phase: new rev 3: x -> 0
83 test-debug-phase: new rev 3: x -> 0
84 test-debug-phase: new rev 4: x -> 0
84 test-debug-phase: new rev 4: x -> 0
85 (run 'hg heads' to see heads, 'hg merge' to merge)
85 (run 'hg heads' to see heads, 'hg merge' to merge)
86 $ hgph
86 $ hgph
87 o 4 public a-D - b555f63b6063
87 o 4 public a-D - b555f63b6063
88 |
88 |
89 o 3 public a-C - 54acac6f23ab
89 o 3 public a-C - 54acac6f23ab
90 |
90 |
91 | @ 2 draft b-A - f54f1bb90ff3
91 | @ 2 draft b-A - f54f1bb90ff3
92 |/
92 |/
93 o 1 public a-B - 548a3d25dbf0
93 o 1 public a-B - 548a3d25dbf0
94 |
94 |
95 o 0 public a-A - 054250a37db4
95 o 0 public a-A - 054250a37db4
96
96
97
97
98 pull did not updated ../alpha state.
98 pull did not updated ../alpha state.
99 push from alpha to beta should update phase even if nothing is transferred
99 push from alpha to beta should update phase even if nothing is transferred
100
100
101 $ cd ../alpha
101 $ cd ../alpha
102 $ hgph # not updated by remote pull
102 $ hgph # not updated by remote pull
103 @ 3 draft a-D - b555f63b6063
103 @ 3 draft a-D - b555f63b6063
104 |
104 |
105 o 2 draft a-C - 54acac6f23ab
105 o 2 draft a-C - 54acac6f23ab
106 |
106 |
107 o 1 public a-B - 548a3d25dbf0
107 o 1 public a-B - 548a3d25dbf0
108 |
108 |
109 o 0 public a-A - 054250a37db4
109 o 0 public a-A - 054250a37db4
110
110
111 $ hg push -r 2 ../beta
111 $ hg push -r 2 ../beta
112 pushing to ../beta
112 pushing to ../beta
113 searching for changes
113 searching for changes
114 no changes found
114 no changes found
115 test-debug-phase: move rev 2: 1 -> 0
115 test-debug-phase: move rev 2: 1 -> 0
116 [1]
116 [1]
117 $ hgph
117 $ hgph
118 @ 3 draft a-D - b555f63b6063
118 @ 3 draft a-D - b555f63b6063
119 |
119 |
120 o 2 public a-C - 54acac6f23ab
120 o 2 public a-C - 54acac6f23ab
121 |
121 |
122 o 1 public a-B - 548a3d25dbf0
122 o 1 public a-B - 548a3d25dbf0
123 |
123 |
124 o 0 public a-A - 054250a37db4
124 o 0 public a-A - 054250a37db4
125
125
126 $ hg push ../beta
126 $ hg push ../beta
127 pushing to ../beta
127 pushing to ../beta
128 searching for changes
128 searching for changes
129 no changes found
129 no changes found
130 test-debug-phase: move rev 3: 1 -> 0
130 test-debug-phase: move rev 3: 1 -> 0
131 [1]
131 [1]
132 $ hgph
132 $ hgph
133 @ 3 public a-D - b555f63b6063
133 @ 3 public a-D - b555f63b6063
134 |
134 |
135 o 2 public a-C - 54acac6f23ab
135 o 2 public a-C - 54acac6f23ab
136 |
136 |
137 o 1 public a-B - 548a3d25dbf0
137 o 1 public a-B - 548a3d25dbf0
138 |
138 |
139 o 0 public a-A - 054250a37db4
139 o 0 public a-A - 054250a37db4
140
140
141
141
142 update must update phase of common changeset too
142 update must update phase of common changeset too
143
143
144 $ hg pull ../beta # getting b-A
144 $ hg pull ../beta # getting b-A
145 pulling from ../beta
145 pulling from ../beta
146 searching for changes
146 searching for changes
147 adding changesets
147 adding changesets
148 adding manifests
148 adding manifests
149 adding file changes
149 adding file changes
150 added 1 changesets with 1 changes to 1 files (+1 heads)
150 added 1 changesets with 1 changes to 1 files (+1 heads)
151 new changesets f54f1bb90ff3
151 new changesets f54f1bb90ff3
152 test-debug-phase: new rev 4: x -> 0
152 test-debug-phase: new rev 4: x -> 0
153 (run 'hg heads' to see heads, 'hg merge' to merge)
153 (run 'hg heads' to see heads, 'hg merge' to merge)
154
154
155 $ cd ../beta
155 $ cd ../beta
156 $ hgph # not updated by remote pull
156 $ hgph # not updated by remote pull
157 o 4 public a-D - b555f63b6063
157 o 4 public a-D - b555f63b6063
158 |
158 |
159 o 3 public a-C - 54acac6f23ab
159 o 3 public a-C - 54acac6f23ab
160 |
160 |
161 | @ 2 draft b-A - f54f1bb90ff3
161 | @ 2 draft b-A - f54f1bb90ff3
162 |/
162 |/
163 o 1 public a-B - 548a3d25dbf0
163 o 1 public a-B - 548a3d25dbf0
164 |
164 |
165 o 0 public a-A - 054250a37db4
165 o 0 public a-A - 054250a37db4
166
166
167 $ hg pull ../alpha
167 $ hg pull ../alpha
168 pulling from ../alpha
168 pulling from ../alpha
169 searching for changes
169 searching for changes
170 no changes found
170 no changes found
171 1 local changesets published
171 1 local changesets published
172 test-debug-phase: move rev 2: 1 -> 0
172 test-debug-phase: move rev 2: 1 -> 0
173 $ hgph
173 $ hgph
174 o 4 public a-D - b555f63b6063
174 o 4 public a-D - b555f63b6063
175 |
175 |
176 o 3 public a-C - 54acac6f23ab
176 o 3 public a-C - 54acac6f23ab
177 |
177 |
178 | @ 2 public b-A - f54f1bb90ff3
178 | @ 2 public b-A - f54f1bb90ff3
179 |/
179 |/
180 o 1 public a-B - 548a3d25dbf0
180 o 1 public a-B - 548a3d25dbf0
181 |
181 |
182 o 0 public a-A - 054250a37db4
182 o 0 public a-A - 054250a37db4
183
183
184
184
185 Publish configuration option
185 Publish configuration option
186 ----------------------------
186 ----------------------------
187
187
188 Pull
188 Pull
189 ````
189 ````
190
190
191 changegroup are added without phase movement
191 changegroup are added without phase movement
192
192
193 $ hg bundle -a ../base.bundle
193 $ hg bundle -a ../base.bundle
194 5 changesets found
194 5 changesets found
195 $ cd ..
195 $ cd ..
196 $ hg init mu
196 $ hg init mu
197 $ cd mu
197 $ cd mu
198 $ cat > .hg/hgrc << EOF
198 $ cat > .hg/hgrc << EOF
199 > [phases]
199 > [phases]
200 > publish=0
200 > publish=0
201 > EOF
201 > EOF
202 $ hg unbundle ../base.bundle
202 $ hg unbundle ../base.bundle
203 adding changesets
203 adding changesets
204 adding manifests
204 adding manifests
205 adding file changes
205 adding file changes
206 added 5 changesets with 5 changes to 5 files (+1 heads)
206 added 5 changesets with 5 changes to 5 files (+1 heads)
207 new changesets 054250a37db4:b555f63b6063 (5 drafts)
207 new changesets 054250a37db4:b555f63b6063 (5 drafts)
208 test-debug-phase: new rev 0: x -> 1
208 test-debug-phase: new rev 0: x -> 1
209 test-debug-phase: new rev 1: x -> 1
209 test-debug-phase: new rev 1: x -> 1
210 test-debug-phase: new rev 2: x -> 1
210 test-debug-phase: new rev 2: x -> 1
211 test-debug-phase: new rev 3: x -> 1
211 test-debug-phase: new rev 3: x -> 1
212 test-debug-phase: new rev 4: x -> 1
212 test-debug-phase: new rev 4: x -> 1
213 (run 'hg heads' to see heads, 'hg merge' to merge)
213 (run 'hg heads' to see heads, 'hg merge' to merge)
214 $ hgph
214 $ hgph
215 o 4 draft a-D - b555f63b6063
215 o 4 draft a-D - b555f63b6063
216 |
216 |
217 o 3 draft a-C - 54acac6f23ab
217 o 3 draft a-C - 54acac6f23ab
218 |
218 |
219 | o 2 draft b-A - f54f1bb90ff3
219 | o 2 draft b-A - f54f1bb90ff3
220 |/
220 |/
221 o 1 draft a-B - 548a3d25dbf0
221 o 1 draft a-B - 548a3d25dbf0
222 |
222 |
223 o 0 draft a-A - 054250a37db4
223 o 0 draft a-A - 054250a37db4
224
224
225 $ cd ..
225 $ cd ..
226
226
227 Pulling from publish=False to publish=False does not move boundary.
227 Pulling from publish=False to publish=False does not move boundary.
228
228
229 $ hg init nu
229 $ hg init nu
230 $ cd nu
230 $ cd nu
231 $ cat > .hg/hgrc << EOF
231 $ cat > .hg/hgrc << EOF
232 > [phases]
232 > [phases]
233 > publish=0
233 > publish=0
234 > EOF
234 > EOF
235 $ hg pull ../mu -r 54acac6f23ab
235 $ hg pull ../mu -r 54acac6f23ab
236 pulling from ../mu
236 pulling from ../mu
237 adding changesets
237 adding changesets
238 adding manifests
238 adding manifests
239 adding file changes
239 adding file changes
240 added 3 changesets with 3 changes to 3 files
240 added 3 changesets with 3 changes to 3 files
241 new changesets 054250a37db4:54acac6f23ab (3 drafts)
241 new changesets 054250a37db4:54acac6f23ab (3 drafts)
242 test-debug-phase: new rev 0: x -> 1
242 test-debug-phase: new rev 0: x -> 1
243 test-debug-phase: new rev 1: x -> 1
243 test-debug-phase: new rev 1: x -> 1
244 test-debug-phase: new rev 2: x -> 1
244 test-debug-phase: new rev 2: x -> 1
245 (run 'hg update' to get a working copy)
245 (run 'hg update' to get a working copy)
246 $ hgph
246 $ hgph
247 o 2 draft a-C - 54acac6f23ab
247 o 2 draft a-C - 54acac6f23ab
248 |
248 |
249 o 1 draft a-B - 548a3d25dbf0
249 o 1 draft a-B - 548a3d25dbf0
250 |
250 |
251 o 0 draft a-A - 054250a37db4
251 o 0 draft a-A - 054250a37db4
252
252
253
253
254 Even for common
254 Even for common
255
255
256 $ hg pull ../mu -r f54f1bb90ff3
256 $ hg pull ../mu -r f54f1bb90ff3
257 pulling from ../mu
257 pulling from ../mu
258 searching for changes
258 searching for changes
259 adding changesets
259 adding changesets
260 adding manifests
260 adding manifests
261 adding file changes
261 adding file changes
262 added 1 changesets with 1 changes to 1 files (+1 heads)
262 added 1 changesets with 1 changes to 1 files (+1 heads)
263 new changesets f54f1bb90ff3 (1 drafts)
263 new changesets f54f1bb90ff3 (1 drafts)
264 test-debug-phase: new rev 3: x -> 1
264 test-debug-phase: new rev 3: x -> 1
265 (run 'hg heads' to see heads, 'hg merge' to merge)
265 (run 'hg heads' to see heads, 'hg merge' to merge)
266 $ hgph
266 $ hgph
267 o 3 draft b-A - f54f1bb90ff3
267 o 3 draft b-A - f54f1bb90ff3
268 |
268 |
269 | o 2 draft a-C - 54acac6f23ab
269 | o 2 draft a-C - 54acac6f23ab
270 |/
270 |/
271 o 1 draft a-B - 548a3d25dbf0
271 o 1 draft a-B - 548a3d25dbf0
272 |
272 |
273 o 0 draft a-A - 054250a37db4
273 o 0 draft a-A - 054250a37db4
274
274
275
275
276
276
277 Pulling from Publish=True to Publish=False move boundary in common set.
277 Pulling from Publish=True to Publish=False move boundary in common set.
278 we are in nu
278 we are in nu
279
279
280 $ hg pull ../alpha -r b555f63b6063
280 $ hg pull ../alpha -r b555f63b6063
281 pulling from ../alpha
281 pulling from ../alpha
282 searching for changes
282 searching for changes
283 adding changesets
283 adding changesets
284 adding manifests
284 adding manifests
285 adding file changes
285 adding file changes
286 added 1 changesets with 1 changes to 1 files
286 added 1 changesets with 1 changes to 1 files
287 new changesets b555f63b6063
287 new changesets b555f63b6063
288 3 local changesets published
288 3 local changesets published
289 test-debug-phase: move rev 0: 1 -> 0
289 test-debug-phase: move rev 0: 1 -> 0
290 test-debug-phase: move rev 1: 1 -> 0
290 test-debug-phase: move rev 1: 1 -> 0
291 test-debug-phase: move rev 2: 1 -> 0
291 test-debug-phase: move rev 2: 1 -> 0
292 test-debug-phase: new rev 4: x -> 0
292 test-debug-phase: new rev 4: x -> 0
293 (run 'hg update' to get a working copy)
293 (run 'hg update' to get a working copy)
294 $ hgph # f54f1bb90ff3 stay draft, not ancestor of -r
294 $ hgph # f54f1bb90ff3 stay draft, not ancestor of -r
295 o 4 public a-D - b555f63b6063
295 o 4 public a-D - b555f63b6063
296 |
296 |
297 | o 3 draft b-A - f54f1bb90ff3
297 | o 3 draft b-A - f54f1bb90ff3
298 | |
298 | |
299 o | 2 public a-C - 54acac6f23ab
299 o | 2 public a-C - 54acac6f23ab
300 |/
300 |/
301 o 1 public a-B - 548a3d25dbf0
301 o 1 public a-B - 548a3d25dbf0
302 |
302 |
303 o 0 public a-A - 054250a37db4
303 o 0 public a-A - 054250a37db4
304
304
305
305
306 pulling from Publish=False to publish=False with some public
306 pulling from Publish=False to publish=False with some public
307
307
308 $ hg up -q f54f1bb90ff3
308 $ hg up -q f54f1bb90ff3
309 $ mkcommit n-A
309 $ mkcommit n-A
310 test-debug-phase: new rev 5: x -> 1
310 test-debug-phase: new rev 5: x -> 1
311 $ mkcommit n-B
311 $ mkcommit n-B
312 test-debug-phase: new rev 6: x -> 1
312 test-debug-phase: new rev 6: x -> 1
313 $ hgph
313 $ hgph
314 @ 6 draft n-B - 145e75495359
314 @ 6 draft n-B - 145e75495359
315 |
315 |
316 o 5 draft n-A - d6bcb4f74035
316 o 5 draft n-A - d6bcb4f74035
317 |
317 |
318 | o 4 public a-D - b555f63b6063
318 | o 4 public a-D - b555f63b6063
319 | |
319 | |
320 o | 3 draft b-A - f54f1bb90ff3
320 o | 3 draft b-A - f54f1bb90ff3
321 | |
321 | |
322 | o 2 public a-C - 54acac6f23ab
322 | o 2 public a-C - 54acac6f23ab
323 |/
323 |/
324 o 1 public a-B - 548a3d25dbf0
324 o 1 public a-B - 548a3d25dbf0
325 |
325 |
326 o 0 public a-A - 054250a37db4
326 o 0 public a-A - 054250a37db4
327
327
328 $ cd ../mu
328 $ cd ../mu
329 $ hg pull ../nu
329 $ hg pull ../nu
330 pulling from ../nu
330 pulling from ../nu
331 searching for changes
331 searching for changes
332 adding changesets
332 adding changesets
333 adding manifests
333 adding manifests
334 adding file changes
334 adding file changes
335 added 2 changesets with 2 changes to 2 files
335 added 2 changesets with 2 changes to 2 files
336 new changesets d6bcb4f74035:145e75495359 (2 drafts)
336 new changesets d6bcb4f74035:145e75495359 (2 drafts)
337 4 local changesets published
337 4 local changesets published
338 test-debug-phase: move rev 0: 1 -> 0
338 test-debug-phase: move rev 0: 1 -> 0
339 test-debug-phase: move rev 1: 1 -> 0
339 test-debug-phase: move rev 1: 1 -> 0
340 test-debug-phase: move rev 3: 1 -> 0
340 test-debug-phase: move rev 3: 1 -> 0
341 test-debug-phase: move rev 4: 1 -> 0
341 test-debug-phase: move rev 4: 1 -> 0
342 test-debug-phase: new rev 5: x -> 1
342 test-debug-phase: new rev 5: x -> 1
343 test-debug-phase: new rev 6: x -> 1
343 test-debug-phase: new rev 6: x -> 1
344 (run 'hg update' to get a working copy)
344 (run 'hg update' to get a working copy)
345 $ hgph
345 $ hgph
346 o 6 draft n-B - 145e75495359
346 o 6 draft n-B - 145e75495359
347 |
347 |
348 o 5 draft n-A - d6bcb4f74035
348 o 5 draft n-A - d6bcb4f74035
349 |
349 |
350 | o 4 public a-D - b555f63b6063
350 | o 4 public a-D - b555f63b6063
351 | |
351 | |
352 | o 3 public a-C - 54acac6f23ab
352 | o 3 public a-C - 54acac6f23ab
353 | |
353 | |
354 o | 2 draft b-A - f54f1bb90ff3
354 o | 2 draft b-A - f54f1bb90ff3
355 |/
355 |/
356 o 1 public a-B - 548a3d25dbf0
356 o 1 public a-B - 548a3d25dbf0
357 |
357 |
358 o 0 public a-A - 054250a37db4
358 o 0 public a-A - 054250a37db4
359
359
360 $ cd ..
360 $ cd ..
361
361
362 pulling into publish=True
362 pulling into publish=True
363
363
364 $ cd alpha
364 $ cd alpha
365 $ hgph
365 $ hgph
366 o 4 public b-A - f54f1bb90ff3
366 o 4 public b-A - f54f1bb90ff3
367 |
367 |
368 | @ 3 public a-D - b555f63b6063
368 | @ 3 public a-D - b555f63b6063
369 | |
369 | |
370 | o 2 public a-C - 54acac6f23ab
370 | o 2 public a-C - 54acac6f23ab
371 |/
371 |/
372 o 1 public a-B - 548a3d25dbf0
372 o 1 public a-B - 548a3d25dbf0
373 |
373 |
374 o 0 public a-A - 054250a37db4
374 o 0 public a-A - 054250a37db4
375
375
376 $ hg pull ../mu
376 $ hg pull ../mu
377 pulling from ../mu
377 pulling from ../mu
378 searching for changes
378 searching for changes
379 adding changesets
379 adding changesets
380 adding manifests
380 adding manifests
381 adding file changes
381 adding file changes
382 added 2 changesets with 2 changes to 2 files
382 added 2 changesets with 2 changes to 2 files
383 new changesets d6bcb4f74035:145e75495359 (2 drafts)
383 new changesets d6bcb4f74035:145e75495359 (2 drafts)
384 test-debug-phase: new rev 5: x -> 1
384 test-debug-phase: new rev 5: x -> 1
385 test-debug-phase: new rev 6: x -> 1
385 test-debug-phase: new rev 6: x -> 1
386 (run 'hg update' to get a working copy)
386 (run 'hg update' to get a working copy)
387 $ hgph
387 $ hgph
388 o 6 draft n-B - 145e75495359
388 o 6 draft n-B - 145e75495359
389 |
389 |
390 o 5 draft n-A - d6bcb4f74035
390 o 5 draft n-A - d6bcb4f74035
391 |
391 |
392 o 4 public b-A - f54f1bb90ff3
392 o 4 public b-A - f54f1bb90ff3
393 |
393 |
394 | @ 3 public a-D - b555f63b6063
394 | @ 3 public a-D - b555f63b6063
395 | |
395 | |
396 | o 2 public a-C - 54acac6f23ab
396 | o 2 public a-C - 54acac6f23ab
397 |/
397 |/
398 o 1 public a-B - 548a3d25dbf0
398 o 1 public a-B - 548a3d25dbf0
399 |
399 |
400 o 0 public a-A - 054250a37db4
400 o 0 public a-A - 054250a37db4
401
401
402 $ cd ..
402 $ cd ..
403
403
404 pulling back into original repo
404 pulling back into original repo
405
405
406 $ cd nu
406 $ cd nu
407 $ hg pull ../alpha
407 $ hg pull ../alpha
408 pulling from ../alpha
408 pulling from ../alpha
409 searching for changes
409 searching for changes
410 no changes found
410 no changes found
411 3 local changesets published
411 3 local changesets published
412 test-debug-phase: move rev 3: 1 -> 0
412 test-debug-phase: move rev 3: 1 -> 0
413 test-debug-phase: move rev 5: 1 -> 0
413 test-debug-phase: move rev 5: 1 -> 0
414 test-debug-phase: move rev 6: 1 -> 0
414 test-debug-phase: move rev 6: 1 -> 0
415 $ hgph
415 $ hgph
416 @ 6 public n-B - 145e75495359
416 @ 6 public n-B - 145e75495359
417 |
417 |
418 o 5 public n-A - d6bcb4f74035
418 o 5 public n-A - d6bcb4f74035
419 |
419 |
420 | o 4 public a-D - b555f63b6063
420 | o 4 public a-D - b555f63b6063
421 | |
421 | |
422 o | 3 public b-A - f54f1bb90ff3
422 o | 3 public b-A - f54f1bb90ff3
423 | |
423 | |
424 | o 2 public a-C - 54acac6f23ab
424 | o 2 public a-C - 54acac6f23ab
425 |/
425 |/
426 o 1 public a-B - 548a3d25dbf0
426 o 1 public a-B - 548a3d25dbf0
427 |
427 |
428 o 0 public a-A - 054250a37db4
428 o 0 public a-A - 054250a37db4
429
429
430
430
431 Push
431 Push
432 ````
432 ````
433
433
434 (inserted)
434 (inserted)
435
435
436 Test that phase are pushed even when they are nothing to pus
436 Test that phase are pushed even when they are nothing to pus
437 (this might be tested later bu are very convenient to not alter too much test)
437 (this might be tested later bu are very convenient to not alter too much test)
438
438
439 Push back to alpha
439 Push back to alpha
440
440
441 $ hg push ../alpha # from nu
441 $ hg push ../alpha # from nu
442 pushing to ../alpha
442 pushing to ../alpha
443 searching for changes
443 searching for changes
444 no changes found
444 no changes found
445 test-debug-phase: move rev 5: 1 -> 0
445 test-debug-phase: move rev 5: 1 -> 0
446 test-debug-phase: move rev 6: 1 -> 0
446 test-debug-phase: move rev 6: 1 -> 0
447 [1]
447 [1]
448 $ cd ..
448 $ cd ..
449 $ cd alpha
449 $ cd alpha
450 $ hgph
450 $ hgph
451 o 6 public n-B - 145e75495359
451 o 6 public n-B - 145e75495359
452 |
452 |
453 o 5 public n-A - d6bcb4f74035
453 o 5 public n-A - d6bcb4f74035
454 |
454 |
455 o 4 public b-A - f54f1bb90ff3
455 o 4 public b-A - f54f1bb90ff3
456 |
456 |
457 | @ 3 public a-D - b555f63b6063
457 | @ 3 public a-D - b555f63b6063
458 | |
458 | |
459 | o 2 public a-C - 54acac6f23ab
459 | o 2 public a-C - 54acac6f23ab
460 |/
460 |/
461 o 1 public a-B - 548a3d25dbf0
461 o 1 public a-B - 548a3d25dbf0
462 |
462 |
463 o 0 public a-A - 054250a37db4
463 o 0 public a-A - 054250a37db4
464
464
465
465
466 (end insertion)
466 (end insertion)
467
467
468
468
469 initial setup
469 initial setup
470
470
471 $ hg log -G # of alpha
471 $ hg log -G # of alpha
472 o changeset: 6:145e75495359
472 o changeset: 6:145e75495359
473 | tag: tip
473 | tag: tip
474 | user: test
474 | user: test
475 | date: Thu Jan 01 00:00:00 1970 +0000
475 | date: Thu Jan 01 00:00:00 1970 +0000
476 | summary: n-B
476 | summary: n-B
477 |
477 |
478 o changeset: 5:d6bcb4f74035
478 o changeset: 5:d6bcb4f74035
479 | user: test
479 | user: test
480 | date: Thu Jan 01 00:00:00 1970 +0000
480 | date: Thu Jan 01 00:00:00 1970 +0000
481 | summary: n-A
481 | summary: n-A
482 |
482 |
483 o changeset: 4:f54f1bb90ff3
483 o changeset: 4:f54f1bb90ff3
484 | parent: 1:548a3d25dbf0
484 | parent: 1:548a3d25dbf0
485 | user: test
485 | user: test
486 | date: Thu Jan 01 00:00:00 1970 +0000
486 | date: Thu Jan 01 00:00:00 1970 +0000
487 | summary: b-A
487 | summary: b-A
488 |
488 |
489 | @ changeset: 3:b555f63b6063
489 | @ changeset: 3:b555f63b6063
490 | | user: test
490 | | user: test
491 | | date: Thu Jan 01 00:00:00 1970 +0000
491 | | date: Thu Jan 01 00:00:00 1970 +0000
492 | | summary: a-D
492 | | summary: a-D
493 | |
493 | |
494 | o changeset: 2:54acac6f23ab
494 | o changeset: 2:54acac6f23ab
495 |/ user: test
495 |/ user: test
496 | date: Thu Jan 01 00:00:00 1970 +0000
496 | date: Thu Jan 01 00:00:00 1970 +0000
497 | summary: a-C
497 | summary: a-C
498 |
498 |
499 o changeset: 1:548a3d25dbf0
499 o changeset: 1:548a3d25dbf0
500 | user: test
500 | user: test
501 | date: Thu Jan 01 00:00:00 1970 +0000
501 | date: Thu Jan 01 00:00:00 1970 +0000
502 | summary: a-B
502 | summary: a-B
503 |
503 |
504 o changeset: 0:054250a37db4
504 o changeset: 0:054250a37db4
505 user: test
505 user: test
506 date: Thu Jan 01 00:00:00 1970 +0000
506 date: Thu Jan 01 00:00:00 1970 +0000
507 summary: a-A
507 summary: a-A
508
508
509 $ mkcommit a-E
509 $ mkcommit a-E
510 test-debug-phase: new rev 7: x -> 1
510 test-debug-phase: new rev 7: x -> 1
511 $ mkcommit a-F
511 $ mkcommit a-F
512 test-debug-phase: new rev 8: x -> 1
512 test-debug-phase: new rev 8: x -> 1
513 $ mkcommit a-G
513 $ mkcommit a-G
514 test-debug-phase: new rev 9: x -> 1
514 test-debug-phase: new rev 9: x -> 1
515 $ hg up d6bcb4f74035 -q
515 $ hg up d6bcb4f74035 -q
516 $ mkcommit a-H
516 $ mkcommit a-H
517 test-debug-phase: new rev 10: x -> 1
517 test-debug-phase: new rev 10: x -> 1
518 created new head
518 created new head
519 $ hgph
519 $ hgph
520 @ 10 draft a-H - 967b449fbc94
520 @ 10 draft a-H - 967b449fbc94
521 |
521 |
522 | o 9 draft a-G - 3e27b6f1eee1
522 | o 9 draft a-G - 3e27b6f1eee1
523 | |
523 | |
524 | o 8 draft a-F - b740e3e5c05d
524 | o 8 draft a-F - b740e3e5c05d
525 | |
525 | |
526 | o 7 draft a-E - e9f537e46dea
526 | o 7 draft a-E - e9f537e46dea
527 | |
527 | |
528 +---o 6 public n-B - 145e75495359
528 +---o 6 public n-B - 145e75495359
529 | |
529 | |
530 o | 5 public n-A - d6bcb4f74035
530 o | 5 public n-A - d6bcb4f74035
531 | |
531 | |
532 o | 4 public b-A - f54f1bb90ff3
532 o | 4 public b-A - f54f1bb90ff3
533 | |
533 | |
534 | o 3 public a-D - b555f63b6063
534 | o 3 public a-D - b555f63b6063
535 | |
535 | |
536 | o 2 public a-C - 54acac6f23ab
536 | o 2 public a-C - 54acac6f23ab
537 |/
537 |/
538 o 1 public a-B - 548a3d25dbf0
538 o 1 public a-B - 548a3d25dbf0
539 |
539 |
540 o 0 public a-A - 054250a37db4
540 o 0 public a-A - 054250a37db4
541
541
542
542
543 Pulling from bundle does not alter phases of changeset not present in the bundle
543 Pulling from bundle does not alter phases of changeset not present in the bundle
544
544
545 #if repobundlerepo
545 #if repobundlerepo
546 $ hg bundle --base 1 -r 6 -r 3 ../partial-bundle.hg
546 $ hg bundle --base 1 -r 6 -r 3 ../partial-bundle.hg
547 5 changesets found
547 5 changesets found
548 $ hg pull ../partial-bundle.hg
548 $ hg pull ../partial-bundle.hg
549 pulling from ../partial-bundle.hg
549 pulling from ../partial-bundle.hg
550 searching for changes
550 searching for changes
551 no changes found
551 no changes found
552 $ hgph
552 $ hgph
553 @ 10 draft a-H - 967b449fbc94
553 @ 10 draft a-H - 967b449fbc94
554 |
554 |
555 | o 9 draft a-G - 3e27b6f1eee1
555 | o 9 draft a-G - 3e27b6f1eee1
556 | |
556 | |
557 | o 8 draft a-F - b740e3e5c05d
557 | o 8 draft a-F - b740e3e5c05d
558 | |
558 | |
559 | o 7 draft a-E - e9f537e46dea
559 | o 7 draft a-E - e9f537e46dea
560 | |
560 | |
561 +---o 6 public n-B - 145e75495359
561 +---o 6 public n-B - 145e75495359
562 | |
562 | |
563 o | 5 public n-A - d6bcb4f74035
563 o | 5 public n-A - d6bcb4f74035
564 | |
564 | |
565 o | 4 public b-A - f54f1bb90ff3
565 o | 4 public b-A - f54f1bb90ff3
566 | |
566 | |
567 | o 3 public a-D - b555f63b6063
567 | o 3 public a-D - b555f63b6063
568 | |
568 | |
569 | o 2 public a-C - 54acac6f23ab
569 | o 2 public a-C - 54acac6f23ab
570 |/
570 |/
571 o 1 public a-B - 548a3d25dbf0
571 o 1 public a-B - 548a3d25dbf0
572 |
572 |
573 o 0 public a-A - 054250a37db4
573 o 0 public a-A - 054250a37db4
574
574
575 #endif
575 #endif
576
576
577 Pushing to Publish=False (unknown changeset)
577 Pushing to Publish=False (unknown changeset)
578
578
579 $ hg push ../mu -r b740e3e5c05d # a-F
579 $ hg push ../mu -r b740e3e5c05d # a-F
580 pushing to ../mu
580 pushing to ../mu
581 searching for changes
581 searching for changes
582 adding changesets
582 adding changesets
583 adding manifests
583 adding manifests
584 adding file changes
584 adding file changes
585 added 2 changesets with 2 changes to 2 files
585 added 2 changesets with 2 changes to 2 files
586 test-debug-phase: new rev 7: x -> 1
586 test-debug-phase: new rev 7: x -> 1
587 test-debug-phase: new rev 8: x -> 1
587 test-debug-phase: new rev 8: x -> 1
588 $ hgph
588 $ hgph
589 @ 10 draft a-H - 967b449fbc94
589 @ 10 draft a-H - 967b449fbc94
590 |
590 |
591 | o 9 draft a-G - 3e27b6f1eee1
591 | o 9 draft a-G - 3e27b6f1eee1
592 | |
592 | |
593 | o 8 draft a-F - b740e3e5c05d
593 | o 8 draft a-F - b740e3e5c05d
594 | |
594 | |
595 | o 7 draft a-E - e9f537e46dea
595 | o 7 draft a-E - e9f537e46dea
596 | |
596 | |
597 +---o 6 public n-B - 145e75495359
597 +---o 6 public n-B - 145e75495359
598 | |
598 | |
599 o | 5 public n-A - d6bcb4f74035
599 o | 5 public n-A - d6bcb4f74035
600 | |
600 | |
601 o | 4 public b-A - f54f1bb90ff3
601 o | 4 public b-A - f54f1bb90ff3
602 | |
602 | |
603 | o 3 public a-D - b555f63b6063
603 | o 3 public a-D - b555f63b6063
604 | |
604 | |
605 | o 2 public a-C - 54acac6f23ab
605 | o 2 public a-C - 54acac6f23ab
606 |/
606 |/
607 o 1 public a-B - 548a3d25dbf0
607 o 1 public a-B - 548a3d25dbf0
608 |
608 |
609 o 0 public a-A - 054250a37db4
609 o 0 public a-A - 054250a37db4
610
610
611
611
612 $ cd ../mu
612 $ cd ../mu
613 $ hgph # again f54f1bb90ff3, d6bcb4f74035 and 145e75495359 stay draft,
613 $ hgph # again f54f1bb90ff3, d6bcb4f74035 and 145e75495359 stay draft,
614 > # not ancestor of -r
614 > # not ancestor of -r
615 o 8 draft a-F - b740e3e5c05d
615 o 8 draft a-F - b740e3e5c05d
616 |
616 |
617 o 7 draft a-E - e9f537e46dea
617 o 7 draft a-E - e9f537e46dea
618 |
618 |
619 | o 6 draft n-B - 145e75495359
619 | o 6 draft n-B - 145e75495359
620 | |
620 | |
621 | o 5 draft n-A - d6bcb4f74035
621 | o 5 draft n-A - d6bcb4f74035
622 | |
622 | |
623 o | 4 public a-D - b555f63b6063
623 o | 4 public a-D - b555f63b6063
624 | |
624 | |
625 o | 3 public a-C - 54acac6f23ab
625 o | 3 public a-C - 54acac6f23ab
626 | |
626 | |
627 | o 2 draft b-A - f54f1bb90ff3
627 | o 2 draft b-A - f54f1bb90ff3
628 |/
628 |/
629 o 1 public a-B - 548a3d25dbf0
629 o 1 public a-B - 548a3d25dbf0
630 |
630 |
631 o 0 public a-A - 054250a37db4
631 o 0 public a-A - 054250a37db4
632
632
633
633
634 Pushing to Publish=True (unknown changeset)
634 Pushing to Publish=True (unknown changeset)
635
635
636 $ hg push ../beta -r b740e3e5c05d
636 $ hg push ../beta -r b740e3e5c05d
637 pushing to ../beta
637 pushing to ../beta
638 searching for changes
638 searching for changes
639 adding changesets
639 adding changesets
640 adding manifests
640 adding manifests
641 adding file changes
641 adding file changes
642 added 2 changesets with 2 changes to 2 files
642 added 2 changesets with 2 changes to 2 files
643 test-debug-phase: new rev 5: x -> 0
643 test-debug-phase: new rev 5: x -> 0
644 test-debug-phase: new rev 6: x -> 0
644 test-debug-phase: new rev 6: x -> 0
645 test-debug-phase: move rev 7: 1 -> 0
645 test-debug-phase: move rev 7: 1 -> 0
646 test-debug-phase: move rev 8: 1 -> 0
646 test-debug-phase: move rev 8: 1 -> 0
647 $ hgph # again f54f1bb90ff3, d6bcb4f74035 and 145e75495359 stay draft,
647 $ hgph # again f54f1bb90ff3, d6bcb4f74035 and 145e75495359 stay draft,
648 > # not ancestor of -r
648 > # not ancestor of -r
649 o 8 public a-F - b740e3e5c05d
649 o 8 public a-F - b740e3e5c05d
650 |
650 |
651 o 7 public a-E - e9f537e46dea
651 o 7 public a-E - e9f537e46dea
652 |
652 |
653 | o 6 draft n-B - 145e75495359
653 | o 6 draft n-B - 145e75495359
654 | |
654 | |
655 | o 5 draft n-A - d6bcb4f74035
655 | o 5 draft n-A - d6bcb4f74035
656 | |
656 | |
657 o | 4 public a-D - b555f63b6063
657 o | 4 public a-D - b555f63b6063
658 | |
658 | |
659 o | 3 public a-C - 54acac6f23ab
659 o | 3 public a-C - 54acac6f23ab
660 | |
660 | |
661 | o 2 draft b-A - f54f1bb90ff3
661 | o 2 draft b-A - f54f1bb90ff3
662 |/
662 |/
663 o 1 public a-B - 548a3d25dbf0
663 o 1 public a-B - 548a3d25dbf0
664 |
664 |
665 o 0 public a-A - 054250a37db4
665 o 0 public a-A - 054250a37db4
666
666
667
667
668 Pushing to Publish=True (common changeset)
668 Pushing to Publish=True (common changeset)
669
669
670 $ cd ../beta
670 $ cd ../beta
671 $ hg push ../alpha
671 $ hg push ../alpha
672 pushing to ../alpha
672 pushing to ../alpha
673 searching for changes
673 searching for changes
674 no changes found
674 no changes found
675 test-debug-phase: move rev 7: 1 -> 0
675 test-debug-phase: move rev 7: 1 -> 0
676 test-debug-phase: move rev 8: 1 -> 0
676 test-debug-phase: move rev 8: 1 -> 0
677 [1]
677 [1]
678 $ hgph
678 $ hgph
679 o 6 public a-F - b740e3e5c05d
679 o 6 public a-F - b740e3e5c05d
680 |
680 |
681 o 5 public a-E - e9f537e46dea
681 o 5 public a-E - e9f537e46dea
682 |
682 |
683 o 4 public a-D - b555f63b6063
683 o 4 public a-D - b555f63b6063
684 |
684 |
685 o 3 public a-C - 54acac6f23ab
685 o 3 public a-C - 54acac6f23ab
686 |
686 |
687 | @ 2 public b-A - f54f1bb90ff3
687 | @ 2 public b-A - f54f1bb90ff3
688 |/
688 |/
689 o 1 public a-B - 548a3d25dbf0
689 o 1 public a-B - 548a3d25dbf0
690 |
690 |
691 o 0 public a-A - 054250a37db4
691 o 0 public a-A - 054250a37db4
692
692
693 $ cd ../alpha
693 $ cd ../alpha
694 $ hgph
694 $ hgph
695 @ 10 draft a-H - 967b449fbc94
695 @ 10 draft a-H - 967b449fbc94
696 |
696 |
697 | o 9 draft a-G - 3e27b6f1eee1
697 | o 9 draft a-G - 3e27b6f1eee1
698 | |
698 | |
699 | o 8 public a-F - b740e3e5c05d
699 | o 8 public a-F - b740e3e5c05d
700 | |
700 | |
701 | o 7 public a-E - e9f537e46dea
701 | o 7 public a-E - e9f537e46dea
702 | |
702 | |
703 +---o 6 public n-B - 145e75495359
703 +---o 6 public n-B - 145e75495359
704 | |
704 | |
705 o | 5 public n-A - d6bcb4f74035
705 o | 5 public n-A - d6bcb4f74035
706 | |
706 | |
707 o | 4 public b-A - f54f1bb90ff3
707 o | 4 public b-A - f54f1bb90ff3
708 | |
708 | |
709 | o 3 public a-D - b555f63b6063
709 | o 3 public a-D - b555f63b6063
710 | |
710 | |
711 | o 2 public a-C - 54acac6f23ab
711 | o 2 public a-C - 54acac6f23ab
712 |/
712 |/
713 o 1 public a-B - 548a3d25dbf0
713 o 1 public a-B - 548a3d25dbf0
714 |
714 |
715 o 0 public a-A - 054250a37db4
715 o 0 public a-A - 054250a37db4
716
716
717
717
718 Pushing to Publish=False (common changeset that change phase + unknown one)
718 Pushing to Publish=False (common changeset that change phase + unknown one)
719
719
720 $ hg push ../mu -r 967b449fbc94 -f
720 $ hg push ../mu -r 967b449fbc94 -f
721 pushing to ../mu
721 pushing to ../mu
722 searching for changes
722 searching for changes
723 adding changesets
723 adding changesets
724 adding manifests
724 adding manifests
725 adding file changes
725 adding file changes
726 added 1 changesets with 1 changes to 1 files (+1 heads)
726 added 1 changesets with 1 changes to 1 files (+1 heads)
727 test-debug-phase: move rev 2: 1 -> 0
727 test-debug-phase: move rev 2: 1 -> 0
728 test-debug-phase: move rev 5: 1 -> 0
728 test-debug-phase: move rev 5: 1 -> 0
729 test-debug-phase: new rev 9: x -> 1
729 test-debug-phase: new rev 9: x -> 1
730 $ hgph
730 $ hgph
731 @ 10 draft a-H - 967b449fbc94
731 @ 10 draft a-H - 967b449fbc94
732 |
732 |
733 | o 9 draft a-G - 3e27b6f1eee1
733 | o 9 draft a-G - 3e27b6f1eee1
734 | |
734 | |
735 | o 8 public a-F - b740e3e5c05d
735 | o 8 public a-F - b740e3e5c05d
736 | |
736 | |
737 | o 7 public a-E - e9f537e46dea
737 | o 7 public a-E - e9f537e46dea
738 | |
738 | |
739 +---o 6 public n-B - 145e75495359
739 +---o 6 public n-B - 145e75495359
740 | |
740 | |
741 o | 5 public n-A - d6bcb4f74035
741 o | 5 public n-A - d6bcb4f74035
742 | |
742 | |
743 o | 4 public b-A - f54f1bb90ff3
743 o | 4 public b-A - f54f1bb90ff3
744 | |
744 | |
745 | o 3 public a-D - b555f63b6063
745 | o 3 public a-D - b555f63b6063
746 | |
746 | |
747 | o 2 public a-C - 54acac6f23ab
747 | o 2 public a-C - 54acac6f23ab
748 |/
748 |/
749 o 1 public a-B - 548a3d25dbf0
749 o 1 public a-B - 548a3d25dbf0
750 |
750 |
751 o 0 public a-A - 054250a37db4
751 o 0 public a-A - 054250a37db4
752
752
753 $ cd ../mu
753 $ cd ../mu
754 $ hgph # d6bcb4f74035 should have changed phase
754 $ hgph # d6bcb4f74035 should have changed phase
755 > # 145e75495359 is still draft. not ancestor of -r
755 > # 145e75495359 is still draft. not ancestor of -r
756 o 9 draft a-H - 967b449fbc94
756 o 9 draft a-H - 967b449fbc94
757 |
757 |
758 | o 8 public a-F - b740e3e5c05d
758 | o 8 public a-F - b740e3e5c05d
759 | |
759 | |
760 | o 7 public a-E - e9f537e46dea
760 | o 7 public a-E - e9f537e46dea
761 | |
761 | |
762 +---o 6 draft n-B - 145e75495359
762 +---o 6 draft n-B - 145e75495359
763 | |
763 | |
764 o | 5 public n-A - d6bcb4f74035
764 o | 5 public n-A - d6bcb4f74035
765 | |
765 | |
766 | o 4 public a-D - b555f63b6063
766 | o 4 public a-D - b555f63b6063
767 | |
767 | |
768 | o 3 public a-C - 54acac6f23ab
768 | o 3 public a-C - 54acac6f23ab
769 | |
769 | |
770 o | 2 public b-A - f54f1bb90ff3
770 o | 2 public b-A - f54f1bb90ff3
771 |/
771 |/
772 o 1 public a-B - 548a3d25dbf0
772 o 1 public a-B - 548a3d25dbf0
773 |
773 |
774 o 0 public a-A - 054250a37db4
774 o 0 public a-A - 054250a37db4
775
775
776
776
777
777
778 Pushing to Publish=True (common changeset from publish=False)
778 Pushing to Publish=True (common changeset from publish=False)
779
779
780 (in mu)
780 (in mu)
781 $ hg push ../alpha
781 $ hg push ../alpha
782 pushing to ../alpha
782 pushing to ../alpha
783 searching for changes
783 searching for changes
784 no changes found
784 no changes found
785 test-debug-phase: move rev 10: 1 -> 0
785 test-debug-phase: move rev 10: 1 -> 0
786 test-debug-phase: move rev 6: 1 -> 0
786 test-debug-phase: move rev 6: 1 -> 0
787 test-debug-phase: move rev 9: 1 -> 0
787 test-debug-phase: move rev 9: 1 -> 0
788 [1]
788 [1]
789 $ hgph
789 $ hgph
790 o 9 public a-H - 967b449fbc94
790 o 9 public a-H - 967b449fbc94
791 |
791 |
792 | o 8 public a-F - b740e3e5c05d
792 | o 8 public a-F - b740e3e5c05d
793 | |
793 | |
794 | o 7 public a-E - e9f537e46dea
794 | o 7 public a-E - e9f537e46dea
795 | |
795 | |
796 +---o 6 public n-B - 145e75495359
796 +---o 6 public n-B - 145e75495359
797 | |
797 | |
798 o | 5 public n-A - d6bcb4f74035
798 o | 5 public n-A - d6bcb4f74035
799 | |
799 | |
800 | o 4 public a-D - b555f63b6063
800 | o 4 public a-D - b555f63b6063
801 | |
801 | |
802 | o 3 public a-C - 54acac6f23ab
802 | o 3 public a-C - 54acac6f23ab
803 | |
803 | |
804 o | 2 public b-A - f54f1bb90ff3
804 o | 2 public b-A - f54f1bb90ff3
805 |/
805 |/
806 o 1 public a-B - 548a3d25dbf0
806 o 1 public a-B - 548a3d25dbf0
807 |
807 |
808 o 0 public a-A - 054250a37db4
808 o 0 public a-A - 054250a37db4
809
809
810 $ hgph -R ../alpha # a-H should have been synced to 0
810 $ hgph -R ../alpha # a-H should have been synced to 0
811 @ 10 public a-H - 967b449fbc94
811 @ 10 public a-H - 967b449fbc94
812 |
812 |
813 | o 9 draft a-G - 3e27b6f1eee1
813 | o 9 draft a-G - 3e27b6f1eee1
814 | |
814 | |
815 | o 8 public a-F - b740e3e5c05d
815 | o 8 public a-F - b740e3e5c05d
816 | |
816 | |
817 | o 7 public a-E - e9f537e46dea
817 | o 7 public a-E - e9f537e46dea
818 | |
818 | |
819 +---o 6 public n-B - 145e75495359
819 +---o 6 public n-B - 145e75495359
820 | |
820 | |
821 o | 5 public n-A - d6bcb4f74035
821 o | 5 public n-A - d6bcb4f74035
822 | |
822 | |
823 o | 4 public b-A - f54f1bb90ff3
823 o | 4 public b-A - f54f1bb90ff3
824 | |
824 | |
825 | o 3 public a-D - b555f63b6063
825 | o 3 public a-D - b555f63b6063
826 | |
826 | |
827 | o 2 public a-C - 54acac6f23ab
827 | o 2 public a-C - 54acac6f23ab
828 |/
828 |/
829 o 1 public a-B - 548a3d25dbf0
829 o 1 public a-B - 548a3d25dbf0
830 |
830 |
831 o 0 public a-A - 054250a37db4
831 o 0 public a-A - 054250a37db4
832
832
833
833
834
834
835 Bare push with next changeset and common changeset needing sync (issue3575)
835 Bare push with next changeset and common changeset needing sync (issue3575)
836
836
837 (reset some stat on remote repo to avoid confusing other tests)
837 (reset some stat on remote repo to avoid confusing other tests)
838
838
839 $ hg -R ../alpha --config extensions.strip= strip --no-backup 967b449fbc94
839 $ hg -R ../alpha --config extensions.strip= strip --no-backup 967b449fbc94
840 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
840 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
841 $ hg phase --force --draft b740e3e5c05d 967b449fbc94
841 $ hg phase --force --draft b740e3e5c05d 967b449fbc94
842 test-debug-phase: move rev 8: 0 -> 1
842 test-debug-phase: move rev 8: 0 -> 1
843 test-debug-phase: move rev 9: 0 -> 1
843 test-debug-phase: move rev 9: 0 -> 1
844 $ hg push -fv ../alpha
844 $ hg push -fv ../alpha
845 pushing to ../alpha
845 pushing to ../alpha
846 searching for changes
846 searching for changes
847 1 changesets found
847 1 changesets found
848 uncompressed size of bundle content:
848 uncompressed size of bundle content:
849 178 (changelog)
849 178 (changelog)
850 165 (manifests)
850 165 (manifests)
851 131 a-H
851 131 a-H
852 adding changesets
852 adding changesets
853 adding manifests
853 adding manifests
854 adding file changes
854 adding file changes
855 added 1 changesets with 1 changes to 1 files (+1 heads)
855 added 1 changesets with 1 changes to 1 files (+1 heads)
856 test-debug-phase: new rev 10: x -> 0
856 test-debug-phase: new rev 10: x -> 0
857 test-debug-phase: move rev 8: 1 -> 0
857 test-debug-phase: move rev 8: 1 -> 0
858 test-debug-phase: move rev 9: 1 -> 0
858 test-debug-phase: move rev 9: 1 -> 0
859 $ hgph
859 $ hgph
860 o 9 public a-H - 967b449fbc94
860 o 9 public a-H - 967b449fbc94
861 |
861 |
862 | o 8 public a-F - b740e3e5c05d
862 | o 8 public a-F - b740e3e5c05d
863 | |
863 | |
864 | o 7 public a-E - e9f537e46dea
864 | o 7 public a-E - e9f537e46dea
865 | |
865 | |
866 +---o 6 public n-B - 145e75495359
866 +---o 6 public n-B - 145e75495359
867 | |
867 | |
868 o | 5 public n-A - d6bcb4f74035
868 o | 5 public n-A - d6bcb4f74035
869 | |
869 | |
870 | o 4 public a-D - b555f63b6063
870 | o 4 public a-D - b555f63b6063
871 | |
871 | |
872 | o 3 public a-C - 54acac6f23ab
872 | o 3 public a-C - 54acac6f23ab
873 | |
873 | |
874 o | 2 public b-A - f54f1bb90ff3
874 o | 2 public b-A - f54f1bb90ff3
875 |/
875 |/
876 o 1 public a-B - 548a3d25dbf0
876 o 1 public a-B - 548a3d25dbf0
877 |
877 |
878 o 0 public a-A - 054250a37db4
878 o 0 public a-A - 054250a37db4
879
879
880
880
881 $ hg -R ../alpha update 967b449fbc94 #for latter test consistency
881 $ hg -R ../alpha update 967b449fbc94 #for latter test consistency
882 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
882 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
883 $ hgph -R ../alpha
883 $ hgph -R ../alpha
884 @ 10 public a-H - 967b449fbc94
884 @ 10 public a-H - 967b449fbc94
885 |
885 |
886 | o 9 draft a-G - 3e27b6f1eee1
886 | o 9 draft a-G - 3e27b6f1eee1
887 | |
887 | |
888 | o 8 public a-F - b740e3e5c05d
888 | o 8 public a-F - b740e3e5c05d
889 | |
889 | |
890 | o 7 public a-E - e9f537e46dea
890 | o 7 public a-E - e9f537e46dea
891 | |
891 | |
892 +---o 6 public n-B - 145e75495359
892 +---o 6 public n-B - 145e75495359
893 | |
893 | |
894 o | 5 public n-A - d6bcb4f74035
894 o | 5 public n-A - d6bcb4f74035
895 | |
895 | |
896 o | 4 public b-A - f54f1bb90ff3
896 o | 4 public b-A - f54f1bb90ff3
897 | |
897 | |
898 | o 3 public a-D - b555f63b6063
898 | o 3 public a-D - b555f63b6063
899 | |
899 | |
900 | o 2 public a-C - 54acac6f23ab
900 | o 2 public a-C - 54acac6f23ab
901 |/
901 |/
902 o 1 public a-B - 548a3d25dbf0
902 o 1 public a-B - 548a3d25dbf0
903 |
903 |
904 o 0 public a-A - 054250a37db4
904 o 0 public a-A - 054250a37db4
905
905
906
906
907 Discovery locally secret changeset on a remote repository:
907 Discovery locally secret changeset on a remote repository:
908
908
909 - should make it non-secret
909 - should make it non-secret
910
910
911 $ cd ../alpha
911 $ cd ../alpha
912 $ mkcommit A-secret --config phases.new-commit=2
912 $ mkcommit A-secret --config phases.new-commit=2
913 test-debug-phase: new rev 11: x -> 2
913 test-debug-phase: new rev 11: x -> 2
914 $ hgph
914 $ hgph
915 @ 11 secret A-secret - 435b5d83910c
915 @ 11 secret A-secret - 435b5d83910c
916 |
916 |
917 o 10 public a-H - 967b449fbc94
917 o 10 public a-H - 967b449fbc94
918 |
918 |
919 | o 9 draft a-G - 3e27b6f1eee1
919 | o 9 draft a-G - 3e27b6f1eee1
920 | |
920 | |
921 | o 8 public a-F - b740e3e5c05d
921 | o 8 public a-F - b740e3e5c05d
922 | |
922 | |
923 | o 7 public a-E - e9f537e46dea
923 | o 7 public a-E - e9f537e46dea
924 | |
924 | |
925 +---o 6 public n-B - 145e75495359
925 +---o 6 public n-B - 145e75495359
926 | |
926 | |
927 o | 5 public n-A - d6bcb4f74035
927 o | 5 public n-A - d6bcb4f74035
928 | |
928 | |
929 o | 4 public b-A - f54f1bb90ff3
929 o | 4 public b-A - f54f1bb90ff3
930 | |
930 | |
931 | o 3 public a-D - b555f63b6063
931 | o 3 public a-D - b555f63b6063
932 | |
932 | |
933 | o 2 public a-C - 54acac6f23ab
933 | o 2 public a-C - 54acac6f23ab
934 |/
934 |/
935 o 1 public a-B - 548a3d25dbf0
935 o 1 public a-B - 548a3d25dbf0
936 |
936 |
937 o 0 public a-A - 054250a37db4
937 o 0 public a-A - 054250a37db4
938
938
939 $ hg bundle --base 'parents(.)' -r . ../secret-bundle.hg
939 $ hg bundle --base 'parents(.)' -r . ../secret-bundle.hg
940 1 changesets found
940 1 changesets found
941 $ hg -R ../mu unbundle ../secret-bundle.hg
941 $ hg -R ../mu unbundle ../secret-bundle.hg
942 adding changesets
942 adding changesets
943 adding manifests
943 adding manifests
944 adding file changes
944 adding file changes
945 added 1 changesets with 1 changes to 1 files
945 added 1 changesets with 1 changes to 1 files
946 new changesets 435b5d83910c (1 drafts)
946 new changesets 435b5d83910c (1 drafts)
947 test-debug-phase: new rev 10: x -> 1
947 test-debug-phase: new rev 10: x -> 1
948 (run 'hg update' to get a working copy)
948 (run 'hg update' to get a working copy)
949 $ hgph -R ../mu
949 $ hgph -R ../mu
950 o 10 draft A-secret - 435b5d83910c
950 o 10 draft A-secret - 435b5d83910c
951 |
951 |
952 o 9 public a-H - 967b449fbc94
952 o 9 public a-H - 967b449fbc94
953 |
953 |
954 | o 8 public a-F - b740e3e5c05d
954 | o 8 public a-F - b740e3e5c05d
955 | |
955 | |
956 | o 7 public a-E - e9f537e46dea
956 | o 7 public a-E - e9f537e46dea
957 | |
957 | |
958 +---o 6 public n-B - 145e75495359
958 +---o 6 public n-B - 145e75495359
959 | |
959 | |
960 o | 5 public n-A - d6bcb4f74035
960 o | 5 public n-A - d6bcb4f74035
961 | |
961 | |
962 | o 4 public a-D - b555f63b6063
962 | o 4 public a-D - b555f63b6063
963 | |
963 | |
964 | o 3 public a-C - 54acac6f23ab
964 | o 3 public a-C - 54acac6f23ab
965 | |
965 | |
966 o | 2 public b-A - f54f1bb90ff3
966 o | 2 public b-A - f54f1bb90ff3
967 |/
967 |/
968 o 1 public a-B - 548a3d25dbf0
968 o 1 public a-B - 548a3d25dbf0
969 |
969 |
970 o 0 public a-A - 054250a37db4
970 o 0 public a-A - 054250a37db4
971
971
972 $ hg pull ../mu
972 $ hg pull ../mu
973 pulling from ../mu
973 pulling from ../mu
974 searching for changes
974 searching for changes
975 no changes found
975 no changes found
976 test-debug-phase: move rev 11: 2 -> 1
976 test-debug-phase: move rev 11: 2 -> 1
977 $ hgph
977 $ hgph
978 @ 11 draft A-secret - 435b5d83910c
978 @ 11 draft A-secret - 435b5d83910c
979 |
979 |
980 o 10 public a-H - 967b449fbc94
980 o 10 public a-H - 967b449fbc94
981 |
981 |
982 | o 9 draft a-G - 3e27b6f1eee1
982 | o 9 draft a-G - 3e27b6f1eee1
983 | |
983 | |
984 | o 8 public a-F - b740e3e5c05d
984 | o 8 public a-F - b740e3e5c05d
985 | |
985 | |
986 | o 7 public a-E - e9f537e46dea
986 | o 7 public a-E - e9f537e46dea
987 | |
987 | |
988 +---o 6 public n-B - 145e75495359
988 +---o 6 public n-B - 145e75495359
989 | |
989 | |
990 o | 5 public n-A - d6bcb4f74035
990 o | 5 public n-A - d6bcb4f74035
991 | |
991 | |
992 o | 4 public b-A - f54f1bb90ff3
992 o | 4 public b-A - f54f1bb90ff3
993 | |
993 | |
994 | o 3 public a-D - b555f63b6063
994 | o 3 public a-D - b555f63b6063
995 | |
995 | |
996 | o 2 public a-C - 54acac6f23ab
996 | o 2 public a-C - 54acac6f23ab
997 |/
997 |/
998 o 1 public a-B - 548a3d25dbf0
998 o 1 public a-B - 548a3d25dbf0
999 |
999 |
1000 o 0 public a-A - 054250a37db4
1000 o 0 public a-A - 054250a37db4
1001
1001
1002
1002
1003 pushing a locally public and draft changesets remotely secret should make them
1003 pushing a locally public and draft changesets remotely secret should make them
1004 appear on the remote side.
1004 appear on the remote side.
1005
1005
1006 $ hg -R ../mu phase --secret --force 967b449fbc94
1006 $ hg -R ../mu phase --secret --force 967b449fbc94
1007 test-debug-phase: move rev 9: 0 -> 2
1007 test-debug-phase: move rev 9: 0 -> 2
1008 test-debug-phase: move rev 10: 1 -> 2
1008 test-debug-phase: move rev 10: 1 -> 2
1009 $ hg push -r 435b5d83910c ../mu
1009 $ hg push -r 435b5d83910c ../mu
1010 pushing to ../mu
1010 pushing to ../mu
1011 searching for changes
1011 searching for changes
1012 abort: push creates new remote head 435b5d83910c!
1012 abort: push creates new remote head 435b5d83910c!
1013 (merge or see 'hg help push' for details about pushing new heads)
1013 (merge or see 'hg help push' for details about pushing new heads)
1014 [255]
1014 [255]
1015 $ hg push -fr 435b5d83910c ../mu # because the push will create new visible head
1015 $ hg push -fr 435b5d83910c ../mu # because the push will create new visible head
1016 pushing to ../mu
1016 pushing to ../mu
1017 searching for changes
1017 searching for changes
1018 adding changesets
1018 adding changesets
1019 adding manifests
1019 adding manifests
1020 adding file changes
1020 adding file changes
1021 added 0 changesets with 0 changes to 2 files
1021 added 0 changesets with 0 changes to 2 files
1022 test-debug-phase: move rev 9: 2 -> 0
1022 test-debug-phase: move rev 9: 2 -> 0
1023 test-debug-phase: move rev 10: 2 -> 1
1023 test-debug-phase: move rev 10: 2 -> 1
1024 $ hgph -R ../mu
1024 $ hgph -R ../mu
1025 o 10 draft A-secret - 435b5d83910c
1025 o 10 draft A-secret - 435b5d83910c
1026 |
1026 |
1027 o 9 public a-H - 967b449fbc94
1027 o 9 public a-H - 967b449fbc94
1028 |
1028 |
1029 | o 8 public a-F - b740e3e5c05d
1029 | o 8 public a-F - b740e3e5c05d
1030 | |
1030 | |
1031 | o 7 public a-E - e9f537e46dea
1031 | o 7 public a-E - e9f537e46dea
1032 | |
1032 | |
1033 +---o 6 public n-B - 145e75495359
1033 +---o 6 public n-B - 145e75495359
1034 | |
1034 | |
1035 o | 5 public n-A - d6bcb4f74035
1035 o | 5 public n-A - d6bcb4f74035
1036 | |
1036 | |
1037 | o 4 public a-D - b555f63b6063
1037 | o 4 public a-D - b555f63b6063
1038 | |
1038 | |
1039 | o 3 public a-C - 54acac6f23ab
1039 | o 3 public a-C - 54acac6f23ab
1040 | |
1040 | |
1041 o | 2 public b-A - f54f1bb90ff3
1041 o | 2 public b-A - f54f1bb90ff3
1042 |/
1042 |/
1043 o 1 public a-B - 548a3d25dbf0
1043 o 1 public a-B - 548a3d25dbf0
1044 |
1044 |
1045 o 0 public a-A - 054250a37db4
1045 o 0 public a-A - 054250a37db4
1046
1046
1047
1047
1048 pull new changeset with common draft locally
1048 pull new changeset with common draft locally
1049
1049
1050 $ hg up -q 967b449fbc94 # create a new root for draft
1050 $ hg up -q 967b449fbc94 # create a new root for draft
1051 $ mkcommit 'alpha-more'
1051 $ mkcommit 'alpha-more'
1052 test-debug-phase: new rev 12: x -> 1
1052 test-debug-phase: new rev 12: x -> 1
1053 created new head
1053 created new head
1054 $ hg push -fr . ../mu
1054 $ hg push -fr . ../mu
1055 pushing to ../mu
1055 pushing to ../mu
1056 searching for changes
1056 searching for changes
1057 adding changesets
1057 adding changesets
1058 adding manifests
1058 adding manifests
1059 adding file changes
1059 adding file changes
1060 added 1 changesets with 1 changes to 1 files (+1 heads)
1060 added 1 changesets with 1 changes to 1 files (+1 heads)
1061 test-debug-phase: new rev 11: x -> 1
1061 test-debug-phase: new rev 11: x -> 1
1062 $ cd ../mu
1062 $ cd ../mu
1063 $ hg phase --secret --force 1c5cfd894796
1063 $ hg phase --secret --force 1c5cfd894796
1064 test-debug-phase: move rev 11: 1 -> 2
1064 test-debug-phase: move rev 11: 1 -> 2
1065 $ hg up -q 435b5d83910c
1065 $ hg up -q 435b5d83910c
1066 $ mkcommit 'mu-more'
1066 $ mkcommit 'mu-more'
1067 test-debug-phase: new rev 12: x -> 1
1067 test-debug-phase: new rev 12: x -> 1
1068 $ cd ../alpha
1068 $ cd ../alpha
1069 $ hg pull ../mu
1069 $ hg pull ../mu
1070 pulling from ../mu
1070 pulling from ../mu
1071 searching for changes
1071 searching for changes
1072 adding changesets
1072 adding changesets
1073 adding manifests
1073 adding manifests
1074 adding file changes
1074 adding file changes
1075 added 1 changesets with 1 changes to 1 files
1075 added 1 changesets with 1 changes to 1 files
1076 new changesets 5237fb433fc8 (1 drafts)
1076 new changesets 5237fb433fc8 (1 drafts)
1077 test-debug-phase: new rev 13: x -> 1
1077 test-debug-phase: new rev 13: x -> 1
1078 (run 'hg update' to get a working copy)
1078 (run 'hg update' to get a working copy)
1079 $ hgph
1079 $ hgph
1080 o 13 draft mu-more - 5237fb433fc8
1080 o 13 draft mu-more - 5237fb433fc8
1081 |
1081 |
1082 | @ 12 draft alpha-more - 1c5cfd894796
1082 | @ 12 draft alpha-more - 1c5cfd894796
1083 | |
1083 | |
1084 o | 11 draft A-secret - 435b5d83910c
1084 o | 11 draft A-secret - 435b5d83910c
1085 |/
1085 |/
1086 o 10 public a-H - 967b449fbc94
1086 o 10 public a-H - 967b449fbc94
1087 |
1087 |
1088 | o 9 draft a-G - 3e27b6f1eee1
1088 | o 9 draft a-G - 3e27b6f1eee1
1089 | |
1089 | |
1090 | o 8 public a-F - b740e3e5c05d
1090 | o 8 public a-F - b740e3e5c05d
1091 | |
1091 | |
1092 | o 7 public a-E - e9f537e46dea
1092 | o 7 public a-E - e9f537e46dea
1093 | |
1093 | |
1094 +---o 6 public n-B - 145e75495359
1094 +---o 6 public n-B - 145e75495359
1095 | |
1095 | |
1096 o | 5 public n-A - d6bcb4f74035
1096 o | 5 public n-A - d6bcb4f74035
1097 | |
1097 | |
1098 o | 4 public b-A - f54f1bb90ff3
1098 o | 4 public b-A - f54f1bb90ff3
1099 | |
1099 | |
1100 | o 3 public a-D - b555f63b6063
1100 | o 3 public a-D - b555f63b6063
1101 | |
1101 | |
1102 | o 2 public a-C - 54acac6f23ab
1102 | o 2 public a-C - 54acac6f23ab
1103 |/
1103 |/
1104 o 1 public a-B - 548a3d25dbf0
1104 o 1 public a-B - 548a3d25dbf0
1105 |
1105 |
1106 o 0 public a-A - 054250a37db4
1106 o 0 public a-A - 054250a37db4
1107
1107
1108
1108
1109 Test that test are properly ignored on remote event when existing locally
1109 Test that test are properly ignored on remote event when existing locally
1110
1110
1111 $ cd ..
1111 $ cd ..
1112 $ hg clone -qU -r b555f63b6063 -r f54f1bb90ff3 beta gamma
1112 $ hg clone -qU -r b555f63b6063 -r f54f1bb90ff3 beta gamma
1113 test-debug-phase: new rev 0: x -> 0
1113 test-debug-phase: new rev 0: x -> 0
1114 test-debug-phase: new rev 1: x -> 0
1114 test-debug-phase: new rev 1: x -> 0
1115 test-debug-phase: new rev 2: x -> 0
1115 test-debug-phase: new rev 2: x -> 0
1116 test-debug-phase: new rev 3: x -> 0
1116 test-debug-phase: new rev 3: x -> 0
1117 test-debug-phase: new rev 4: x -> 0
1117 test-debug-phase: new rev 4: x -> 0
1118
1118
1119 # pathological case are
1119 # pathological case are
1120 #
1120 #
1121 # * secret remotely
1121 # * secret remotely
1122 # * known locally
1122 # * known locally
1123 # * repo have uncommon changeset
1123 # * repo have uncommon changeset
1124
1124
1125 $ hg -R beta phase --secret --force f54f1bb90ff3
1125 $ hg -R beta phase --secret --force f54f1bb90ff3
1126 test-debug-phase: move rev 2: 0 -> 2
1126 test-debug-phase: move rev 2: 0 -> 2
1127 $ hg -R gamma phase --draft --force f54f1bb90ff3
1127 $ hg -R gamma phase --draft --force f54f1bb90ff3
1128 test-debug-phase: move rev 2: 0 -> 1
1128 test-debug-phase: move rev 2: 0 -> 1
1129
1129
1130 $ cd gamma
1130 $ cd gamma
1131 $ hg pull ../beta
1131 $ hg pull ../beta
1132 pulling from ../beta
1132 pulling from ../beta
1133 searching for changes
1133 searching for changes
1134 adding changesets
1134 adding changesets
1135 adding manifests
1135 adding manifests
1136 adding file changes
1136 adding file changes
1137 added 2 changesets with 2 changes to 2 files
1137 added 2 changesets with 2 changes to 2 files
1138 new changesets e9f537e46dea:b740e3e5c05d
1138 new changesets e9f537e46dea:b740e3e5c05d
1139 test-debug-phase: new rev 5: x -> 0
1139 test-debug-phase: new rev 5: x -> 0
1140 test-debug-phase: new rev 6: x -> 0
1140 test-debug-phase: new rev 6: x -> 0
1141 (run 'hg update' to get a working copy)
1141 (run 'hg update' to get a working copy)
1142 $ hg phase f54f1bb90ff3
1142 $ hg phase f54f1bb90ff3
1143 2: draft
1143 2: draft
1144
1144
1145 same over the wire
1145 same over the wire
1146
1146
1147 $ cd ../beta
1147 $ cd ../beta
1148 $ hg serve -p $HGPORT -d --pid-file=../beta.pid -E ../beta-error.log
1148 $ hg serve -p $HGPORT -d --pid-file=../beta.pid -E ../beta-error.log
1149 $ cat ../beta.pid >> $DAEMON_PIDS
1149 $ cat ../beta.pid >> $DAEMON_PIDS
1150 $ cd ../gamma
1150 $ cd ../gamma
1151
1151
1152 $ hg pull http://localhost:$HGPORT/ # bundle2+
1152 $ hg pull http://localhost:$HGPORT/ # bundle2+
1153 pulling from http://localhost:$HGPORT/
1153 pulling from http://localhost:$HGPORT/
1154 searching for changes
1154 searching for changes
1155 no changes found
1155 no changes found
1156 $ hg phase f54f1bb90ff3
1156 $ hg phase f54f1bb90ff3
1157 2: draft
1157 2: draft
1158
1158
1159 enforce bundle1
1159 enforce bundle1
1160
1160
1161 $ hg pull http://localhost:$HGPORT/ --config devel.legacy.exchange=bundle1
1161 $ hg pull http://localhost:$HGPORT/ --config devel.legacy.exchange=bundle1
1162 pulling from http://localhost:$HGPORT/
1162 pulling from http://localhost:$HGPORT/
1163 searching for changes
1163 searching for changes
1164 no changes found
1164 no changes found
1165 $ hg phase f54f1bb90ff3
1165 $ hg phase f54f1bb90ff3
1166 2: draft
1166 2: draft
1167
1167
1168 check that secret local on both side are not synced to public
1168 check that secret local on both side are not synced to public
1169
1169
1170 $ hg push -r b555f63b6063 http://localhost:$HGPORT/
1170 $ hg push -r b555f63b6063 http://localhost:$HGPORT/
1171 pushing to http://localhost:$HGPORT/
1171 pushing to http://localhost:$HGPORT/
1172 searching for changes
1172 searching for changes
1173 no changes found
1173 no changes found
1174 [1]
1174 [1]
1175 $ hg phase f54f1bb90ff3
1175 $ hg phase f54f1bb90ff3
1176 2: draft
1176 2: draft
1177
1177
1178 $ killdaemons.py
1178 $ killdaemons.py
1179
1179
1180 put the changeset in the draft state again
1180 put the changeset in the draft state again
1181 (first test after this one expect to be able to copy)
1181 (first test after this one expect to be able to copy)
1182
1182
1183 $ cd ..
1183 $ cd ..
1184
1184
1185
1185
1186 Test Clone behavior
1186 Test Clone behavior
1187
1187
1188 A. Clone without secret changeset
1188 A. Clone without secret changeset
1189
1189
1190 1. cloning non-publishing repository
1190 1. cloning non-publishing repository
1191 (Phase should be preserved)
1191 (Phase should be preserved)
1192
1192
1193 # make sure there is no secret so we can use a copy clone
1193 # make sure there is no secret so we can use a copy clone
1194
1194
1195 $ hg -R mu phase --draft 'secret()'
1195 $ hg -R mu phase --draft 'secret()'
1196 test-debug-phase: move rev 11: 2 -> 1
1196 test-debug-phase: move rev 11: 2 -> 1
1197
1197
1198 $ hg clone -U mu Tau
1198 $ hg clone -U mu Tau
1199 $ hgph -R Tau
1199 $ hgph -R Tau
1200 o 12 draft mu-more - 5237fb433fc8
1200 o 12 draft mu-more - 5237fb433fc8
1201 |
1201 |
1202 | o 11 draft alpha-more - 1c5cfd894796
1202 | o 11 draft alpha-more - 1c5cfd894796
1203 | |
1203 | |
1204 o | 10 draft A-secret - 435b5d83910c
1204 o | 10 draft A-secret - 435b5d83910c
1205 |/
1205 |/
1206 o 9 public a-H - 967b449fbc94
1206 o 9 public a-H - 967b449fbc94
1207 |
1207 |
1208 | o 8 public a-F - b740e3e5c05d
1208 | o 8 public a-F - b740e3e5c05d
1209 | |
1209 | |
1210 | o 7 public a-E - e9f537e46dea
1210 | o 7 public a-E - e9f537e46dea
1211 | |
1211 | |
1212 +---o 6 public n-B - 145e75495359
1212 +---o 6 public n-B - 145e75495359
1213 | |
1213 | |
1214 o | 5 public n-A - d6bcb4f74035
1214 o | 5 public n-A - d6bcb4f74035
1215 | |
1215 | |
1216 | o 4 public a-D - b555f63b6063
1216 | o 4 public a-D - b555f63b6063
1217 | |
1217 | |
1218 | o 3 public a-C - 54acac6f23ab
1218 | o 3 public a-C - 54acac6f23ab
1219 | |
1219 | |
1220 o | 2 public b-A - f54f1bb90ff3
1220 o | 2 public b-A - f54f1bb90ff3
1221 |/
1221 |/
1222 o 1 public a-B - 548a3d25dbf0
1222 o 1 public a-B - 548a3d25dbf0
1223 |
1223 |
1224 o 0 public a-A - 054250a37db4
1224 o 0 public a-A - 054250a37db4
1225
1225
1226
1226
1227 2. cloning publishing repository
1227 2. cloning publishing repository
1228
1228
1229 (everything should be public)
1229 (everything should be public)
1230
1230
1231 $ hg clone -U alpha Upsilon
1231 $ hg clone -U alpha Upsilon
1232 $ hgph -R Upsilon
1232 $ hgph -R Upsilon
1233 o 13 public mu-more - 5237fb433fc8
1233 o 13 public mu-more - 5237fb433fc8
1234 |
1234 |
1235 | o 12 public alpha-more - 1c5cfd894796
1235 | o 12 public alpha-more - 1c5cfd894796
1236 | |
1236 | |
1237 o | 11 public A-secret - 435b5d83910c
1237 o | 11 public A-secret - 435b5d83910c
1238 |/
1238 |/
1239 o 10 public a-H - 967b449fbc94
1239 o 10 public a-H - 967b449fbc94
1240 |
1240 |
1241 | o 9 public a-G - 3e27b6f1eee1
1241 | o 9 public a-G - 3e27b6f1eee1
1242 | |
1242 | |
1243 | o 8 public a-F - b740e3e5c05d
1243 | o 8 public a-F - b740e3e5c05d
1244 | |
1244 | |
1245 | o 7 public a-E - e9f537e46dea
1245 | o 7 public a-E - e9f537e46dea
1246 | |
1246 | |
1247 +---o 6 public n-B - 145e75495359
1247 +---o 6 public n-B - 145e75495359
1248 | |
1248 | |
1249 o | 5 public n-A - d6bcb4f74035
1249 o | 5 public n-A - d6bcb4f74035
1250 | |
1250 | |
1251 o | 4 public b-A - f54f1bb90ff3
1251 o | 4 public b-A - f54f1bb90ff3
1252 | |
1252 | |
1253 | o 3 public a-D - b555f63b6063
1253 | o 3 public a-D - b555f63b6063
1254 | |
1254 | |
1255 | o 2 public a-C - 54acac6f23ab
1255 | o 2 public a-C - 54acac6f23ab
1256 |/
1256 |/
1257 o 1 public a-B - 548a3d25dbf0
1257 o 1 public a-B - 548a3d25dbf0
1258 |
1258 |
1259 o 0 public a-A - 054250a37db4
1259 o 0 public a-A - 054250a37db4
1260
1260
1261 #if unix-permissions no-root
1261 #if unix-permissions no-root
1262
1262
1263 Pushing From an unlockable repo
1263 Pushing From an unlockable repo
1264 --------------------------------
1264 --------------------------------
1265 (issue3684)
1265 (issue3684)
1266
1266
1267 Unability to lock the source repo should not prevent the push. It will prevent
1267 Unability to lock the source repo should not prevent the push. It will prevent
1268 the retrieval of remote phase during push. For example, pushing to a publishing
1268 the retrieval of remote phase during push. For example, pushing to a publishing
1269 server won't turn changeset public.
1269 server won't turn changeset public.
1270
1270
1271 1. Test that push is not prevented
1271 1. Test that push is not prevented
1272
1272
1273 $ hg init Phi
1273 $ hg init Phi
1274 $ cd Upsilon
1274 $ cd Upsilon
1275 $ chmod -R -w .hg
1275 $ chmod -R -w .hg
1276 $ hg push ../Phi
1276 $ hg push ../Phi
1277 pushing to ../Phi
1277 pushing to ../Phi
1278 searching for changes
1278 searching for changes
1279 adding changesets
1279 adding changesets
1280 adding manifests
1280 adding manifests
1281 adding file changes
1281 adding file changes
1282 added 14 changesets with 14 changes to 14 files (+3 heads)
1282 added 14 changesets with 14 changes to 14 files (+3 heads)
1283 test-debug-phase: new rev 0: x -> 0
1283 test-debug-phase: new rev 0: x -> 0
1284 test-debug-phase: new rev 1: x -> 0
1284 test-debug-phase: new rev 1: x -> 0
1285 test-debug-phase: new rev 2: x -> 0
1285 test-debug-phase: new rev 2: x -> 0
1286 test-debug-phase: new rev 3: x -> 0
1286 test-debug-phase: new rev 3: x -> 0
1287 test-debug-phase: new rev 4: x -> 0
1287 test-debug-phase: new rev 4: x -> 0
1288 test-debug-phase: new rev 5: x -> 0
1288 test-debug-phase: new rev 5: x -> 0
1289 test-debug-phase: new rev 6: x -> 0
1289 test-debug-phase: new rev 6: x -> 0
1290 test-debug-phase: new rev 7: x -> 0
1290 test-debug-phase: new rev 7: x -> 0
1291 test-debug-phase: new rev 8: x -> 0
1291 test-debug-phase: new rev 8: x -> 0
1292 test-debug-phase: new rev 9: x -> 0
1292 test-debug-phase: new rev 9: x -> 0
1293 test-debug-phase: new rev 10: x -> 0
1293 test-debug-phase: new rev 10: x -> 0
1294 test-debug-phase: new rev 11: x -> 0
1294 test-debug-phase: new rev 11: x -> 0
1295 test-debug-phase: new rev 12: x -> 0
1295 test-debug-phase: new rev 12: x -> 0
1296 test-debug-phase: new rev 13: x -> 0
1296 test-debug-phase: new rev 13: x -> 0
1297 $ chmod -R +w .hg
1297 $ chmod -R +w .hg
1298
1298
1299 2. Test that failed phases movement are reported
1299 2. Test that failed phases movement are reported
1300
1300
1301 $ hg phase --force --draft 3
1301 $ hg phase --force --draft 3
1302 test-debug-phase: move rev 3: 0 -> 1
1302 test-debug-phase: move rev 3: 0 -> 1
1303 test-debug-phase: move rev 7: 0 -> 1
1303 test-debug-phase: move rev 7: 0 -> 1
1304 test-debug-phase: move rev 8: 0 -> 1
1304 test-debug-phase: move rev 8: 0 -> 1
1305 test-debug-phase: move rev 9: 0 -> 1
1305 test-debug-phase: move rev 9: 0 -> 1
1306 $ chmod -R -w .hg
1306 $ chmod -R -w .hg
1307 $ hg push ../Phi
1307 $ hg push ../Phi
1308 pushing to ../Phi
1308 pushing to ../Phi
1309 searching for changes
1309 searching for changes
1310 no changes found
1310 no changes found
1311 cannot lock source repo, skipping local public phase update
1311 cannot lock source repo, skipping local public phase update
1312 [1]
1312 [1]
1313 $ chmod -R +w .hg
1313 $ chmod -R +w .hg
1314
1314
1315 3. Test that push is prevented if lock was already acquired (not a permission
1315 3. Test that push is prevented if lock was already acquired (not a permission
1316 error, but EEXIST)
1316 error, but EEXIST)
1317
1317
1318 $ touch .hg/store/lock
1318 $ touch .hg/store/lock
1319 $ hg push ../Phi --config ui.timeout=1
1319 $ hg push ../Phi --config ui.timeout=1
1320 pushing to ../Phi
1320 pushing to ../Phi
1321 waiting for lock on repository $TESTTMP/Upsilon held by ''
1321 waiting for lock on repository $TESTTMP/Upsilon held by ''
1322 abort: repository $TESTTMP/Upsilon: timed out waiting for lock held by ''
1322 abort: repository $TESTTMP/Upsilon: timed out waiting for lock held by ''
1323 (lock might be very busy)
1323 (lock might be very busy)
1324 [255]
1324 [255]
1325 $ rm .hg/store/lock
1325 $ rm .hg/store/lock
1326
1326
1327 $ cd ..
1327 $ cd ..
1328
1328
1329 #endif
1329 #endif
1330
1330
1331 Test that clone behaves like pull and doesn't publish changesets as plain push
1331 Test that clone behaves like pull and doesn't publish changesets as plain push
1332 does. The conditional output accounts for changes in the conditional block
1332 does. The conditional output accounts for changes in the conditional block
1333 above.
1333 above.
1334
1334
1335 #if unix-permissions no-root
1335 #if unix-permissions no-root
1336 $ hg -R Upsilon phase -q --force --draft 2
1336 $ hg -R Upsilon phase -q --force --draft 2
1337 test-debug-phase: move rev 2: 0 -> 1
1337 test-debug-phase: move rev 2: 0 -> 1
1338 #else
1338 #else
1339 $ hg -R Upsilon phase -q --force --draft 2
1339 $ hg -R Upsilon phase -q --force --draft 2
1340 test-debug-phase: move rev 2: 0 -> 1
1340 test-debug-phase: move rev 2: 0 -> 1
1341 test-debug-phase: move rev 3: 0 -> 1
1341 test-debug-phase: move rev 3: 0 -> 1
1342 test-debug-phase: move rev 7: 0 -> 1
1342 test-debug-phase: move rev 7: 0 -> 1
1343 test-debug-phase: move rev 8: 0 -> 1
1343 test-debug-phase: move rev 8: 0 -> 1
1344 test-debug-phase: move rev 9: 0 -> 1
1344 test-debug-phase: move rev 9: 0 -> 1
1345 #endif
1345 #endif
1346
1346
1347 $ hg clone -q Upsilon Pi -r 7
1347 $ hg clone -q Upsilon Pi -r 7
1348 test-debug-phase: new rev 0: x -> 0
1348 test-debug-phase: new rev 0: x -> 0
1349 test-debug-phase: new rev 1: x -> 0
1349 test-debug-phase: new rev 1: x -> 0
1350 test-debug-phase: new rev 2: x -> 0
1350 test-debug-phase: new rev 2: x -> 0
1351 test-debug-phase: new rev 3: x -> 0
1351 test-debug-phase: new rev 3: x -> 0
1352 test-debug-phase: new rev 4: x -> 0
1352 test-debug-phase: new rev 4: x -> 0
1353 $ hgph Upsilon -r 'min(draft())'
1353 $ hgph Upsilon -r 'min(draft())'
1354 o 2 draft a-C - 54acac6f23ab
1354 o 2 draft a-C - 54acac6f23ab
1355 |
1355 |
1356 ~
1356 ~
1357
1357
1358 $ hg -R Upsilon push Pi -r 7
1358 $ hg -R Upsilon push Pi -r 7
1359 pushing to Pi
1359 pushing to Pi
1360 searching for changes
1360 searching for changes
1361 no changes found
1361 no changes found
1362 test-debug-phase: move rev 2: 1 -> 0
1362 test-debug-phase: move rev 2: 1 -> 0
1363 test-debug-phase: move rev 3: 1 -> 0
1363 test-debug-phase: move rev 3: 1 -> 0
1364 test-debug-phase: move rev 7: 1 -> 0
1364 test-debug-phase: move rev 7: 1 -> 0
1365 [1]
1365 [1]
1366 $ hgph Upsilon -r 'min(draft())'
1366 $ hgph Upsilon -r 'min(draft())'
1367 o 8 draft a-F - b740e3e5c05d
1367 o 8 draft a-F - b740e3e5c05d
1368 |
1368 |
1369 ~
1369 ~
1370
1370
1371 $ hg -R Upsilon push Pi -r 8
1371 $ hg -R Upsilon push Pi -r 8
1372 pushing to Pi
1372 pushing to Pi
1373 searching for changes
1373 searching for changes
1374 adding changesets
1374 adding changesets
1375 adding manifests
1375 adding manifests
1376 adding file changes
1376 adding file changes
1377 added 1 changesets with 1 changes to 1 files
1377 added 1 changesets with 1 changes to 1 files
1378 test-debug-phase: new rev 5: x -> 0
1378 test-debug-phase: new rev 5: x -> 0
1379 test-debug-phase: move rev 8: 1 -> 0
1379 test-debug-phase: move rev 8: 1 -> 0
1380
1380
1381 $ hgph Upsilon -r 'min(draft())'
1381 $ hgph Upsilon -r 'min(draft())'
1382 o 9 draft a-G - 3e27b6f1eee1
1382 o 9 draft a-G - 3e27b6f1eee1
1383 |
1383 |
1384 ~
1384 ~
1385
1385
1386 Test phases exchange when a phaseroot is on a merge
1386 Test phases exchange when a phaseroot is on a merge
1387
1387
1388 $ hg init mergetest
1388 $ hg init mergetest
1389 $ cd mergetest
1389 $ cd mergetest
1390 > cat > .hg/hgrc << EOF
1390 > cat > .hg/hgrc << EOF
1391 > [phases]
1391 > [phases]
1392 > publish = false
1392 > publish = false
1393 > EOF
1393 > EOF
1394
1394
1395 $ hg debugdrawdag << EOF
1395 $ hg debugdrawdag << EOF
1396 > E Z
1396 > E Z
1397 > |\|
1397 > |\|
1398 > D Y
1398 > D Y
1399 > | |
1399 > | |
1400 > C X
1400 > C X
1401 > |/
1401 > |/
1402 > B
1402 > B
1403 > |
1403 > |
1404 > A
1404 > A
1405 > EOF
1405 > EOF
1406 test-debug-phase: new rev 0: x -> 1
1406 test-debug-phase: new rev 0: x -> 1
1407 test-debug-phase: new rev 1: x -> 1
1407 test-debug-phase: new rev 1: x -> 1
1408 test-debug-phase: new rev 2: x -> 1
1408 test-debug-phase: new rev 2: x -> 1
1409 test-debug-phase: new rev 3: x -> 1
1409 test-debug-phase: new rev 3: x -> 1
1410 test-debug-phase: new rev 4: x -> 1
1410 test-debug-phase: new rev 4: x -> 1
1411 test-debug-phase: new rev 5: x -> 1
1411 test-debug-phase: new rev 5: x -> 1
1412 test-debug-phase: new rev 6: x -> 1
1412 test-debug-phase: new rev 6: x -> 1
1413 test-debug-phase: new rev 7: x -> 1
1413 test-debug-phase: new rev 7: x -> 1
1414
1414
1415 $ hg phase --public -r D
1415 $ hg phase --public -r D
1416 test-debug-phase: move rev 0: 1 -> 0
1416 test-debug-phase: move rev 0: 1 -> 0
1417 test-debug-phase: move rev 1: 1 -> 0
1417 test-debug-phase: move rev 1: 1 -> 0
1418 test-debug-phase: move rev 2: 1 -> 0
1418 test-debug-phase: move rev 2: 1 -> 0
1419 test-debug-phase: move rev 4: 1 -> 0
1419 test-debug-phase: move rev 4: 1 -> 0
1420
1420
1421 $ hg log -G -T '{shortest(node, 5)} {phase}'
1421 $ hg log -G -T '{shortest(node, 5)} {phase}'
1422 o bb947 draft
1422 o bb947 draft
1423 |
1423 |
1424 | o 5ac28 draft
1424 | o 5ac28 draft
1425 |/|
1425 |/|
1426 o | 13b7b draft
1426 o | 13b7b draft
1427 | |
1427 | |
1428 | o f5853 public
1428 | o f5853 public
1429 | |
1429 | |
1430 o | c67c4 draft
1430 o | c67c4 draft
1431 | |
1431 | |
1432 | o 26805 public
1432 | o 26805 public
1433 |/
1433 |/
1434 o 11247 public
1434 o 11247 public
1435 |
1435 |
1436 o 426ba public
1436 o 426ba public
1437
1437
1438 $ cd ..
1438 $ cd ..
1439
1439
1440 Works with default settings
1440 Works with default settings
1441
1441
1442 $ hg -R mergetest serve -p $HGPORT -d --pid-file=hg.pid
1442 $ hg -R mergetest serve -p $HGPORT -d --pid-file=hg.pid
1443 $ cat hg.pid >> $DAEMON_PIDS
1443 $ cat hg.pid >> $DAEMON_PIDS
1444
1444
1445 $ hg clone -U http://localhost:$HGPORT mergetest-normal
1445 $ hg clone -U http://localhost:$HGPORT mergetest-normal
1446 requesting all changes
1446 requesting all changes
1447 adding changesets
1447 adding changesets
1448 adding manifests
1448 adding manifests
1449 adding file changes
1449 adding file changes
1450 added 8 changesets with 7 changes to 7 files (+1 heads)
1450 added 8 changesets with 7 changes to 7 files (+1 heads)
1451 new changesets 426bada5c675:bb94757e651a (4 drafts)
1451 new changesets 426bada5c675:bb94757e651a (4 drafts)
1452 test-debug-phase: new rev 0: x -> 0
1452 test-debug-phase: new rev 0: x -> 0
1453 test-debug-phase: new rev 1: x -> 0
1453 test-debug-phase: new rev 1: x -> 0
1454 test-debug-phase: new rev 2: x -> 0
1454 test-debug-phase: new rev 2: x -> 0
1455 test-debug-phase: new rev 3: x -> 1
1455 test-debug-phase: new rev 3: x -> 1
1456 test-debug-phase: new rev 4: x -> 0
1456 test-debug-phase: new rev 4: x -> 0
1457 test-debug-phase: new rev 5: x -> 1
1457 test-debug-phase: new rev 5: x -> 1
1458 test-debug-phase: new rev 6: x -> 1
1458 test-debug-phase: new rev 6: x -> 1
1459 test-debug-phase: new rev 7: x -> 1
1459 test-debug-phase: new rev 7: x -> 1
1460
1460
1461 $ hg -R mergetest-normal log -G -T '{shortest(node, 5)} {phase}'
1461 $ hg -R mergetest-normal log -G -T '{shortest(node, 5)} {phase}'
1462 o bb947 draft
1462 o bb947 draft
1463 |
1463 |
1464 | o 5ac28 draft
1464 | o 5ac28 draft
1465 |/|
1465 |/|
1466 o | 13b7b draft
1466 o | 13b7b draft
1467 | |
1467 | |
1468 | o f5853 public
1468 | o f5853 public
1469 | |
1469 | |
1470 o | c67c4 draft
1470 o | c67c4 draft
1471 | |
1471 | |
1472 | o 26805 public
1472 | o 26805 public
1473 |/
1473 |/
1474 o 11247 public
1474 o 11247 public
1475 |
1475 |
1476 o 426ba public
1476 o 426ba public
1477
1477
1478 $ killdaemons.py
1478 $ killdaemons.py
1479
1479
1480 With legacy listkeys over bundle2
1480 With legacy listkeys over bundle2
1481 (issue 5939: public phase was lost on 26805 and f5853 before, due to a bug
1481 (issue 5939: public phase was lost on 26805 and f5853 before, due to a bug
1482 of phase heads computation)
1482 of phase heads computation)
1483
1483
1484 $ hg -R mergetest --config devel.legacy.exchange=phases serve -p $HGPORT -d --pid-file=hg.pid
1484 $ hg -R mergetest --config devel.legacy.exchange=phases serve -p $HGPORT -d --pid-file=hg.pid
1485 $ cat hg.pid >> $DAEMON_PIDS
1485 $ cat hg.pid >> $DAEMON_PIDS
1486
1486
1487 $ hg clone -U http://localhost:$HGPORT mergetest-nobinarypart
1487 $ hg clone -U http://localhost:$HGPORT mergetest-nobinarypart
1488 requesting all changes
1488 requesting all changes
1489 adding changesets
1489 adding changesets
1490 adding manifests
1490 adding manifests
1491 adding file changes
1491 adding file changes
1492 added 8 changesets with 7 changes to 7 files (+1 heads)
1492 added 8 changesets with 7 changes to 7 files (+1 heads)
1493 new changesets 426bada5c675:bb94757e651a (4 drafts)
1493 new changesets 426bada5c675:bb94757e651a (4 drafts)
1494 test-debug-phase: new rev 0: x -> 0
1494 test-debug-phase: new rev 0: x -> 0
1495 test-debug-phase: new rev 1: x -> 0
1495 test-debug-phase: new rev 1: x -> 0
1496 test-debug-phase: new rev 2: x -> 0
1496 test-debug-phase: new rev 2: x -> 0
1497 test-debug-phase: new rev 3: x -> 1
1497 test-debug-phase: new rev 3: x -> 1
1498 test-debug-phase: new rev 4: x -> 0
1498 test-debug-phase: new rev 4: x -> 0
1499 test-debug-phase: new rev 5: x -> 1
1499 test-debug-phase: new rev 5: x -> 1
1500 test-debug-phase: new rev 6: x -> 1
1500 test-debug-phase: new rev 6: x -> 1
1501 test-debug-phase: new rev 7: x -> 1
1501 test-debug-phase: new rev 7: x -> 1
1502
1502
1503 $ hg -R mergetest-nobinarypart log -G -T '{shortest(node, 5)} {phase}'
1503 $ hg -R mergetest-nobinarypart log -G -T '{shortest(node, 5)} {phase}'
1504 o bb947 draft
1504 o bb947 draft
1505 |
1505 |
1506 | o 5ac28 draft
1506 | o 5ac28 draft
1507 |/|
1507 |/|
1508 o | 13b7b draft
1508 o | 13b7b draft
1509 | |
1509 | |
1510 | o f5853 public
1510 | o f5853 public
1511 | |
1511 | |
1512 o | c67c4 draft
1512 o | c67c4 draft
1513 | |
1513 | |
1514 | o 26805 public
1514 | o 26805 public
1515 |/
1515 |/
1516 o 11247 public
1516 o 11247 public
1517 |
1517 |
1518 o 426ba public
1518 o 426ba public
1519
1519
1520 $ killdaemons.py
1520 $ killdaemons.py
1521
1521
1522 Without bundle2
1522 Without bundle2
1523 (issue 5939: public phase was lost on 26805 and f5853 before, due to a bug
1523 (issue 5939: public phase was lost on 26805 and f5853 before, due to a bug
1524 of phase heads computation)
1524 of phase heads computation)
1525
1525
1526 $ hg -R mergetest serve -p $HGPORT -d --pid-file=hg.pid
1526 $ hg -R mergetest serve -p $HGPORT -d --pid-file=hg.pid
1527 $ cat hg.pid >> $DAEMON_PIDS
1527 $ cat hg.pid >> $DAEMON_PIDS
1528
1528
1529 $ hg --config devel.legacy.exchange=bundle1 clone -U http://localhost:$HGPORT mergetest-bundle1
1529 $ hg --config devel.legacy.exchange=bundle1 clone -U http://localhost:$HGPORT mergetest-bundle1
1530 requesting all changes
1530 requesting all changes
1531 adding changesets
1531 adding changesets
1532 adding manifests
1532 adding manifests
1533 adding file changes
1533 adding file changes
1534 added 8 changesets with 7 changes to 7 files (+1 heads)
1534 added 8 changesets with 7 changes to 7 files (+1 heads)
1535 new changesets 426bada5c675:bb94757e651a (4 drafts)
1535 new changesets 426bada5c675:bb94757e651a (4 drafts)
1536 test-debug-phase: new rev 0: x -> 0
1536 test-debug-phase: new rev 0: x -> 0
1537 test-debug-phase: new rev 1: x -> 0
1537 test-debug-phase: new rev 1: x -> 0
1538 test-debug-phase: new rev 2: x -> 0
1538 test-debug-phase: new rev 2: x -> 0
1539 test-debug-phase: new rev 3: x -> 1
1539 test-debug-phase: new rev 3: x -> 1
1540 test-debug-phase: new rev 4: x -> 0
1540 test-debug-phase: new rev 4: x -> 0
1541 test-debug-phase: new rev 5: x -> 1
1541 test-debug-phase: new rev 5: x -> 1
1542 test-debug-phase: new rev 6: x -> 1
1542 test-debug-phase: new rev 6: x -> 1
1543 test-debug-phase: new rev 7: x -> 1
1543 test-debug-phase: new rev 7: x -> 1
1544
1544
1545 $ hg -R mergetest-bundle1 log -G -T '{shortest(node, 5)} {phase}'
1545 $ hg -R mergetest-bundle1 log -G -T '{shortest(node, 5)} {phase}'
1546 o bb947 draft
1546 o bb947 draft
1547 |
1547 |
1548 | o 5ac28 draft
1548 | o 5ac28 draft
1549 |/|
1549 |/|
1550 o | 13b7b draft
1550 o | 13b7b draft
1551 | |
1551 | |
1552 | o f5853 public
1552 | o f5853 public
1553 | |
1553 | |
1554 o | c67c4 draft
1554 o | c67c4 draft
1555 | |
1555 | |
1556 | o 26805 public
1556 | o 26805 public
1557 |/
1557 |/
1558 o 11247 public
1558 o 11247 public
1559 |
1559 |
1560 o 426ba public
1560 o 426ba public
1561
1561
1562 $ killdaemons.py
1563
1564
1565 --publish flag
1566 --------------
1567
1568 $ hg init doesnt-publish
1569 $ cd doesnt-publish
1570 $ cat > .hg/hgrc << EOF
1571 > [phases]
1572 > publish=0
1573 > EOF
1574 $ mkcommit orig-root
1575 test-debug-phase: new rev 0: x -> 1
1576 $ hg phase --public -r 'all()'
1577 test-debug-phase: move rev 0: 1 -> 0
1578 $ cd ..
1579
1580 $ hg clone -q doesnt-publish client
1581 $ cd client
1582
1583 pushing nothing
1584
1585 $ mkcommit new-A
1586 test-debug-phase: new rev 1: x -> 1
1587 $ mkcommit new-B
1588 test-debug-phase: new rev 2: x -> 1
1589 $ hg push --publish -r null
1590 pushing to $TESTTMP/doesnt-publish
1591 searching for changes
1592 no changes found
1593 [1]
1594 $ hgph
1595 @ 2 draft new-B - 89512e87d697
1596 |
1597 o 1 draft new-A - 4826e44e690e
1598 |
1599 o 0 public orig-root - c48edaf99a10
1600
1601
1602 pushing a new changeset (selective)
1603
1604 $ hg push --publish -r 'desc("new-A")'
1605 pushing to $TESTTMP/doesnt-publish
1606 searching for changes
1607 adding changesets
1608 adding manifests
1609 adding file changes
1610 added 1 changesets with 1 changes to 1 files
1611 test-debug-phase: new rev 1: x -> 0
1612 test-debug-phase: move rev 1: 1 -> 0
1613 $ hgph
1614 @ 2 draft new-B - 89512e87d697
1615 |
1616 o 1 public new-A - 4826e44e690e
1617 |
1618 o 0 public orig-root - c48edaf99a10
1619
1620
1621 pushing a new changeset (linear)
1622
1623 $ hg push --publish
1624 pushing to $TESTTMP/doesnt-publish
1625 searching for changes
1626 adding changesets
1627 adding manifests
1628 adding file changes
1629 added 1 changesets with 1 changes to 1 files
1630 test-debug-phase: new rev 2: x -> 0
1631 test-debug-phase: move rev 2: 1 -> 0
1632 $ hgph
1633 @ 2 public new-B - 89512e87d697
1634 |
1635 o 1 public new-A - 4826e44e690e
1636 |
1637 o 0 public orig-root - c48edaf99a10
1638
1639
1640 pushing new changesets (different branches)
1641
1642 $ mkcommit new-C
1643 test-debug-phase: new rev 3: x -> 1
1644 $ hg update -q '.^'
1645 $ hg branch -q another
1646 $ mkcommit new-D
1647 test-debug-phase: new rev 4: x -> 1
1648 $ hg push --new-branch --publish
1649 pushing to $TESTTMP/doesnt-publish
1650 searching for changes
1651 adding changesets
1652 adding manifests
1653 adding file changes
1654 added 2 changesets with 2 changes to 2 files (+1 heads)
1655 test-debug-phase: new rev 3: x -> 0
1656 test-debug-phase: new rev 4: x -> 0
1657 test-debug-phase: move rev 3: 1 -> 0
1658 test-debug-phase: move rev 4: 1 -> 0
1659 $ hgph
1660 @ 4 public new-D - 5e53dcafd13c
1661 |
1662 | o 3 public new-C - 1665482cc06d
1663 |/
1664 o 2 public new-B - 89512e87d697
1665 |
1666 o 1 public new-A - 4826e44e690e
1667 |
1668 o 0 public orig-root - c48edaf99a10
1669
1670
1671 pushing a shared changeset
1672
1673 $ mkcommit new-E
1674 test-debug-phase: new rev 5: x -> 1
1675 $ hg push
1676 pushing to $TESTTMP/doesnt-publish
1677 searching for changes
1678 adding changesets
1679 adding manifests
1680 adding file changes
1681 added 1 changesets with 1 changes to 1 files
1682 test-debug-phase: new rev 5: x -> 1
1683 $ hg push --publish
1684 pushing to $TESTTMP/doesnt-publish
1685 searching for changes
1686 no changes found
1687 test-debug-phase: move rev 5: 1 -> 0
1688 test-debug-phase: move rev 5: 1 -> 0
1689 [1]
1690 $ hgph
1691 @ 5 public new-E - 48931ee3529c
1692 |
1693 o 4 public new-D - 5e53dcafd13c
1694 |
1695 | o 3 public new-C - 1665482cc06d
1696 |/
1697 o 2 public new-B - 89512e87d697
1698 |
1699 o 1 public new-A - 4826e44e690e
1700 |
1701 o 0 public orig-root - c48edaf99a10
1702
1703 $ cd ..
1704
1705 --publish with subrepos (doesn't propagate to subrepos currently)
1706
1707 $ hg init with-subrepo
1708 $ cd with-subrepo
1709 $ cat > .hg/hgrc << EOF
1710 > [phases]
1711 > publish=0
1712 > EOF
1713 $ hg init subrepo
1714 $ cd subrepo
1715 $ cat > .hg/hgrc << EOF
1716 > [phases]
1717 > publish=0
1718 > EOF
1719 $ echo foo > foo
1720 $ hg ci -qAm0
1721 test-debug-phase: new rev 0: x -> 1
1722 $ cd ..
1723 $ echo 'subrepo = subrepo' > .hgsub
1724 $ hg add .hgsub
1725 $ hg ci -m 'Adding subrepo'
1726 test-debug-phase: new rev 0: x -> 1
1727 $ hgph
1728 @ 0 draft Adding subrepo - 74d5b62379c0
1729
1730 $ hgph -R subrepo
1731 @ 0 draft 0 - 4b3f578e3344
1732
1733 $ cd ..
1734 $ hg clone with-subrepo client-with-subrepo
1735 updating to branch default
1736 cloning subrepo subrepo from $TESTTMP/with-subrepo/subrepo
1737 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1738 $ cd client-with-subrepo
1739 $ hg push --publish
1740 pushing to $TESTTMP/with-subrepo
1741 no changes made to subrepo subrepo since last push to $TESTTMP/with-subrepo/subrepo
1742 searching for changes
1743 no changes found
1744 test-debug-phase: move rev 0: 1 -> 0
1745 test-debug-phase: move rev 0: 1 -> 0
1746 [1]
1747 $ hgph
1748 @ 0 public Adding subrepo - 74d5b62379c0
1749
1750 $ hgph -R subrepo
1751 @ 0 draft 0 - 4b3f578e3344
1752
General Comments 0
You need to be logged in to leave comments. Login now